///*
// * Copyright (C) 2016 The Android Open Source Project
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package com.android.apksig.internal.apk.v1;
//
//import com.android.apksig.ApkVerifier.Issue;
//import com.android.apksig.ApkVerifier.IssueWithParams;
//import com.android.apksig.apk.ApkFormatException;
//import com.android.apksig.apk.ApkUtils;
//import com.android.apksig.internal.jar.ManifestParser;
//import com.android.apksig.internal.util.AndroidSdkVersion;
//import com.android.apksig.internal.util.InclusiveIntRange;
//import com.android.apksig.internal.util.MessageDigestSink;
//import com.android.apksig.internal.zip.CentralDirectoryRecord;
//import com.android.apksig.internal.zip.LocalFileRecord;
//import com.android.apksig.util.DataSource;
//import com.android.apksig.zip.ZipFormatException;
//import java.io.IOException;
//import java.nio.ByteBuffer;
//import java.nio.ByteOrder;
//import java.security.MessageDigest;
//import java.security.NoSuchAlgorithmException;
//import java.security.SignatureException;
//import java.security.cert.CertificateException;
//import java.security.cert.X509Certificate;
//import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.Base64;
//import java.util.Base64.Decoder;
//import java.util.Collection;
//import java.util.Collections;
//import java.util.HashMap;
//import java.util.HashSet;
//import java.util.List;
//import java.util.Locale;
//import java.util.Map;
//import java.util.Set;
//import java.util.StringTokenizer;
//import java.util.jar.Attributes;
//import sun.security.pkcs.PKCS7;
//import sun.security.pkcs.SignerInfo;
//
///**
// * APK verifier which uses JAR signing (aka v1 signing scheme).
// *
// * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">Signed JAR File</a>
// */
//public abstract class V1SchemeVerifier {
//
// private static final String MANIFEST_ENTRY_NAME = V1SchemeSigner.MANIFEST_ENTRY_NAME;
//
// private V1SchemeVerifier() {}
//
// /**
// * Verifies the provided APK's JAR 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 IOException if an I/O error occurs when reading the APK
// * @throws NoSuchAlgorithmException if the APK's JAR signatures cannot be verified because a
// * required cryptographic algorithm implementation is missing
// */
// public static Result verify(
// DataSource apk,
// ApkUtils.ZipSections apkSections,
// Map<Integer, String> supportedApkSigSchemeNames,
// Set<Integer> foundApkSigSchemeIds,
// int minSdkVersion,
// int maxSdkVersion) throws IOException, ApkFormatException, NoSuchAlgorithmException {
// if (minSdkVersion > maxSdkVersion) {
// throw new IllegalArgumentException(
// "minSdkVersion (" + minSdkVersion + ") > maxSdkVersion (" + maxSdkVersion
// + ")");
// }
//
// Result result = new Result();
//
// // Parse the ZIP Central Directory and check that there are no entries with duplicate names.
// List<CentralDirectoryRecord> cdRecords = parseZipCentralDirectory(apk, apkSections);
// Set<String> cdEntryNames = checkForDuplicateEntries(cdRecords, result);
// if (result.containsErrors()) {
// return result;
// }
//
// // Verify JAR signature(s).
// Signers.verify(
// apk,
// apkSections.getZipCentralDirectoryOffset(),
// cdRecords,
// cdEntryNames,
// supportedApkSigSchemeNames,
// foundApkSigSchemeIds,
// minSdkVersion,
// maxSdkVersion,
// result);
//
// return result;
// }
//
// /**
// * Returns the set of entry names and reports any duplicate entry names in the {@code result}
// * as errors.
// */
// private static Set<String> checkForDuplicateEntries(
// List<CentralDirectoryRecord> cdRecords, Result result) {
// Set<String> cdEntryNames = new HashSet<>(cdRecords.size());
// Set<String> duplicateCdEntryNames = null;
// for (CentralDirectoryRecord cdRecord : cdRecords) {
// String entryName = cdRecord.getName();
// if (!cdEntryNames.add(entryName)) {
// // This is an error. Report this once per duplicate name.
// if (duplicateCdEntryNames == null) {
// duplicateCdEntryNames = new HashSet<>();
// }
// if (duplicateCdEntryNames.add(entryName)) {
// result.addError(Issue.JAR_SIG_DUPLICATE_ZIP_ENTRY, entryName);
// }
// }
// }
// return cdEntryNames;
// }
//
// /**
// * All JAR signers of an APK.
// */
// private static class Signers {
//
// /**
// * Verifies JAR signatures of the provided APK and populates the provided result container
// * with errors, warnings, and information about signers. The APK is considered verified if
// * the {@link Result#verified} is {@code true}.
// */
// private static void verify(
// DataSource apk,
// long cdStartOffset,
// List<CentralDirectoryRecord> cdRecords,
// Set<String> cdEntryNames,
// Map<Integer, String> supportedApkSigSchemeNames,
// Set<Integer> foundApkSigSchemeIds,
// int minSdkVersion,
// int maxSdkVersion,
// Result result) throws ApkFormatException, IOException, NoSuchAlgorithmException {
//
// // Find JAR manifest and signature block files.
// CentralDirectoryRecord manifestEntry = null;
// Map<String, CentralDirectoryRecord> sigFileEntries = new HashMap<>(1);
// List<CentralDirectoryRecord> sigBlockEntries = new ArrayList<>(1);
// for (CentralDirectoryRecord cdRecord : cdRecords) {
// String entryName = cdRecord.getName();
// if (!entryName.startsWith("META-INF/")) {
// continue;
// }
// if ((manifestEntry == null) && (MANIFEST_ENTRY_NAME.equals(entryName))) {
// manifestEntry = cdRecord;
// continue;
// }
// if (entryName.endsWith(".SF")) {
// sigFileEntries.put(entryName, cdRecord);
// continue;
// }
// if ((entryName.endsWith(".RSA"))
// || (entryName.endsWith(".DSA"))
// || (entryName.endsWith(".EC"))) {
// sigBlockEntries.add(cdRecord);
// continue;
// }
// }
// if (manifestEntry == null) {
// result.addError(Issue.JAR_SIG_NO_MANIFEST);
// return;
// }
//
// // Parse the JAR manifest and check that all JAR entries it references exist in the APK.
// byte[] manifestBytes;
// try {
// manifestBytes =
// LocalFileRecord.getUncompressedData(apk, manifestEntry, cdStartOffset);
// } catch (ZipFormatException e) {
// throw new ApkFormatException("Malformed ZIP entry: " + manifestEntry.getName(), e);
// }
// Map<String, ManifestParser.Section> entryNameToManifestSection = null;
// ManifestParser manifest = new ManifestParser(manifestBytes);
// ManifestParser.Section manifestMainSection = manifest.readSection();
// List<ManifestParser.Section> manifestIndividualSections = manifest.readAllSections();
// entryNameToManifestSection = new HashMap<>(manifestIndividualSections.size());
// int manifestSectionNumber = 0;
// for (ManifestParser.Section manifestSection : manifestIndividualSections) {
// manifestSectionNumber++;
// String entryName = manifestSection.getName();
// if (entryName == null) {
// result.addError(Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION, manifestSectionNumber);
// continue;
// }
// if (entryNameToManifestSection.put(entryName, manifestSection) != null) {
// result.addError(Issue.JAR_SIG_DUPLICATE_MANIFEST_SECTION, entryName);
// continue;
// }
// if (!cdEntryNames.contains(entryName)) {
// result.addError(
// Issue.JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST, entryName);
// continue;
// }
// }
// if (result.containsErrors()) {
// return;
// }
// // STATE OF AFFAIRS:
// // * All JAR entries listed in JAR manifest are present in the APK.
//
// // Identify signers
// List<Signer> signers = new ArrayList<>(sigBlockEntries.size());
// for (CentralDirectoryRecord sigBlockEntry : sigBlockEntries) {
// String sigBlockEntryName = sigBlockEntry.getName();
// int extensionDelimiterIndex = sigBlockEntryName.lastIndexOf('.');
// if (extensionDelimiterIndex == -1) {
// throw new RuntimeException(
// "Signature block file name does not contain extension: "
// + sigBlockEntryName);
// }
// String sigFileEntryName =
// sigBlockEntryName.substring(0, extensionDelimiterIndex) + ".SF";
// CentralDirectoryRecord sigFileEntry = sigFileEntries.get(sigFileEntryName);
// if (sigFileEntry == null) {
// result.addWarning(
// Issue.JAR_SIG_MISSING_FILE, sigBlockEntryName, sigFileEntryName);
// continue;
// }
// String signerName = sigBlockEntryName.substring("META-INF/".length());
// Result.SignerInfo signerInfo =
// new Result.SignerInfo(
// signerName, sigBlockEntryName, sigFileEntry.getName());
// Signer signer = new Signer(signerName, sigBlockEntry, sigFileEntry, signerInfo);
// signers.add(signer);
// }
// if (signers.isEmpty()) {
// result.addError(Issue.JAR_SIG_NO_SIGNATURES);
// return;
// }
//
// // Verify each signer's signature block file .(RSA|DSA|EC) against the corresponding
// // signature file .SF. Any error encountered for any signer terminates verification, to
// // mimic Android's behavior.
// for (Signer signer : signers) {
// signer.verifySigBlockAgainstSigFile(
// apk, cdStartOffset, minSdkVersion, maxSdkVersion);
// if (signer.getResult().containsErrors()) {
// result.signers.add(signer.getResult());
// }
// }
// if (result.containsErrors()) {
// return;
// }
// // STATE OF AFFAIRS:
// // * All JAR entries listed in JAR manifest are present in the APK.
// // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
//
// // Verify each signer's signature file (.SF) against the JAR manifest.
// List<Signer> remainingSigners = new ArrayList<>(signers.size());
// for (Signer signer : signers) {
// signer.verifySigFileAgainstManifest(
// manifestBytes,
// manifestMainSection,
// entryNameToManifestSection,
// supportedApkSigSchemeNames,
// foundApkSigSchemeIds,
// minSdkVersion,
// maxSdkVersion);
// if (signer.isIgnored()) {
// result.ignoredSigners.add(signer.getResult());
// } else {
// if (signer.getResult().containsErrors()) {
// result.signers.add(signer.getResult());
// } else {
// remainingSigners.add(signer);
// }
// }
// }
// if (result.containsErrors()) {
// return;
// }
// signers = remainingSigners;
// if (signers.isEmpty()) {
// result.addError(Issue.JAR_SIG_NO_SIGNATURES);
// return;
// }
// // STATE OF AFFAIRS:
// // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
// // * Contents of all JAR manifest sections listed in .SF files verify against .SF files.
// // * All JAR entries listed in JAR manifest are present in the APK.
//
// // Verify data of JAR entries against JAR manifest and .SF files. On Android, an APK's
// // JAR entry is considered signed by signers associated with an .SF file iff the entry
// // is mentioned in the .SF file and the entry's digest(s) mentioned in the JAR manifest
// // match theentry's uncompressed data. Android requires that all such JAR entries are
// // signed by the same set of signers. This set may be smaller than the set of signers
// // we've identified so far.
// Set<Signer> apkSigners =
// verifyJarEntriesAgainstManifestAndSigners(
// apk,
// cdStartOffset,
// cdRecords,
// entryNameToManifestSection,
// signers,
// minSdkVersion,
// maxSdkVersion,
// result);
// if (result.containsErrors()) {
// return;
// }
// // STATE OF AFFAIRS:
// // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
// // * Contents of all JAR manifest sections listed in .SF files verify against .SF files.
// // * All JAR entries listed in JAR manifest are present in the APK.
// // * All JAR entries present in the APK and supposed to be covered by JAR signature
// // (i.e., reside outside of META-INF/) are covered by signatures from the same set
// // of signers.
//
// // Report any JAR entries which aren't covered by signature.
// Set<String> signatureEntryNames = new HashSet<>(1 + result.signers.size() * 2);
// signatureEntryNames.add(manifestEntry.getName());
// for (Signer signer : apkSigners) {
// signatureEntryNames.add(signer.getSignatureBlockEntryName());
// signatureEntryNames.add(signer.getSignatureFileEntryName());
// }
// for (CentralDirectoryRecord cdRecord : cdRecords) {
// String entryName = cdRecord.getName();
// if ((entryName.startsWith("META-INF/"))
// && (!entryName.endsWith("/"))
// && (!signatureEntryNames.contains(entryName))) {
// result.addWarning(Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY, entryName);
// }
// }
//
// // Reflect the sets of used signers and ignored signers in the result.
// for (Signer signer : signers) {
// if (apkSigners.contains(signer)) {
// result.signers.add(signer.getResult());
// } else {
// result.ignoredSigners.add(signer.getResult());
// }
// }
//
// result.verified = true;
// }
// }
//
// private static class Signer {
// private final String mName;
// private final Result.SignerInfo mResult;
// private final CentralDirectoryRecord mSignatureFileEntry;
// private final CentralDirectoryRecord mSignatureBlockEntry;
// private boolean mIgnored;
//
// private byte[] mSigFileBytes;
// private Set<String> mSigFileEntryNames;
//
// private Signer(
// String name,
// CentralDirectoryRecord sigBlockEntry,
// CentralDirectoryRecord sigFileEntry,
// Result.SignerInfo result) {
// mName = name;
// mResult = result;
// mSignatureBlockEntry = sigBlockEntry;
// mSignatureFileEntry = sigFileEntry;
// }
//
// public String getName() {
// return mName;
// }
//
// public String getSignatureFileEntryName() {
// return mSignatureFileEntry.getName();
// }
//
// public String getSignatureBlockEntryName() {
// return mSignatureBlockEntry.getName();
// }
//
// void setIgnored() {
// mIgnored = true;
// }
//
// public boolean isIgnored() {
// return mIgnored;
// }
//
// public Set<String> getSigFileEntryNames() {
// return mSigFileEntryNames;
// }
//
// public Result.SignerInfo getResult() {
// return mResult;
// }
//
// @SuppressWarnings("restriction")
// public void verifySigBlockAgainstSigFile(
// DataSource apk, long cdStartOffset, int minSdkVersion, int maxSdkVersion)
// throws IOException, ApkFormatException, NoSuchAlgorithmException {
// byte[] sigBlockBytes;
// try {
// sigBlockBytes =
// LocalFileRecord.getUncompressedData(
// apk, mSignatureBlockEntry, cdStartOffset);
// } catch (ZipFormatException e) {
// throw new ApkFormatException(
// "Malformed ZIP entry: " + mSignatureBlockEntry.getName(), e);
// }
// try {
// mSigFileBytes =
// LocalFileRecord.getUncompressedData(
// apk, mSignatureFileEntry, cdStartOffset);
// } catch (ZipFormatException e) {
// throw new ApkFormatException(
// "Malformed ZIP entry: " + mSignatureFileEntry.getName(), e);
// }
// PKCS7 sigBlock;
// try {
// sigBlock = new PKCS7(sigBlockBytes);
// } catch (IOException e) {
// if (e.getCause() instanceof CertificateException) {
// mResult.addError(
// Issue.JAR_SIG_MALFORMED_CERTIFICATE, mSignatureBlockEntry.getName(), e);
// } else {
// mResult.addError(
// Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e);
// }
// return;
// }
// SignerInfo[] unverifiedSignerInfos = sigBlock.getSignerInfos();
// if ((unverifiedSignerInfos == null) || (unverifiedSignerInfos.length == 0)) {
// mResult.addError(Issue.JAR_SIG_NO_SIGNERS, mSignatureBlockEntry.getName());
// return;
// }
//
// SignerInfo verifiedSignerInfo = null;
// if ((unverifiedSignerInfos != null) && (unverifiedSignerInfos.length > 0)) {
// for (int i = 0; i < unverifiedSignerInfos.length; i++) {
// SignerInfo unverifiedSignerInfo = unverifiedSignerInfos[i];
// String digestAlgorithmOid =
// unverifiedSignerInfo.getDigestAlgorithmId().getOID().toString();
// String signatureAlgorithmOid =
// unverifiedSignerInfo
// .getDigestEncryptionAlgorithmId().getOID().toString();
// InclusiveIntRange desiredApiLevels =
// InclusiveIntRange.fromTo(minSdkVersion, maxSdkVersion);
// List<InclusiveIntRange> apiLevelsWhereDigestAndSigAlgorithmSupported =
// getSigAlgSupportedApiLevels(digestAlgorithmOid, signatureAlgorithmOid);
// List<InclusiveIntRange> apiLevelsWhereDigestAlgorithmNotSupported =
// desiredApiLevels.getValuesNotIn(apiLevelsWhereDigestAndSigAlgorithmSupported);
// if (!apiLevelsWhereDigestAlgorithmNotSupported.isEmpty()) {
// mResult.addError(
// Issue.JAR_SIG_UNSUPPORTED_SIG_ALG,
// mSignatureBlockEntry.getName(),
// digestAlgorithmOid,
// signatureAlgorithmOid,
// String.valueOf(apiLevelsWhereDigestAlgorithmNotSupported));
// return;
// }
// try {
// verifiedSignerInfo = sigBlock.verify(unverifiedSignerInfo, mSigFileBytes);
// } catch (SignatureException e) {
// mResult.addError(
// Issue.JAR_SIG_VERIFY_EXCEPTION,
// mSignatureBlockEntry.getName(),
// mSignatureFileEntry.getName(),
// e);
// return;
// }
// if (verifiedSignerInfo != null) {
// // Verified
// break;
// }
//
// // Did not verify
// if (minSdkVersion < AndroidSdkVersion.N) {
// // Prior to N, Android attempted to verify only the first SignerInfo.
// mResult.addError(
// Issue.JAR_SIG_DID_NOT_VERIFY,
// mSignatureBlockEntry.getName(),
// mSignatureFileEntry.getName());
// return;
// }
// }
// }
// if (verifiedSignerInfo == null) {
// mResult.addError(Issue.JAR_SIG_NO_SIGNERS, mSignatureBlockEntry.getName());
// return;
// }
//
// // TODO: PKCS7 class doesn't guarantee that returned certificates' getEncoded returns
// // the original encoded form of certificates rather than the DER re-encoded form. We
// // need to replace the PKCS7 parser/verifier.
// List<X509Certificate> certChain;
// try {
// certChain = verifiedSignerInfo.getCertificateChain(sigBlock);
// } catch (IOException e) {
// throw new RuntimeException(
// "Failed to obtain cert chain from " + mSignatureBlockEntry.getName(), e);
// }
// if ((certChain == null) || (certChain.isEmpty())) {
// throw new RuntimeException("Verified SignerInfo does not have a certificate chain");
// }
// mResult.certChain.clear();
// mResult.certChain.addAll(certChain);
// }
//
// private static final String OID_DIGEST_MD5 = "1.2.840.113549.2.5";
// private static final String OID_DIGEST_SHA1 = "1.3.14.3.2.26";
// private static final String OID_DIGEST_SHA224 = "2.16.840.1.101.3.4.2.4";
// private static final String OID_DIGEST_SHA256 = "2.16.840.1.101.3.4.2.1";
// private static final String OID_DIGEST_SHA384 = "2.16.840.1.101.3.4.2.2";
// private static final String OID_DIGEST_SHA512 = "2.16.840.1.101.3.4.2.3";
//
// private static final String OID_SIG_RSA = "1.2.840.113549.1.1.1";
// private static final String OID_SIG_MD5_WITH_RSA = "1.2.840.113549.1.1.4";
// private static final String OID_SIG_SHA1_WITH_RSA = "1.2.840.113549.1.1.5";
// private static final String OID_SIG_SHA224_WITH_RSA = "1.2.840.113549.1.1.14";
// private static final String OID_SIG_SHA256_WITH_RSA = "1.2.840.113549.1.1.11";
// private static final String OID_SIG_SHA384_WITH_RSA = "1.2.840.113549.1.1.12";
// private static final String OID_SIG_SHA512_WITH_RSA = "1.2.840.113549.1.1.13";
//
// private static final String OID_SIG_DSA = "1.2.840.10040.4.1";
// private static final String OID_SIG_SHA1_WITH_DSA = "1.2.840.10040.4.3";
// private static final String OID_SIG_SHA224_WITH_DSA = "2.16.840.1.101.3.4.3.1";
// private static final String OID_SIG_SHA256_WITH_DSA = "2.16.840.1.101.3.4.3.2";
//
// private static final String OID_SIG_EC_PUBLIC_KEY = "1.2.840.10045.2.1";
// private static final String OID_SIG_SHA1_WITH_ECDSA = "1.2.840.10045.4.1";
// private static final String OID_SIG_SHA224_WITH_ECDSA = "1.2.840.10045.4.3.1";
// private static final String OID_SIG_SHA256_WITH_ECDSA = "1.2.840.10045.4.3.2";
// private static final String OID_SIG_SHA384_WITH_ECDSA = "1.2.840.10045.4.3.3";
// private static final String OID_SIG_SHA512_WITH_ECDSA = "1.2.840.10045.4.3.4";
//
// private static final Map<String, List<InclusiveIntRange>> SUPPORTED_SIG_ALG_OIDS =
// new HashMap<>();
// {
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_RSA,
// InclusiveIntRange.from(0));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_MD5_WITH_RSA,
// InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA1_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA224_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA256_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA384_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA512_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_RSA,
// InclusiveIntRange.from(0));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_MD5_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_RSA,
// InclusiveIntRange.from(0));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_RSA,
// InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_MD5_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_RSA,
// InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_RSA,
// InclusiveIntRange.fromTo(21, 21));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_RSA,
// InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_MD5_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_RSA,
// InclusiveIntRange.fromTo(21, 21));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_RSA,
// InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_RSA,
// InclusiveIntRange.from(18));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_MD5_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_RSA,
// InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_RSA,
// InclusiveIntRange.from(18));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_MD5_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_RSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_RSA,
// InclusiveIntRange.fromTo(21, 21));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_RSA,
// InclusiveIntRange.from(21));
//
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA1_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA224_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA256_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_DSA,
// InclusiveIntRange.from(0));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_DSA,
// InclusiveIntRange.from(9));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_DSA,
// InclusiveIntRange.from(22));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_DSA,
// InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_DSA,
// InclusiveIntRange.from(22));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_DSA,
// InclusiveIntRange.from(21));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_DSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_EC_PUBLIC_KEY,
// InclusiveIntRange.from(18));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_EC_PUBLIC_KEY,
// InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_EC_PUBLIC_KEY,
// InclusiveIntRange.from(18));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_EC_PUBLIC_KEY,
// InclusiveIntRange.from(18));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_EC_PUBLIC_KEY,
// InclusiveIntRange.from(18));
//
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA1_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA224_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA256_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA384_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_MD5, OID_SIG_SHA512_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_ECDSA,
// InclusiveIntRange.from(18));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_ECDSA,
// InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_ECDSA,
// InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_ECDSA,
// InclusiveIntRange.from(21));
// addSupportedSigAlg(
// OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
//
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_ECDSA,
// InclusiveIntRange.fromTo(21, 23));
// addSupportedSigAlg(
// OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_ECDSA,
// InclusiveIntRange.from(21));
// }
//
// private static void addSupportedSigAlg(
// String digestAlgorithmOid,
// String signatureAlgorithmOid,
// InclusiveIntRange... supportedApiLevels) {
// SUPPORTED_SIG_ALG_OIDS.put(
// digestAlgorithmOid + "with" + signatureAlgorithmOid,
// Arrays.asList(supportedApiLevels));
// }
//
// private List<InclusiveIntRange> getSigAlgSupportedApiLevels(
// String digestAlgorithmOid,
// String signatureAlgorithmOid) {
// List<InclusiveIntRange> result =
// SUPPORTED_SIG_ALG_OIDS.get(digestAlgorithmOid + "with" + signatureAlgorithmOid);
// return (result != null) ? result : Collections.emptyList();
// }
//
// public void verifySigFileAgainstManifest(
// byte[] manifestBytes,
// ManifestParser.Section manifestMainSection,
// Map<String, ManifestParser.Section> entryNameToManifestSection,
// Map<Integer, String> supportedApkSigSchemeNames,
// Set<Integer> foundApkSigSchemeIds,
// int minSdkVersion,
// int maxSdkVersion) throws NoSuchAlgorithmException {
// // Inspect the main section of the .SF file.
// ManifestParser sf = new ManifestParser(mSigFileBytes);
// ManifestParser.Section sfMainSection = sf.readSection();
// if (sfMainSection.getAttributeValue(Attributes.Name.SIGNATURE_VERSION) == null) {
// mResult.addError(
// Issue.JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE,
// mSignatureFileEntry.getName());
// setIgnored();
// return;
// }
//
// if (maxSdkVersion >= AndroidSdkVersion.N) {
// // Android N and newer rejects APKs whose .SF file says they were supposed to be
// // signed with APK Signature Scheme v2 (or newer) and yet no such signature was
// // found.
// checkForStrippedApkSignatures(
// sfMainSection, supportedApkSigSchemeNames, foundApkSigSchemeIds);
// if (mResult.containsErrors()) {
// return;
// }
// }
//
// boolean createdBySigntool = false;
// String createdBy = sfMainSection.getAttributeValue("Created-By");
// if (createdBy != null) {
// createdBySigntool = createdBy.indexOf("signtool") != -1;
// }
// boolean manifestDigestVerified =
// verifyManifestDigest(
// sfMainSection,
// createdBySigntool,
// manifestBytes,
// minSdkVersion,
// maxSdkVersion);
// if (!createdBySigntool) {
// verifyManifestMainSectionDigest(
// sfMainSection,
// manifestMainSection,
// manifestBytes,
// minSdkVersion,
// maxSdkVersion);
// }
// if (mResult.containsErrors()) {
// return;
// }
//
// // Inspect per-entry sections of .SF file. Technically, if the digest of JAR manifest
// // verifies, per-entry sections should be ignored. However, most Android platform
// // implementations require that such sections exist.
// List<ManifestParser.Section> sfSections = sf.readAllSections();
// Set<String> sfEntryNames = new HashSet<>(sfSections.size());
// int sfSectionNumber = 0;
// for (ManifestParser.Section sfSection : sfSections) {
// sfSectionNumber++;
// String entryName = sfSection.getName();
// if (entryName == null) {
// mResult.addError(
// Issue.JAR_SIG_UNNNAMED_SIG_FILE_SECTION,
// mSignatureFileEntry.getName(),
// sfSectionNumber);
// setIgnored();
// return;
// }
// if (!sfEntryNames.add(entryName)) {
// mResult.addError(
// Issue.JAR_SIG_DUPLICATE_SIG_FILE_SECTION,
// mSignatureFileEntry.getName(),
// entryName);
// setIgnored();
// return;
// }
// if (manifestDigestVerified) {
// // No need to verify this entry's corresponding JAR manifest entry because the
// // JAR manifest verifies in full.
// continue;
// }
// // Whole-file digest of JAR manifest hasn't been verified. Thus, we need to verify
// // the digest of the JAR manifest section corresponding to this .SF section.
// ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName);
// if (manifestSection == null) {
// mResult.addError(
// Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE,
// entryName,
// mSignatureFileEntry.getName());
// setIgnored();
// continue;
// }
// verifyManifestIndividualSectionDigest(
// sfSection,
// createdBySigntool,
// manifestSection,
// manifestBytes,
// minSdkVersion,
// maxSdkVersion);
// }
// mSigFileEntryNames = sfEntryNames;
// }
//
//
// /**
// * Returns {@code true} if the whole-file digest of the manifest against the main section of
// * the .SF file.
// */
// private boolean verifyManifestDigest(
// ManifestParser.Section sfMainSection,
// boolean createdBySigntool,
// byte[] manifestBytes,
// int minSdkVersion,
// int maxSdkVersion) throws NoSuchAlgorithmException {
// Collection<NamedDigest> expectedDigests =
// getDigestsToVerify(
// sfMainSection,
// ((createdBySigntool) ? "-Digest" : "-Digest-Manifest"),
// minSdkVersion,
// maxSdkVersion);
// boolean digestFound = !expectedDigests.isEmpty();
// if (!digestFound) {
// mResult.addWarning(
// Issue.JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE,
// mSignatureFileEntry.getName());
// return false;
// }
//
// boolean verified = true;
// for (NamedDigest expectedDigest : expectedDigests) {
// String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
// byte[] actual = digest(jcaDigestAlgorithm, manifestBytes);
// byte[] expected = expectedDigest.digest;
// if (!Arrays.equals(expected, actual)) {
// mResult.addWarning(
// Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
// V1SchemeSigner.MANIFEST_ENTRY_NAME,
// jcaDigestAlgorithm,
// mSignatureFileEntry.getName(),
// Base64.getEncoder().encodeToString(actual),
// Base64.getEncoder().encodeToString(expected));
// verified = false;
// }
// }
// return verified;
// }
//
// /**
// * Verifies the digest of the manifest's main section against the main section of the .SF
// * file.
// */
// private void verifyManifestMainSectionDigest(
// ManifestParser.Section sfMainSection,
// ManifestParser.Section manifestMainSection,
// byte[] manifestBytes,
// int minSdkVersion,
// int maxSdkVersion) throws NoSuchAlgorithmException {
// Collection<NamedDigest> expectedDigests =
// getDigestsToVerify(
// sfMainSection,
// "-Digest-Manifest-Main-Attributes",
// minSdkVersion,
// maxSdkVersion);
// if (expectedDigests.isEmpty()) {
// return;
// }
//
// for (NamedDigest expectedDigest : expectedDigests) {
// String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
// byte[] actual =
// digest(
// jcaDigestAlgorithm,
// manifestBytes,
// manifestMainSection.getStartOffset(),
// manifestMainSection.getSizeBytes());
// byte[] expected = expectedDigest.digest;
// if (!Arrays.equals(expected, actual)) {
// mResult.addError(
// Issue.JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY,
// jcaDigestAlgorithm,
// mSignatureFileEntry.getName(),
// Base64.getEncoder().encodeToString(actual),
// Base64.getEncoder().encodeToString(expected));
// }
// }
// }
//
// /**
// * Verifies the digest of the manifest's individual section against the corresponding
// * individual section of the .SF file.
// */
// private void verifyManifestIndividualSectionDigest(
// ManifestParser.Section sfIndividualSection,
// boolean createdBySigntool,
// ManifestParser.Section manifestIndividualSection,
// byte[] manifestBytes,
// int minSdkVersion,
// int maxSdkVersion) throws NoSuchAlgorithmException {
// String entryName = sfIndividualSection.getName();
// Collection<NamedDigest> expectedDigests =
// getDigestsToVerify(
// sfIndividualSection, "-Digest", minSdkVersion, maxSdkVersion);
// if (expectedDigests.isEmpty()) {
// mResult.addError(
// Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE,
// entryName,
// mSignatureFileEntry.getName());
// return;
// }
//
// int sectionStartIndex = manifestIndividualSection.getStartOffset();
// int sectionSizeBytes = manifestIndividualSection.getSizeBytes();
// if (createdBySigntool) {
// int sectionEndIndex = sectionStartIndex + sectionSizeBytes;
// if ((manifestBytes[sectionEndIndex - 1] == '\n')
// && (manifestBytes[sectionEndIndex - 2] == '\n')) {
// sectionSizeBytes--;
// }
// }
// for (NamedDigest expectedDigest : expectedDigests) {
// String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
// byte[] actual =
// digest(
// jcaDigestAlgorithm,
// manifestBytes,
// sectionStartIndex,
// sectionSizeBytes);
// byte[] expected = expectedDigest.digest;
// if (!Arrays.equals(expected, actual)) {
// mResult.addError(
// Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY,
// entryName,
// jcaDigestAlgorithm,
// mSignatureFileEntry.getName(),
// Base64.getEncoder().encodeToString(actual),
// Base64.getEncoder().encodeToString(expected));
// }
// }
// }
//
// private void checkForStrippedApkSignatures(
// ManifestParser.Section sfMainSection,
// Map<Integer, String> supportedApkSigSchemeNames,
// Set<Integer> foundApkSigSchemeIds) {
// String signedWithApkSchemes =
// sfMainSection.getAttributeValue(
// V1SchemeSigner.SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR);
// // This field contains a comma-separated list of APK signature scheme IDs which were
// // used to sign this APK. Android rejects APKs where an ID is known to the platform but
// // the APK didn't verify using that scheme.
//
// if (signedWithApkSchemes == null) {
// // APK signature (e.g., v2 scheme) stripping protections not enabled.
// if (!foundApkSigSchemeIds.isEmpty()) {
// // APK is signed with an APK signature scheme such as v2 scheme.
// mResult.addWarning(
// Issue.JAR_SIG_NO_APK_SIG_STRIP_PROTECTION,
// mSignatureFileEntry.getName());
// }
// return;
// }
//
// if (supportedApkSigSchemeNames.isEmpty()) {
// return;
// }
//
// Set<Integer> supportedApkSigSchemeIds = supportedApkSigSchemeNames.keySet();
// Set<Integer> supportedExpectedApkSigSchemeIds = new HashSet<>(1);
// StringTokenizer tokenizer = new StringTokenizer(signedWithApkSchemes, ",");
// while (tokenizer.hasMoreTokens()) {
// String idText = tokenizer.nextToken().trim();
// if (idText.isEmpty()) {
// continue;
// }
// int id;
// try {
// id = Integer.parseInt(idText);
// } catch (Exception ignored) {
// continue;
// }
// // This APK was supposed to be signed with the APK signature scheme having
// // this ID.
// if (supportedApkSigSchemeIds.contains(id)) {
// supportedExpectedApkSigSchemeIds.add(id);
// } else {
// mResult.addWarning(
// Issue.JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID,
// mSignatureFileEntry.getName(),
// id);
// }
// }
//
// for (int id : supportedExpectedApkSigSchemeIds) {
// if (!foundApkSigSchemeIds.contains(id)) {
// String apkSigSchemeName = supportedApkSigSchemeNames.get(id);
// mResult.addError(
// Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED,
// mSignatureFileEntry.getName(),
// id,
// apkSigSchemeName);
// }
// }
// }
// }
//
// private static Collection<NamedDigest> getDigestsToVerify(
// ManifestParser.Section section,
// String digestAttrSuffix,
// int minSdkVersion,
// int maxSdkVersion) {
// Decoder base64Decoder = Base64.getDecoder();
// List<NamedDigest> result = new ArrayList<>(1);
// if (minSdkVersion < AndroidSdkVersion.JELLY_BEAN_MR2) {
// // Prior to JB MR2, Android platform's logic for picking a digest algorithm to verify is
// // to rely on the ancient Digest-Algorithms attribute which contains
// // whitespace-separated list of digest algorithms (defaulting to SHA-1) to try. The
// // first digest attribute (with supported digest algorithm) found using the list is
// // used.
// String algs = section.getAttributeValue("Digest-Algorithms");
// if (algs == null) {
// algs = "SHA SHA1";
// }
// StringTokenizer tokens = new StringTokenizer(algs);
// while (tokens.hasMoreTokens()) {
// String alg = tokens.nextToken();
// String attrName = alg + digestAttrSuffix;
// String digestBase64 = section.getAttributeValue(attrName);
// if (digestBase64 == null) {
// // Attribute not found
// continue;
// }
// alg = getCanonicalJcaMessageDigestAlgorithm(alg);
// if ((alg == null)
// || (getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(alg)
// > minSdkVersion)) {
// // Unsupported digest algorithm
// continue;
// }
// // Supported digest algorithm
// result.add(new NamedDigest(alg, base64Decoder.decode(digestBase64)));
// break;
// }
// // No supported digests found -- this will fail to verify on pre-JB MR2 Androids.
// if (result.isEmpty()) {
// return result;
// }
// }
//
// if (maxSdkVersion >= AndroidSdkVersion.JELLY_BEAN_MR2) {
// // On JB MR2 and newer, Android platform picks the strongest algorithm out of:
// // SHA-512, SHA-384, SHA-256, SHA-1.
// for (String alg : JB_MR2_AND_NEWER_DIGEST_ALGS) {
// String attrName = getJarDigestAttributeName(alg, digestAttrSuffix);
// String digestBase64 = section.getAttributeValue(attrName);
// if (digestBase64 == null) {
// // Attribute not found
// continue;
// }
// byte[] digest = base64Decoder.decode(digestBase64);
// byte[] digestInResult = getDigest(result, alg);
// if ((digestInResult == null) || (!Arrays.equals(digestInResult, digest))) {
// result.add(new NamedDigest(alg, digest));
// }
// break;
// }
// }
//
// return result;
// }
//
// private static final String[] JB_MR2_AND_NEWER_DIGEST_ALGS = {
// "SHA-512",
// "SHA-384",
// "SHA-256",
// "SHA-1",
// };
//
// private static String getCanonicalJcaMessageDigestAlgorithm(String algorithm) {
// return UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.get(algorithm.toUpperCase(Locale.US));
// }
//
// public static int getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(
// String jcaAlgorithmName) {
// Integer result =
// MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.get(
// jcaAlgorithmName.toUpperCase(Locale.US));
// return (result != null) ? result : Integer.MAX_VALUE;
// }
//
// private static String getJarDigestAttributeName(
// String jcaDigestAlgorithm, String attrNameSuffix) {
// if ("SHA-1".equalsIgnoreCase(jcaDigestAlgorithm)) {
// return "SHA1" + attrNameSuffix;
// } else {
// return jcaDigestAlgorithm + attrNameSuffix;
// }
// }
//
// private static final Map<String, String> UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL;
// static {
// UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL = new HashMap<>(8);
// UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("MD5", "MD5");
// UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA", "SHA-1");
// UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA1", "SHA-1");
// UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-1", "SHA-1");
// UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-256", "SHA-256");
// UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-384", "SHA-384");
// UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-512", "SHA-512");
// }
//
// private static final Map<String, Integer>
// MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST;
// static {
// MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST = new HashMap<>(5);
// MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("MD5", 0);
// MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-1", 0);
// MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-256", 0);
// MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put(
// "SHA-384", AndroidSdkVersion.GINGERBREAD);
// MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put(
// "SHA-512", AndroidSdkVersion.GINGERBREAD);
// }
//
// private static byte[] getDigest(Collection<NamedDigest> digests, String jcaDigestAlgorithm) {
// for (NamedDigest digest : digests) {
// if (digest.jcaDigestAlgorithm.equalsIgnoreCase(jcaDigestAlgorithm)) {
// return digest.digest;
// }
// }
// return null;
// }
//
// public static List<CentralDirectoryRecord> parseZipCentralDirectory(
// DataSource apk,
// ApkUtils.ZipSections apkSections)
// throws IOException, ApkFormatException {
// // Read the ZIP Central Directory
// long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes();
// if (cdSizeBytes > Integer.MAX_VALUE) {
// throw new ApkFormatException("ZIP Central Directory too large: " + cdSizeBytes);
// }
// long cdOffset = apkSections.getZipCentralDirectoryOffset();
// ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes);
// cd.order(ByteOrder.LITTLE_ENDIAN);
//
// // Parse the ZIP Central Directory
// int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount();
// List<CentralDirectoryRecord> cdRecords = new ArrayList<>(expectedCdRecordCount);
// for (int i = 0; i < expectedCdRecordCount; i++) {
// CentralDirectoryRecord cdRecord;
// int offsetInsideCd = cd.position();
// try {
// cdRecord = CentralDirectoryRecord.getRecord(cd);
// } catch (ZipFormatException e) {
// throw new ApkFormatException(
// "Malformed ZIP Central Directory record #" + (i + 1)
// + " at file offset " + (cdOffset + offsetInsideCd),
// e);
// }
// String entryName = cdRecord.getName();
// if (entryName.endsWith("/")) {
// // Ignore directory entries
// continue;
// }
// cdRecords.add(cdRecord);
// }
// // There may be more data in Central Directory, but we don't warn or throw because Android
// // ignores unused CD data.
//
// return cdRecords;
// }
//
// /**
// * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's
// * manifest for the APK to verify on Android.
// */
// private static boolean isJarEntryDigestNeededInManifest(String entryName) {
// // NOTE: This logic is different from what's required by the JAR signing scheme. This is
// // because Android's APK verification logic differs from that spec. In particular, JAR
// // signing spec includes into JAR manifest all files in subdirectories of META-INF and
// // any files inside META-INF not related to signatures.
// if (entryName.startsWith("META-INF/")) {
// return false;
// }
// return !entryName.endsWith("/");
// }
//
// private static Set<Signer> verifyJarEntriesAgainstManifestAndSigners(
// DataSource apk,
// long cdOffsetInApk,
// Collection<CentralDirectoryRecord> cdRecords,
// Map<String, ManifestParser.Section> entryNameToManifestSection,
// List<Signer> signers,
// int minSdkVersion,
// int maxSdkVersion,
// Result result) throws ApkFormatException, IOException, NoSuchAlgorithmException {
// // Iterate over APK contents as sequentially as possible to improve performance.
// List<CentralDirectoryRecord> cdRecordsSortedByLocalFileHeaderOffset =
// new ArrayList<>(cdRecords);
// Collections.sort(
// cdRecordsSortedByLocalFileHeaderOffset,
// CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
// Set<String> manifestEntryNamesMissingFromApk =
// new HashSet<>(entryNameToManifestSection.keySet());
// List<Signer> firstSignedEntrySigners = null;
// String firstSignedEntryName = null;
// for (CentralDirectoryRecord cdRecord : cdRecordsSortedByLocalFileHeaderOffset) {
// String entryName = cdRecord.getName();
// manifestEntryNamesMissingFromApk.remove(entryName);
// if (!isJarEntryDigestNeededInManifest(entryName)) {
// continue;
// }
//
// ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName);
// if (manifestSection == null) {
// result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
// continue;
// }
//
// List<Signer> entrySigners = new ArrayList<>(signers.size());
// for (Signer signer : signers) {
// if (signer.getSigFileEntryNames().contains(entryName)) {
// entrySigners.add(signer);
// }
// }
// if (entrySigners.isEmpty()) {
// result.addError(Issue.JAR_SIG_ZIP_ENTRY_NOT_SIGNED, entryName);
// continue;
// }
// if (firstSignedEntrySigners == null) {
// firstSignedEntrySigners = entrySigners;
// firstSignedEntryName = entryName;
// } else if (!entrySigners.equals(firstSignedEntrySigners)) {
// result.addError(
// Issue.JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH,
// firstSignedEntryName,
// getSignerNames(firstSignedEntrySigners),
// entryName,
// getSignerNames(entrySigners));
// continue;
// }
//
// Collection<NamedDigest> expectedDigests =
// getDigestsToVerify(manifestSection, "-Digest", minSdkVersion, maxSdkVersion);
// if (expectedDigests.isEmpty()) {
// result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
// continue;
// }
//
// MessageDigest[] mds = new MessageDigest[expectedDigests.size()];
// int mdIndex = 0;
// for (NamedDigest expectedDigest : expectedDigests) {
// mds[mdIndex] = getMessageDigest(expectedDigest.jcaDigestAlgorithm);
// mdIndex++;
// }
//
// try {
// LocalFileRecord.outputUncompressedData(
// apk,
// cdRecord,
// cdOffsetInApk,
// new MessageDigestSink(mds));
// } catch (ZipFormatException e) {
// throw new ApkFormatException("Malformed ZIP entry: " + entryName, e);
// } catch (IOException e) {
// throw new IOException("Failed to read entry: " + entryName, e);
// }
//
// mdIndex = 0;
// for (NamedDigest expectedDigest : expectedDigests) {
// byte[] actualDigest = mds[mdIndex].digest();
// if (!Arrays.equals(expectedDigest.digest, actualDigest)) {
// result.addError(
// Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
// entryName,
// expectedDigest.jcaDigestAlgorithm,
// V1SchemeSigner.MANIFEST_ENTRY_NAME,
// Base64.getEncoder().encodeToString(actualDigest),
// Base64.getEncoder().encodeToString(expectedDigest.digest));
// }
// }
// }
//
// if (firstSignedEntrySigners == null) {
// result.addError(Issue.JAR_SIG_NO_SIGNED_ZIP_ENTRIES);
// return Collections.emptySet();
// } else {
// return new HashSet<>(firstSignedEntrySigners);
// }
// }
//
// private static List<String> getSignerNames(List<Signer> signers) {
// if (signers.isEmpty()) {
// return Collections.emptyList();
// }
// List<String> result = new ArrayList<>(signers.size());
// for (Signer signer : signers) {
// result.add(signer.getName());
// }
// return result;
// }
//
// private static MessageDigest getMessageDigest(String algorithm)
// throws NoSuchAlgorithmException {
// return MessageDigest.getInstance(algorithm);
// }
//
// private static byte[] digest(String algorithm, byte[] data, int offset, int length)
// throws NoSuchAlgorithmException {
// MessageDigest md = getMessageDigest(algorithm);
// md.update(data, offset, length);
// return md.digest();
// }
//
// private static byte[] digest(String algorithm, byte[] data) throws NoSuchAlgorithmException {
// return getMessageDigest(algorithm).digest(data);
// }
//
// private static class NamedDigest {
// private final String jcaDigestAlgorithm;
// private final byte[] digest;
//
// private NamedDigest(String jcaDigestAlgorithm, byte[] digest) {
// this.jcaDigestAlgorithm = jcaDigestAlgorithm;
// this.digest = digest;
// }
// }
//
// public static class Result {
//
// /** Whether the APK's JAR signature verifies. */
// public boolean verified;
//
// /** List of APK's signers. These signers are used by Android. */
// public final List<SignerInfo> signers = new ArrayList<>();
//
// /**
// * Signers encountered in the APK but not included in the set of the APK's signers. These
// * signers are ignored by Android.
// */
// public final List<SignerInfo> ignoredSigners = new ArrayList<>();
//
// private final List<IssueWithParams> mWarnings = new ArrayList<>();
// private final List<IssueWithParams> mErrors = new ArrayList<>();
//
// private boolean containsErrors() {
// if (!mErrors.isEmpty()) {
// return true;
// }
// for (SignerInfo signer : signers) {
// if (signer.containsErrors()) {
// return true;
// }
// }
// return false;
// }
//
// private void addError(Issue msg, Object... parameters) {
// mErrors.add(new IssueWithParams(msg, parameters));
// }
//
// private void addWarning(Issue msg, Object... parameters) {
// mWarnings.add(new IssueWithParams(msg, parameters));
// }
//
// public List<IssueWithParams> getErrors() {
// return mErrors;
// }
//
// public List<IssueWithParams> getWarnings() {
// return mWarnings;
// }
//
// public static class SignerInfo {
// public final String name;
// public final String signatureFileName;
// public final String signatureBlockFileName;
// public final List<X509Certificate> certChain = new ArrayList<>();
//
// private final List<IssueWithParams> mWarnings = new ArrayList<>();
// private final List<IssueWithParams> mErrors = new ArrayList<>();
//
// private SignerInfo(
// String name, String signatureBlockFileName, String signatureFileName) {
// this.name = name;
// this.signatureBlockFileName = signatureBlockFileName;
// this.signatureFileName = signatureFileName;
// }
//
// private boolean containsErrors() {
// return !mErrors.isEmpty();
// }
//
// private void addError(Issue msg, Object... parameters) {
// mErrors.add(new IssueWithParams(msg, parameters));
// }
//
// private void addWarning(Issue msg, Object... parameters) {
// mWarnings.add(new IssueWithParams(msg, parameters));
// }
//
// public List<IssueWithParams> getErrors() {
// return mErrors;
// }
//
// public List<IssueWithParams> getWarnings() {
// return mWarnings;
// }
// }
// }
//}