1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package apk
18
19 import (
20 "archive/zip"
21 "bytes"
22 "crypto"
23 "crypto/ecdsa"
24 "crypto/hmac"
25 "crypto/rsa"
26 "crypto/x509"
27 "encoding/binary"
28 "fmt"
29 "io"
30 "os"
31 "strings"
32
33 "github.com/pkg/errors"
34 "github.com/sassoftware/relic/lib/pkcs7"
35 "github.com/sassoftware/relic/lib/pkcs9"
36 "github.com/sassoftware/relic/lib/signjar"
37 "github.com/sassoftware/relic/lib/x509tools"
38 "github.com/sassoftware/relic/lib/zipslicer"
39 "github.com/sassoftware/relic/signers"
40 "github.com/sassoftware/relic/signers/sigerrors"
41 )
42
43 func verify(f *os.File, opts signers.VerifyOpts) ([]*signers.Signature, error) {
44
45 inz, block, err := getSigBlock(f)
46 if err != nil {
47 return nil, err
48 }
49 var allSigs []*signers.Signature
50 for len(block) > 0 {
51 if len(block) < 12 {
52 return nil, errTruncated
53 }
54 partSize := binary.LittleEndian.Uint64(block)
55 block = block[8:]
56 if partSize < 4 || partSize > uint64(len(block)) {
57 return nil, errTruncated
58 }
59 partType := binary.LittleEndian.Uint32(block)
60 partBlob := block[4:partSize]
61 block = block[partSize:]
62 if partType != sigApkV2 {
63 continue
64 }
65 var signerList []apkSigner
66 if err := unmarshal(partBlob, &signerList); err != nil {
67 return nil, errors.Wrap(err, "parsing signature block")
68 } else if len(signerList) == 0 {
69 return nil, errors.New("empty APK signing block")
70 }
71 for i, signer := range signerList {
72 sig, err := signer.Verify(nil)
73 if err != nil {
74 return nil, errors.Wrapf(err, "APK signature #%d", i+1)
75 }
76 allSigs = append(allSigs, sig)
77 }
78 }
79 v2present := len(allSigs) != 0
80
81 inzr, err := zip.NewReader(f, inz.Size)
82 if err != nil {
83 return nil, err
84 }
85 jarSigs, err := signjar.Verify(inzr, false)
86 if err != nil {
87 if _, ok := err.(sigerrors.NotSignedError); !ok {
88 return nil, err
89 }
90 }
91 for _, jarSig := range jarSigs {
92 apk := jarSig.SignatureHeader.Get("X-Android-APK-Signed")
93 if strings.ContainsRune(apk, '2') && !v2present {
94 return nil, errors.New("V1 signature contains X-Android-APK-Signed header but no V2 signature exists")
95 }
96 allSigs = append(allSigs, &signers.Signature{
97 SigInfo: "v1",
98 Hash: jarSig.Hash,
99 X509Signature: &jarSig.TimestampedSignature,
100 })
101 }
102 if len(allSigs) == 0 {
103 return nil, sigerrors.NotSignedError{Type: "APK"}
104 }
105 return allSigs, nil
106 }
107
108 func getSigBlock(f *os.File) (*zipslicer.Directory, []byte, error) {
109 size, err := f.Seek(0, io.SeekEnd)
110 if err != nil {
111 return nil, nil, err
112 }
113 inz, err := zipslicer.Read(f, size)
114 if err != nil {
115 return nil, nil, err
116 }
117
118 if len(inz.File) == 0 {
119 return nil, nil, errors.New("no files in APK")
120 }
121 sigLoc, err := inz.NextFileOffset()
122 if err != nil {
123 return nil, nil, err
124 }
125 if sigLoc == inz.DirLoc {
126
127 return inz, nil, nil
128 }
129
130 blob := make([]byte, inz.DirLoc-sigLoc)
131 if _, err := f.ReadAt(blob, sigLoc); err != nil {
132 return nil, nil, err
133 }
134
135 if !bytes.HasSuffix(blob, []byte(sigMagic)) {
136 return nil, nil, errMalformed
137 }
138 expected := uint64(len(blob) - 8)
139 size1 := binary.LittleEndian.Uint64(blob)
140 size2 := binary.LittleEndian.Uint64(blob[len(blob)-24:])
141 if size1 != expected || size2 != expected {
142 return nil, nil, errMalformed
143 }
144 return inz, blob[8 : len(blob)-24], nil
145 }
146
147 func (s *apkSigner) Verify(inz *zipslicer.Directory) (*signers.Signature, error) {
148 if len(s.Signatures) == 0 {
149 return nil, errors.New("no signatures in APK signer block")
150 }
151
152 publicKey, err := x509.ParsePKIXPublicKey(s.PublicKey)
153 if err != nil {
154 return nil, err
155 }
156 var bestHash crypto.Hash
157 for _, sig := range s.Signatures {
158 hash, err := sig.VerifySignature(publicKey, s.SignedData.Bytes())
159 if err != nil {
160 return nil, err
161 }
162
163 if hash > bestHash {
164 bestHash = hash
165 }
166 }
167
168 var signedData apkSignedData
169 if err := unmarshal(s.SignedData, &signedData); err != nil {
170 return nil, err
171 }
172 if len(signedData.Digests) == 0 {
173 return nil, errors.New("no digests in APK signed data block")
174 }
175 if inz != nil {
176 hashes := make([]crypto.Hash, len(signedData.Digests))
177 for i, digest := range signedData.Digests {
178 st, err := sigTypeByID(digest.ID)
179 if err != nil {
180 return nil, err
181 }
182 hashes[i] = st.hash
183 }
184 hasher := newMerkleHasher(hashes)
185 for _, f := range inz.File {
186 if _, err := f.Dump(hasher); err != nil {
187 return nil, err
188 }
189 }
190 digests, err := hasher.Finish(inz, false)
191 if err != nil {
192 return nil, err
193 }
194 for i, digest := range signedData.Digests {
195 if !hmac.Equal(digest.Value, digests[i]) {
196 return nil, fmt.Errorf("digest mismatch for algorithm 0x%04x", digest.ID)
197 }
198 }
199 }
200
201 certs, err := signedData.ParseCertificates()
202 if err != nil {
203 return nil, err
204 }
205 var leaf *x509.Certificate
206 var intermediates []*x509.Certificate
207 for _, cert := range certs {
208 if bytes.Equal(cert.RawSubjectPublicKeyInfo, s.PublicKey) {
209 leaf = cert
210 } else {
211 intermediates = append(intermediates, cert)
212 }
213 }
214 if leaf == nil {
215 return nil, errors.New("public key does not match any certificate")
216 }
217 return &signers.Signature{
218 SigInfo: "v2",
219 Hash: bestHash,
220 X509Signature: &pkcs9.TimestampedSignature{
221 Signature: pkcs7.Signature{
222 Certificate: leaf,
223 Intermediates: intermediates,
224 },
225 },
226 }, nil
227 }
228
229 func (sig *apkSignature) VerifySignature(publicKey interface{}, signedData []byte) (crypto.Hash, error) {
230 st, err := sigTypeByID(sig.ID)
231 if err != nil {
232 return 0, err
233 }
234 d := st.hash.New()
235 d.Write(signedData)
236 hashed := d.Sum(nil)
237 switch st.alg {
238 case x509.RSA:
239 pub, ok := publicKey.(*rsa.PublicKey)
240 if !ok {
241 return 0, errors.New("public key algorithm mismatch")
242 }
243 if st.pss {
244 err = rsa.VerifyPSS(pub, st.hash, hashed, sig.Value, &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash})
245 } else {
246 err = rsa.VerifyPKCS1v15(pub, st.hash, hashed, sig.Value)
247 }
248 if err != nil {
249 return 0, err
250 }
251 case x509.ECDSA:
252 pub, ok := publicKey.(*ecdsa.PublicKey)
253 if !ok {
254 return 0, errors.New("public key algorithm mismatch")
255 }
256 esig, err := x509tools.UnmarshalEcdsaSignature(sig.Value)
257 if err != nil {
258 return 0, err
259 }
260 if !ecdsa.Verify(pub, hashed, esig.R, esig.S) {
261 return 0, errors.New("ECDSA verification failed")
262 }
263 default:
264 return 0, errors.New("unsupported public key algorithm")
265 }
266 return st.hash, nil
267 }
268
View as plain text