Import of apksig using Copybara.

  - 242732457 Sync apksig from AOSP to pick up the latest changes by kudasov <kudasov@google.com>

PiperOrigin-RevId: 242732457
Change-Id: I6aeaf6a3c81321335d99a1a0d62bd1097c314329
diff --git a/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java b/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
index cc69af3..cfa9844 100644
--- a/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
+++ b/src/main/java/com/android/apksig/internal/apk/ApkSigningBlockUtils.java
@@ -21,10 +21,16 @@
 import com.android.apksig.apk.ApkFormatException;
 import com.android.apksig.apk.ApkSigningBlockNotFoundException;
 import com.android.apksig.apk.ApkUtils;
+import com.android.apksig.internal.asn1.Asn1BerParser;
+import com.android.apksig.internal.asn1.Asn1DecodingException;
+import com.android.apksig.internal.asn1.Asn1DerEncoder;
+import com.android.apksig.internal.asn1.Asn1EncodingException;
 import com.android.apksig.internal.util.ByteBufferDataSource;
 import com.android.apksig.internal.util.ChainedDataSource;
 import com.android.apksig.internal.util.Pair;
 import com.android.apksig.internal.util.VerityTreeBuilder;
+import com.android.apksig.internal.x509.RSAPublicKey;
+import com.android.apksig.internal.x509.SubjectPublicKeyInfo;
 import com.android.apksig.internal.zip.ZipUtils;
 import com.android.apksig.util.DataSink;
 import com.android.apksig.util.DataSinks;
@@ -33,6 +39,7 @@
 
 import com.android.apksig.util.RunnablesExecutor;
 import java.io.IOException;
