private static ByteBuffer findApkSignatureSchemeV2Block( ByteBuffer apkSigningBlock, Result result) throws SignatureNotFoundException { checkByteOrderLittleEndian(apkSigningBlock); ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24); return getByteBuffer(pairs, len - 4);
/** * Returns positive number if {@code alg1} is preferred over {@code alg2}, {@code -1} if * {@code alg2} is preferred over {@code alg1}, and {@code 0} if there is no preference. */ private static int compareSignatureAlgorithm(SignatureAlgorithm alg1, SignatureAlgorithm alg2) { ContentDigestAlgorithm digestAlg1 = alg1.getContentDigestAlgorithm(); ContentDigestAlgorithm digestAlg2 = alg2.getContentDigestAlgorithm(); return compareContentDigestAlgorithm(digestAlg1, digestAlg2); }
private static List<SupportedSignature> getSignaturesToVerify( List<SupportedSignature> signatures) { // Pick the signature with the strongest algorithm, to mimic Android's behavior. SignatureAlgorithm bestSigAlgorithm = null; byte[] bestSigAlgorithmSignatureBytes = null; for (SupportedSignature sig : signatures) { SignatureAlgorithm sigAlgorithm = sig.algorithm; if ((bestSigAlgorithm == null) || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) { bestSigAlgorithm = sigAlgorithm; bestSigAlgorithmSignatureBytes = sig.signature; } } if (bestSigAlgorithm == null) { return Collections.emptyList(); } else { return Collections.singletonList( new SupportedSignature(bestSigAlgorithm, bestSigAlgorithmSignatureBytes)); } }
ByteBuffer signers; try { signers = getLengthPrefixedSlice(apkSignatureSchemeV2Block); } catch (ApkFormatException e) { result.addError(Issue.V2_SIG_MALFORMED_SIGNERS); result.signers.add(signerInfo); try { ByteBuffer signer = getLengthPrefixedSlice(signers); parseSigner(signer, certFactory, signerInfo, contentDigestsToVerify); } catch (ApkFormatException | BufferUnderflowException e) { signerInfo.addError(Issue.V2_SIG_MALFORMED_SIGNER);
/** * Verifies the provided APK's APK Signature Scheme v2 signatures and returns the result of * verification. APK is considered verified only if {@link Result#verified} is {@code true}. If * verification fails, the result will contain errors -- see {@link Result#getErrors()}. * * @throws ApkFormatException if the APK is malformed * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a * required cryptographic algorithm implementation is missing * @throws SignatureNotFoundException if no APK Signature Scheme v2 signatures are found * @throws IOException if an I/O error occurs when reading the APK */ public static Result verify(DataSource apk, ApkUtils.ZipSections zipSections) throws IOException, ApkFormatException, NoSuchAlgorithmException, SignatureNotFoundException { Result result = new Result(); SignatureInfo signatureInfo = findSignature(apk, zipSections, result); DataSource beforeApkSigningBlock = apk.slice(0, signatureInfo.apkSigningBlockOffset); DataSource centralDir = apk.slice( signatureInfo.centralDirOffset, signatureInfo.eocdOffset - signatureInfo.centralDirOffset); ByteBuffer eocd = signatureInfo.eocd; verify(beforeApkSigningBlock, signatureInfo.signatureBlock, centralDir, eocd, result); return result; }
Set<ContentDigestAlgorithm> contentDigestsToVerify) throws ApkFormatException, NoSuchAlgorithmException { ByteBuffer signedData = getLengthPrefixedSlice(signerBlock); byte[] signedDataBytes = new byte[signedData.remaining()]; signedData.get(signedDataBytes); ByteBuffer signatures = getLengthPrefixedSlice(signerBlock); byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock); signatureCount++; try { ByteBuffer signature = getLengthPrefixedSlice(signatures); int sigAlgorithmId = signature.getInt(); byte[] sigBytes = readLengthPrefixedByteArray(signature); result.signatures.add( new Result.SignerInfo.Signature(sigAlgorithmId, sigBytes)); List<SupportedSignature> signaturesToVerify = getSignaturesToVerify(supportedSignatures); if (signaturesToVerify.isEmpty()) { result.addError(Issue.V2_SIG_NO_SUPPORTED_SIGNATURES); ByteBuffer digests = getLengthPrefixedSlice(signedData); ByteBuffer certificates = getLengthPrefixedSlice(signedData); ByteBuffer additionalAttributes = getLengthPrefixedSlice(signedData); while (certificates.hasRemaining()) { certificateIndex++; byte[] encodedCert = readLengthPrefixedByteArray(certificates); X509Certificate certificate;
/** * Returns the APK Signature Scheme v2 block contained in the provided APK file and the * additional information relevant for verifying the block against the file. * * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2 * @throws IOException if an I/O error occurs while reading the APK */ private static SignatureInfo findSignature( DataSource apk, ApkUtils.ZipSections zipSections, Result result) throws IOException, SignatureNotFoundException { // Find the APK Signing Block. The block immediately precedes the Central Directory. ByteBuffer eocd = zipSections.getZipEndOfCentralDirectory(); Pair<DataSource, Long> apkSigningBlockAndOffset = findApkSigningBlock(apk, zipSections); DataSource apkSigningBlock = apkSigningBlockAndOffset.getFirst(); long apkSigningBlockOffset = apkSigningBlockAndOffset.getSecond(); ByteBuffer apkSigningBlockBuf = apkSigningBlock.getByteBuffer(0, (int) apkSigningBlock.size()); apkSigningBlockBuf.order(ByteOrder.LITTLE_ENDIAN); // Find the APK Signature Scheme v2 Block inside the APK Signing Block. ByteBuffer apkSignatureSchemeV2Block = findApkSignatureSchemeV2Block(apkSigningBlockBuf, result); return new SignatureInfo( apkSignatureSchemeV2Block, apkSigningBlockOffset, zipSections.getZipCentralDirectoryOffset(), zipSections.getZipEndOfCentralDirectoryOffset(), eocd); }
private static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws ApkFormatException { if (source.remaining() < 4) { throw new ApkFormatException( "Remaining buffer too short to contain length of length-prefixed field" + ". Remaining: " + source.remaining()); } int len = source.getInt(); if (len < 0) { throw new IllegalArgumentException("Negative length"); } else if (len > source.remaining()) { throw new ApkFormatException( "Length-prefixed field longer than remaining buffer" + ". Field length: " + len + ", remaining: " + source.remaining()); } return getByteBuffer(source, len); }
try { Pair<DataSource, Long> apkSigningBlockAndOffset = V2SchemeVerifier.findApkSigningBlock(inputApk, inputZipSections); inputApkSigningBlock = apkSigningBlockAndOffset.getFirst(); inputApkSigningBlockOffset = apkSigningBlockAndOffset.getSecond();