From 549ce7a482ed4fe170ca445324fb38c447030404 Mon Sep 17 00:00:00 2001 From: Khaled Abdelmohsen Date: Fri, 12 Jun 2020 12:27:33 +0100 Subject: [PATCH] Modify source stamp format Modify source stamp verifier to account for APK signature schemes V1, V2, and V3. The verifier would verify the signature schemes found in the APK. If the APK is signed using a signature scheme that does not have a corresponding verified source stamp signed digest, the source stamp is considered unverified. Bug: 158196850 Test: atest FrameworksCoreTests:SourceStampVerifierTest Change-Id: I07e3606a346b598d192771945335770ce964726e --- .../util/apk/ApkSigningBlockUtils.java | 1 + .../android/util/apk/SourceStampVerifier.java | 233 ++++++++++++------ core/java/android/util/apk/TEST_MAPPING | 12 + ...tch.apk => stamp-apk-hash-mismatch-v1.apk} | Bin 16854 -> 16854 bytes .../stamp-apk-hash-mismatch-v2.apk | Bin 0 -> 16854 bytes .../stamp-apk-hash-mismatch-v3.apk | Bin 0 -> 16854 bytes .../stamp-certificate-mismatch.apk | Bin 16854 -> 12758 bytes .../stamp-malformed-signature.apk | Bin 16854 -> 12758 bytes .../stamp-without-block.apk | Bin 12758 -> 12758 bytes .../SourceStampVerifierTest/valid-stamp.apk | Bin 16854 -> 16854 bytes .../util/apk/SourceStampVerifierTest.java | 117 +++++---- .../SourceStampTestApk.apk | Bin 2075621 -> 2075621 bytes 12 files changed, 251 insertions(+), 112 deletions(-) create mode 100644 core/java/android/util/apk/TEST_MAPPING rename core/tests/coretests/assets/SourceStampVerifierTest/{stamp-apk-hash-mismatch.apk => stamp-apk-hash-mismatch-v1.apk} (73%) create mode 100644 core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v2.apk create mode 100644 core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 2a4b65d23e647..6efe95cb9e92d 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -420,6 +420,7 @@ final class ApkSigningBlockUtils { static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1; static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2; static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3; + static final int CONTENT_DIGEST_SHA256 = 4; private static final int[] V4_CONTENT_DIGEST_ALGORITHMS = {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256, diff --git a/core/java/android/util/apk/SourceStampVerifier.java b/core/java/android/util/apk/SourceStampVerifier.java index a7ae32d1baa2d..5fc242353d519 100644 --- a/core/java/android/util/apk/SourceStampVerifier.java +++ b/core/java/android/util/apk/SourceStampVerifier.java @@ -16,6 +16,7 @@ package android.util.apk; +import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_SHA256; import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getLengthPrefixedSlice; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContentDigestAlgorithm; @@ -27,12 +28,10 @@ import android.util.Pair; import android.util.Slog; import android.util.jar.StrictJarFile; -import libcore.io.IoUtils; +import libcore.io.Streams; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; @@ -49,11 +48,13 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; +import java.util.jar.JarFile; import java.util.zip.ZipEntry; /** @@ -74,7 +75,11 @@ public abstract class SourceStampVerifier { private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a; private static final int APK_SIGNATURE_SCHEME_V3_BLOCK_ID = 0xf05368c0; - private static final int SOURCE_STAMP_BLOCK_ID = 0x2b09189e; + private static final int SOURCE_STAMP_BLOCK_ID = 0x6dff800d; + + private static final int VERSION_JAR_SIGNATURE_SCHEME = 1; + private static final int VERSION_APK_SIGNATURE_SCHEME_V2 = 2; + private static final int VERSION_APK_SIGNATURE_SCHEME_V3 = 3; /** Name of the SourceStamp certificate hash ZIP entry in APKs. */ private static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256"; @@ -115,7 +120,8 @@ public abstract class SourceStampVerifier { // SourceStamp present. return SourceStampVerificationResult.notPresent(); } - return verify(apk, sourceStampCertificateDigest); + byte[] manifestBytes = getManifestBytes(apkJar); + return verify(apk, sourceStampCertificateDigest, manifestBytes); } catch (IOException e) { // Any exception in reading the APK returns a non-present SourceStamp outcome // without affecting the outcome of any of the other signature schemes. @@ -126,22 +132,71 @@ public abstract class SourceStampVerifier { } private static SourceStampVerificationResult verify( - RandomAccessFile apk, byte[] sourceStampCertificateDigest) { + RandomAccessFile apk, byte[] sourceStampCertificateDigest, byte[] manifestBytes) { try { SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk, SOURCE_STAMP_BLOCK_ID); - Map apkContentDigests = getApkContentDigests(apk); - return verify(signatureInfo, apkContentDigests, sourceStampCertificateDigest); - } catch (IOException | SignatureNotFoundException e) { + Map> signatureSchemeApkContentDigests = + getSignatureSchemeApkContentDigests(apk, manifestBytes); + return verify( + signatureInfo, + getSignatureSchemeDigests(signatureSchemeApkContentDigests), + sourceStampCertificateDigest); + } catch (IOException | SignatureNotFoundException | RuntimeException e) { return SourceStampVerificationResult.notVerified(); } } private static SourceStampVerificationResult verify( SignatureInfo signatureInfo, - Map apkContentDigests, + Map signatureSchemeDigests, byte[] sourceStampCertificateDigest) throws SecurityException, IOException { + ByteBuffer sourceStampBlock = signatureInfo.signatureBlock; + ByteBuffer sourceStampBlockData = + ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlock); + + X509Certificate sourceStampCertificate = + verifySourceStampCertificate(sourceStampBlockData, sourceStampCertificateDigest); + + // Parse signed signature schemes block. + ByteBuffer signedSignatureSchemes = + ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlockData); + Map signedSignatureSchemeData = new HashMap<>(); + while (signedSignatureSchemes.hasRemaining()) { + ByteBuffer signedSignatureScheme = + ApkSigningBlockUtils.getLengthPrefixedSlice(signedSignatureSchemes); + int signatureSchemeId = signedSignatureScheme.getInt(); + signedSignatureSchemeData.put(signatureSchemeId, signedSignatureScheme); + } + + for (Map.Entry signatureSchemeDigest : signatureSchemeDigests.entrySet()) { + if (!signedSignatureSchemeData.containsKey(signatureSchemeDigest.getKey())) { + throw new SecurityException( + String.format( + "No signatures found for signature scheme %d", + signatureSchemeDigest.getKey())); + } + verifySourceStampSignature( + signedSignatureSchemeData.get(signatureSchemeDigest.getKey()), + sourceStampCertificate, + signatureSchemeDigest.getValue()); + } + + return SourceStampVerificationResult.verified(sourceStampCertificate); + } + + /** + * Verify the SourceStamp certificate found in the signing block is the same as the SourceStamp + * certificate found in the APK. It returns the verified certificate. + * + * @param sourceStampBlockData the source stamp block in the APK signing block which contains + * the certificate used to sign the stamp digests. + * @param sourceStampCertificateDigest the source stamp certificate digest found in the APK. + */ + private static X509Certificate verifySourceStampCertificate( + ByteBuffer sourceStampBlockData, byte[] sourceStampCertificateDigest) + throws IOException { CertificateFactory certFactory; try { certFactory = CertificateFactory.getInstance("X.509"); @@ -149,17 +204,6 @@ public abstract class SourceStampVerifier { throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e); } - List> digests = - apkContentDigests.entrySet().stream() - .sorted(Map.Entry.comparingByKey()) - .map(e -> Pair.create(e.getKey(), e.getValue())) - .collect(Collectors.toList()); - byte[] digestBytes = encodeApkContentDigests(digests); - - ByteBuffer sourceStampBlock = signatureInfo.signatureBlock; - ByteBuffer sourceStampBlockData = - ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlock); - // Parse the SourceStamp certificate. byte[] sourceStampEncodedCertificate = ApkSigningBlockUtils.readLengthPrefixedByteArray(sourceStampBlockData); @@ -172,24 +216,30 @@ public abstract class SourceStampVerifier { } catch (CertificateException e) { throw new SecurityException("Failed to decode certificate", e); } - sourceStampCertificate = - new VerbatimX509Certificate(sourceStampCertificate, sourceStampEncodedCertificate); - // Verify the SourceStamp certificate found in the signing block is the same as the - // SourceStamp certificate found in the APK. - try { - MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); - messageDigest.update(sourceStampEncodedCertificate); - byte[] sourceStampBlockCertificateDigest = messageDigest.digest(); - if (!Arrays.equals(sourceStampCertificateDigest, sourceStampBlockCertificateDigest)) { - throw new SecurityException("Certificate mismatch between APK and signature block"); - } - } catch (NoSuchAlgorithmException e) { - throw new SecurityException("Failed to find SHA-256", e); + byte[] sourceStampBlockCertificateDigest = + computeSha256Digest(sourceStampEncodedCertificate); + if (!Arrays.equals(sourceStampCertificateDigest, sourceStampBlockCertificateDigest)) { + throw new SecurityException("Certificate mismatch between APK and signature block"); } + return new VerbatimX509Certificate(sourceStampCertificate, sourceStampEncodedCertificate); + } + + /** + * Verify the SourceStamp signature found in the signing block is signed by the SourceStamp + * certificate found in the APK. + * + * @param signedBlockData the source stamp block in the APK signing block which contains the + * stamp signed digests. + * @param sourceStampCertificate the source stamp certificate used to sign the stamp digests. + * @param digest the digest to be verified being signed by the source stamp certificate. + */ + private static void verifySourceStampSignature( + ByteBuffer signedBlockData, X509Certificate sourceStampCertificate, byte[] digest) + throws IOException { // Parse the signatures block and identify supported signatures - ByteBuffer signatures = ApkSigningBlockUtils.getLengthPrefixedSlice(sourceStampBlockData); + ByteBuffer signatures = ApkSigningBlockUtils.getLengthPrefixedSlice(signedBlockData); int signatureCount = 0; int bestSigAlgorithm = -1; byte[] bestSigAlgorithmSignatureBytes = null; @@ -235,7 +285,7 @@ public abstract class SourceStampVerifier { if (jcaSignatureAlgorithmParams != null) { sig.setParameter(jcaSignatureAlgorithmParams); } - sig.update(digestBytes); + sig.update(digest); sigVerified = sig.verify(bestSigAlgorithmSignatureBytes); } catch (InvalidKeyException | InvalidAlgorithmParameterException @@ -247,27 +297,44 @@ public abstract class SourceStampVerifier { if (!sigVerified) { throw new SecurityException(jcaSignatureAlgorithm + " signature did not verify"); } - - return SourceStampVerificationResult.verified(sourceStampCertificate); } - private static Map getApkContentDigests(RandomAccessFile apk) - throws IOException, SignatureNotFoundException { - // Retrieve APK content digests in V3 signing block. If a V3 signature is not found, the APK - // content digests would be re-tried from V2 signature. + private static Map> getSignatureSchemeApkContentDigests( + RandomAccessFile apk, byte[] manifestBytes) throws IOException { + Map> signatureSchemeApkContentDigests = new HashMap<>(); + + // Retrieve APK content digests in V3 signing block. try { SignatureInfo v3SignatureInfo = ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID); - return getApkContentDigestsFromSignatureBlock(v3SignatureInfo.signatureBlock); + signatureSchemeApkContentDigests.put( + VERSION_APK_SIGNATURE_SCHEME_V3, + getApkContentDigestsFromSignatureBlock(v3SignatureInfo.signatureBlock)); } catch (SignatureNotFoundException e) { // It's fine not to find a V3 signature. } - // Retrieve APK content digests in V2 signing block. If a V2 signature is not found, the - // process of retrieving APK content digests stops, and the stamp is considered un-verified. - SignatureInfo v2SignatureInfo = - ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID); - return getApkContentDigestsFromSignatureBlock(v2SignatureInfo.signatureBlock); + // Retrieve APK content digests in V2 signing block. + try { + SignatureInfo v2SignatureInfo = + ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID); + signatureSchemeApkContentDigests.put( + VERSION_APK_SIGNATURE_SCHEME_V2, + getApkContentDigestsFromSignatureBlock(v2SignatureInfo.signatureBlock)); + } catch (SignatureNotFoundException e) { + // It's fine not to find a V2 signature. + } + + // Retrieve manifest digest. + if (manifestBytes != null) { + Map jarSignatureSchemeApkContentDigests = new HashMap<>(); + jarSignatureSchemeApkContentDigests.put( + CONTENT_DIGEST_SHA256, computeSha256Digest(manifestBytes)); + signatureSchemeApkContentDigests.put( + VERSION_JAR_SIGNATURE_SCHEME, jarSignatureSchemeApkContentDigests); + } + + return signatureSchemeApkContentDigests; } private static Map getApkContentDigestsFromSignatureBlock( @@ -289,27 +356,45 @@ public abstract class SourceStampVerifier { return apkContentDigests; } - private static byte[] getSourceStampCertificateDigest(StrictJarFile apkJar) throws IOException { - InputStream inputStream = null; - try { - ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME); - if (zipEntry == null) { - // SourceStamp certificate hash file not found, which means that there is not - // SourceStamp present. - return null; - } - inputStream = apkJar.getInputStream(zipEntry); - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - - // Trying to read the certificate digest, which should be less than 1024 bytes. - byte[] buffer = new byte[1024]; - int count = inputStream.read(buffer, 0, buffer.length); - byteArrayOutputStream.write(buffer, 0, count); - - return byteArrayOutputStream.toByteArray(); - } finally { - IoUtils.closeQuietly(inputStream); + private static Map getSignatureSchemeDigests( + Map> signatureSchemeApkContentDigests) { + Map digests = new HashMap<>(); + for (Map.Entry> signatureSchemeApkContentDigest : + signatureSchemeApkContentDigests.entrySet()) { + List> apkDigests = + getApkDigests(signatureSchemeApkContentDigest.getValue()); + digests.put( + signatureSchemeApkContentDigest.getKey(), encodeApkContentDigests(apkDigests)); } + return digests; + } + + private static List> getApkDigests( + Map apkContentDigests) { + List> digests = new ArrayList<>(); + for (Map.Entry apkContentDigest : apkContentDigests.entrySet()) { + digests.add(Pair.create(apkContentDigest.getKey(), apkContentDigest.getValue())); + } + digests.sort(Comparator.comparing(pair -> pair.first)); + return digests; + } + + private static byte[] getSourceStampCertificateDigest(StrictJarFile apkJar) throws IOException { + ZipEntry zipEntry = apkJar.findEntry(SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME); + if (zipEntry == null) { + // SourceStamp certificate hash file not found, which means that there is not + // SourceStamp present. + return null; + } + return Streams.readFully(apkJar.getInputStream(zipEntry)); + } + + private static byte[] getManifestBytes(StrictJarFile apkJar) throws IOException { + ZipEntry zipEntry = apkJar.findEntry(JarFile.MANIFEST_NAME); + if (zipEntry == null) { + return null; + } + return Streams.readFully(apkJar.getInputStream(zipEntry)); } private static byte[] encodeApkContentDigests(List> apkContentDigests) { @@ -329,6 +414,16 @@ public abstract class SourceStampVerifier { return result.array(); } + private static byte[] computeSha256Digest(byte[] input) { + try { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(input); + return messageDigest.digest(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Failed to find SHA-256", e); + } + } + private static void closeApkJar(StrictJarFile apkJar) { try { if (apkJar == null) { diff --git a/core/java/android/util/apk/TEST_MAPPING b/core/java/android/util/apk/TEST_MAPPING new file mode 100644 index 0000000000000..8544e82e04e03 --- /dev/null +++ b/core/java/android/util/apk/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.util.apk.SourceStampVerifierTest" + } + ] + } + ] +} diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v1.apk similarity index 73% rename from core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch.apk rename to core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v1.apk index 1dc1e998f1eec74997b607ad755b70f4ad1a7a84..add4aa0387083adeff1a37e73ecef42c7a8cddbc 100644 GIT binary patch delta 2747 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 B4{J6o}DGw_% zyU-TXFV!q>0fZeS)yr}KcCPsIlinQ9H4D~n8(Cg|2|d*%ME)8NyClZx0C;{WBE48w zhY{F;uUOMq-V3f8#8ssaZjwN)7!{1b9`T!I?5&roZ`yNjiJNfP6YFk4iJ39~BGyx1 z7sV_g%Qaqoa?g60ELyZbXglcfW%XlQ3S(%?8O1X0$jb83ysf)qi>t4)Wq;84f%BKp zK>v*6m(!5`8OJ|qkT1(B0skx2=jWVO{v*|xUn*5L5N<_L;2(1J_A*btyr_KZ{T&X+ z56g*7YH&f@^~#O8b?XIyi@8{Zi;ib9E^gxK?uUz3;gT5k)xv4Ob{y8a+h{Bq^-&(;(~xxYowm%d3Aq4TO+R@Zbym2TmWZ>y9*@8 z)=nVp&a?6Ed|OBIz3O zaX+IGw-x;9cCQ*3;`!TZ=+g0CtI-U$^KsGBoVcF;v6auEG{dK7^3$W`+C1$+Q(P>G;ZZ&Qh4{c# z@rnSu<-cUqKdr2n!K=*H8dfRQ(Ra;*3$33lwm=_p7eMv9JkhY?)Ba3R@sqRuo{pOF%)!+tn4)Dw4mFa8T5hfFuXi~>u@8A= z;N8aKD`wsjKc^yIC2EIITGSiOJJi|lWEgoer*o-(IN!Aa#ZTbq4J)?4N%HivB zoO=-ubSTK0mb-7$Re4)N@sVqkH)Z+=Or)hd8ojp<%KBUWp0Fa%iUM@hkS2A!oH)fL zRW*1WXNJ;~_vGSlqt9+Ecu)vwL%nH63@47|Zl2}>8oH`BPAVfWH52{fd>~$LL&v)} zI5hf5{-z{3qv?|t!-M(tAmp?Nx?wyUqPvqPCcYXRC1i=VJ#2!_e%OJgPKsl$UEHU!RJ=2<%)YV!cpCdH=HGr>z=Gd5^m09VQtxmy6ILVTe zupPP4bbubt?B1e&nUH2_-9FKX#kLDhdXhW&E|2_<-HB!LkO}Hw7^@z_t)RyL>XQbH tj*9(gdpudVHd)SUhW=)II9X!_@E;XB&fZuMn2rlHRs{c9mEw#|{|&B{7heDX diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v2.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v2.apk new file mode 100644 index 0000000000000000000000000000000000000000..e55eb903c68bbe13fc3f240aef44f8245f1bd271 GIT binary patch literal 16854 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%0Aw!U{@7-(m6jb$TAC@H=%;=rS ziXt&xhczjG7TTS6@(I_5-a?p6Yp-rSLGutPX;C@s2K*(FTGGgT z-?y~dx&mt&$-4(8TRLX;$IpqgRV&YqGnTDAV!X(gm5_cxSn=KM`eVfVm4o}tG3GeW|ZSzY98YEIUJj>F%%U0GhOr}68?O7?i>>k8zIP`VR zfNwA>YUBn&E8kzdZM6~d)tA*tFHqz%D2Pe^H<;mi755UrYU-}53=nBj%@@{xG?Nqk0BWr=vO}ny zBU_t;-!j#`5kLqW(v8fls6Qn+mGL}WR8!99L|vZPo02Mhwv3jl3NsLn(0)qqQ&e?5 zFRXmGl)$j5ApILZ%|u)LB`1EabR>z=exQwwz&3c~Mw!Pf-PAzCH^-Xb#DvF2eo-hs zw)76;sH?QaSN-uG6iSN5V>T(yc3++~yP>5@tQB@>OsE7;f2{EF9k}1cLLy{*L&|aQ zioVO6uv_iaPh-sUWOpvt$%n!thPF1rSX{7AN&kS$oo$EeIycX}+X@g~rq6Lh-SS{f z_M>*ZY}Ar`PFu>PdNmGv z#dB|r@?6#oZ!wHenuENa_h^0z3Ljls&Eow$v~OCO0(J{;bw!dnd$Wl+1h?x=5N8!ux-P;M>kf? z=$ffDC`!AbY`YWVp1XAYVIA#XnNG|2PD22;dOFA%H^w zhX4)%90E85a0uWKz#)J`;9nr{^F6_&p%* zPsLJ_zX^}~UH$jiw?EZS0QJ9%g!|pX@9|oHT8IU{$Uj;DqPKoG@q1{-pC)E0e=+e( XpoX>v!H+#?A|N`y8N2HykJb4%?CE{{ literal 0 HcmV?d00001 diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk new file mode 100644 index 0000000000000000000000000000000000000000..de23558220939d4377f11bf5730863328b1a7ef3 GIT binary patch literal 16854 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%0Aw!U{@7-(m6jb$TAC@H=%;=rS ziXt&xhczjG7TTS6@(I_5-a?p6Yp-rSLGutPX;C@s2K*(FTGGgT z-?y~dx&mt&$-4(8TRLX;$IpqgRV&YqGnTDAV!X(gm5_cxSn=KM`eVfVm4o}tG3GeW|ZSzY98YEIUJj>F%%U0GhOr}68?O7?i>>k8zIP`VR zfNwA>YUBn&E8kzdZM6~d)tA*tFHqz%D2Pe^H<;mi755v%3?r20Ag|{=nqPv#N0(N4l-7wn9wu>z``RfAdL{F3NsG-KBGI+@)y8q&zCChA z#rL<^Fs_OqVc7S&G5vWmP-d^vtoBH_*&u9Ydi_GSwqKX|TQD?i+wktujTJMxW@-(J z(rzf*?!>s~Zld}-jH+0g*!046uBLu%cLS;$LPOpwiHIgOtKN!7<=Nh*WzOr1s(b~~ zio*5GK`c+iYVWD7*D`*SykiP-Rp!Iuf_+N*2V9=sK54dqo)_FUuu?I9*{uBuq+V{G z^Jt;|SXV!%YqMyc<5pADlGbN}C|#j2X?9i77H9=cP2F{s0U}MR`NH~-W^$q*K&@3q zb_lg|WNUNqTc)}<0tkV_%aNHC^`|7KGMDD#-5n;K~N=2#P)nDE%h zFABxSmfm3;b(OaGsz2U?LP^nh%qGRz?#r`gH?&lVwZaaK36AYVIA#XnNG|2PD22;dOFA%H^w zhX4)%90E85a0uWKz#)J`;9nr{^F6_&p%* zPsLJ_zX^}~UH$jiw?EZS0QJ9%g!|pX@9|oHT8IU{$Uj;DqPKoG@q1{-pC)E0e=+e( XpoX>v!H+#?A|N`y8N2HykJb4%WGQ|8 literal 0 HcmV?d00001 diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-certificate-mismatch.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-certificate-mismatch.apk index 562805cf67eb0effef39d2e6a6980a4910911e06..f1105f96664c919203ddd481e77cba6937a0acb5 100644 GIT binary patch delta 2860 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 delta 2851 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
  • _+|BNl1hX(V^ViWg=~Cu0L4YXbamF? zYK@@n+<_ksXJCC7G0^DI(A)4zTfe!;7^1ty4SlZIlgeBq#kLN{T}tpDeQAHGI-_AfC5Nrz73q;@ z;-;98Z*L6@$(`p1zJ6qQDD$i11c$q7?ZdR$u>6`c4rLiRym#^q@$Mh|tOC4bG`})w zR)~NzgVw93mJX?`lh#_2;AL;M!knfcPusY}B7^zn{*E)7cpOKtdmH8fWJx5u(45g4m6^zfDOVypx zoZ3_JKuYb0JfbR;bcFZCJfEOqa25W$?eC_W*y?Ieza_#kj@x#l)GWS6%*~aS7nZnF zG{YggbV6IZR7>^J3{7z}aCx?Onj^O_eKI#MoLq9dYjX4Jlv9LQ!QAs2%p(KZh(Ap{ z4iY5qO8xcYs892}T$Q@24?d!ob9+H2HrlB~%n}z~aXDh~Q7d9+hwsX$$=LR8nB4|( z{&i*_u3p;qe!Su4vCvWh$(=b`@=)CbyVzNFlJ%&E+WnCCvyTVUg!t0pIblzfR+?U3 zYv+kREi+NcC6Z3J3H8ZuzA?DFrxCA&v8ux4Vk%mx=I7@WX}kGL!qRz)mE-|C$_Iz# zC&r~6eO+YW{{+d_yy&k)q;rem-QF7e5Kutm>`S(Ts5$=jtoX<-E}O|bo+BmdP4b+V zy(9T2l>L5+@U~UvtM*yf)Rd&(Yryh$sFz%m7A{xh1hlb*_Bl>?Lgg1RK8sczaXn`# zObpZ=o(6K`bi;1W6se@JfXD+zL5A;S-|b^;XyJiCC76`}ZcG11;?>3YL z(@&Y~j9u|Gv(0?_m1=n#s4^3Rc^gZhhRc88%c91jY+8G42}1HiugQ43~(tPK`bO zY412O37#<5Gu|R^?S0mw;DLIm>3v9*D^j3}J8JN)Eq^Mz<-xNdjrJno8JK2=Wi#r>{z=-tc4ZxbuU=3Iy0b=~tDYj84UOt!Wt|7g(^Q>I^6;DeyN^&FI^_ zz-3g4@|(}L$v;L3YHef&PVW)ACV^8?~W~Kx(e2& zMHJ!BCb=Bk8*SA!-WWsmf4#9eSCu#k=D|jpK6N&yt-Z~|T+JtMv^N9@_f0ygOP6Piv zj{ls7`1d&eN#l39%5yNJd1xd-wi95+_<(WAp$JU7C<17%5X=N(VqycvLP2_Las{SD zU-9G0>L^w8X%q@|3Wc&0;QT#R{WC?u(R?T#7O*8V7|QM+;G&?6Qc-tOCWcEQ@C~Tg zR<>i-zMqcJ>xkQ)pR>D_AZ+$`*1VQ=yA6jEnQjE%nr0<-^^UDBc~LcAUd>FnCDmeY zD)cK+KR9X1VZ}~oy4bJ5vODa3__C?2Om=;+r7P5B?US5F;c0x~k~BK-!Nt^^V^Q(B z;P%`w4C`YV<2h0s>B&HKKD%#+A6{%(exzCie*1@~NJ5Y*KQ3Il zvENa7K_Fh>e(c;zsN_cU;|xc5=-_*61!q0At{zb1QclmUbBg&fLq8_dkh_J2#|WH? zx?f07t!2Yt%m%+npg*r}6eG%Y)@#@5D{d9&dFC3uSZsotKTr``hzk(Rjgr&yV`97; zpdu{F1SQHqB+A9i&kV!#TD?#<vd%4p*y1drEI2AVR~kJMQ4 z)?;yR&N;SlIt%MHh0ZC6lna?6J)6u2almHlt`-T#%i%Y zWux!!5FCyacS{d(?Weyo%1i18VjNp+E+o4)0lZgnPWRRN9VoAB4E+6P>m&1RDpB?E zE z`ZXbiEsx=j#?=L{%N`RVV$enR`zP9Fw7U=ZsA~2;I9vT^DGSC!mM5b$JhHXlAX8=( zu1}eLT?w|$Gdt<{U5lwDaI#qnJLaZci5Te zV#F^>AO8Fu^r6lOA2$2;jMrz2;fyc9-)x^KiI#!<-lr0=XgDZ==#G|!{LzaM(VCF+ HhqeC$_W_#t diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/stamp-without-block.apk b/core/tests/coretests/assets/SourceStampVerifierTest/stamp-without-block.apk index 9dec2f5911334989b135a135e6858c008e0afaf6..604fe6f43523732bbb080160d8e3c6f7c8b2585e 100644 GIT binary patch delta 1870 zcmbW2c{JPU8pk7|qN7NVQj8G6+y+5RQ`H(XW2rF15NfNfQu|tBDZkWcDW&+KmZ2lP zWx`mRv>8idX)P7fw6&C2I&_2@`>tqj<}P#YocrH>&iS18J?}ZsKcDw~pYMqOh<{_V zl!VjpXeSnIlTtk;hK9WPxbMX=qm=F)U_yiPgv2BO@lhhT1kil3)%)$H4D8@12a-5U z0Rn-;ArOd$DRFZ9Yf$zm9>~1lc;0h0Fb}E-W3;HGJ6a_0ZXx!zOnR!S;;1pv8|Cs^ z^FDR3?XZN^Bj%hXB{Hv7<_3-w+$;PFa(+by) zjp#U*k(_ka%?}nEOKWOxD%YJg=dCC6Lt0{$^~Y)yFZfq@&{U)VXa14S0LdIhi$p}C zS#Qi;{L{?H{ar@j>#^yGcFkvVgvElM=h^$-$TEx$A~{f5M#cW2Q5eGW9%bc*WO8K= z5OjrB;3vq5I<)C|WfttoE=x)Qca8`sO1oa}$4iypkw=|Bc!cTAhY}0#P_;TA5|LLd zG%DxsRhDL^FXzwZFz;K(Vs3?>UBxd%hisAeqvxt!I?mOVdRPQCP>dJDMgc9V z^>RCGa)Rp43Aw|VQ?%Ecoo^wlE!|u5b9F28@wEqMoa?||^*WhSYUs*(b}gmckXSad z5c5sJewKHp$6AA|VFP92B?FH4HQ#Q}@(1U90u2ShnmvwUPAPYb?>< zFv~tTnhi+_0nC8+Ri|Bk$R}gJr;kr^^KpBP9md`Cj6zs~Ia7dY#+}vI!xMTsut0A;Phq|CxY%iw z$GRNWlj?ei?>7>%g@22W!|A=Y4ZZb!(^grlw<%9!uDBNd3(yz4q&+x5(`YA9G=)QI z?;In1($BZv+^Hd!5HUMjw0LOP>hRPljaZBo9-ug(mnv!QxfIbE#x;FO@D zE6yaEtLI6iyd7^PEZWXOQHnN(^0d1?bB8gmIy{Rkhw*t5*9+i$n>aTqOtE<5zd7`H zNk(G0hS&Xw6R|7Lazr%9fDU5td^Fq!ln{MzQJqMaipmo0(?y&Yhd^+mGhGqlVh{*g zv{sF}WYe3jnuQdO>`m=)c1ik;C&jFX-I9(Sux+C%xGZlS332cyMgfo(b-+dZ(FqZ8 zb>)og>QqL~&`qIcFMTc}OgBS!2EbM(Z%+d`(#e_UQi;JeH2OC}7A`!gSA2-*%8_wf zXVTGF%RHqlU?ABKWU6MznA4*#T~;Jd_kSuis%ebuZRCALy;#VPLx=0S7Qm3LRR@Fj zZe%WLJ8HAuRuMmOZ%pCInI<@uS>Is^qKR;`S9z{D$uFMebmoftS7 zzT)!vqdfNnX(T4ZIvb_fcK&v|9MqE4QuG4QNnQ*dTT)AM?zzvx(*nag`8+nnIO>1Q42C8hD1Nt;|G zk{P^HH@jw_v_sl%PexY0)eLu@g*@xzT9oP^=Z*Af7ACNiQ9)@(Ri{ zQB8h(LaLSs3Y;uQ4Bg5DvD);6hmE!e+?Ya@T-3S$`(f0 delta 2504 zcmchZc{J2}AIE=8EGXT#_9#+Wtq9h8L(T}i&cDnWK(eM54&}Lsr1nFG zHUwWOG;Cc)YmZ5nTR-w7Kuh~xWs4O{!6qXCTtNdbnx?9g&Fv4&cKz4~a$MXSXcJRQ zu$7#e&NO)S;<#z#7`aBxHz+lNUVo2loT}lM@z~8hE%{*|W8uR1cqWwk;an_ld^UKG zzvy^TzhHuMS+~t^MsR$fK34tZ!Yit(>55_Y3EWwcEo0`y!&Djq%t4ZZ__(+^417(rQV@KK}&9ONpU~qM!w2yMQRD(X%N0}CiDEH z?)Z+1hphZ=SSjNKsUxBzP0w#BvW}UXkn^%g0G-2d#s=^f=mkS8e97^?ieXmJoBgyU24o63Sden zfPKnv5q7uD_lhNmB{(hHu&Zy{xhjZPDGLDlm=gPZM|@1pXDNtfn}M{UlRP?( z-yN34;U?gQS(_|V&*0)9zqu4_@*44a7j4V@eBns*iR*5zW-uYq2z)^o>xO+|Q)LM2 zl-cI+H7|49+@3ECt5ASqE(XmXRQ(jwJ%}!{$_VUuClR#q;mNv1g}QS`ttbk{H*NZ2 z8B=WJ)o7wV*;giPH*7sHx0B$SG??qA!zq~XpOp=+ z<|#q3_!$>fC-XFpAw}zav-y#wOAtm&IP21yORz2|oHWrsiJrCTH0YoPC)c93X&dDJ=rRALe8a8g`+t#Q=87#9z&fKl)+k zC?O4&GSfZMs&v&`+p_qMX87gXU02T)vv`Gv>hcTs?2~uAXsHC;ycW5$cz4!BBB-bKrUC&6E;N4VHU* ze#MH~;;vU!KcOn6f1M0!i8&Di-6j669-Pyu{JJeGN8%h8hFF`!PfQE?gIGuJDGXXr zC8@pqXq*0Xv{>Omk4=w9VMy%_#bLbhB%++pzmoJed!u-Ge)(mF+*cZ3aefyX`29G( zI}PdmIKHJp4U;>@@n5Mv_q1I4k5q%ct5i8bge3&@8@W1MH;fLOSIT*RkIQ0uF|tVo z4l`S=T$@?FGzVB4idR@`*hdpWMxX9X2bxGs#p<1X;k>EiHbr*~2N3IqxOW+s<3; zOdLh$!ADTl*s+51k`(8B9QW3`D3>n|C3^A}^UkO#ug`mjbUnuQ zVjx!q#Q?@H-dxOx!?jkK-t>ixm@=w**`*m; zAJ;Yr?Y=q2?B8C*TYDc*4Y=fOKuXn*aeX_R+JyS2T>tMh@cVImcN)_BaePbTFS&Bc za8mV-BRJVZmawEU@^ACir>Z6X)l1p4`+J?8Y=`xa*d@pz6~TYm00IUH<47U6BN5=g Jzd8a^|4&3xbiM!p diff --git a/core/tests/coretests/assets/SourceStampVerifierTest/valid-stamp.apk b/core/tests/coretests/assets/SourceStampVerifierTest/valid-stamp.apk index 8056e0bf6e50d06eee7fa657e08b3a02f6e40e64..2f2a5923d24aad2d8c046025ff9fffa07f9e8734 100644 GIT binary patch delta 2747 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 AYXATM delta 3445 zcmchacTf|^7snGqI06C0BoqN5gf1l@0)h~FQ9_j}gd#-((iBh;LYFEfRH+Jr(z%GB z0w={nlPW=~h#+V}M}oj{@$P=VIe+)}&o}dC-|pz_ct-Y0JZ zP{cL}|Eb=zMb--%{h7B=_f;-+Q<;iTGxj0h!V<&t0-fTw*~~V<0JC!-MmPY#%nSek zkg5c^92}981x>hTYi{Hak@`)00qGh7^BC2--k%KK-4?CEVyP>O9~aeLA#d?)u)cWS znVBv!R_4))WC0~2UorEL>oji+U(|&~73sC-EopjHIUBO)1~_-*QmS-_6!y0VUU{Fg z)pts&j-XKMmlpaxVF=Yl?+J_A-kXQDf*EZRwD4K30}n}D^{C3FfMdo9RNA49uBwjD#Y0d z%za4L?Rj|u8$MNCqu?8q97b-qQ*D^6?3eb$-6JLO;Q(dj?DTXxob*vUnk6<9dsR=Th2h`_2GKxT52#_gvM^@XUgEUxV{-uXC5YZN`Qgo{Pn2n76V%IhBv=y zO5PMn;JR~V@Ub3h>OI^3iIX=ciZ&W>U1F*tvuol%LT+MxkSR_oVO0VV3g*-GW;Lew z@%Y}Iz`*+mS?H5TR|&x{WGlj1DR9#1!2|Y*Se>+zDzrX3l`5muE*Rpq>cqrh2p@$ z(yo-RAIN^K3sSWzN`8d!ezu)O-PmZSazQJ6Sk?LPrMvCO-CfuPvdQ?)-c9>W;=;@9 z0eq8~T|%PK*748^Zjs%2YU)ViB#Yn~mLeOnr*gvew{!P~Gk9SciEQxua;q)R+&Vd< zPm531u=8iqY(xDDTQ3jq?W-lqVXSL0`IxG9iiPI9EOoDNnNO@hwx)W>p7h>vusXjD4Gj@me6!inE|qdOF}_M!Hh_*j+1$;xW=j>*n1Izo#=%B!B;Fig?5Lm59&3?tbayjBaBzna^1Er?4!4>ggwRfz z?2cdXGPld_`$Dn02BiZack zBd=mZVfDbPa*$Y=G`L+2YK@YKf*%xpRSeF0CHtm5Ba2^~$*ia@3tE&C^m|b~xvwB- z1zoQ2`jcJiuaUe3M?7~uA77V0vm-uEFr1YrV-0vvKAO2xJifI4I!)v|jqf;r3Jt=K zIDR?}p&xPln+EB+h!pU@Qhk2Oa_v7-4f?54WdIS^B;fy&E6!0r^7@i=*84k579Uo_ zo8^!Qlg$TP^P9$t0Be1|3TtJDNMgw3v;7ZOjQHoGHML&4?5exZk(~mAS?sKfLVA>r zx^6c)#O|JLAB{O1ls$9ziq|{9x`CO2i4J)o{Al&vqN&bXfi#(nZ8_4c%IVWB_+vae>?jYA3HH{i2h z?igiv?g~>ytu6w}*HTQ0!M9an{e9s^tpTSUF3YtMh2tVTZ=`@6X#PM$$ zf67&w?|b^vt%wJ50!EJy>5m*bieV2O1)MJdF#s7DAb{~upgu&Z*p%omB&nc+R+2xB zMx#%m(e~VIe@>PDo}v-*Ff=C<$chmJWeL3EBBOv-RB=)uhKV2v&FI*6$Z;G0k4I>Y z#2v3sxjk}Vh{KI_?-jisqtRrB%OO{1n2Fu}<7>;_6pd#Wvy-lhwmF#cekB@&q)a=m z+Uw4g1vFdr+hq;|W=lh4Ks9+^7e3;nNL7c)OcOrK} z?EGq|$Y%7tEF2n93EoJz}0GA^RV-;yx14(Y@iaUFs%k7Ofhs3mLTNWJyZCNunNbzB>p zGoOA-=)4SnC9f$;YU%W7#?kJ$o^dnu3&*x|uXvsog2vI>a5pcT2*T%&9Ea0Oe;@pTIZG7_8jec zy0++j-gA;)5W3`%aH3;Yr{@qxQFidd+ZjAcTf~l7os3lT%++~~N}H9rG;RK6HN>uf z4Lam$Ov(6V+eUgcHt)FClbXLYP84?vM9aO8E0yA~REEhiY)$)Y zZ=@qQtLmoa;?MWqe=Mm$4jt~#hkQ|ellFQP*Gy`S&&hmvEwe#V`kuI#t&?VbgE~Xc zUiqr{Tig9yITNGq*8;MhuHF4_^hoA*-HQ5YO`VtJCq{Kg6?V z9nYB=PsM3+VMKHCN89YVu%`&#Jx&&Z_%m?PfU60V%WOD~LpD;OJeZwc4-wZe6My$f u1DdLQ-`gGsI<9TH)0?4x*a1;QOC0=X#ZENSLI9J99$FIMzp7HAmd+o`I3CCV diff --git a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java index 37b2817928aa6..81d54b57486ca 100644 --- a/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java +++ b/core/tests/coretests/src/android/util/apk/SourceStampVerifierTest.java @@ -26,8 +26,11 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import android.content.Context; -import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.core.app.ApplicationProvider; +import libcore.io.Streams; + +import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -46,8 +49,20 @@ import java.util.zip.ZipFile; @RunWith(JUnit4.class) public class SourceStampVerifierTest { - private final Context mContext = - InstrumentationRegistry.getInstrumentation().getTargetContext(); + private final Context mContext = ApplicationProvider.getApplicationContext(); + + private File mPrimaryApk; + private File mSecondaryApk; + + @After + public void tearDown() throws Exception { + if (mPrimaryApk != null) { + mPrimaryApk.delete(); + } + if (mSecondaryApk != null) { + mSecondaryApk.delete(); + } + } @Test public void testSourceStamp_noStamp() throws Exception { @@ -63,17 +78,11 @@ public class SourceStampVerifierTest { @Test public void testSourceStamp_correctSignature() throws Exception { - File testApk = getApk("SourceStampVerifierTest/valid-stamp.apk"); - ZipFile apkZipFile = new ZipFile(testApk); - ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256"); - int size = (int) stampCertZipEntry.getSize(); - byte[] expectedStampCertHash = new byte[size]; - try (InputStream inputStream = apkZipFile.getInputStream(stampCertZipEntry)) { - inputStream.read(expectedStampCertHash); - } + mPrimaryApk = getApk("SourceStampVerifierTest/valid-stamp.apk"); + byte[] expectedStampCertHash = getSourceStampCertificateHashFromApk(mPrimaryApk); SourceStampVerificationResult result = - SourceStampVerifier.verify(testApk.getAbsolutePath()); + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); assertTrue(result.isPresent()); assertTrue(result.isVerified()); @@ -85,10 +94,10 @@ public class SourceStampVerifierTest { @Test public void testSourceStamp_signatureMissing() throws Exception { - File testApk = getApk("SourceStampVerifierTest/stamp-without-block.apk"); + mPrimaryApk = getApk("SourceStampVerifierTest/stamp-without-block.apk"); SourceStampVerificationResult result = - SourceStampVerifier.verify(testApk.getAbsolutePath()); + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); assertTrue(result.isPresent()); assertFalse(result.isVerified()); @@ -97,10 +106,10 @@ public class SourceStampVerifierTest { @Test public void testSourceStamp_certificateMismatch() throws Exception { - File testApk = getApk("SourceStampVerifierTest/stamp-certificate-mismatch.apk"); + mPrimaryApk = getApk("SourceStampVerifierTest/stamp-certificate-mismatch.apk"); SourceStampVerificationResult result = - SourceStampVerifier.verify(testApk.getAbsolutePath()); + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); assertTrue(result.isPresent()); assertFalse(result.isVerified()); @@ -108,11 +117,35 @@ public class SourceStampVerifierTest { } @Test - public void testSourceStamp_apkHashMismatch() throws Exception { - File testApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch.apk"); + public void testSourceStamp_apkHashMismatch_v1SignatureScheme() throws Exception { + mPrimaryApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch-v1.apk"); SourceStampVerificationResult result = - SourceStampVerifier.verify(testApk.getAbsolutePath()); + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); + + assertTrue(result.isPresent()); + assertFalse(result.isVerified()); + assertNull(result.getCertificate()); + } + + @Test + public void testSourceStamp_apkHashMismatch_v2SignatureScheme() throws Exception { + mPrimaryApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch-v2.apk"); + + SourceStampVerificationResult result = + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); + + assertTrue(result.isPresent()); + assertFalse(result.isVerified()); + assertNull(result.getCertificate()); + } + + @Test + public void testSourceStamp_apkHashMismatch_v3SignatureScheme() throws Exception { + mPrimaryApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk"); + + SourceStampVerificationResult result = + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); assertTrue(result.isPresent()); assertFalse(result.isVerified()); @@ -121,10 +154,10 @@ public class SourceStampVerifierTest { @Test public void testSourceStamp_malformedSignature() throws Exception { - File testApk = getApk("SourceStampVerifierTest/stamp-malformed-signature.apk"); + mPrimaryApk = getApk("SourceStampVerifierTest/stamp-malformed-signature.apk"); SourceStampVerificationResult result = - SourceStampVerifier.verify(testApk.getAbsolutePath()); + SourceStampVerifier.verify(mPrimaryApk.getAbsolutePath()); assertTrue(result.isPresent()); assertFalse(result.isVerified()); @@ -133,21 +166,14 @@ public class SourceStampVerifierTest { @Test public void testSourceStamp_multiApk_validStamps() throws Exception { - File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk"); - File testApk2 = getApk("SourceStampVerifierTest/valid-stamp.apk"); - ZipFile apkZipFile = new ZipFile(testApk1); - ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256"); - int size = (int) stampCertZipEntry.getSize(); - byte[] expectedStampCertHash = new byte[size]; - try (InputStream inputStream = apkZipFile.getInputStream(stampCertZipEntry)) { - inputStream.read(expectedStampCertHash); - } + mPrimaryApk = getApk("SourceStampVerifierTest/valid-stamp.apk"); + mSecondaryApk = getApk("SourceStampVerifierTest/valid-stamp.apk"); + byte[] expectedStampCertHash = getSourceStampCertificateHashFromApk(mPrimaryApk); List apkFiles = new ArrayList<>(); - apkFiles.add(testApk1.getAbsolutePath()); - apkFiles.add(testApk2.getAbsolutePath()); + apkFiles.add(mPrimaryApk.getAbsolutePath()); + apkFiles.add(mSecondaryApk.getAbsolutePath()); - SourceStampVerificationResult result = - SourceStampVerifier.verify(apkFiles); + SourceStampVerificationResult result = SourceStampVerifier.verify(apkFiles); assertTrue(result.isPresent()); assertTrue(result.isVerified()); @@ -159,14 +185,13 @@ public class SourceStampVerifierTest { @Test public void testSourceStamp_multiApk_invalidStamps() throws Exception { - File testApk1 = getApk("SourceStampVerifierTest/valid-stamp.apk"); - File testApk2 = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch.apk"); + mPrimaryApk = getApk("SourceStampVerifierTest/valid-stamp.apk"); + mSecondaryApk = getApk("SourceStampVerifierTest/stamp-apk-hash-mismatch-v3.apk"); List apkFiles = new ArrayList<>(); - apkFiles.add(testApk1.getAbsolutePath()); - apkFiles.add(testApk2.getAbsolutePath()); + apkFiles.add(mPrimaryApk.getAbsolutePath()); + apkFiles.add(mSecondaryApk.getAbsolutePath()); - SourceStampVerificationResult result = - SourceStampVerifier.verify(apkFiles); + SourceStampVerificationResult result = SourceStampVerifier.verify(apkFiles); assertTrue(result.isPresent()); assertFalse(result.isVerified()); @@ -174,10 +199,16 @@ public class SourceStampVerifierTest { } private File getApk(String apkPath) throws IOException { - File testApk = File.createTempFile("SourceStampApk", ".apk"); + File apk = File.createTempFile("SourceStampApk", ".apk"); try (InputStream inputStream = mContext.getAssets().open(apkPath)) { - Files.copy(inputStream, testApk.toPath(), REPLACE_EXISTING); + Files.copy(inputStream, apk.toPath(), REPLACE_EXISTING); } - return testApk; + return apk; + } + + private byte[] getSourceStampCertificateHashFromApk(File apk) throws IOException { + ZipFile apkZipFile = new ZipFile(apk); + ZipEntry stampCertZipEntry = apkZipFile.getEntry("stamp-cert-sha256"); + return Streams.readFully(apkZipFile.getInputStream(stampCertZipEntry)); } } diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/SourceStampTestApk.apk index 211e064399a803956e0135b1731eff08545b3854..bd871ae7488c7d43b9968620ea224976fd71da0d 100644 GIT binary patch delta 439 zcmaEQz3%Dtx`r0U7N!>F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#7J(MQ7NHj57LgXw z7O@ub7Ks+g7O58L7MT{=Epoiu_%^dLfB|pA|J>EA+oiY3RWjEbFflNQFfuRz5d#m9 z-~eJp79a;m^Su9I(N{Iyo7;|W-^vq4$sMsjVpt_O4DWNA{|!1k>G;}?1An)#5!y0O znk}o5=Xq`Y+$B>hCY_MmD!5g(Lh-ZfD~*SfKdNtSkMWq0R)EDXm~4|Bemam;Xv=YeTPVILmLv)yW9QMq349lq+b%+Y0*eOl}ve9AK4 zsPcS!6RLRYuBg-wbKlGAZZm$!?v9cIf*Q%-=9M*F7M2#)7Pc1l7LFFq7OocV7M>Q~7QPn#7J(MQ7NHj57LgXw z7O@ub7Ks+g7O58L7MT{=Epoiu`1)BGz+j#Pr*=EbcIj<$mCW0B?2;2;WU^u1ermUz c9Lw|uOWB*#AN-Q`p0+`bWBZ5wa$jEq06XU^Z2$lO