Snap for 6626121 from d07c3bde5513af873f2e675a6be8d3ac073d5263 to rvc-release

Change-Id: I2e4baabe3e855b5eb1a4bf08862e081f180b252a
diff --git a/src/apksigner/java/com/android/apksigner/ApkSignerTool.java b/src/apksigner/java/com/android/apksigner/ApkSignerTool.java
index 13d56c8..5783518 100644
--- a/src/apksigner/java/com/android/apksigner/ApkSignerTool.java
+++ b/src/apksigner/java/com/android/apksigner/ApkSignerTool.java
@@ -122,6 +122,7 @@
         boolean v2SigningEnabled = true;
         boolean v3SigningEnabled = true;
         boolean v4SigningEnabled = true;
+        boolean forceSourceStampOverwrite = false;
         boolean verityEnabled = false;
         boolean debuggableApkPermitted = true;
         int minSdkVersion = 1;
@@ -161,6 +162,8 @@
             } else if ("v4-signing-enabled".equals(optionName)) {
                 v4SigningEnabled = optionsParser.getOptionalBooleanValue(true);
                 v4SigningFlagFound = true;
+            } else if ("force-stamp-overwrite".equals(optionName)) {
+                forceSourceStampOverwrite = optionsParser.getOptionalBooleanValue(true);
             } else if ("verity-enabled".equals(optionName)) {
                 verityEnabled = optionsParser.getOptionalBooleanValue(true);
             } else if ("debuggable-apk-permitted".equals(optionName)) {
@@ -323,6 +326,7 @@
                         .setV2SigningEnabled(v2SigningEnabled)
                         .setV3SigningEnabled(v3SigningEnabled)
                         .setV4SigningEnabled(v4SigningEnabled)
+                        .setForceSourceStampOverwrite(forceSourceStampOverwrite)
                         .setVerityEnabled(verityEnabled)
                         .setV4ErrorReportingEnabled(v4SigningEnabled && v4SigningFlagFound)
                         .setDebuggableApkPermitted(debuggableApkPermitted)
diff --git a/src/apksigner/java/com/android/apksigner/help_sign.txt b/src/apksigner/java/com/android/apksigner/help_sign.txt
index c576712..1285810 100644
--- a/src/apksigner/java/com/android/apksigner/help_sign.txt
+++ b/src/apksigner/java/com/android/apksigner/help_sign.txt
@@ -46,6 +46,10 @@
                       enabled based on min and max SDK version (see
                       --min-sdk-version and --max-sdk-version).
 
+--force-stamp-overwrite  Whether to overwrite existing source stamp in the
+                      APK, if found. By default, it is set to false. It has no
+                      effect if no source stamp signer config is provided.
+
 --verity-enabled      Whether to enable the verity signature algorithm for the
                       v2 and v3 signature schemes.
 
diff --git a/src/main/java/com/android/apksig/ApkSigner.java b/src/main/java/com/android/apksig/ApkSigner.java
index 432fc35..154e917 100644
--- a/src/main/java/com/android/apksig/ApkSigner.java
+++ b/src/main/java/com/android/apksig/ApkSigner.java
@@ -46,6 +46,7 @@
 import java.security.SignatureException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -85,6 +86,7 @@
 
     private final List<SignerConfig> mSignerConfigs;
     private final SignerConfig mSourceStampSignerConfig;
+    private final boolean mForceSourceStampOverwrite;
     private final Integer mMinSdkVersion;
     private final boolean mV1SigningEnabled;
     private final boolean mV2SigningEnabled;
@@ -112,6 +114,7 @@
     private ApkSigner(
             List<SignerConfig> signerConfigs,
             SignerConfig sourceStampSignerConfig,
+            boolean forceSourceStampOverwrite,
             Integer minSdkVersion,
             boolean v1SigningEnabled,
             boolean v2SigningEnabled,
@@ -133,6 +136,7 @@
 
         mSignerConfigs = signerConfigs;
         mSourceStampSignerConfig = sourceStampSignerConfig;
+        mForceSourceStampOverwrite = forceSourceStampOverwrite;
         mMinSdkVersion = minSdkVersion;
         mV1SigningEnabled = v1SigningEnabled;
         mV2SigningEnabled = v2SigningEnabled;
@@ -173,7 +177,7 @@
      */
     public void sign()
             throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException,
-            SignatureException, IllegalStateException {
+                    SignatureException, IllegalStateException {
         Closeable in = null;
         DataSource inputApk;
         try {
@@ -219,7 +223,7 @@
 
     private void sign(DataSource inputApk, DataSink outputApkOut, DataSource outputApkIn)
             throws IOException, ApkFormatException, NoSuchAlgorithmException, InvalidKeyException,
-            SignatureException {
+                    SignatureException {
         // Step 1. Find input APK's main ZIP sections
         ApkUtils.ZipSections inputZipSections;
         try {
@@ -275,9 +279,9 @@
             for (SignerConfig signerConfig : mSignerConfigs) {
                 engineSignerConfigs.add(
                         new DefaultApkSignerEngine.SignerConfig.Builder(
-                                signerConfig.getName(),
-                                signerConfig.getPrivateKey(),
-                                signerConfig.getCertificates())
+                                        signerConfig.getName(),
+                                        signerConfig.getPrivateKey(),
+                                        signerConfig.getCertificates())
                                 .build());
             }
             DefaultApkSignerEngine.Builder signerEngineBuilder =
@@ -295,9 +299,9 @@
             if (mSourceStampSignerConfig != null) {
                 signerEngineBuilder.setStampSignerConfig(
                         new DefaultApkSignerEngine.SignerConfig.Builder(
-                                mSourceStampSignerConfig.getName(),
-                                mSourceStampSignerConfig.getPrivateKey(),
-                                mSourceStampSignerConfig.getCertificates())
+                                        mSourceStampSignerConfig.getName(),
+                                        mSourceStampSignerConfig.getPrivateKey(),
+                                        mSourceStampSignerConfig.getCertificates())
                                 .build());
             }
             signerEngine = signerEngineBuilder.build();
@@ -321,6 +325,7 @@
         int lastModifiedTimeForNewEntries = -1;
         long inputOffset = 0;
         long outputOffset = 0;
+        byte[] sourceStampCertificateDigest = null;
         Map<String, CentralDirectoryRecord> outputCdRecordsByName =
                 new HashMap<>(inputCdRecords.size());
         for (final CentralDirectoryRecord inputCdRecord : inputCdRecordsSortedByLfhOffset) {
@@ -328,6 +333,16 @@
             if (Hints.PIN_BYTE_RANGE_ZIP_ENTRY_NAME.equals(entryName)) {
                 continue; // We'll re-add below if needed.
             }
+            if (SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME.equals(entryName)) {
+                try {
+                    sourceStampCertificateDigest =
+                            LocalFileRecord.getUncompressedData(
+                                    inputApkLfhSection, inputCdRecord, inputApkLfhSection.size());
+                } catch (ZipFormatException ex) {
+                    throw new ApkFormatException("Bad source stamp entry");
+                }
+                continue; // Existing source stamp is handled below as needed.
+            }
             ApkSignerEngine.InputJarEntryInstructions entryInstructions =
                     signerEngine.inputJarEntry(entryName);
             boolean shouldOutput;
@@ -379,7 +394,7 @@
                 if ((lastModifiedDateForNewEntries == -1)
                         || (lastModifiedDate > lastModifiedDateForNewEntries)
                         || ((lastModifiedDate == lastModifiedDateForNewEntries)
-                        && (lastModifiedTime > lastModifiedTimeForNewEntries))) {
+                                && (lastModifiedTime > lastModifiedTimeForNewEntries))) {
                     lastModifiedDateForNewEntries = lastModifiedDate;
                     lastModifiedTimeForNewEntries = lastModifiedTime;
                 }
@@ -466,15 +481,26 @@
         // records.
         if (signerEngine.isEligibleForSourceStamp()) {
             byte[] uncompressedData = signerEngine.generateSourceStampCertificateDigest();
-            outputOffset +=
-                    outputDataToOutputApk(
-                            SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME,
-                            uncompressedData,
-                            outputOffset,
-                            outputCdRecords,
-                            lastModifiedTimeForNewEntries,
-                            lastModifiedDateForNewEntries,
-                            outputApkOut);
+            if (mForceSourceStampOverwrite
+                    || sourceStampCertificateDigest == null
+                    || Arrays.equals(uncompressedData, sourceStampCertificateDigest)) {
+                outputOffset +=
+                        outputDataToOutputApk(
+                                SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME,
+                                uncompressedData,
+                                outputOffset,
+                                outputCdRecords,
+                                lastModifiedTimeForNewEntries,
+                                lastModifiedDateForNewEntries,
+                                outputApkOut);
+            } else {
+                throw new ApkFormatException(
+                        String.format(
+                                "Cannot generate SourceStamp. APK contains an existing entry with"
+                                    + " the name: %s, and it is different than the provided source"
+                                    + " stamp certificate",
+                                SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME));
+            }
         }
 
         // Step 8. Generate and output JAR signatures, if necessary. This may output more Local File
@@ -655,7 +681,7 @@
         int dataAlignmentMultiple = getInputJarEntryDataAlignmentMultiple(inputRecord);
         if ((dataAlignmentMultiple <= 1)
                 || ((inputOffset % dataAlignmentMultiple)
-                == (outputOffset % dataAlignmentMultiple))) {
+                        == (outputOffset % dataAlignmentMultiple))) {
             // This record's data will be aligned same as in the input APK.
             return new OutputSizeAndDataOffset(
                     inputRecord.outputRecord(inputLfhSection, outputLfhSection),
@@ -996,6 +1022,7 @@
     public static class Builder {
         private final List<SignerConfig> mSignerConfigs;
         private SignerConfig mSourceStampSignerConfig;
+        private boolean mForceSourceStampOverwrite = false;
         private boolean mV1SigningEnabled = true;
         private boolean mV2SigningEnabled = true;
         private boolean mV3SigningEnabled = true;
@@ -1074,6 +1101,16 @@
         }
 
         /**
+         * Sets whether the APK should overwrite existing source stamp, if found.
+         *
+         * @param force {@code true} to require the APK to be overwrite existing source stamp
+         */
+        public Builder setForceSourceStampOverwrite(boolean force) {
+            mForceSourceStampOverwrite = force;
+            return this;
+        }
+
+        /**
          * Sets the APK to be signed.
          *
          * @see #setInputApk(DataSource)
@@ -1278,7 +1315,7 @@
          * <p>V4 signing requires that the APK be v2 or v3 signed.
          *
          * @param enabled {@code true} to require the APK to be signed using APK Signature Scheme v2
-         * or v3 and generate an v4 signature file
+         *     or v3 and generate an v4 signature file
          */
         public Builder setV4SigningEnabled(boolean enabled) {
             checkInitializedWithoutEngine();
@@ -1296,7 +1333,7 @@
          * the user did not explicitly request the v4 signing.
          *
          * @param enabled {@code false} to prevent errors encountered during the V4 signing from
-         * halting the signing process
+         *     halting the signing process
          */
         public Builder setV4ErrorReportingEnabled(boolean enabled) {
             checkInitializedWithoutEngine();
@@ -1309,7 +1346,7 @@
          * schemes.
          *
          * @param enabled {@code true} to enable the verity signature algorithm for inclusion in the
-         *                            v2 and v3 signature blocks.
+         *     v2 and v3 signature blocks.
          */
         public Builder setVerityEnabled(boolean enabled) {
             checkInitializedWithoutEngine();
@@ -1418,8 +1455,8 @@
                     mV4SigningEnabled = false;
                 } else {
                     throw new IllegalStateException(
-                        "APK Signature Scheme v4 signing requires at least "
-                            + "v2 or v3 signing to be enabled");
+                            "APK Signature Scheme v4 signing requires at least "
+                                    + "v2 or v3 signing to be enabled");
                 }
             }
 
@@ -1428,6 +1465,7 @@
             return new ApkSigner(
                     mSignerConfigs,
                     mSourceStampSignerConfig,
+                    mForceSourceStampOverwrite,
                     mMinSdkVersion,
                     mV1SigningEnabled,
                     mV2SigningEnabled,
diff --git a/src/main/java/com/android/apksig/DefaultApkSignerEngine.java b/src/main/java/com/android/apksig/DefaultApkSignerEngine.java
index e7c6e8f..f0796fb 100644
--- a/src/main/java/com/android/apksig/DefaultApkSignerEngine.java
+++ b/src/main/java/com/android/apksig/DefaultApkSignerEngine.java
@@ -379,7 +379,7 @@
             throws InvalidKeyException {
         return createSigningBlockSignerConfig(
                 mSourceStampSignerConfig,
-                /* apkSigningBlockPaddingSupported= */ true,
+                /* apkSigningBlockPaddingSupported= */ false,
                 ApkSigningBlockUtils.VERSION_SOURCE_STAMP);
     }
 
@@ -459,7 +459,7 @@
             case ApkSigningBlockUtils.VERSION_SOURCE_STAMP:
                 newSignerConfig.signatureAlgorithms =
                         Collections.singletonList(
-                                SignatureAlgorithm.VERITY_RSA_PKCS1_V1_5_WITH_SHA256);
+                                SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256);
                 break;
             default:
                 throw new IllegalArgumentException("Unknown APK Signature Scheme ID requested");
diff --git a/src/test/java/com/android/apksig/ApkSignerTest.java b/src/test/java/com/android/apksig/ApkSignerTest.java
index 74f1c7e..560202c 100644
--- a/src/test/java/com/android/apksig/ApkSignerTest.java
+++ b/src/test/java/com/android/apksig/ApkSignerTest.java
@@ -20,6 +20,7 @@
 import static com.android.apksig.apk.ApkUtils.findZipSections;
 
 import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
@@ -35,6 +36,7 @@
 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.AndroidSdkVersion;
 import com.android.apksig.internal.util.Resources;
 import com.android.apksig.internal.x509.RSAPublicKey;
 import com.android.apksig.internal.x509.SubjectPublicKeyInfo;
@@ -1032,6 +1034,80 @@
     }
 
     @Test
+    public void testSignApk_existingStampFile_sameSourceStamp() throws Exception {
+        List<ApkSigner.SignerConfig> signers =
+                Collections.singletonList(
+                        getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
+        ApkSigner.SignerConfig sourceStampSigner =
+                getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
+
+        DataSource signedApk =
+                sign(
+                        "original-with-stamp-file.apk",
+                        new ApkSigner.Builder(signers)
+                                .setV1SigningEnabled(true)
+                                .setV2SigningEnabled(true)
+                                .setV3SigningEnabled(true)
+                                .setSourceStampSignerConfig(sourceStampSigner));
+
+        ApkVerifier.Result sourceStampVerificationResult =
+                verify(signedApk, /* minSdkVersionOverride= */ null);
+        assertSourceStampVerified(signedApk, sourceStampVerificationResult);
+    }
+
+    @Test
+    public void testSignApk_existingStampFile_differentSourceStamp() throws Exception {
+        List<ApkSigner.SignerConfig> signers =
+                Collections.singletonList(
+                        getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
+        ApkSigner.SignerConfig sourceStampSigner =
+                getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
+
+        Exception exception =
+                assertThrows(
+                        ApkFormatException.class,
+                        () ->
+                                sign(
+                                        "original-with-stamp-file.apk",
+                                        new ApkSigner.Builder(signers)
+                                                .setV1SigningEnabled(true)
+                                                .setV2SigningEnabled(true)
+                                                .setV3SigningEnabled(true)
+                                                .setSourceStampSignerConfig(sourceStampSigner)));
+        assertEquals(
+                String.format(
+                        "Cannot generate SourceStamp. APK contains an existing entry with the"
+                                + " name: %s, and it is different than the provided source stamp"
+                                + " certificate",
+                        SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME),
+                exception.getMessage());
+    }
+
+    @Test
+    public void testSignApk_existingStampFile_differentSourceStamp_forceOverwrite()
+            throws Exception {
+        List<ApkSigner.SignerConfig> signers =
+                Collections.singletonList(
+                        getDefaultSignerConfigFromResources(FIRST_RSA_2048_SIGNER_RESOURCE_NAME));
+        ApkSigner.SignerConfig sourceStampSigner =
+                getDefaultSignerConfigFromResources(SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
+
+        DataSource signedApk =
+                sign(
+                        "original-with-stamp-file.apk",
+                        new ApkSigner.Builder(signers)
+                                .setV1SigningEnabled(true)
+                                .setV2SigningEnabled(true)
+                                .setV3SigningEnabled(true)
+                                .setForceSourceStampOverwrite(true)
+                                .setSourceStampSignerConfig(sourceStampSigner));
+
+        ApkVerifier.Result sourceStampVerificationResult =
+                verify(signedApk, /* minSdkVersionOverride= */ null);
+        assertSourceStampVerified(signedApk, sourceStampVerificationResult);
+    }
+
+    @Test
     public void testSignApk_stampBlock_noStampGenerated() throws Exception {
         List<ApkSigner.SignerConfig> signersList =
                 Collections.singletonList(
@@ -1075,12 +1151,9 @@
                                 .setV3SigningEnabled(false)
                                 .setSourceStampSignerConfig(sourceStampSigner));
 
-        SignatureInfo signatureInfo =
-                getSignatureInfoFromApk(
-                        signedApk,
-                        ApkSigningBlockUtils.VERSION_SOURCE_STAMP,
-                        V2SourceStampSigner.V2_SOURCE_STAMP_BLOCK_ID);
-        assertNotNull(signatureInfo.signatureBlock);
+        ApkVerifier.Result sourceStampVerificationResult =
+                verify(signedApk, /* minSdkVersionOverride= */ null);
+        assertSourceStampVerified(signedApk, sourceStampVerificationResult);
     }
 
     @Test
@@ -1100,12 +1173,9 @@
                                 .setV3SigningEnabled(false)
                                 .setSourceStampSignerConfig(sourceStampSigner));
 
-        SignatureInfo signatureInfo =
-                getSignatureInfoFromApk(
-                        signedApk,
-                        ApkSigningBlockUtils.VERSION_SOURCE_STAMP,
-                        V2SourceStampSigner.V2_SOURCE_STAMP_BLOCK_ID);
-        assertNotNull(signatureInfo.signatureBlock);
+        ApkVerifier.Result sourceStampVerificationResult =
+                verifyForMinSdkVersion(signedApk, /* minSdkVersion= */ AndroidSdkVersion.N);
+        assertSourceStampVerified(signedApk, sourceStampVerificationResult);
     }
 
     @Test
@@ -1125,12 +1195,9 @@
                                 .setV3SigningEnabled(true)
                                 .setSourceStampSignerConfig(sourceStampSigner));
 
-        SignatureInfo signatureInfo =
-                getSignatureInfoFromApk(
-                        signedApk,
-                        ApkSigningBlockUtils.VERSION_SOURCE_STAMP,
-                        V2SourceStampSigner.V2_SOURCE_STAMP_BLOCK_ID);
-        assertNotNull(signatureInfo.signatureBlock);
+        ApkVerifier.Result sourceStampVerificationResult =
+                verifyForMinSdkVersion(signedApk, /* minSdkVersion= */ AndroidSdkVersion.N);
+        assertSourceStampVerified(signedApk, sourceStampVerificationResult);
     }
 
     private RSAPublicKey getRSAPublicKeyFromSigningBlock(DataSource apk, int signatureVersionId)
@@ -1272,6 +1339,18 @@
         ApkVerifierTest.assertVerified(result);
     }
 
+    private static void assertSourceStampVerified(DataSource signedApk, ApkVerifier.Result result)
+            throws ApkSigningBlockUtils.SignatureNotFoundException, IOException,
+                    ZipFormatException {
+        SignatureInfo signatureInfo =
+                getSignatureInfoFromApk(
+                        signedApk,
+                        ApkSigningBlockUtils.VERSION_SOURCE_STAMP,
+                        V2SourceStampSigner.V2_SOURCE_STAMP_BLOCK_ID);
+        assertNotNull(signatureInfo.signatureBlock);
+        assertTrue(result.isSourceStampVerified());
+    }
+
     private static void assertVerificationFailure(ApkVerifier.Result result, Issue expectedIssue) {
         ApkVerifierTest.assertVerificationFailure(result, expectedIssue);
     }
diff --git a/src/test/resources/com/android/apksig/original-with-stamp-file.apk b/src/test/resources/com/android/apksig/original-with-stamp-file.apk
new file mode 100644
index 0000000..604fe6f
--- /dev/null
+++ b/src/test/resources/com/android/apksig/original-with-stamp-file.apk
Binary files differ