diff options
author | Sami Tolvanen <samitolvanen@google.com> | 2014-11-12 07:47:53 -0800 |
---|---|---|
committer | Iliyan Malchev <malchev@google.com> | 2014-11-13 15:35:31 -0800 |
commit | 578a347c1939b62805442ff3a769092b19c42db2 (patch) | |
tree | b95516b4b79bcbba656ba4f194aa185c34ff5953 | |
parent | 29131b97ed091bb2b10917036a64f3403c507eb7 (diff) | |
download | extras-578a347c1939b62805442ff3a769092b19c42db2.tar.gz |
DO NOT MERGE: Add a tool for verifying verity signatures in images
This tool takes a sparse ext4 image file, reads verity metadata
from it, and verifies the signature of the verity table.
Bug: 15984840
Bug: 18120110
Change-Id: I384a9ff885a0ffcd8febf100e76f3a1e5c7bbdab
(cherry picked from commit c18aa9d69a69549b50966e322abe59cfa04edfe4)
-rw-r--r-- | verity/Android.mk | 17 | ||||
-rw-r--r-- | verity/VerityVerifier.java | 166 | ||||
-rw-r--r-- | verity/VerityVerifier.mf | 1 | ||||
-rwxr-xr-x | verity/verity_verifier | 8 |
4 files changed, 192 insertions, 0 deletions
diff --git a/verity/Android.mk b/verity/Android.mk index ab1659b3..018d40f9 100644 --- a/verity/Android.mk +++ b/verity/Android.mk @@ -10,6 +10,14 @@ LOCAL_C_INCLUDES += external/openssl/include include $(BUILD_HOST_EXECUTABLE) include $(CLEAR_VARS) +LOCAL_SRC_FILES := VerityVerifier.java Utils.java +LOCAL_MODULE := VerityVerifier +LOCAL_JAR_MANIFEST := VerityVerifier.mf +LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host +include $(BUILD_HOST_JAVA_LIBRARY) + +include $(CLEAR_VARS) LOCAL_SRC_FILES := VeritySigner.java Utils.java LOCAL_MODULE := VeritySigner LOCAL_JAR_MANIFEST := VeritySigner.mf @@ -34,6 +42,15 @@ LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host include $(BUILD_HOST_JAVA_LIBRARY) include $(CLEAR_VARS) +LOCAL_SRC_FILES := verity_verifier +LOCAL_MODULE := verity_verifier +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE_TAGS := optional +LOCAL_REQUIRED_MODULES := VerityVerifier +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) LOCAL_SRC_FILES := verity_signer LOCAL_MODULE := verity_signer LOCAL_MODULE_CLASS := EXECUTABLES diff --git a/verity/VerityVerifier.java b/verity/VerityVerifier.java new file mode 100644 index 00000000..5c9d7d28 --- /dev/null +++ b/verity/VerityVerifier.java @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.verity; + +import java.io.File; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.lang.Process; +import java.lang.Runtime; +import java.security.PublicKey; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.X509Certificate; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class VerityVerifier { + + private static final int EXT4_SB_MAGIC = 0xEF53; + private static final int EXT4_SB_OFFSET = 0x400; + private static final int EXT4_SB_OFFSET_MAGIC = EXT4_SB_OFFSET + 0x38; + private static final int EXT4_SB_OFFSET_LOG_BLOCK_SIZE = EXT4_SB_OFFSET + 0x18; + private static final int EXT4_SB_OFFSET_BLOCKS_COUNT_LO = EXT4_SB_OFFSET + 0x4; + private static final int EXT4_SB_OFFSET_BLOCKS_COUNT_HI = EXT4_SB_OFFSET + 0x150; + private static final int VERITY_MAGIC = 0xB001B001; + private static final int VERITY_SIGNATURE_SIZE = 256; + private static final int VERITY_VERSION = 0; + + /** + * Converts a 4-byte little endian value to a Java integer + * @param value Little endian integer to convert + */ + public static int fromle(int value) { + byte[] bytes = ByteBuffer.allocate(4).putInt(value).array(); + return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt(); + } + + /** + * Converts a 2-byte little endian value to Java a integer + * @param value Little endian short to convert + */ + public static int fromle(short value) { + return fromle(value << 16); + } + + /** + * Unsparses a sparse image into a temporary file and returns a + * handle to the file + * @param fname Path to a sparse image file + */ + public static RandomAccessFile openImage(String fname) throws Exception { + File tmp = File.createTempFile("system", ".raw"); + tmp.deleteOnExit(); + + Process p = Runtime.getRuntime().exec("simg2img " + fname + + " " + tmp.getAbsoluteFile()); + + p.waitFor(); + if (p.exitValue() != 0) { + throw new IllegalArgumentException("Invalid image: failed to unsparse"); + } + + return new RandomAccessFile(tmp, "r"); + } + + /** + * Reads the ext4 superblock and calculates the size of the system image, + * after which we should find the verity metadata + * @param img File handle to the image file + */ + public static long getMetadataPosition(RandomAccessFile img) + throws Exception { + img.seek(EXT4_SB_OFFSET_MAGIC); + int magic = fromle(img.readShort()); + + if (magic != EXT4_SB_MAGIC) { + throw new IllegalArgumentException("Invalid image: not a valid ext4 image"); + } + + img.seek(EXT4_SB_OFFSET_BLOCKS_COUNT_LO); + long blocksCountLo = fromle(img.readInt()); + + img.seek(EXT4_SB_OFFSET_LOG_BLOCK_SIZE); + long logBlockSize = fromle(img.readInt()); + + img.seek(EXT4_SB_OFFSET_BLOCKS_COUNT_HI); + long blocksCountHi = fromle(img.readInt()); + + long blockSizeBytes = 1L << (10 + logBlockSize); + long blockCount = (blocksCountHi << 32) + blocksCountLo; + return blockSizeBytes * blockCount; + } + + /** + * Reads and validates verity metadata, and check the signature against the + * given public key + * @param img File handle to the image file + * @param key Public key to use for signature verification + */ + public static boolean verifyMetaData(RandomAccessFile img, PublicKey key) + throws Exception { + img.seek(getMetadataPosition(img)); + int magic = fromle(img.readInt()); + + if (magic != VERITY_MAGIC) { + throw new IllegalArgumentException("Invalid image: verity metadata not found"); + } + + int version = fromle(img.readInt()); + + if (version != VERITY_VERSION) { + throw new IllegalArgumentException("Invalid image: unknown metadata version"); + } + + byte[] signature = new byte[VERITY_SIGNATURE_SIZE]; + img.readFully(signature); + + int tableSize = fromle(img.readInt()); + + byte[] table = new byte[tableSize]; + img.readFully(table); + + return Utils.verify(key, table, signature, + Utils.getSignatureAlgorithmIdentifier(key)); + } + + public static void main(String[] args) throws Exception { + if (args.length != 2) { + System.err.println("Usage: VerityVerifier <sparse.img> <certificate.x509.pem>"); + System.exit(1); + } + + Security.addProvider(new BouncyCastleProvider()); + + X509Certificate cert = Utils.loadPEMCertificate(args[1]); + PublicKey key = cert.getPublicKey(); + RandomAccessFile img = openImage(args[0]); + + try { + if (verifyMetaData(img, key)) { + System.err.println("Signature is VALID"); + System.exit(0); + } else { + System.err.println("Signature is INVALID"); + } + } catch (Exception e) { + e.printStackTrace(System.err); + } + + System.exit(1); + } +} diff --git a/verity/VerityVerifier.mf b/verity/VerityVerifier.mf new file mode 100644 index 00000000..6118b31c --- /dev/null +++ b/verity/VerityVerifier.mf @@ -0,0 +1 @@ +Main-Class: com.android.verity.VerityVerifier diff --git a/verity/verity_verifier b/verity/verity_verifier new file mode 100755 index 00000000..f145228f --- /dev/null +++ b/verity/verity_verifier @@ -0,0 +1,8 @@ +#!/bin/sh + +# Start-up script for VerityVerifier + +VERITYVERIFIER_HOME=`dirname "$0"` +VERITYVERIFIER_HOME=`dirname "$VERITYVERIFIER_HOME"` + +java -Xmx512M -jar "$VERITYVERIFIER_HOME"/framework/VerityVerifier.jar "$@" |