summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorTreeHugger Robot <treehugger-gerrit@google.com>2018-01-23 23:19:59 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2018-01-23 23:19:59 +0000
commit4ec3efce54b14157ce9084faed51be07cbbbe274 (patch)
treee3fac3fe3d7f69492b5374dff66657b3fda2221d /core/java
parent555c6fcd96cfd00bfa7f8d26de0faca41d103675 (diff)
parent77029c5b16351775cb2333369ef9a4bc1d9acf58 (diff)
Merge "Add proof-of-rotation information to PackageParser.SigningDetails"
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/content/pm/PackageParser.java149
-rw-r--r--core/java/android/util/apk/ApkSignatureVerifier.java44
2 files changed, 178 insertions, 15 deletions
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4a71467f4b36..4efd08134065 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -5684,23 +5684,74 @@ public class PackageParser {
@Nullable
public final ArraySet<PublicKey> publicKeys;
+ /**
+ * Collection of {@code Signature} objects, each of which is formed from a former signing
+ * certificate of this APK before it was changed by signing certificate rotation.
+ */
+ @Nullable
+ public final Signature[] pastSigningCertificates;
+
+ /**
+ * Flags for the {@code pastSigningCertificates} collection, which indicate the capabilities
+ * the including APK wishes to grant to its past signing certificates.
+ */
+ @Nullable
+ public final int[] pastSigningCertificatesFlags;
+
/** A representation of unknown signing details. Use instead of null. */
public static final SigningDetails UNKNOWN =
- new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null);
+ new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null, null);
@VisibleForTesting
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion,
- ArraySet<PublicKey> keys) {
+ ArraySet<PublicKey> keys, Signature[] pastSigningCertificates,
+ int[] pastSigningCertificatesFlags) {
this.signatures = signatures;
this.signatureSchemeVersion = signatureSchemeVersion;
this.publicKeys = keys;
+ this.pastSigningCertificates = pastSigningCertificates;
+ this.pastSigningCertificatesFlags = pastSigningCertificatesFlags;
+ }
+
+ public SigningDetails(Signature[] signatures,
+ @SignatureSchemeVersion int signatureSchemeVersion,
+ Signature[] pastSigningCertificates, int[] pastSigningCertificatesFlags)
+ throws CertificateException {
+ this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+ pastSigningCertificates, pastSigningCertificatesFlags);
}
public SigningDetails(Signature[] signatures,
@SignatureSchemeVersion int signatureSchemeVersion)
throws CertificateException {
- this(signatures, signatureSchemeVersion, toSigningKeys(signatures));
+ this(signatures, signatureSchemeVersion,
+ null, null);
+ }
+
+ public SigningDetails(SigningDetails orig) {
+ if (orig != null) {
+ if (orig.signatures != null) {
+ this.signatures = orig.signatures.clone();
+ } else {
+ this.signatures = null;
+ }
+ this.signatureSchemeVersion = orig.signatureSchemeVersion;
+ this.publicKeys = new ArraySet<>(orig.publicKeys);
+ if (orig.pastSigningCertificates != null) {
+ this.pastSigningCertificates = orig.pastSigningCertificates.clone();
+ this.pastSigningCertificatesFlags = orig.pastSigningCertificatesFlags.clone();
+ } else {
+ this.pastSigningCertificates = null;
+ this.pastSigningCertificatesFlags = null;
+ }
+ } else {
+ this.signatures = null;
+ this.signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ this.publicKeys = null;
+ this.pastSigningCertificates = null;
+ this.pastSigningCertificatesFlags = null;
+ }
}
/** Returns true if the signing details have one or more signatures. */
@@ -5728,6 +5779,8 @@ public class PackageParser {
dest.writeTypedArray(this.signatures, flags);
dest.writeInt(this.signatureSchemeVersion);
dest.writeArraySet(this.publicKeys);
+ dest.writeTypedArray(this.pastSigningCertificates, flags);
+ dest.writeIntArray(this.pastSigningCertificatesFlags);
}
protected SigningDetails(Parcel in) {
@@ -5735,6 +5788,8 @@ public class PackageParser {
this.signatures = in.createTypedArray(Signature.CREATOR);
this.signatureSchemeVersion = in.readInt();
this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+ this.pastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+ this.pastSigningCertificatesFlags = in.createIntArray();
}
public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
@@ -5761,8 +5816,23 @@ public class PackageParser {
if (signatureSchemeVersion != that.signatureSchemeVersion) return false;
if (!Signature.areExactMatch(signatures, that.signatures)) return false;
- return publicKeys != null ? publicKeys.equals(that.publicKeys)
- : that.publicKeys == null;
+ if (publicKeys != null) {
+ if (!publicKeys.equals((that.publicKeys))) {
+ return false;
+ }
+ } else if (that.publicKeys != null) {
+ return false;
+ }
+
+ // can't use Signature.areExactMatch() because order matters with the past signing certs
+ if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
+ return false;
+ }
+ if (!Arrays.equals(pastSigningCertificatesFlags, that.pastSigningCertificatesFlags)) {
+ return false;
+ }
+
+ return true;
}
@Override
@@ -5770,8 +5840,77 @@ public class PackageParser {
int result = +Arrays.hashCode(signatures);
result = 31 * result + signatureSchemeVersion;
result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(pastSigningCertificates);
+ result = 31 * result + Arrays.hashCode(pastSigningCertificatesFlags);
return result;
}
+
+ /**
+ * Builder of {@code SigningDetails} instances.
+ */
+ public static class Builder {
+ private Signature[] mSignatures;
+ private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+ private Signature[] mPastSigningCertificates;
+ private int[] mPastSigningCertificatesFlags;
+
+ public Builder() {
+ }
+
+ /** get signing certificates used to sign the current APK */
+ public Builder setSignatures(Signature[] signatures) {
+ mSignatures = signatures;
+ return this;
+ }
+
+ /** set the signature scheme version used to sign the APK */
+ public Builder setSignatureSchemeVersion(int signatureSchemeVersion) {
+ mSignatureSchemeVersion = signatureSchemeVersion;
+ return this;
+ }
+
+ /** set the signing certificates by which the APK proved it can be authenticated */
+ public Builder setPastSigningCertificates(Signature[] pastSigningCertificates) {
+ mPastSigningCertificates = pastSigningCertificates;
+ return this;
+ }
+
+ /** set the flags for the {@code pastSigningCertificates} */
+ public Builder setPastSigningCertificatesFlags(int[] pastSigningCertificatesFlags) {
+ mPastSigningCertificatesFlags = pastSigningCertificatesFlags;
+ return this;
+ }
+
+ private void checkInvariants() {
+ // must have signatures and scheme version set
+ if (mSignatures == null) {
+ throw new IllegalStateException("SigningDetails requires the current signing"
+ + " certificates.");
+ }
+
+ // pastSigningCerts and flags must match up
+ boolean pastMismatch = false;
+ if (mPastSigningCertificates != null && mPastSigningCertificatesFlags != null) {
+ if (mPastSigningCertificates.length != mPastSigningCertificatesFlags.length) {
+ pastMismatch = true;
+ }
+ } else if (!(mPastSigningCertificates == null
+ && mPastSigningCertificatesFlags == null)) {
+ pastMismatch = true;
+ }
+ if (pastMismatch) {
+ throw new IllegalStateException("SigningDetails must have a one to one mapping "
+ + "between pastSigningCertificates and pastSigningCertificatesFlags");
+ }
+ }
+ /** build a {@code SigningDetails} object */
+ public SigningDetails build()
+ throws CertificateException {
+ checkInvariants();
+ return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+ mPastSigningCertificates, mPastSigningCertificatesFlags);
+ }
+ }
}
/**
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index a2a76169c83a..87943725ba21 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -80,10 +80,22 @@ public class ApkSignatureVerifier {
ApkSignatureSchemeV3Verifier.verify(apkPath);
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new PackageParser.SigningDetails(signerSigs,
- SignatureSchemeVersion.SIGNING_BLOCK_V3);
+ Signature[] pastSignerSigs = null;
+ int[] pastSignerSigsFlags = null;
+ if (vSigner.por != null) {
+ // populate proof-of-rotation information
+ pastSignerSigs = new Signature[vSigner.por.certs.size()];
+ pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
+ for (int i = 0; i < pastSignerSigs.length; i++) {
+ pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
+ pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+ }
+ }
+ return new PackageParser.SigningDetails(
+ signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ pastSignerSigs, pastSignerSigsFlags);
} catch (SignatureNotFoundException e) {
- // not signed with v2, try older if allowed
+ // not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
@@ -92,7 +104,7 @@ public class ApkSignatureVerifier {
// APK Signature Scheme v2 signature found but did not verify
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v2", e);
+ + " using APK Signature Scheme v3", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -304,25 +316,37 @@ public class ApkSignatureVerifier {
}
// first try v3
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3");
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV3");
try {
ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
ApkSignatureSchemeV3Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
Signature[] signerSigs = convertToSignatures(signerCerts);
- return new PackageParser.SigningDetails(signerSigs,
- SignatureSchemeVersion.SIGNING_BLOCK_V3);
+ Signature[] pastSignerSigs = null;
+ int[] pastSignerSigsFlags = null;
+ if (vSigner.por != null) {
+ // populate proof-of-rotation information
+ pastSignerSigs = new Signature[vSigner.por.certs.size()];
+ pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
+ for (int i = 0; i < pastSignerSigs.length; i++) {
+ pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
+ pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+ }
+ }
+ return new PackageParser.SigningDetails(
+ signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ pastSignerSigs, pastSignerSigsFlags);
} catch (SignatureNotFoundException e) {
- // not signed with v2, try older if allowed
+ // not signed with v3, try older if allowed
if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"No APK Signature Scheme v3 signature in package " + apkPath, e);
}
} catch (Exception e) {
- // APK Signature Scheme v2 signature found but did not verify
+ // APK Signature Scheme v3 signature found but did not verify
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
"Failed to collect certificates from " + apkPath
- + " using APK Signature Scheme v2", e);
+ + " using APK Signature Scheme v3", e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}