aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Groover <mpgroover@google.com>2022-05-06 17:05:31 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2022-05-06 17:05:31 +0000
commit14f49463152da71c86926ec832c066ba0b8108ea (patch)
tree14c963da1fec0a62377183704ae44846928381c2
parentaa8ad504730b6b4704744a5a1aa3f0d0c53941e3 (diff)
parent495c7bb224dd39a2c2318b6e953ed12b589c5e37 (diff)
downloadapksig-14f49463152da71c86926ec832c066ba0b8108ea.tar.gz
Merge "Add timestamp attribute to source stamp block" into tm-dev am: 495c7bb224
Original change: https://googleplex-android-review.googlesource.com/c/platform/tools/apksig/+/18152826 Change-Id: Iaa640cd3972b326efaf6bfe6d56f263974a9a78f Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--src/main/java/com/android/apksig/ApkVerificationIssue.java2
-rw-r--r--src/main/java/com/android/apksig/ApkVerifier.java24
-rw-r--r--src/main/java/com/android/apksig/SourceStampVerifier.java11
-rw-r--r--src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java1
-rw-r--r--src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java7
-rw-r--r--src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java9
-rw-r--r--src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java17
-rw-r--r--src/test/java/com/android/apksig/ApkSignerTest.java26
-rw-r--r--src/test/java/com/android/apksig/ApkVerifierTest.java117
-rw-r--r--src/test/java/com/android/apksig/SourceStampVerifierTest.java106
-rw-r--r--src/test/resources/com/android/apksig/stamp-int-timestamp-value.apkbin0 -> 8659 bytes
-rw-r--r--src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-less-than-zero.apkbin0 -> 8659 bytes
-rw-r--r--src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-zero.apkbin0 -> 8659 bytes
-rw-r--r--src/test/resources/com/android/apksig/stamp-timestamp-in-last-8-of-16-byte-buffer.apkbin0 -> 8659 bytes
-rw-r--r--src/test/resources/com/android/apksig/stamp-valid-timestamp-16-byte-buffer.apkbin0 -> 8659 bytes
-rw-r--r--src/test/resources/com/android/apksig/stamp-valid-timestamp-value-modified.apkbin0 -> 8659 bytes
-rw-r--r--src/test/resources/com/android/apksig/stamp-valid-timestamp-value.apkbin0 -> 8659 bytes
17 files changed, 319 insertions, 1 deletions
diff --git a/src/main/java/com/android/apksig/ApkVerificationIssue.java b/src/main/java/com/android/apksig/ApkVerificationIssue.java
index 2aa9d0b..fa2b7aa 100644
--- a/src/main/java/com/android/apksig/ApkVerificationIssue.java
+++ b/src/main/java/com/android/apksig/ApkVerificationIssue.java
@@ -116,6 +116,8 @@ public class ApkVerificationIssue {
public static final int JAR_SIG_NO_SIGNATURES = 36;
/** An exception was encountered when parsing the V1 / jar signer in the signature block. */
public static final int JAR_SIG_PARSE_EXCEPTION = 37;
+ /** The source stamp timestamp attribute has an invalid value. */
+ public static final int SOURCE_STAMP_INVALID_TIMESTAMP = 38;
private final int mIssueId;
private final String mFormat;
diff --git a/src/main/java/com/android/apksig/ApkVerifier.java b/src/main/java/com/android/apksig/ApkVerifier.java
index 1613c26..fc28864 100644
--- a/src/main/java/com/android/apksig/ApkVerifier.java
+++ b/src/main/java/com/android/apksig/ApkVerifier.java
@@ -1767,6 +1767,8 @@ public class ApkVerifier {
private final SourceStampVerificationStatus mSourceStampVerificationStatus;
+ private final long mTimestamp;
+
private SourceStampInfo(ApkSignerInfo result) {
mCertificates = result.certs;
mCertificateLineage = result.certificateLineage;
@@ -1780,6 +1782,7 @@ public class ApkVerifier {
mSourceStampVerificationStatus =
SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED;
}
+ mTimestamp = result.timestamp;
}
SourceStampInfo(SourceStampVerificationStatus sourceStampVerificationStatus) {
@@ -1788,6 +1791,7 @@ public class ApkVerifier {
mErrors = Collections.emptyList();
mWarnings = Collections.emptyList();
mSourceStampVerificationStatus = sourceStampVerificationStatus;
+ mTimestamp = 0;
}
/**
@@ -1827,6 +1831,14 @@ public class ApkVerifier {
public SourceStampVerificationStatus getSourceStampVerificationStatus() {
return mSourceStampVerificationStatus;
}
+
+ /**
+ * Returns the epoch timestamp in seconds representing the time this source stamp block
+ * was signed, or 0 if the timestamp is not available.
+ */
+ public long getTimestampEpochSeconds() {
+ return mTimestamp;
+ }
}
}
@@ -3000,6 +3012,16 @@ public class ApkVerifier {
+ "contains a proof-of-rotation record with signature(s) that did not verify."),
/**
+ * The source stamp timestamp attribute has an invalid value (<= 0).
+ * <ul>
+ * <li>Parameter 1: The invalid timestamp value.
+ * </ul>
+ */
+ SOURCE_STAMP_INVALID_TIMESTAMP(
+ "The source stamp"
+ + " timestamp attribute has an invalid value: %1$d"),
+
+ /**
* The APK could not be properly parsed due to a ZIP or APK format exception.
* <ul>
* <li>Parameter 1: The {@code Exception} caught when attempting to parse the APK.
@@ -3295,6 +3317,8 @@ public class ApkVerifier {
Issue.JAR_SIG_NO_SIGNATURES);
sVerificationIssueIdToIssue.put(ApkVerificationIssue.JAR_SIG_PARSE_EXCEPTION,
Issue.JAR_SIG_PARSE_EXCEPTION);
+ sVerificationIssueIdToIssue.put(ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP,
+ Issue.SOURCE_STAMP_INVALID_TIMESTAMP);
}
/**
diff --git a/src/main/java/com/android/apksig/SourceStampVerifier.java b/src/main/java/com/android/apksig/SourceStampVerifier.java
index 587cbd3..b155341 100644
--- a/src/main/java/com/android/apksig/SourceStampVerifier.java
+++ b/src/main/java/com/android/apksig/SourceStampVerifier.java
@@ -730,6 +730,8 @@ public class SourceStampVerifier {
private final List<ApkVerificationIssue> mErrors = new ArrayList<>();
private final List<ApkVerificationIssue> mWarnings = new ArrayList<>();
+ private final long mTimestamp;
+
/*
* Since this utility is intended just to verify the source stamp, and the source stamp
* currently only logs warnings to prevent failing the APK signature verification, treat
@@ -744,6 +746,7 @@ public class SourceStampVerifier {
mCertificateLineage = result.certificateLineage;
mErrors.addAll(result.getErrors());
mWarnings.addAll(result.getWarnings());
+ mTimestamp = result.timestamp;
}
/**
@@ -794,6 +797,14 @@ public class SourceStampVerifier {
public List<ApkVerificationIssue> getWarnings() {
return mWarnings;
}
+
+ /**
+ * Returns the epoch timestamp in seconds representing the time this source stamp block
+ * was signed, or 0 if the timestamp is not available.
+ */
+ public long getTimestampEpochSeconds() {
+ return mTimestamp;
+ }
}
}
diff --git a/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java b/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java
index e0ea365..12e54d0 100644
--- a/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java
+++ b/src/main/java/com/android/apksig/internal/apk/ApkSignerInfo.java
@@ -27,6 +27,7 @@ import java.util.List;
*/
public class ApkSignerInfo {
public int index;
+ public long timestamp;
public List<X509Certificate> certs = new ArrayList<>();
public List<X509Certificate> certificateLineage = new ArrayList<>();
diff --git a/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java b/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java
index 465fbb0..2a949ad 100644
--- a/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java
+++ b/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampConstants.java
@@ -24,4 +24,11 @@ public class SourceStampConstants {
public static final int V2_SOURCE_STAMP_BLOCK_ID = 0x6dff800d;
public static final String SOURCE_STAMP_CERTIFICATE_HASH_ZIP_ENTRY_NAME = "stamp-cert-sha256";
public static final int PROOF_OF_ROTATION_ATTR_ID = 0x9d6303f7;
+ /**
+ * The source stamp timestamp attribute value is an 8-byte little-endian encoded long
+ * representing the epoch time in seconds when the stamp block was signed. The first 8 bytes
+ * of the attribute value buffer will be used to read the timestamp, and any additional buffer
+ * space will be ignored.
+ */
+ public static final int STAMP_TIME_ATTR_ID = 0xe43c5946;
}
diff --git a/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java b/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java
index 59fa791..aace413 100644
--- a/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java
+++ b/src/main/java/com/android/apksig/internal/apk/stamp/SourceStampVerifier.java
@@ -318,6 +318,15 @@ class SourceStampVerifier {
byte[] value = ByteBufferUtils.toByteArray(attribute);
if (id == SourceStampConstants.PROOF_OF_ROTATION_ATTR_ID) {
readStampCertificateLineage(value, sourceStampCertificate, result);
+ } else if (id == SourceStampConstants.STAMP_TIME_ATTR_ID) {
+ long timestamp = ByteBuffer.wrap(value).order(
+ ByteOrder.LITTLE_ENDIAN).getLong();
+ if (timestamp > 0) {
+ result.timestamp = timestamp;
+ } else {
+ result.addWarning(ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP,
+ timestamp);
+ }
} else {
result.addWarning(ApkVerificationIssue.SOURCE_STAMP_UNKNOWN_ATTRIBUTE, id);
}
diff --git a/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java b/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java
index 1c1570a..9c00a88 100644
--- a/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java
+++ b/src/main/java/com/android/apksig/internal/apk/stamp/V2SourceStampSigner.java
@@ -35,6 +35,7 @@ import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
+import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -202,6 +203,22 @@ public abstract class V2SourceStampSigner {
private static Map<Integer, byte[]> generateStampAttributes(SigningCertificateLineage lineage) {
HashMap<Integer, byte[]> stampAttributes = new HashMap<>();
+
+ // Write the current epoch time as the timestamp for the source stamp.
+ long timestamp = Instant.now().getEpochSecond();
+ if (timestamp > 0) {
+ ByteBuffer attributeBuffer = ByteBuffer.allocate(8);
+ attributeBuffer.order(ByteOrder.LITTLE_ENDIAN);
+ attributeBuffer.putLong(timestamp);
+ stampAttributes.put(SourceStampConstants.STAMP_TIME_ATTR_ID, attributeBuffer.array());
+ } else {
+ // The epoch time should never be <= 0, and since security decisions can potentially
+ // be made based on the value in the timestamp, throw an Exception to ensure the issues
+ // with the environment are resolved before allowing the signing.
+ throw new IllegalStateException(
+ "Received an invalid value from Instant#getTimestamp: " + timestamp);
+ }
+
if (lineage != null) {
stampAttributes.put(SourceStampConstants.PROOF_OF_ROTATION_ATTR_ID,
lineage.encodeSigningCertificateLineage());
diff --git a/src/test/java/com/android/apksig/ApkSignerTest.java b/src/test/java/com/android/apksig/ApkSignerTest.java
index 9004d80..9740d75 100644
--- a/src/test/java/com/android/apksig/ApkSignerTest.java
+++ b/src/test/java/com/android/apksig/ApkSignerTest.java
@@ -51,7 +51,6 @@ import com.android.apksig.zip.ZipFormatException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -1999,6 +1998,31 @@ public class ApkSignerTest {
SECOND_RSA_2048_SIGNER_RESOURCE_NAME);
}
+ @Test
+ public void testSourceStampTimestamp_signWithSourceStamp_validTimestampValue()
+ throws Exception {
+ // Source stamps should include a timestamp attribute with the epoch time the stamp block
+ // was signed. This test verifies a standard signing with a source stamp includes a valid
+ // value for the source stamp timestamp attribute.
+ ApkSigner.SignerConfig rsa2048SignerConfig = getDefaultSignerConfigFromResources(
+ FIRST_RSA_2048_SIGNER_RESOURCE_NAME);
+ List<ApkSigner.SignerConfig> ecP256SignerConfig = Collections.singletonList(
+ getDefaultSignerConfigFromResources(EC_P256_SIGNER_RESOURCE_NAME));
+
+ File signedApk = sign("original.apk",
+ new ApkSigner.Builder(ecP256SignerConfig)
+ .setV1SigningEnabled(true)
+ .setV2SigningEnabled(true)
+ .setV3SigningEnabled(true)
+ .setV4SigningEnabled(false)
+ .setSourceStampSignerConfig(rsa2048SignerConfig));
+ ApkVerifier.Result result = verify(signedApk, null);
+
+ assertSourceStampVerified(signedApk, result);
+ long timestamp = result.getSourceStampInfo().getTimestampEpochSeconds();
+ assertTrue("Invalid source stamp timestamp value: " + timestamp, timestamp > 0);
+ }
+
/**
* Asserts the provided {@code signedApk} contains a signature block with the expected
* {@code byte[]} value and block ID as specified in the {@code expectedBlock}.
diff --git a/src/test/java/com/android/apksig/ApkVerifierTest.java b/src/test/java/com/android/apksig/ApkVerifierTest.java
index 31ed430..9de2b59 100644
--- a/src/test/java/com/android/apksig/ApkVerifierTest.java
+++ b/src/test/java/com/android/apksig/ApkVerifierTest.java
@@ -1313,6 +1313,123 @@ public class ApkVerifierTest {
}
@Test
+ public void verifySourceStamp_noTimestamp_returnsDefaultValue() throws Exception {
+ // A timestamp attribute was added to the source stamp, but verification of APKs that were
+ // generated prior to the addition of the timestamp should still complete successfully,
+ // returning a default value of 0 for the timestamp.
+ ApkVerifier.Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk");
+
+ assertTrue(verificationResult.isSourceStampVerified());
+ assertEquals(
+ "A value of 0 should be returned for the timestamp when the attribute is not "
+ + "present",
+ 0, verificationResult.getSourceStampInfo().getTimestampEpochSeconds());
+ }
+
+ @Test
+ public void verifySourceStamp_validTimestamp_returnsExpectedValue() throws Exception {
+ // Once an APK is signed with a source stamp that contains a valid value for the timestamp
+ // attribute, verification of the source stamp should result in the same value for the
+ // timestamp returned to the verifier.
+ ApkVerifier.Result verificationResult = verifySourceStamp(
+ "stamp-valid-timestamp-value.apk");
+
+ assertTrue(verificationResult.isSourceStampVerified());
+ assertEquals(1644886584, verificationResult.getSourceStampInfo().getTimestampEpochSeconds());
+ }
+
+ @Test
+ public void verifySourceStamp_validTimestampLargerBuffer_returnsExpectedValue()
+ throws Exception {
+ // The source stamp timestamp attribute value is expected to be written to an 8 byte buffer
+ // as a little-endian long; while a larger buffer will not result in an error, any
+ // additional space after the buffer's initial 8 bytes will be ignored. This test verifies a
+ // valid timestamp value written to the first 8 bytes of a 16 byte buffer can still be read
+ // successfully.
+ ApkVerifier.Result verificationResult = verifySourceStamp(
+ "stamp-valid-timestamp-16-byte-buffer.apk");
+
+ assertTrue(verificationResult.isSourceStampVerified());
+ assertEquals(1645126786,
+ verificationResult.getSourceStampInfo().getTimestampEpochSeconds());
+ }
+
+ @Test
+ public void verifySourceStamp_invalidTimestampValueEqualsZero_verificationFails()
+ throws Exception {
+ // If the source stamp timestamp attribute exists and is <= 0, then a warning should be
+ // reported to notify the caller to the invalid attribute value. This test verifies a
+ // a warning is reported when the timestamp attribute value is 0.
+ ApkVerifier.Result verificationResult = verifySourceStamp(
+ "stamp-invalid-timestamp-value-zero.apk");
+
+ assertSourceStampVerificationStatus(verificationResult,
+ SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
+ assertSourceStampVerificationFailure(verificationResult,
+ Issue.SOURCE_STAMP_INVALID_TIMESTAMP);
+ }
+
+ @Test
+ public void verifySourceStamp_invalidTimestampValueLessThanZero_verificationFails()
+ throws Exception {
+ // If the source stamp timestamp attribute exists and is <= 0, then a warning should be
+ // reported to notify the caller to the invalid attribute value. This test verifies a
+ // a warning is reported when the timestamp attribute value is < 0.
+ ApkVerifier.Result verificationResult = verifySourceStamp(
+ "stamp-invalid-timestamp-value-less-than-zero.apk");
+
+ assertSourceStampVerificationStatus(verificationResult,
+ SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
+ assertSourceStampVerificationFailure(verificationResult,
+ Issue.SOURCE_STAMP_INVALID_TIMESTAMP);
+ }
+
+ @Test
+ public void verifySourceStamp_invalidTimestampZeroInFirst8BytesOfBuffer_verificationFails()
+ throws Exception {
+ // The source stamp's timestamp attribute value is expected to be written to the first 8
+ // bytes of the attribute's value buffer; if a larger buffer is used and the timestamp
+ // value is not written as a little-endian long to the first 8 bytes of the buffer, then
+ // an error should be reported for the timestamp attribute since the rest of the buffer will
+ // be ignored.
+ ApkVerifier.Result verificationResult = verifySourceStamp(
+ "stamp-timestamp-in-last-8-of-16-byte-buffer.apk");
+
+ assertSourceStampVerificationStatus(verificationResult,
+ SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
+ assertSourceStampVerificationFailure(verificationResult,
+ Issue.SOURCE_STAMP_INVALID_TIMESTAMP);
+ }
+
+
+ @Test
+ public void verifySourceStamp_intTimestampValue_verificationFails() throws Exception {
+ // Since the source stamp timestamp attribute value is a long, an attribute value with
+ // insufficient space to hold a long value should result in a warning reported to the user.
+ ApkVerifier.Result verificationResult = verifySourceStamp(
+ "stamp-int-timestamp-value.apk");
+
+ assertSourceStampVerificationStatus(verificationResult,
+ SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
+ assertSourceStampVerificationFailure(verificationResult,
+ Issue.SOURCE_STAMP_MALFORMED_ATTRIBUTE);
+ }
+
+ @Test
+ public void verifySourceStamp_modifiedTimestampValue_verificationFails() throws Exception {
+ // The source stamp timestamp attribute is part of the block's signed data; this test
+ // verifies if the value of the timestamp in the stamp block is modified then verification
+ // of the source stamp should fail.
+ ApkVerifier.Result verificationResult = verifySourceStamp(
+ "stamp-valid-timestamp-value-modified.apk");
+
+ assertSourceStampVerificationStatus(verificationResult,
+ SourceStampVerificationStatus.STAMP_VERIFICATION_FAILED);
+ assertSourceStampVerificationFailure(verificationResult,
+ Issue.SOURCE_STAMP_DID_NOT_VERIFY);
+ }
+
+ @Test
public void apkVerificationIssueAdapter_verifyAllBaseIssuesMapped() throws Exception {
Field[] fields = ApkVerificationIssue.class.getFields();
StringBuilder msg = new StringBuilder();
diff --git a/src/test/java/com/android/apksig/SourceStampVerifierTest.java b/src/test/java/com/android/apksig/SourceStampVerifierTest.java
index f5020cc..2e54a8a 100644
--- a/src/test/java/com/android/apksig/SourceStampVerifierTest.java
+++ b/src/test/java/com/android/apksig/SourceStampVerifierTest.java
@@ -21,10 +21,12 @@ import static com.android.apksig.internal.apk.ApkSigningBlockUtilsLite.toHex;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.android.apksig.SourceStampVerifier.Result;
import com.android.apksig.SourceStampVerifier.Result.SignerInfo;
+import com.android.apksig.internal.util.AndroidSdkVersion;
import com.android.apksig.internal.util.Resources;
import com.android.apksig.util.DataSources;
@@ -269,6 +271,110 @@ public class SourceStampVerifierTest {
ApkVerificationIssue.JAR_SIG_NO_SIGNATURES);
}
+ @Test
+ public void verifySourceStamp_noTimestamp_returnsDefaultValue() throws Exception {
+ // A timestamp attribute was added to the source stamp, but verification of APKs that were
+ // generated prior to the addition of the timestamp should still complete successfully,
+ // returning a default value of 0 for the timestamp.
+ Result verificationResult = verifySourceStamp("v3-only-with-stamp.apk", AndroidSdkVersion.P,
+ AndroidSdkVersion.P);
+
+ assertVerified(verificationResult);
+ assertEquals(
+ "A value of 0 should be returned for the timestamp when the attribute is not "
+ + "present",
+ 0, verificationResult.getSourceStampInfo().getTimestampEpochSeconds());
+ }
+
+ @Test
+ public void verifySourceStamp_validTimestamp_returnsExpectedValue() throws Exception {
+ // Once an APK is signed with a source stamp that contains a valid value for the timestamp
+ // attribute, verification of the source stamp should result in the same value for the
+ // timestamp returned to the verifier.
+ Result verificationResult = verifySourceStamp("stamp-valid-timestamp-value.apk");
+
+ assertVerified(verificationResult);
+ assertEquals(1644886584, verificationResult.getSourceStampInfo().getTimestampEpochSeconds());
+ }
+
+ @Test
+ public void verifySourceStamp_validTimestampLargerBuffer_returnsExpectedValue()
+ throws Exception {
+ // The source stamp timestamp attribute value is expected to be written to an 8 byte buffer
+ // as a little-endian long; while a larger buffer will not result in an error, any
+ // additional space after the buffer's initial 8 bytes will be ignored. This test verifies a
+ // valid timestamp value written to the first 8 bytes of a 16 byte buffer can still be read
+ // successfully.
+ Result verificationResult = verifySourceStamp("stamp-valid-timestamp-16-byte-buffer.apk");
+
+ assertEquals(1645126786,
+ verificationResult.getSourceStampInfo().getTimestampEpochSeconds());
+ }
+
+ @Test
+ public void verifySourceStamp_invalidTimestampValueEqualsZero_verificationFails()
+ throws Exception {
+ // If the source stamp timestamp attribute exists and is <= 0, then a warning should be
+ // reported to notify the caller to the invalid attribute value. This test verifies a
+ // a warning is reported when the timestamp attribute value is 0.
+ Result verificationResult = verifySourceStamp("stamp-invalid-timestamp-value-zero.apk");
+
+ assertSourceStampVerificationFailure(verificationResult,
+ ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP);
+ }
+
+ @Test
+ public void verifySourceStamp_invalidTimestampValueLessThanZero_verificationFails()
+ throws Exception {
+ // If the source stamp timestamp attribute exists and is <= 0, then a warning should be
+ // reported to notify the caller to the invalid attribute value. This test verifies a
+ // a warning is reported when the timestamp attribute value is < 0.
+ Result verificationResult = verifySourceStamp(
+ "stamp-invalid-timestamp-value-less-than-zero.apk");
+
+ assertSourceStampVerificationFailure(verificationResult,
+ ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP);
+ }
+
+ @Test
+ public void verifySourceStamp_invalidTimestampZeroInFirst8BytesOfBuffer_verificationFails()
+ throws Exception {
+ // The source stamp's timestamp attribute value is expected to be written to the first 8
+ // bytes of the attribute's value buffer; if a larger buffer is used and the timestamp
+ // value is not written as a little-endian long to the first 8 bytes of the buffer, then
+ // an error should be reported for the timestamp attribute since the rest of the buffer will
+ // be ignored.
+ Result verificationResult = verifySourceStamp(
+ "stamp-timestamp-in-last-8-of-16-byte-buffer.apk");
+
+ assertSourceStampVerificationFailure(verificationResult,
+ ApkVerificationIssue.SOURCE_STAMP_INVALID_TIMESTAMP);
+ }
+
+ @Test
+ public void verifySourceStamp_intTimestampValue_verificationFails() throws Exception {
+ // Since the source stamp timestamp attribute value is a long, an attribute value with
+ // insufficient space to hold a long value should result in a warning reported to the user.
+ Result verificationResult = verifySourceStamp(
+ "stamp-int-timestamp-value.apk");
+
+ assertSourceStampVerificationFailure(verificationResult,
+ ApkVerificationIssue.SOURCE_STAMP_MALFORMED_ATTRIBUTE);
+ }
+
+ @Test
+ public void verifySourceStamp_modifiedTimestampValue_verificationFails() throws Exception {
+ // The source stamp timestamp attribute is part of the block's signed data; this test
+ // verifies if the value of the timestamp in the stamp block is modified then verification
+ // of the source stamp should fail.
+ Result verificationResult = verifySourceStamp(
+ "stamp-valid-timestamp-value-modified.apk");
+
+ assertSourceStampVerificationFailure(verificationResult,
+ ApkVerificationIssue.SOURCE_STAMP_DID_NOT_VERIFY);
+ }
+
+
private Result verifySourceStamp(String apkFilenameInResources)
throws Exception {
return verifySourceStamp(apkFilenameInResources, null, null, null);
diff --git a/src/test/resources/com/android/apksig/stamp-int-timestamp-value.apk b/src/test/resources/com/android/apksig/stamp-int-timestamp-value.apk
new file mode 100644
index 0000000..0cd740c
--- /dev/null
+++ b/src/test/resources/com/android/apksig/stamp-int-timestamp-value.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-less-than-zero.apk b/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-less-than-zero.apk
new file mode 100644
index 0000000..f4a189e
--- /dev/null
+++ b/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-less-than-zero.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-zero.apk b/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-zero.apk
new file mode 100644
index 0000000..6cfd082
--- /dev/null
+++ b/src/test/resources/com/android/apksig/stamp-invalid-timestamp-value-zero.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/stamp-timestamp-in-last-8-of-16-byte-buffer.apk b/src/test/resources/com/android/apksig/stamp-timestamp-in-last-8-of-16-byte-buffer.apk
new file mode 100644
index 0000000..260b0ca
--- /dev/null
+++ b/src/test/resources/com/android/apksig/stamp-timestamp-in-last-8-of-16-byte-buffer.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/stamp-valid-timestamp-16-byte-buffer.apk b/src/test/resources/com/android/apksig/stamp-valid-timestamp-16-byte-buffer.apk
new file mode 100644
index 0000000..da9c34d
--- /dev/null
+++ b/src/test/resources/com/android/apksig/stamp-valid-timestamp-16-byte-buffer.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/stamp-valid-timestamp-value-modified.apk b/src/test/resources/com/android/apksig/stamp-valid-timestamp-value-modified.apk
new file mode 100644
index 0000000..eefc148
--- /dev/null
+++ b/src/test/resources/com/android/apksig/stamp-valid-timestamp-value-modified.apk
Binary files differ
diff --git a/src/test/resources/com/android/apksig/stamp-valid-timestamp-value.apk b/src/test/resources/com/android/apksig/stamp-valid-timestamp-value.apk
new file mode 100644
index 0000000..3c6a501
--- /dev/null
+++ b/src/test/resources/com/android/apksig/stamp-valid-timestamp-value.apk
Binary files differ