use of com.android.apksig.internal.apk.ApkSigResult in project apksig by venshine.
the class SourceStampVerifier method verifySourceStamp.
/**
* Verifies the provided {@code apk}'s source stamp signature, including verification of the
* SHA-256 digest of the stamp signing certificate matches the {@code expectedCertDigest}, and
* returns the result of the verification.
*
* @see #verifySourceStamp(String)
*/
private SourceStampVerifier.Result verifySourceStamp(DataSource apk, String expectedCertDigest) {
Result result = new Result();
try {
ZipSections zipSections = ApkUtilsLite.findZipSections(apk);
// Attempt to obtain the source stamp's certificate digest from the APK.
List<CentralDirectoryRecord> cdRecords = ZipUtils.parseZipCentralDirectory(apk, zipSections);
CentralDirectoryRecord sourceStampCdRecord = null;
for (CentralDirectoryRecord cdRecord : cdRecords) {
if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) {
sourceStampCdRecord = cdRecord;
break;
}
}
// APK's signature block to determine the appropriate status to return.
if (sourceStampCdRecord == null) {
boolean stampSigningBlockFound;
try {
ApkSigningBlockUtilsLite.findSignature(apk, zipSections, SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID);
stampSigningBlockFound = true;
} catch (SignatureNotFoundException e) {
stampSigningBlockFound = false;
}
result.addVerificationError(stampSigningBlockFound ? ApkVerificationIssue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST : ApkVerificationIssue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING);
return result;
}
// Verify that the contents of the source stamp certificate digest match the expected
// value, if provided.
byte[] sourceStampCertificateDigest = LocalFileRecord.getUncompressedData(apk, sourceStampCdRecord, zipSections.getZipCentralDirectoryOffset());
if (expectedCertDigest != null) {
String actualCertDigest = ApkSigningBlockUtilsLite.toHex(sourceStampCertificateDigest);
if (!expectedCertDigest.equalsIgnoreCase(actualCertDigest)) {
result.addVerificationError(ApkVerificationIssue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH, actualCertDigest, expectedCertDigest);
return result;
}
}
Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeApkContentDigests = new HashMap<>();
if (mMaxSdkVersion >= AndroidSdkVersion.P) {
SignatureInfo signatureInfo;
try {
signatureInfo = ApkSigningBlockUtilsLite.findSignature(apk, zipSections, V3SchemeConstants.APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
} catch (SignatureNotFoundException e) {
signatureInfo = null;
}
if (signatureInfo != null) {
Map<ContentDigestAlgorithm, byte[]> apkContentDigests = new EnumMap<>(ContentDigestAlgorithm.class);
parseSigners(signatureInfo.signatureBlock, VERSION_APK_SIGNATURE_SCHEME_V3, apkContentDigests, result);
signatureSchemeApkContentDigests.put(VERSION_APK_SIGNATURE_SCHEME_V3, apkContentDigests);
}
}
if (mMaxSdkVersion >= AndroidSdkVersion.N && (mMinSdkVersion < AndroidSdkVersion.P || signatureSchemeApkContentDigests.isEmpty())) {
SignatureInfo signatureInfo;
try {
signatureInfo = ApkSigningBlockUtilsLite.findSignature(apk, zipSections, V2SchemeConstants.APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
} catch (SignatureNotFoundException e) {
signatureInfo = null;
}
if (signatureInfo != null) {
Map<ContentDigestAlgorithm, byte[]> apkContentDigests = new EnumMap<>(ContentDigestAlgorithm.class);
parseSigners(signatureInfo.signatureBlock, VERSION_APK_SIGNATURE_SCHEME_V2, apkContentDigests, result);
signatureSchemeApkContentDigests.put(VERSION_APK_SIGNATURE_SCHEME_V2, apkContentDigests);
}
}
if (mMinSdkVersion < AndroidSdkVersion.N || signatureSchemeApkContentDigests.isEmpty()) {
Map<ContentDigestAlgorithm, byte[]> apkContentDigests = getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections, result);
signatureSchemeApkContentDigests.put(VERSION_JAR_SIGNATURE_SCHEME, apkContentDigests);
}
ApkSigResult sourceStampResult = V2SourceStampVerifier.verify(apk, zipSections, sourceStampCertificateDigest, signatureSchemeApkContentDigests, mMinSdkVersion, mMaxSdkVersion);
result.mergeFrom(sourceStampResult);
return result;
} catch (ApkFormatException | IOException | ZipFormatException e) {
result.addVerificationError(ApkVerificationIssue.MALFORMED_APK, e);
} catch (NoSuchAlgorithmException e) {
result.addVerificationError(ApkVerificationIssue.UNEXPECTED_EXCEPTION, e);
} catch (SignatureNotFoundException e) {
result.addVerificationError(ApkVerificationIssue.SOURCE_STAMP_SIG_MISSING);
}
return result;
}
use of com.android.apksig.internal.apk.ApkSigResult in project apksig by venshine.
the class V2SourceStampVerifier method verify.
/**
* Verifies the provided APK's SourceStamp signatures and returns the result of verification.
* The APK must be considered verified only if {@link ApkSigResult#verified} is
* {@code true}. If verification fails, the result will contain errors -- see {@link
* ApkSigResult#getErrors()}.
*
* @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
* required cryptographic algorithm implementation is missing
* @throws SignatureNotFoundException if no SourceStamp signatures are
* found
* @throws IOException if an I/O error occurs when reading the APK
*/
public static ApkSigResult verify(DataSource apk, ZipSections zipSections, byte[] sourceStampCertificateDigest, Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeApkContentDigests, int minSdkVersion, int maxSdkVersion) throws IOException, NoSuchAlgorithmException, SignatureNotFoundException {
ApkSigResult result = new ApkSigResult(Constants.VERSION_SOURCE_STAMP);
SignatureInfo signatureInfo = ApkSigningBlockUtilsLite.findSignature(apk, zipSections, V2_SOURCE_STAMP_BLOCK_ID);
verify(signatureInfo.signatureBlock, sourceStampCertificateDigest, signatureSchemeApkContentDigests, minSdkVersion, maxSdkVersion, result);
return result;
}
use of com.android.apksig.internal.apk.ApkSigResult in project apksig by venshine.
the class ApkVerifier method verify.
/**
* Verifies the APK's signatures and returns the result of verification. The APK can be
* considered verified iff the result's {@link Result#isVerified()} returns {@code true}.
* The verification result also includes errors, warnings, and information about signers.
*
* @param apk APK file contents
* @throws IOException if an I/O error is encountered while reading the APK
* @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
*/
private Result verify(DataSource apk) throws IOException, ApkFormatException, NoSuchAlgorithmException {
int maxSdkVersion = mMaxSdkVersion;
ApkUtils.ZipSections zipSections;
try {
zipSections = ApkUtils.findZipSections(apk);
} catch (ZipFormatException e) {
throw new ApkFormatException("Malformed APK: not a ZIP archive", e);
}
ByteBuffer androidManifest = null;
int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections);
Result result = new Result();
Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeApkContentDigests = new HashMap<>();
// The SUPPORTED_APK_SIG_SCHEME_NAMES contains the mapping from version number to scheme
// name, but the verifiers use this parameter as the schemes supported by the target SDK
// range. Since the code below skips signature verification based on max SDK the mapping of
// supported schemes needs to be modified to ensure the verifiers do not report a stripped
// signature for an SDK range that does not support that signature version. For instance an
// APK with V1, V2, and V3 signatures and a max SDK of O would skip the V3 signature
// verification, but the SUPPORTED_APK_SIG_SCHEME_NAMES contains version 3, so when the V2
// verification is performed it would see the stripping protection attribute, see that V3
// is in the list of supported signatures, and report a stripped signature.
Map<Integer, String> supportedSchemeNames = getSupportedSchemeNames(maxSdkVersion);
// Android N and newer attempts to verify APKs using the APK Signing Block, which can
// include v2 and/or v3 signatures. If none is found, it falls back to JAR signature
// verification. If the signature is found but does not verify, the APK is rejected.
Set<Integer> foundApkSigSchemeIds = new HashSet<>(2);
if (maxSdkVersion >= AndroidSdkVersion.N) {
RunnablesExecutor executor = RunnablesExecutor.SINGLE_THREADED;
// Android P and newer attempts to verify APKs using APK Signature Scheme v3
if (maxSdkVersion >= AndroidSdkVersion.P) {
try {
ApkSigningBlockUtils.Result v3Result = V3SchemeVerifier.verify(executor, apk, zipSections, Math.max(minSdkVersion, AndroidSdkVersion.P), maxSdkVersion);
foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
result.mergeFrom(v3Result);
signatureSchemeApkContentDigests.put(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3, getApkContentDigestsFromSigningSchemeResult(v3Result));
} catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) {
// v3 signature not required
}
if (result.containsErrors()) {
return result;
}
}
// no APK Signature Scheme v3 (or newer scheme) signatures were found.
if (minSdkVersion < AndroidSdkVersion.P || foundApkSigSchemeIds.isEmpty()) {
try {
ApkSigningBlockUtils.Result v2Result = V2SchemeVerifier.verify(executor, apk, zipSections, supportedSchemeNames, foundApkSigSchemeIds, Math.max(minSdkVersion, AndroidSdkVersion.N), maxSdkVersion);
foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
result.mergeFrom(v2Result);
signatureSchemeApkContentDigests.put(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2, getApkContentDigestsFromSigningSchemeResult(v2Result));
} catch (ApkSigningBlockUtils.SignatureNotFoundException ignored) {
// v2 signature not required
}
if (result.containsErrors()) {
return result;
}
}
// If v4 file is specified, use additional verification on it
if (mV4SignatureFile != null) {
final ApkSigningBlockUtils.Result v4Result = V4SchemeVerifier.verify(apk, mV4SignatureFile);
foundApkSigSchemeIds.add(ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V4);
result.mergeFrom(v4Result);
if (result.containsErrors()) {
return result;
}
}
}
// are signed using APK Signature Scheme v2 or newer.
if (maxSdkVersion >= AndroidSdkVersion.O) {
if (androidManifest == null) {
androidManifest = getAndroidManifestFromApk(apk, zipSections);
}
int targetSandboxVersion = getTargetSandboxVersionFromBinaryAndroidManifest(androidManifest.slice());
if (targetSandboxVersion > 1) {
if (foundApkSigSchemeIds.isEmpty()) {
result.addError(Issue.NO_SIG_FOR_TARGET_SANDBOX_VERSION, targetSandboxVersion);
}
}
}
List<CentralDirectoryRecord> cdRecords = V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections);
// scheme) signatures were found.
if ((minSdkVersion < AndroidSdkVersion.N) || (foundApkSigSchemeIds.isEmpty())) {
V1SchemeVerifier.Result v1Result = V1SchemeVerifier.verify(apk, zipSections, supportedSchemeNames, foundApkSigSchemeIds, minSdkVersion, maxSdkVersion);
result.mergeFrom(v1Result);
signatureSchemeApkContentDigests.put(ApkSigningBlockUtils.VERSION_JAR_SIGNATURE_SCHEME, getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections));
}
if (result.containsErrors()) {
return result;
}
// Verify the SourceStamp, if found in the APK.
try {
CentralDirectoryRecord sourceStampCdRecord = null;
for (CentralDirectoryRecord cdRecord : cdRecords) {
if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) {
sourceStampCdRecord = cdRecord;
break;
}
}
// block in the APK signing block as well.
if (sourceStampCdRecord != null) {
byte[] sourceStampCertificateDigest = LocalFileRecord.getUncompressedData(apk, sourceStampCdRecord, zipSections.getZipCentralDirectoryOffset());
ApkSigResult sourceStampResult = V2SourceStampVerifier.verify(apk, zipSections, sourceStampCertificateDigest, signatureSchemeApkContentDigests, Math.max(minSdkVersion, AndroidSdkVersion.R), maxSdkVersion);
result.mergeFrom(sourceStampResult);
}
} catch (SignatureNotFoundException ignored) {
result.addWarning(Issue.SOURCE_STAMP_SIG_MISSING);
} catch (ZipFormatException e) {
throw new ApkFormatException("Failed to read APK", e);
}
if (result.containsErrors()) {
return result;
}
// signatures verified.
if ((result.isVerifiedUsingV1Scheme()) && (result.isVerifiedUsingV2Scheme())) {
ArrayList<Result.V1SchemeSignerInfo> v1Signers = new ArrayList<>(result.getV1SchemeSigners());
ArrayList<Result.V2SchemeSignerInfo> v2Signers = new ArrayList<>(result.getV2SchemeSigners());
ArrayList<ByteArray> v1SignerCerts = new ArrayList<>();
ArrayList<ByteArray> v2SignerCerts = new ArrayList<>();
for (Result.V1SchemeSignerInfo signer : v1Signers) {
try {
v1SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded()));
} catch (CertificateEncodingException e) {
throw new IllegalStateException("Failed to encode JAR signer " + signer.getName() + " certs", e);
}
}
for (Result.V2SchemeSignerInfo signer : v2Signers) {
try {
v2SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded()));
} catch (CertificateEncodingException e) {
throw new IllegalStateException("Failed to encode APK Signature Scheme v2 signer (index: " + signer.getIndex() + ") certs", e);
}
}
for (int i = 0; i < v1SignerCerts.size(); i++) {
ByteArray v1Cert = v1SignerCerts.get(i);
if (!v2SignerCerts.contains(v1Cert)) {
Result.V1SchemeSignerInfo v1Signer = v1Signers.get(i);
v1Signer.addError(Issue.V2_SIG_MISSING);
break;
}
}
for (int i = 0; i < v2SignerCerts.size(); i++) {
ByteArray v2Cert = v2SignerCerts.get(i);
if (!v1SignerCerts.contains(v2Cert)) {
Result.V2SchemeSignerInfo v2Signer = v2Signers.get(i);
v2Signer.addError(Issue.JAR_SIG_MISSING);
break;
}
}
}
// matches the oldest signing certificate in the provided SigningCertificateLineage
if (result.isVerifiedUsingV3Scheme() && (result.isVerifiedUsingV1Scheme() || result.isVerifiedUsingV2Scheme())) {
SigningCertificateLineage lineage = result.getSigningCertificateLineage();
X509Certificate oldSignerCert;
if (result.isVerifiedUsingV1Scheme()) {
List<Result.V1SchemeSignerInfo> v1Signers = result.getV1SchemeSigners();
if (v1Signers.size() != 1) {
// APK Signature Scheme v3 only supports single-signers, error to sign with
// multiple and then only one
result.addError(Issue.V3_SIG_MULTIPLE_PAST_SIGNERS);
}
oldSignerCert = v1Signers.get(0).mCertChain.get(0);
} else {
List<Result.V2SchemeSignerInfo> v2Signers = result.getV2SchemeSigners();
if (v2Signers.size() != 1) {
// APK Signature Scheme v3 only supports single-signers, error to sign with
// multiple and then only one
result.addError(Issue.V3_SIG_MULTIPLE_PAST_SIGNERS);
}
oldSignerCert = v2Signers.get(0).mCerts.get(0);
}
if (lineage == null) {
// no signing certificate history with which to contend, just make sure that v3
// matches previous versions
List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners();
if (v3Signers.size() != 1) {
// multiple v3 signers should never exist without rotation history, since
// multiple signers implies a different signer for different platform versions
result.addError(Issue.V3_SIG_MULTIPLE_SIGNERS);
}
try {
if (!Arrays.equals(oldSignerCert.getEncoded(), v3Signers.get(0).mCerts.get(0).getEncoded())) {
result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
}
} catch (CertificateEncodingException e) {
// we just go the encoding for the v1/v2 certs above, so must be v3
throw new RuntimeException("Failed to encode APK Signature Scheme v3 signer cert", e);
}
} else {
// as our v1/v2 signer
try {
lineage = lineage.getSubLineage(oldSignerCert);
if (lineage.size() != 1) {
// the v1/v2 signer was found, but not at the root of the lineage
result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
}
} catch (IllegalArgumentException e) {
// the v1/v2 signer was not found in the lineage
result.addError(Issue.V3_SIG_PAST_SIGNERS_MISMATCH);
}
}
}
// The apkDigest field in the v4 signature should match the selected v2/v3.
if (result.isVerifiedUsingV4Scheme()) {
List<Result.V4SchemeSignerInfo> v4Signers = result.getV4SchemeSigners();
if (v4Signers.size() != 1) {
result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS);
}
List<ApkSigningBlockUtils.Result.SignerInfo.ContentDigest> digestsFromV4 = v4Signers.get(0).getContentDigests();
if (digestsFromV4.size() != 1) {
result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH);
}
final byte[] digestFromV4 = digestsFromV4.get(0).getValue();
if (result.isVerifiedUsingV3Scheme()) {
List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners();
if (v3Signers.size() != 1) {
result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS);
}
// Compare certificates.
checkV4Certificate(v4Signers.get(0).mCerts, v3Signers.get(0).mCerts, result);
// Compare digests.
final byte[] digestFromV3 = pickBestDigestForV4(v3Signers.get(0).getContentDigests());
if (!Arrays.equals(digestFromV4, digestFromV3)) {
result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH);
}
} else if (result.isVerifiedUsingV2Scheme()) {
List<Result.V2SchemeSignerInfo> v2Signers = result.getV2SchemeSigners();
if (v2Signers.size() != 1) {
result.addError(Issue.V4_SIG_MULTIPLE_SIGNERS);
}
// Compare certificates.
checkV4Certificate(v4Signers.get(0).mCerts, v2Signers.get(0).mCerts, result);
// Compare digests.
final byte[] digestFromV2 = pickBestDigestForV4(v2Signers.get(0).getContentDigests());
if (!Arrays.equals(digestFromV4, digestFromV2)) {
result.addError(Issue.V4_SIG_V2_V3_DIGESTS_MISMATCH);
}
} else {
throw new RuntimeException("V4 signature must be also verified with V2/V3");
}
}
// that the APK was signed with at least that version.
try {
if (androidManifest == null) {
androidManifest = getAndroidManifestFromApk(apk, zipSections);
}
} catch (ApkFormatException e) {
// If the manifest is not available then skip the minimum signature scheme requirement
// to support bundle verification.
}
if (androidManifest != null) {
int targetSdkVersion = getTargetSdkVersionFromBinaryAndroidManifest(androidManifest.slice());
int minSchemeVersion = getMinimumSignatureSchemeVersionForTargetSdk(targetSdkVersion);
// expanded to verify the minimum based on the target and maximum SDK version.
if (minSchemeVersion > VERSION_JAR_SIGNATURE_SCHEME && maxSdkVersion >= targetSdkVersion) {
switch(minSchemeVersion) {
case VERSION_APK_SIGNATURE_SCHEME_V2:
if (result.isVerifiedUsingV2Scheme()) {
break;
}
// later scheme version will also satisfy this requirement.
case VERSION_APK_SIGNATURE_SCHEME_V3:
if (result.isVerifiedUsingV3Scheme()) {
break;
}
result.addError(Issue.MIN_SIG_SCHEME_FOR_TARGET_SDK_NOT_MET, targetSdkVersion, minSchemeVersion);
}
}
}
if (result.containsErrors()) {
return result;
}
// Verified
result.setVerified();
if (result.isVerifiedUsingV3Scheme()) {
List<Result.V3SchemeSignerInfo> v3Signers = result.getV3SchemeSigners();
result.addSignerCertificate(v3Signers.get(v3Signers.size() - 1).getCertificate());
} else if (result.isVerifiedUsingV2Scheme()) {
for (Result.V2SchemeSignerInfo signerInfo : result.getV2SchemeSigners()) {
result.addSignerCertificate(signerInfo.getCertificate());
}
} else if (result.isVerifiedUsingV1Scheme()) {
for (Result.V1SchemeSignerInfo signerInfo : result.getV1SchemeSigners()) {
result.addSignerCertificate(signerInfo.getCertificate());
}
} else {
throw new RuntimeException("APK verified, but has not verified using any of v1, v2 or v3 schemes");
}
return result;
}
use of com.android.apksig.internal.apk.ApkSigResult in project apksig by venshine.
the class ApkVerifier method verifySourceStamp.
/**
* Verifies the provided {@code apk}'s source stamp signature, including verification of the
* SHA-256 digest of the stamp signing certificate matches the {@code expectedCertDigest}, and
* returns the result of the verification.
*
* @see #verifySourceStamp(String)
*/
private Result verifySourceStamp(DataSource apk, String expectedCertDigest) {
try {
ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk);
int minSdkVersion = verifyAndGetMinSdkVersion(apk, zipSections);
// Attempt to obtain the source stamp's certificate digest from the APK.
List<CentralDirectoryRecord> cdRecords = V1SchemeVerifier.parseZipCentralDirectory(apk, zipSections);
CentralDirectoryRecord sourceStampCdRecord = null;
for (CentralDirectoryRecord cdRecord : cdRecords) {
if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(cdRecord.getName())) {
sourceStampCdRecord = cdRecord;
break;
}
}
// APK's signature block to determine the appropriate status to return.
if (sourceStampCdRecord == null) {
boolean stampSigningBlockFound;
try {
ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(ApkSigningBlockUtils.VERSION_SOURCE_STAMP);
ApkSigningBlockUtils.findSignature(apk, zipSections, SourceStampConstants.V2_SOURCE_STAMP_BLOCK_ID, result);
stampSigningBlockFound = true;
} catch (ApkSigningBlockUtils.SignatureNotFoundException e) {
stampSigningBlockFound = false;
}
if (stampSigningBlockFound) {
return createSourceStampResultWithError(Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_NOT_VERIFIED, Issue.SOURCE_STAMP_SIGNATURE_BLOCK_WITHOUT_CERT_DIGEST);
} else {
return createSourceStampResultWithError(Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_MISSING, Issue.SOURCE_STAMP_CERT_DIGEST_AND_SIG_BLOCK_MISSING);
}
}
// Verify that the contents of the source stamp certificate digest match the expected
// value, if provided.
byte[] sourceStampCertificateDigest = LocalFileRecord.getUncompressedData(apk, sourceStampCdRecord, zipSections.getZipCentralDirectoryOffset());
if (expectedCertDigest != null) {
String actualCertDigest = ApkSigningBlockUtils.toHex(sourceStampCertificateDigest);
if (!expectedCertDigest.equalsIgnoreCase(actualCertDigest)) {
return createSourceStampResultWithError(Result.SourceStampInfo.SourceStampVerificationStatus.CERT_DIGEST_MISMATCH, Issue.SOURCE_STAMP_EXPECTED_DIGEST_MISMATCH, actualCertDigest, expectedCertDigest);
}
}
Map<Integer, Map<ContentDigestAlgorithm, byte[]>> signatureSchemeApkContentDigests = new HashMap<>();
Map<Integer, String> supportedSchemeNames = getSupportedSchemeNames(mMaxSdkVersion);
Set<Integer> foundApkSigSchemeIds = new HashSet<>(2);
Result result = new Result();
ApkSigningBlockUtils.Result v3Result = null;
if (mMaxSdkVersion >= AndroidSdkVersion.P) {
v3Result = getApkContentDigests(apk, zipSections, foundApkSigSchemeIds, supportedSchemeNames, signatureSchemeApkContentDigests, VERSION_APK_SIGNATURE_SCHEME_V3, Math.max(minSdkVersion, AndroidSdkVersion.P));
if (v3Result != null && v3Result.containsErrors()) {
result.mergeFrom(v3Result);
return mergeSourceStampResult(Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, result);
}
}
ApkSigningBlockUtils.Result v2Result = null;
if (mMaxSdkVersion >= AndroidSdkVersion.N && (minSdkVersion < AndroidSdkVersion.P || foundApkSigSchemeIds.isEmpty())) {
v2Result = getApkContentDigests(apk, zipSections, foundApkSigSchemeIds, supportedSchemeNames, signatureSchemeApkContentDigests, VERSION_APK_SIGNATURE_SCHEME_V2, Math.max(minSdkVersion, AndroidSdkVersion.N));
if (v2Result != null && v2Result.containsErrors()) {
result.mergeFrom(v2Result);
return mergeSourceStampResult(Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, result);
}
}
if (minSdkVersion < AndroidSdkVersion.N || foundApkSigSchemeIds.isEmpty()) {
signatureSchemeApkContentDigests.put(VERSION_JAR_SIGNATURE_SCHEME, getApkContentDigestFromV1SigningScheme(cdRecords, apk, zipSections));
}
ApkSigResult sourceStampResult = V2SourceStampVerifier.verify(apk, zipSections, sourceStampCertificateDigest, signatureSchemeApkContentDigests, minSdkVersion, mMaxSdkVersion);
result.mergeFrom(sourceStampResult);
// as verified if the source stamp verification was successful.
if (sourceStampResult.verified) {
result.setVerified();
} else {
// To prevent APK signature verification with a failed / missing source stamp the
// source stamp verification will only log warnings; to allow the caller to capture
// the failure reason treat all warnings as errors.
result.setWarningsAsErrors(true);
}
return result;
} catch (ApkFormatException | IOException | ZipFormatException e) {
return createSourceStampResultWithError(Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, Issue.MALFORMED_APK, e);
} catch (NoSuchAlgorithmException e) {
return createSourceStampResultWithError(Result.SourceStampInfo.SourceStampVerificationStatus.VERIFICATION_ERROR, Issue.UNEXPECTED_EXCEPTION, e);
} catch (SignatureNotFoundException e) {
return createSourceStampResultWithError(Result.SourceStampInfo.SourceStampVerificationStatus.STAMP_NOT_VERIFIED, Issue.SOURCE_STAMP_SIG_MISSING);
}
}
Aggregations