summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSami Tolvanen <samitolvanen@google.com>2014-11-12 07:47:53 -0800
committerIliyan Malchev <malchev@google.com>2014-11-13 15:35:31 -0800
commit578a347c1939b62805442ff3a769092b19c42db2 (patch)
treeb95516b4b79bcbba656ba4f194aa185c34ff5953
parent29131b97ed091bb2b10917036a64f3403c507eb7 (diff)
downloadextras-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.mk17
-rw-r--r--verity/VerityVerifier.java166
-rw-r--r--verity/VerityVerifier.mf1
-rwxr-xr-xverity/verity_verifier8
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 "$@"