Merge "Add proof-of-rotation information to PackageParser.SigningDetails"

This commit is contained in:
TreeHugger Robot
2018-01-23 23:19:59 +00:00
committed by Android (Google) Code Review
8 changed files with 431 additions and 147 deletions

View File

@@ -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);
}
}
}
/**

View File

@@ -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);
}