Loading core/java/android/os/incremental/V4Signature.java +7 −7 Original line number Diff line number Diff line Loading @@ -72,16 +72,16 @@ public class V4Signature { * V4 signature data. */ public static class SigningInfo { public final byte[] v3Digest; // used to match with the corresponding APK public final byte[] apkDigest; // used to match with the corresponding APK public final byte[] certificate; // ASN.1 DER form public final byte[] additionalData; // a free-form binary data blob public final byte[] publicKey; // ASN.1 DER, must match the certificate public final int signatureAlgorithmId; // see the APK v2 doc for the list public final byte[] signature; SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData, SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData, byte[] publicKey, int signatureAlgorithmId, byte[] signature) { this.v3Digest = v3Digest; this.apkDigest = apkDigest; this.certificate = certificate; this.additionalData = additionalData; this.publicKey = publicKey; Loading @@ -94,13 +94,13 @@ public class V4Signature { */ public static SigningInfo fromByteArray(byte[] bytes) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); byte[] v3Digest = readBytes(buffer); byte[] apkDigest = readBytes(buffer); byte[] certificate = readBytes(buffer); byte[] additionalData = readBytes(buffer); byte[] publicKey = readBytes(buffer); int signatureAlgorithmId = buffer.getInt(); byte[] signature = readBytes(buffer); return new SigningInfo(v3Digest, certificate, additionalData, publicKey, return new SigningInfo(apkDigest, certificate, additionalData, publicKey, signatureAlgorithmId, signature); } } Loading Loading @@ -150,7 +150,7 @@ public class V4Signature { final int size = 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize( hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize( signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize( signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize( signingInfo.additionalData); ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(size); Loading @@ -159,7 +159,7 @@ public class V4Signature { buffer.put(hashingInfo.log2BlockSize); writeBytes(buffer, hashingInfo.salt); writeBytes(buffer, hashingInfo.rawRootHash); writeBytes(buffer, signingInfo.v3Digest); writeBytes(buffer, signingInfo.apkDigest); writeBytes(buffer, signingInfo.certificate); writeBytes(buffer, signingInfo.additionalData); return buffer.array(); Loading core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +12 −3 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.util.ArrayMap; Loading Loading @@ -117,7 +118,10 @@ public class ApkSignatureSchemeV2Verifier { return vSigner.certs; } private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) /** * Same as above returns the full signer object, containing additional info e.g. digest. */ public static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { return verify(apk, verifyIntegrity); Loading Loading @@ -209,9 +213,11 @@ public class ApkSignatureSchemeV2Verifier { verityDigest, apk.length(), signatureInfo); } byte[] digest = pickBestDigestForV4(contentDigests); return new VerifiedSigner( signerCerts.toArray(new X509Certificate[signerCerts.size()][]), verityRootHash); verityRootHash, digest); } private static X509Certificate[] verifySigner( Loading Loading @@ -426,11 +432,14 @@ public class ApkSignatureSchemeV2Verifier { */ public static class VerifiedSigner { public final X509Certificate[][] certs; public final byte[] verityRootHash; public final byte[] digest; public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) { public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) { this.certs = certs; this.verityRootHash = verityRootHash; this.digest = digest; } } Loading core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +2 −16 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package android.util.apk; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; Loading @@ -26,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.os.Build; Loading Loading @@ -213,24 +212,11 @@ public class ApkSignatureSchemeV3Verifier { verityDigest, apk.length(), signatureInfo); } result.digest = pickBestV3DigestForV4(contentDigests); result.digest = pickBestDigestForV4(contentDigests); return result; } // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier. private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) { final int[] orderedContentDigestTypes = {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256, CONTENT_DIGEST_CHUNKED_SHA256}; for (int contentDigestType : orderedContentDigestTypes) { if (contentDigests.containsKey(contentDigestType)) { return contentDigests.get(contentDigestType); } } return null; } private static VerifiedSigner verifySigner( ByteBuffer signerBlock, Map<Integer, byte[]> contentDigests, Loading core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java +4 −4 Original line number Diff line number Diff line Loading @@ -145,7 +145,7 @@ public class ApkSignatureSchemeV4Verifier { "Public key mismatch between certificate and signature record"); } return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest); return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest); } /** Loading @@ -155,11 +155,11 @@ public class ApkSignatureSchemeV4Verifier { */ public static class VerifiedSigner { public final Certificate[] certs; public byte[] v3Digest; public byte[] apkDigest; public VerifiedSigner(Certificate[] certs, byte[] v3Digest) { public VerifiedSigner(Certificate[] certs, byte[] apkDigest) { this.certs = certs; this.v3Digest = v3Digest; this.apkDigest = apkDigest; } } Loading core/java/android/util/apk/ApkSignatureVerifier.java +29 −11 Original line number Diff line number Diff line Loading @@ -184,27 +184,45 @@ public class ApkSignatureVerifier { Signature[] signerSigs = convertToSignatures(signerCerts); if (verifyFull) { // v4 is an add-on and requires v3 signature to validate against its certificates ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming = byte[] nonstreamingDigest = null; Certificate[][] nonstreamingCerts = null; try { // v4 is an add-on and requires v2 or v3 signature to validate against its // certificate and digest ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer = ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath); Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs}; Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts); nonstreamingDigest = v3Signer.digest; nonstreamingCerts = new Certificate[][]{v3Signer.certs}; } catch (SignatureNotFoundException e) { try { ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer = ApkSignatureSchemeV2Verifier.verify(apkPath, false); nonstreamingDigest = v2Signer.digest; nonstreamingCerts = v2Signer.certs; } catch (SignatureNotFoundException ee) { throw new SecurityException( "V4 verification failed to collect V2/V3 certificates from : " + apkPath, ee); } } Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts); if (nonstreamingSigs.length != signerSigs.length) { throw new SecurityException( "Invalid number of certificates: " + nonstreaming.certs.length); "Invalid number of certificates: " + nonstreamingSigs.length); } for (int i = 0, size = signerSigs.length; i < size; ++i) { if (!nonstreamingSigs[i].equals(signerSigs[i])) { throw new SecurityException("V4 signature certificate does not match V3"); throw new SecurityException( "V4 signature certificate does not match V2/V3"); } } // TODO(b/151240006): add support for v2 digest and make it mandatory. if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest, nonstreaming.digest, vSigner.v3Digest.length)) { throw new SecurityException("V3 digest in V4 signature does not match V3"); if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest, vSigner.apkDigest.length)) { throw new SecurityException("APK digest in V4 signature does not match V2/V3"); } } Loading Loading
core/java/android/os/incremental/V4Signature.java +7 −7 Original line number Diff line number Diff line Loading @@ -72,16 +72,16 @@ public class V4Signature { * V4 signature data. */ public static class SigningInfo { public final byte[] v3Digest; // used to match with the corresponding APK public final byte[] apkDigest; // used to match with the corresponding APK public final byte[] certificate; // ASN.1 DER form public final byte[] additionalData; // a free-form binary data blob public final byte[] publicKey; // ASN.1 DER, must match the certificate public final int signatureAlgorithmId; // see the APK v2 doc for the list public final byte[] signature; SigningInfo(byte[] v3Digest, byte[] certificate, byte[] additionalData, SigningInfo(byte[] apkDigest, byte[] certificate, byte[] additionalData, byte[] publicKey, int signatureAlgorithmId, byte[] signature) { this.v3Digest = v3Digest; this.apkDigest = apkDigest; this.certificate = certificate; this.additionalData = additionalData; this.publicKey = publicKey; Loading @@ -94,13 +94,13 @@ public class V4Signature { */ public static SigningInfo fromByteArray(byte[] bytes) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN); byte[] v3Digest = readBytes(buffer); byte[] apkDigest = readBytes(buffer); byte[] certificate = readBytes(buffer); byte[] additionalData = readBytes(buffer); byte[] publicKey = readBytes(buffer); int signatureAlgorithmId = buffer.getInt(); byte[] signature = readBytes(buffer); return new SigningInfo(v3Digest, certificate, additionalData, publicKey, return new SigningInfo(apkDigest, certificate, additionalData, publicKey, signatureAlgorithmId, signature); } } Loading Loading @@ -150,7 +150,7 @@ public class V4Signature { final int size = 4/*size*/ + 8/*fileSize*/ + 4/*hash_algorithm*/ + 1/*log2_blocksize*/ + bytesSize( hashingInfo.salt) + bytesSize(hashingInfo.rawRootHash) + bytesSize( signingInfo.v3Digest) + bytesSize(signingInfo.certificate) + bytesSize( signingInfo.apkDigest) + bytesSize(signingInfo.certificate) + bytesSize( signingInfo.additionalData); ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(size); Loading @@ -159,7 +159,7 @@ public class V4Signature { buffer.put(hashingInfo.log2BlockSize); writeBytes(buffer, hashingInfo.salt); writeBytes(buffer, hashingInfo.rawRootHash); writeBytes(buffer, signingInfo.v3Digest); writeBytes(buffer, signingInfo.apkDigest); writeBytes(buffer, signingInfo.certificate); writeBytes(buffer, signingInfo.additionalData); return buffer.array(); Loading
core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +12 −3 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.util.ArrayMap; Loading Loading @@ -117,7 +118,10 @@ public class ApkSignatureSchemeV2Verifier { return vSigner.certs; } private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) /** * Same as above returns the full signer object, containing additional info e.g. digest. */ public static VerifiedSigner verify(String apkFile, boolean verifyIntegrity) throws SignatureNotFoundException, SecurityException, IOException { try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) { return verify(apk, verifyIntegrity); Loading Loading @@ -209,9 +213,11 @@ public class ApkSignatureSchemeV2Verifier { verityDigest, apk.length(), signatureInfo); } byte[] digest = pickBestDigestForV4(contentDigests); return new VerifiedSigner( signerCerts.toArray(new X509Certificate[signerCerts.size()][]), verityRootHash); verityRootHash, digest); } private static X509Certificate[] verifySigner( Loading Loading @@ -426,11 +432,14 @@ public class ApkSignatureSchemeV2Verifier { */ public static class VerifiedSigner { public final X509Certificate[][] certs; public final byte[] verityRootHash; public final byte[] digest; public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) { public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash, byte[] digest) { this.certs = certs; this.verityRootHash = verityRootHash; this.digest = digest; } } Loading
core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +2 −16 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package android.util.apk; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512; import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256; import static android.util.apk.ApkSigningBlockUtils.compareSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getContentDigestAlgorithmJcaDigestAlgorithm; Loading @@ -26,6 +24,7 @@ import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmContent import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaKeyAlgorithm; import static android.util.apk.ApkSigningBlockUtils.getSignatureAlgorithmJcaSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.isSupportedSignatureAlgorithm; import static android.util.apk.ApkSigningBlockUtils.pickBestDigestForV4; import static android.util.apk.ApkSigningBlockUtils.readLengthPrefixedByteArray; import android.os.Build; Loading Loading @@ -213,24 +212,11 @@ public class ApkSignatureSchemeV3Verifier { verityDigest, apk.length(), signatureInfo); } result.digest = pickBestV3DigestForV4(contentDigests); result.digest = pickBestDigestForV4(contentDigests); return result; } // Keep in sync with pickBestV3DigestForV4 in apksigner.V3SchemeVerifier. private static byte[] pickBestV3DigestForV4(Map<Integer, byte[]> contentDigests) { final int[] orderedContentDigestTypes = {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256, CONTENT_DIGEST_CHUNKED_SHA256}; for (int contentDigestType : orderedContentDigestTypes) { if (contentDigests.containsKey(contentDigestType)) { return contentDigests.get(contentDigestType); } } return null; } private static VerifiedSigner verifySigner( ByteBuffer signerBlock, Map<Integer, byte[]> contentDigests, Loading
core/java/android/util/apk/ApkSignatureSchemeV4Verifier.java +4 −4 Original line number Diff line number Diff line Loading @@ -145,7 +145,7 @@ public class ApkSignatureSchemeV4Verifier { "Public key mismatch between certificate and signature record"); } return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.v3Digest); return new VerifiedSigner(new Certificate[]{certificate}, signingInfo.apkDigest); } /** Loading @@ -155,11 +155,11 @@ public class ApkSignatureSchemeV4Verifier { */ public static class VerifiedSigner { public final Certificate[] certs; public byte[] v3Digest; public byte[] apkDigest; public VerifiedSigner(Certificate[] certs, byte[] v3Digest) { public VerifiedSigner(Certificate[] certs, byte[] apkDigest) { this.certs = certs; this.v3Digest = v3Digest; this.apkDigest = apkDigest; } } Loading
core/java/android/util/apk/ApkSignatureVerifier.java +29 −11 Original line number Diff line number Diff line Loading @@ -184,27 +184,45 @@ public class ApkSignatureVerifier { Signature[] signerSigs = convertToSignatures(signerCerts); if (verifyFull) { // v4 is an add-on and requires v3 signature to validate against its certificates ApkSignatureSchemeV3Verifier.VerifiedSigner nonstreaming = byte[] nonstreamingDigest = null; Certificate[][] nonstreamingCerts = null; try { // v4 is an add-on and requires v2 or v3 signature to validate against its // certificate and digest ApkSignatureSchemeV3Verifier.VerifiedSigner v3Signer = ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath); Certificate[][] nonstreamingCerts = new Certificate[][]{nonstreaming.certs}; Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts); nonstreamingDigest = v3Signer.digest; nonstreamingCerts = new Certificate[][]{v3Signer.certs}; } catch (SignatureNotFoundException e) { try { ApkSignatureSchemeV2Verifier.VerifiedSigner v2Signer = ApkSignatureSchemeV2Verifier.verify(apkPath, false); nonstreamingDigest = v2Signer.digest; nonstreamingCerts = v2Signer.certs; } catch (SignatureNotFoundException ee) { throw new SecurityException( "V4 verification failed to collect V2/V3 certificates from : " + apkPath, ee); } } Signature[] nonstreamingSigs = convertToSignatures(nonstreamingCerts); if (nonstreamingSigs.length != signerSigs.length) { throw new SecurityException( "Invalid number of certificates: " + nonstreaming.certs.length); "Invalid number of certificates: " + nonstreamingSigs.length); } for (int i = 0, size = signerSigs.length; i < size; ++i) { if (!nonstreamingSigs[i].equals(signerSigs[i])) { throw new SecurityException("V4 signature certificate does not match V3"); throw new SecurityException( "V4 signature certificate does not match V2/V3"); } } // TODO(b/151240006): add support for v2 digest and make it mandatory. if (!ArrayUtils.isEmpty(vSigner.v3Digest) && !ArrayUtils.equals(vSigner.v3Digest, nonstreaming.digest, vSigner.v3Digest.length)) { throw new SecurityException("V3 digest in V4 signature does not match V3"); if (!ArrayUtils.equals(vSigner.apkDigest, nonstreamingDigest, vSigner.apkDigest.length)) { throw new SecurityException("APK digest in V4 signature does not match V2/V3"); } } Loading