OSDN Git Service

Merge "Add NOTICE files"
[android-x86/system-extras.git] / verity / VerityVerifier.java
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.verity;
18
19 import java.io.File;
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;
30
31 public class VerityVerifier {
32
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;
42
43     /**
44      * Converts a 4-byte little endian value to a Java integer
45      * @param value Little endian integer to convert
46      */
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();
50     }
51
52      /**
53      * Converts a 2-byte little endian value to Java a integer
54      * @param value Little endian short to convert
55      */
56      public static int fromle(short value) {
57         return fromle(value << 16);
58     }
59
60     /**
61      * Unsparses a sparse image into a temporary file and returns a
62      * handle to the file
63      * @param fname Path to a sparse image file
64      */
65      public static RandomAccessFile openImage(String fname) throws Exception {
66         File tmp = File.createTempFile("system", ".raw");
67         tmp.deleteOnExit();
68
69         Process p = Runtime.getRuntime().exec("simg2img " + fname +
70                             " " + tmp.getAbsoluteFile());
71
72         p.waitFor();
73         if (p.exitValue() != 0) {
74             throw new IllegalArgumentException("Invalid image: failed to unsparse");
75         }
76
77         return new RandomAccessFile(tmp, "r");
78     }
79
80     /**
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
84      */
85     public static long getMetadataPosition(RandomAccessFile img)
86             throws Exception {
87         img.seek(EXT4_SB_OFFSET_MAGIC);
88         int magic = fromle(img.readShort());
89
90         if (magic != EXT4_SB_MAGIC) {
91             throw new IllegalArgumentException("Invalid image: not a valid ext4 image");
92         }
93
94         img.seek(EXT4_SB_OFFSET_BLOCKS_COUNT_LO);
95         long blocksCountLo = fromle(img.readInt());
96
97         img.seek(EXT4_SB_OFFSET_LOG_BLOCK_SIZE);
98         long logBlockSize = fromle(img.readInt());
99
100         img.seek(EXT4_SB_OFFSET_BLOCKS_COUNT_HI);
101         long blocksCountHi = fromle(img.readInt());
102
103         long blockSizeBytes = 1L << (10 + logBlockSize);
104         long blockCount = (blocksCountHi << 32) + blocksCountLo;
105         return blockSizeBytes * blockCount;
106     }
107
108     /**
109      * Reads and validates verity metadata, and check the signature against the
110      * given public key
111      * @param img File handle to the image file
112      * @param key Public key to use for signature verification
113      */
114     public static boolean verifyMetaData(RandomAccessFile img, PublicKey key)
115             throws Exception {
116         img.seek(getMetadataPosition(img));
117         int magic = fromle(img.readInt());
118
119         if (magic != VERITY_MAGIC) {
120             throw new IllegalArgumentException("Invalid image: verity metadata not found");
121         }
122
123         int version = fromle(img.readInt());
124
125         if (version != VERITY_VERSION) {
126             throw new IllegalArgumentException("Invalid image: unknown metadata version");
127         }
128
129         byte[] signature = new byte[VERITY_SIGNATURE_SIZE];
130         img.readFully(signature);
131
132         int tableSize = fromle(img.readInt());
133
134         byte[] table = new byte[tableSize];
135         img.readFully(table);
136
137         return Utils.verify(key, table, signature,
138                    Utils.getSignatureAlgorithmIdentifier(key));
139     }
140
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>");
144             System.exit(1);
145         }
146
147         Security.addProvider(new BouncyCastleProvider());
148
149         X509Certificate cert = Utils.loadPEMCertificate(args[1]);
150         PublicKey key = cert.getPublicKey();
151         RandomAccessFile img = openImage(args[0]);
152
153         try {
154             if (verifyMetaData(img, key)) {
155                 System.err.println("Signature is VALID");
156                 System.exit(0);
157             } else {
158                 System.err.println("Signature is INVALID");
159             }
160         } catch (Exception e) {
161             e.printStackTrace(System.err);
162         }
163
164         System.exit(1);
165     }
166 }