Merge "Reject PKCS#7 SignerInfo with unsupported parameters."
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/ApkVerifier.java b/tools/apksigner/core/src/com/android/apksigner/core/ApkVerifier.java
index a40607a..9c58085 100644
--- a/tools/apksigner/core/src/com/android/apksigner/core/ApkVerifier.java
+++ b/tools/apksigner/core/src/com/android/apksigner/core/ApkVerifier.java
@@ -663,6 +663,21 @@
         JAR_SIG_VERIFY_EXCEPTION("Failed to verify JAR signature %1$s against %2$s: %3$s"),
 
         /**
+         * JAR signature contains unsupported digest algorithm.
+         *
+         * <ul>
+         * <li>Parameter 1: name of the signature block file ({@code String})</li>
+         * <li>Parameter 2: digest algorithm OID ({@code String})</li>
+         * <li>Parameter 2: signature algorithm OID ({@code String})</li>
+         * <li>Parameter 3: API Levels on which this combination of algorithms is not supported
+         *     ({@code String})</li>
+         * </ul>
+         */
+        JAR_SIG_UNSUPPORTED_SIG_ALG(
+                "JAR signature %1$s uses digest algorithm %2$s and signature algorithm %3$s which"
+                        + " is not supported on API Levels %4$s"),
+
+        /**
          * An exception was encountered while parsing JAR signature contained in a signature block.
          *
          * <ul>
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
index 91d1990..1a4a90b 100644
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
+++ b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
@@ -562,7 +562,8 @@
                     // SignatureAlgorithm: dsaWithSha256 (2.16.840.1.101.3.4.3.2) and
                     // dsa (1.2.840.10040.4.1). The latter works only on API Level 22+. Thus, we use
                     // the former.
-                    sigAlgId = getSupportedAlgorithmId("2.16.840.1.101.3.4.3.2"); // DSA with SHA-256
+                    sigAlgId =
+                            getSupportedAlgorithmId("2.16.840.1.101.3.4.3.2"); // DSA with SHA-256
                     break;
                 default:
                     throw new IllegalArgumentException(
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeVerifier.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeVerifier.java
index 762f5aa..91aa62e 100644
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeVerifier.java
+++ b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeVerifier.java
@@ -44,6 +44,7 @@
 import com.android.apksigner.core.apk.ApkUtils;
 import com.android.apksigner.core.internal.jar.ManifestParser;
 import com.android.apksigner.core.internal.util.AndroidSdkVersion;
+import com.android.apksigner.core.internal.util.InclusiveIntRange;
 import com.android.apksigner.core.internal.util.MessageDigestSink;
 import com.android.apksigner.core.internal.zip.CentralDirectoryRecord;
 import com.android.apksigner.core.internal.zip.LocalFileHeader;
@@ -412,7 +413,8 @@
                     mResult.addError(
                             Issue.JAR_SIG_MALFORMED_CERTIFICATE, mSignatureBlockEntry.getName(), e);
                 } else {
-                    mResult.addError(Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e);
+                    mResult.addError(
+                            Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e);
                 }
                 return;
             }
@@ -426,7 +428,25 @@
             if ((unverifiedSignerInfos != null) && (unverifiedSignerInfos.length > 0)) {
                 for (int i = 0; i < unverifiedSignerInfos.length; i++) {
                     SignerInfo unverifiedSignerInfo = unverifiedSignerInfos[i];
-                    // TODO: Reject sig/dig algorithms not supported on Android
+                    String digestAlgorithmOid =
+                            unverifiedSignerInfo.getDigestAlgorithmId().getOID().toString();
+                    String signatureAlgorithmOid =
+                            unverifiedSignerInfo
+                                    .getDigestEncryptionAlgorithmId().getOID().toString();
+                    InclusiveIntRange desiredApiLevels = InclusiveIntRange.from(minSdkVersion);
+                    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 (NoSuchAlgorithmException | SignatureException e) {
@@ -472,6 +492,351 @@
             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_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_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,
@@ -864,8 +1229,10 @@
         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", 9);
-        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-512", 9);
+        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) {
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/AndroidSdkVersion.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/AndroidSdkVersion.java
index f7fb7cd..3e9d039 100644
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/AndroidSdkVersion.java
+++ b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/AndroidSdkVersion.java
@@ -24,6 +24,9 @@
     /** Hidden constructor to prevent instantiation. */
     private AndroidSdkVersion() {}
 
+    /** Android 2.3. */
+    public static final int GINGERBREAD = 9;
+
     /** Android 4.3. The revenge of the beans. */
     public static final int JELLY_BEAN_MR2 = 18;
 
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/InclusiveIntRange.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/InclusiveIntRange.java
new file mode 100644
index 0000000..baf3655
--- /dev/null
+++ b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/InclusiveIntRange.java
@@ -0,0 +1,89 @@
+/*
+ * 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.apksigner.core.internal.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Inclusive interval of integers.
+ */
+public class InclusiveIntRange {
+    private final int min;
+    private final int max;
+
+    private InclusiveIntRange(int min, int max) {
+        this.min = min;
+        this.max = max;
+    }
+
+    public int getMin() {
+        return min;
+    }
+
+    public int getMax() {
+        return max;
+    }
+
+    public static InclusiveIntRange fromTo(int min, int max) {
+        return new InclusiveIntRange(min, max);
+    }
+
+    public static InclusiveIntRange from(int min) {
+        return new InclusiveIntRange(min, Integer.MAX_VALUE);
+    }
+
+    public List<InclusiveIntRange> getValuesNotIn(
+            List<InclusiveIntRange> sortedNonOverlappingRanges) {
+        if (sortedNonOverlappingRanges.isEmpty()) {
+            return Collections.singletonList(this);
+        }
+
+        int testValue = min;
+        List<InclusiveIntRange> result = null;
+        for (InclusiveIntRange range : sortedNonOverlappingRanges) {
+            int rangeMax = range.max;
+            if (testValue > rangeMax) {
+                continue;
+            }
+            int rangeMin = range.min;
+            if (testValue < range.min) {
+                if (result == null) {
+                    result = new ArrayList<>();
+                }
+                result.add(fromTo(testValue, rangeMin - 1));
+            }
+            if (rangeMax >= max) {
+                return (result != null) ? result : Collections.emptyList();
+            }
+            testValue = rangeMax + 1;
+        }
+        if (testValue <= max) {
+            if (result == null) {
+                result = new ArrayList<>(1);
+            }
+            result.add(fromTo(testValue, max));
+        }
+        return (result != null) ? result : Collections.emptyList();
+    }
+
+    @Override
+    public String toString() {
+        return "[" + min + ", " + ((max < Integer.MAX_VALUE) ? (max + "]") : "\u221e)");
+    }
+}