2 * Copyright (C) 2014 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.verity;
20 import java.io.RandomAccessFile;
21 import java.nio.ByteBuffer;
22 import java.nio.ByteOrder;
23 import java.lang.Process;
24 import java.lang.Runtime;
25 import java.security.PublicKey;
26 import java.security.PrivateKey;
27 import java.security.Security;
28 import java.security.cert.X509Certificate;
29 import org.bouncycastle.jce.provider.BouncyCastleProvider;
31 public class VerityVerifier {
33 private static final int EXT4_SB_MAGIC = 0xEF53;
34 private static final int EXT4_SB_OFFSET = 0x400;
35 private static final int EXT4_SB_OFFSET_MAGIC = EXT4_SB_OFFSET + 0x38;
36 private static final int EXT4_SB_OFFSET_LOG_BLOCK_SIZE = EXT4_SB_OFFSET + 0x18;
37 private static final int EXT4_SB_OFFSET_BLOCKS_COUNT_LO = EXT4_SB_OFFSET + 0x4;
38 private static final int EXT4_SB_OFFSET_BLOCKS_COUNT_HI = EXT4_SB_OFFSET + 0x150;
39 private static final int VERITY_MAGIC = 0xB001B001;
40 private static final int VERITY_SIGNATURE_SIZE = 256;
41 private static final int VERITY_VERSION = 0;
44 * Converts a 4-byte little endian value to a Java integer
45 * @param value Little endian integer to convert
47 public static int fromle(int value) {
48 byte[] bytes = ByteBuffer.allocate(4).putInt(value).array();
49 return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
53 * Converts a 2-byte little endian value to Java a integer
54 * @param value Little endian short to convert
56 public static int fromle(short value) {
57 return fromle(value << 16);
61 * Unsparses a sparse image into a temporary file and returns a
63 * @param fname Path to a sparse image file
65 public static RandomAccessFile openImage(String fname) throws Exception {
66 File tmp = File.createTempFile("system", ".raw");
69 Process p = Runtime.getRuntime().exec("simg2img " + fname +
70 " " + tmp.getAbsoluteFile());
73 if (p.exitValue() != 0) {
74 throw new IllegalArgumentException("Invalid image: failed to unsparse");
77 return new RandomAccessFile(tmp, "r");
81 * Reads the ext4 superblock and calculates the size of the system image,
82 * after which we should find the verity metadata
83 * @param img File handle to the image file
85 public static long getMetadataPosition(RandomAccessFile img)
87 img.seek(EXT4_SB_OFFSET_MAGIC);
88 int magic = fromle(img.readShort());
90 if (magic != EXT4_SB_MAGIC) {
91 throw new IllegalArgumentException("Invalid image: not a valid ext4 image");
94 img.seek(EXT4_SB_OFFSET_BLOCKS_COUNT_LO);
95 long blocksCountLo = fromle(img.readInt());
97 img.seek(EXT4_SB_OFFSET_LOG_BLOCK_SIZE);
98 long logBlockSize = fromle(img.readInt());
100 img.seek(EXT4_SB_OFFSET_BLOCKS_COUNT_HI);
101 long blocksCountHi = fromle(img.readInt());
103 long blockSizeBytes = 1L << (10 + logBlockSize);
104 long blockCount = (blocksCountHi << 32) + blocksCountLo;
105 return blockSizeBytes * blockCount;
109 * Reads and validates verity metadata, and check the signature against the
111 * @param img File handle to the image file
112 * @param key Public key to use for signature verification
114 public static boolean verifyMetaData(RandomAccessFile img, PublicKey key)
116 img.seek(getMetadataPosition(img));
117 int magic = fromle(img.readInt());
119 if (magic != VERITY_MAGIC) {
120 throw new IllegalArgumentException("Invalid image: verity metadata not found");
123 int version = fromle(img.readInt());
125 if (version != VERITY_VERSION) {
126 throw new IllegalArgumentException("Invalid image: unknown metadata version");
129 byte[] signature = new byte[VERITY_SIGNATURE_SIZE];
130 img.readFully(signature);
132 int tableSize = fromle(img.readInt());
134 byte[] table = new byte[tableSize];
135 img.readFully(table);
137 return Utils.verify(key, table, signature,
138 Utils.getSignatureAlgorithmIdentifier(key));
141 public static void main(String[] args) throws Exception {
142 if (args.length != 2) {
143 System.err.println("Usage: VerityVerifier <sparse.img> <certificate.x509.pem>");
147 Security.addProvider(new BouncyCastleProvider());
149 X509Certificate cert = Utils.loadPEMCertificate(args[1]);
150 PublicKey key = cert.getPublicKey();
151 RandomAccessFile img = openImage(args[0]);
154 if (verifyMetaData(img, key)) {
155 System.err.println("Signature is VALID");
158 System.err.println("Signature is INVALID");
160 } catch (Exception e) {
161 e.printStackTrace(System.err);