Merge "ApkSignatureVerifier: expose verity interfaces"

This commit is contained in:
TreeHugger Robot
2018-01-19 23:33:07 +00:00
committed by Android (Google) Code Review
4 changed files with 146 additions and 6 deletions

View File

@@ -16,6 +16,7 @@
package android.util.apk;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
@@ -42,6 +43,7 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -105,7 +107,8 @@ public class ApkSignatureSchemeV2Verifier {
*/
public static X509Certificate[][] verify(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
return verify(apkFile, true);
VerifiedSigner vSigner = verify(apkFile, true);
return vSigner.certs;
}
/**
@@ -119,10 +122,11 @@ public class ApkSignatureSchemeV2Verifier {
*/
public static X509Certificate[][] plsCertsNoVerifyOnlyCerts(String apkFile)
throws SignatureNotFoundException, SecurityException, IOException {
return verify(apkFile, false);
VerifiedSigner vSigner = verify(apkFile, false);
return vSigner.certs;
}
private static X509Certificate[][] verify(String apkFile, boolean verifyIntegrity)
private static VerifiedSigner verify(String apkFile, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
try (RandomAccessFile apk = new RandomAccessFile(apkFile, "r")) {
return verify(apk, verifyIntegrity);
@@ -138,7 +142,7 @@ public class ApkSignatureSchemeV2Verifier {
* verify.
* @throws IOException if an I/O error occurs while reading the APK file.
*/
private static X509Certificate[][] verify(RandomAccessFile apk, boolean verifyIntegrity)
private static VerifiedSigner verify(RandomAccessFile apk, boolean verifyIntegrity)
throws SignatureNotFoundException, SecurityException, IOException {
SignatureInfo signatureInfo = findSignature(apk);
return verify(apk, signatureInfo, verifyIntegrity);
@@ -163,7 +167,7 @@ public class ApkSignatureSchemeV2Verifier {
* @param signatureInfo APK Signature Scheme v2 Block and information relevant for verifying it
* against the APK file.
*/
private static X509Certificate[][] verify(
private static VerifiedSigner verify(
RandomAccessFile apk,
SignatureInfo signatureInfo,
boolean doVerifyIntegrity) throws SecurityException, IOException {
@@ -207,7 +211,14 @@ public class ApkSignatureSchemeV2Verifier {
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
return signerCerts.toArray(new X509Certificate[signerCerts.size()][]);
byte[] verityRootHash = null;
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
}
return new VerifiedSigner(
signerCerts.toArray(new X509Certificate[signerCerts.size()][]),
verityRootHash);
}
private static X509Certificate[] verifySigner(
@@ -383,6 +394,24 @@ public class ApkSignatureSchemeV2Verifier {
return;
}
static byte[] getVerityRootHash(String apkPath)
throws IOException, SignatureNotFoundException, SecurityException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
VerifiedSigner vSigner = verify(apk, false);
return vSigner.verityRootHash;
}
}
static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -400,4 +429,20 @@ public class ApkSignatureSchemeV2Verifier {
return false;
}
}
/**
* Verified APK Signature Scheme v2 signer.
*
* @hide for internal use only.
*/
public static class VerifiedSigner {
public final X509Certificate[][] certs;
public final byte[] verityRootHash;
public VerifiedSigner(X509Certificate[][] certs, byte[] verityRootHash) {
this.certs = certs;
this.verityRootHash = verityRootHash;
}
}
}

View File

@@ -16,6 +16,7 @@
package android.util.apk;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_DSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA256;
import static android.util.apk.ApkSigningBlockUtils.SIGNATURE_ECDSA_WITH_SHA512;
@@ -43,6 +44,7 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.security.DigestException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
@@ -211,6 +213,10 @@ public class ApkSignatureSchemeV3Verifier {
ApkSigningBlockUtils.verifyIntegrity(contentDigests, apk, signatureInfo);
}
if (contentDigests.containsKey(CONTENT_DIGEST_VERITY_CHUNKED_SHA256)) {
result.verityRootHash = contentDigests.get(CONTENT_DIGEST_VERITY_CHUNKED_SHA256);
}
return result;
}
@@ -499,6 +505,24 @@ public class ApkSignatureSchemeV3Verifier {
return new VerifiedProofOfRotation(certs, flagsList);
}
static byte[] getVerityRootHash(String apkPath)
throws IOException, SignatureNotFoundException, SecurityException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
VerifiedSigner vSigner = verify(apk, false);
return vSigner.verityRootHash;
}
}
static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
SignatureInfo signatureInfo = findSignature(apk);
return ApkSigningBlockUtils.generateApkVerity(apkPath, bufferFactory, signatureInfo);
}
}
private static boolean isSupportedSignatureAlgorithm(int sigAlgorithm) {
switch (sigAlgorithm) {
case SIGNATURE_RSA_PSS_WITH_SHA256:
@@ -541,6 +565,8 @@ public class ApkSignatureSchemeV3Verifier {
public final X509Certificate[] certs;
public final VerifiedProofOfRotation por;
public byte[] verityRootHash;
public VerifiedSigner(X509Certificate[] certs, VerifiedProofOfRotation por) {
this.certs = certs;
this.por = por;

View File

@@ -36,7 +36,9 @@ import libcore.io.IoUtils;
import java.io.IOException;
import java.io.InputStream;
import java.security.DigestException;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.ArrayList;
@@ -367,4 +369,51 @@ public class ApkSignatureVerifier {
// v2 didn't work, try jarsigner
return verifyV1Signature(apkPath, false);
}
/**
* @return the verity root hash in the Signing Block.
*/
public static byte[] getVerityRootHash(String apkPath)
throws IOException, SignatureNotFoundException, SecurityException {
// first try v3
try {
return ApkSignatureSchemeV3Verifier.getVerityRootHash(apkPath);
} catch (SignatureNotFoundException e) {
// try older version
}
return ApkSignatureSchemeV2Verifier.getVerityRootHash(apkPath);
}
/**
* Generates the Merkle tree and verity metadata to the buffer allocated by the {@code
* ByteBufferFactory}.
*
* @return the verity root hash of the generated Merkle tree.
*/
public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
NoSuchAlgorithmException {
// first try v3
try {
return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);
} catch (SignatureNotFoundException e) {
// try older version
}
return ApkSignatureSchemeV2Verifier.generateApkVerity(apkPath, bufferFactory);
}
/**
* Result of a successful APK verification operation.
*/
public static class Result {
public final Certificate[][] certs;
public final Signature[] sigs;
public final int signatureSchemeVersion;
public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
this.certs = certs;
this.sigs = sigs;
this.signatureSchemeVersion = signingVersion;
}
}
}

View File

@@ -305,6 +305,26 @@ final class ApkSigningBlockUtils {
}
}
/**
* Generates the fsverity header and hash tree to be used by kernel for the given apk. This
* method does not check whether the root hash exists in the Signing Block or not.
*
* <p>The output is stored in the {@link ByteBuffer} created by the given {@link
* ByteBufferFactory}.
*
* @return the root hash of the generated hash tree.
*/
public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory,
SignatureInfo signatureInfo)
throws IOException, SignatureNotFoundException, SecurityException, DigestException,
NoSuchAlgorithmException {
try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateApkVerity(apk,
signatureInfo, bufferFactory);
return result.rootHash;
}
}
/**
* Returns the ZIP End of Central Directory (EoCD) and its offset in the file.
*