+import java.math.BigInteger;
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
@@ -58,11 +65,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ForkJoinPool;
-import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
@@ -775,6 +778,53 @@
         byte[] encodedPublicKey = null;
         if ("X.509".equals(publicKey.getFormat())) {
             encodedPublicKey = publicKey.getEncoded();
+            // if the key is an RSA key check for a negative modulus
+            if ("RSA".equals(publicKey.getAlgorithm())) {
+                try {
+                    // Parse the encoded public key into the separate elements of the
+                    // SubjectPublicKeyInfo to obtain the SubjectPublicKey.
+                    ByteBuffer encodedPublicKeyBuffer = ByteBuffer.wrap(encodedPublicKey);
+                    SubjectPublicKeyInfo subjectPublicKeyInfo = Asn1BerParser.parse(
+                            encodedPublicKeyBuffer, SubjectPublicKeyInfo.class);
+                    // The SubjectPublicKey is encoded as a bit string within the
+                    // SubjectPublicKeyInfo. The first byte of the encoding is the number of padding
+                    // bits; store this and decode the rest of the bit string into the RSA modulus
+                    // and exponent.
+                    ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey;
+                    byte padding = subjectPublicKeyBuffer.get();
+                    RSAPublicKey rsaPublicKey = Asn1BerParser.parse(subjectPublicKeyBuffer,
+                            RSAPublicKey.class);
+                    // if the modulus is negative then attempt to reencode it with a leading 0 sign
+                    // byte.
+                    if (rsaPublicKey.modulus.compareTo(BigInteger.ZERO) < 0) {
+                        // A negative modulus indicates the leading bit in the integer is 1. Per
+                        // ASN.1 encoding rules to encode a positive integer with the leading bit
+                        // set to 1 a byte containing all zeros should precede the integer encoding.
+                        byte[] encodedModulus = rsaPublicKey.modulus.toByteArray();
+                        byte[] reencodedModulus = new byte[encodedModulus.length + 1];
+                        reencodedModulus[0] = 0;
+                        System.arraycopy(encodedModulus, 0, reencodedModulus, 1,
+                                encodedModulus.length);
+                        rsaPublicKey.modulus = new BigInteger(reencodedModulus);
+                        // Once the modulus has been corrected reencode the RSAPublicKey, then
+                        // restore the padding value in the bit string and reencode the entire
+                        // SubjectPublicKeyInfo to be returned to the caller.
+                        byte[] reencodedRSAPublicKey = Asn1DerEncoder.encode(rsaPublicKey);
+                        byte[] reencodedSubjectPublicKey =
+                                new byte[reencodedRSAPublicKey.length + 1];
+                        reencodedSubjectPublicKey[0] = padding;
+                        System.arraycopy(reencodedRSAPublicKey, 0, reencodedSubjectPublicKey, 1,
+                                reencodedRSAPublicKey.length);
+                        subjectPublicKeyInfo.subjectPublicKey = ByteBuffer.wrap(
+                                reencodedSubjectPublicKey);
+                        encodedPublicKey = Asn1DerEncoder.encode(subjectPublicKeyInfo);
+                    }
+                } catch (Asn1DecodingException | Asn1EncodingException e) {
+                    System.out.println("Caught a exception encoding the public key: " + e);
+                    e.printStackTrace();
+                    encodedPublicKey = null;
+                }
+            }
         }
         if (encodedPublicKey == null) {
             try {
diff --git a/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java b/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java
index d8e4723..f325f8b 100644
--- a/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java
+++ b/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeSigner.java
@@ -70,7 +70,7 @@
      * protected by signatures inside the block.
      */
 
-    private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
+    public static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
 
     /** Hidden constructor to prevent instantiation. */
     private V2SchemeSigner() {}
diff --git a/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java b/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java
index 51c40bd..0ef74a6 100644
--- a/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java
+++ b/src/main/java/com/android/apksig/internal/apk/v2/V2SchemeVerifier.java
@@ -370,7 +370,15 @@
             return;
         }
         X509Certificate mainCertificate = result.certs.get(0);
-        byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
+        byte[] certificatePublicKeyBytes;
+        try {
+            certificatePublicKeyBytes = ApkSigningBlockUtils.encodePublicKey(
+                    mainCertificate.getPublicKey());
+        } catch (InvalidKeyException e) {
+            System.out.println("Caught an exception encoding the public key: " + e);
+            e.printStackTrace();
+            certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
+        }
         if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
             result.addError(
                     Issue.V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD,
diff --git a/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java b/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java
index 16a6408..f263323 100644
--- a/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java
+++ b/src/main/java/com/android/apksig/internal/apk/v3/V3SchemeVerifier.java
@@ -432,7 +432,14 @@
             return;
         }
         X509Certificate mainCertificate = result.certs.get(0);
-        byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
+        byte[] certificatePublicKeyBytes;
+        try {
+            certificatePublicKeyBytes = ApkSigningBlockUtils.encodePublicKey(mainCertificate.getPublicKey());
+        } catch (InvalidKeyException e) {
+            System.out.println("Caught an exception encoding the public key: " + e);
+            e.printStackTrace();
+            certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
+        }
         if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
             result.addError(
                     Issue.V3_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD,
diff --git a/src/main/java/com/android/apksig/internal/x509/RSAPublicKey.java b/src/main/java/com/android/apksig/internal/x509/RSAPublicKey.java
new file mode 100644
index 0000000..521e067
--- /dev/null
+++ b/src/main/java/com/android/apksig/internal/x509/RSAPublicKey.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 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.x509;
+
+import com.android.apksig.internal.asn1.Asn1Class;
+import com.android.apksig.internal.asn1.Asn1Field;
+import com.android.apksig.internal.asn1.Asn1Type;
+
+import java.math.BigInteger;
+
+/**
+ * {@code RSAPublicKey} as specified in RFC 3279.
+ */
+@Asn1Class(type = Asn1Type.SEQUENCE)
+public class RSAPublicKey {
+    @Asn1Field(index = 0, type = Asn1Type.INTEGER)
+    public BigInteger modulus;
+
+    @Asn1Field(index = 1, type = Asn1Type.INTEGER)
+    public BigInteger publicExponent;
+}
diff --git a/src/main/java/com/android/apksig/util/RunnablesExecutor.java b/src/main/java/com/android/apksig/util/RunnablesExecutor.java
index 04ec1d8..4215810 100644
--- a/src/main/java/com/android/apksig/util/RunnablesExecutor.java
+++ b/src/main/java/com/android/apksig/util/RunnablesExecutor.java
@@ -17,7 +17,7 @@
 package com.android.apksig.util;
 
 public interface RunnablesExecutor {
-    RunnablesExecutor SINGLE_THREADED = p -> p.getRunnable().run();
+    RunnablesExecutor SINGLE_THREADED = p -> p.createRunnable().run();
 
     void execute(RunnablesProvider provider);
 }
diff --git a/src/main/java/com/android/apksig/util/RunnablesProvider.java b/src/main/java/com/android/apksig/util/RunnablesProvider.java
index 5b7bad2..f96dcfe 100644
--- a/src/main/java/com/android/apksig/util/RunnablesProvider.java
+++ b/src/main/java/com/android/apksig/util/RunnablesProvider.java
@@ -17,5 +17,5 @@
 package com.android.apksig.util;
 
 public interface RunnablesProvider {
-    Runnable getRunnable();
+    Runnable createRunnable();
 }
diff --git a/src/test/java/com/android/apksig/ApkSignerTest.java b/src/test/java/com/android/apksig/ApkSignerTest.java
index 1434017..65d9149 100644
--- a/src/test/java/com/android/apksig/ApkSignerTest.java
+++ b/src/test/java/com/android/apksig/ApkSignerTest.java
@@ -21,8 +21,16 @@
 
 import com.android.apksig.ApkVerifier.Issue;
 import com.android.apksig.apk.ApkFormatException;
+import com.android.apksig.apk.ApkUtils;
+import com.android.apksig.internal.apk.ApkSigningBlockUtils;
+import com.android.apksig.internal.apk.SignatureInfo;
+import com.android.apksig.internal.apk.v2.V2SchemeSigner;
+import com.android.apksig.internal.apk.v3.V3SchemeSigner;
+import com.android.apksig.internal.asn1.Asn1BerParser;
 import com.android.apksig.internal.util.ByteBufferDataSource;
 import com.android.apksig.internal.util.Resources;
+import com.android.apksig.internal.x509.RSAPublicKey;
+import com.android.apksig.internal.x509.SubjectPublicKeyInfo;
 import com.android.apksig.util.DataSinks;
 import com.android.apksig.util.DataSource;
 import com.android.apksig.util.DataSources;
@@ -44,6 +52,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.math.BigInteger;
+
 @RunWith(JUnit4.class)
 public class ApkSignerTest {
 
@@ -59,6 +69,10 @@
     private static final String SECOND_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_2";
     private static final String THIRD_RSA_2048_SIGNER_RESOURCE_NAME = "rsa-2048_3";
 
+    // This is the same cert as above with the modulus reencoded to remove the leading 0 sign bit.
+    private static final String FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS =
+            "rsa-2048_negmod.x509.der";
+
     private static final String LINEAGE_RSA_2048_2_SIGNERS_RESOURCE_NAME =
             "rsa-2048-lineage-2-signers";
 
@@ -806,6 +820,83 @@
                 lineageFromApk.isSignerInLineage((secondSigner)));
     }
 
+    @Test
+    public void testPublicKeyHasPositiveModulusAfterSigning() throws Exception {
+        // The V2 and V3 signature schemes include the public key from the certificate in the
+        // signing block. If a certificate with an RSAPublicKey is improperly encoded with a
+        // negative modulus this was previously written to the signing block as is and failed on
+        // device verification since on device the public key in the certificate was reencoded with
+        // the correct encoding for the modulus. This test uses an improperly encoded certificate to
+        // sign an APK and verifies that the public key in the signing block is corrected with a
+        // positive modulus to allow on device installs / updates.
+        List<ApkSigner.SignerConfig> signersList = Collections.singletonList(
+                getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME,
+                        FIRST_RSA_2048_SIGNER_CERT_WITH_NEGATIVE_MODULUS));
+        DataSource signedApk = sign("original.apk", new ApkSigner.Builder(signersList)
+                .setV1SigningEnabled(true)
+                .setV2SigningEnabled(true)
+                .setV3SigningEnabled(true));
+        RSAPublicKey v2PublicKey = getRSAPublicKeyFromSigningBlock(signedApk,
+                ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2);
+        assertTrue("The modulus in the public key in the V2 signing block must not be negative",
+                v2PublicKey.modulus.compareTo(BigInteger.ZERO) > 0);
+        RSAPublicKey v3PublicKey = getRSAPublicKeyFromSigningBlock(signedApk,
+                ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3);
+        assertTrue("The modulus in the public key in the V3 signing block must not be negative",
+                v3PublicKey.modulus.compareTo(BigInteger.ZERO) > 0);
+    }
+
+    private RSAPublicKey getRSAPublicKeyFromSigningBlock(DataSource apk, int signatureVersionId)
+            throws Exception {
+        int signatureVersionBlockId;
+        switch (signatureVersionId) {
+            case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V2:
+                signatureVersionBlockId = V2SchemeSigner.APK_SIGNATURE_SCHEME_V2_BLOCK_ID;
+                break;
+            case ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3:
+                signatureVersionBlockId = V3SchemeSigner.APK_SIGNATURE_SCHEME_V3_BLOCK_ID;
+                break;
+            default:
+                throw new Exception(
+                        "Invalid signature version ID specified: " + signatureVersionId);
+        }
+        ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk);
+        ApkSigningBlockUtils.Result result = new ApkSigningBlockUtils.Result(
+                signatureVersionId);
+        SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk, zipSections,
+                signatureVersionBlockId, result);
+        // FORMAT:
+        // * length prefixed sequence of length prefixed signers
+        //   * length-prefixed signed data
+        //   * V3+ only - minSDK (uint32)
+        //   * V3+ only - maxSDK (uint32)
+        //   * length-prefixed sequence of length-prefixed signatures:
+        //   * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded)
+        ByteBuffer signers = ApkSigningBlockUtils.getLengthPrefixedSlice(
+                signatureInfo.signatureBlock);
+        ByteBuffer signer = ApkSigningBlockUtils.getLengthPrefixedSlice(signers);
+        // Since all the data is read from the signer block the signedData and signatures are
+        // discarded.
+        ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
+        // For V3+ signature version IDs discard the min / max SDKs as well
+        if (signatureVersionId >= ApkSigningBlockUtils.VERSION_APK_SIGNATURE_SCHEME_V3) {
+            signer.getInt();
+            signer.getInt();
+        }
+        ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
+        ByteBuffer publicKey = ApkSigningBlockUtils.getLengthPrefixedSlice(signer);
+        SubjectPublicKeyInfo subjectPublicKeyInfo = Asn1BerParser.parse(publicKey,
+                SubjectPublicKeyInfo.class);
+        ByteBuffer subjectPublicKeyBuffer = subjectPublicKeyInfo.subjectPublicKey;
+        // The SubjectPublicKey is stored as a bit string in the SubjectPublicKeyInfo with the first
+        // byte indicating the number of padding bits in the public key. Read this first byte to
+        // allow parsing the rest of the RSAPublicKey as a sequence.
+        subjectPublicKeyBuffer.get();
+        RSAPublicKey rsaPublicKey = Asn1BerParser.parse(subjectPublicKeyBuffer,
+                RSAPublicKey.class);
+        return rsaPublicKey;
+    }
+
     /**
      * Asserts that signing the specified golden input file using the provided signing
      * configuration produces output identical to the specified golden output file.
@@ -903,4 +994,13 @@
                 Resources.toCertificateChain(ApkSignerTest.class, keyNameInResources + ".x509.pem");
         return new ApkSigner.SignerConfig.Builder(keyNameInResources, privateKey, certs).build();
     }
+
+    private static ApkSigner.SignerConfig getDefaultSignerConfigFromResources(
+            String keyNameInResources, String certNameInResources) throws Exception {
+        PrivateKey privateKey = Resources.toPrivateKey(ApkSignerTest.class,
+                keyNameInResources + ".pk8");
+        List<X509Certificate> certs = Resources.toCertificateChain(ApkSignerTest.class,
+                certNameInResources);
+        return new ApkSigner.SignerConfig.Builder(keyNameInResources, privateKey, certs).build();
+    }
 }
diff --git a/src/test/java/com/android/apksig/internal/apk/ApkSigningBlockUtilsTest.java b/src/test/java/com/android/apksig/internal/apk/ApkSigningBlockUtilsTest.java
index 7eb7c9b..1ee3466 100644
--- a/src/test/java/com/android/apksig/internal/apk/ApkSigningBlockUtilsTest.java
+++ b/src/test/java/com/android/apksig/internal/apk/ApkSigningBlockUtilsTest.java
@@ -101,7 +101,7 @@
                     List<Future<?>> jobs = new ArrayList<>(jobCount);
 
                     for (int i = 0; i < jobCount; i++) {
-                        jobs.add(forkJoinPool.submit(provider.getRunnable()));
+                        jobs.add(forkJoinPool.submit(provider.createRunnable()));
                     }
 
                     try {
diff --git a/src/test/resources/com/android/apksig/rsa-2048_negmod.x509.der b/src/test/resources/com/android/apksig/rsa-2048_negmod.x509.der
new file mode 100644
index 0000000..ba7e78e
--- /dev/null
+++ b/src/test/resources/com/android/apksig/rsa-2048_negmod.x509.der
Binary files differ