Merge "ApkSignatureVerifier: expose verity interfaces"
This commit is contained in:
committed by
Android (Google) Code Review
commit
3a29ce00bd
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user