diff options
| author | TreeHugger Robot <treehugger-gerrit@google.com> | 2018-01-23 23:19:59 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2018-01-23 23:19:59 +0000 |
| commit | 4ec3efce54b14157ce9084faed51be07cbbbe274 (patch) | |
| tree | e3fac3fe3d7f69492b5374dff66657b3fda2221d /core/java/android | |
| parent | 555c6fcd96cfd00bfa7f8d26de0faca41d103675 (diff) | |
| parent | 77029c5b16351775cb2333369ef9a4bc1d9acf58 (diff) | |
Merge "Add proof-of-rotation information to PackageParser.SigningDetails"
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/content/pm/PackageParser.java | 149 | ||||
| -rw-r--r-- | core/java/android/util/apk/ApkSignatureVerifier.java | 44 |
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); } |
