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
17package com.android.verity;
18
19import java.io.IOException;
20import java.security.PrivateKey;
21import java.security.PublicKey;
22import java.security.Security;
23import java.security.Signature;
24import java.security.cert.X509Certificate;
25import java.util.Enumeration;
26import org.bouncycastle.asn1.ASN1Encodable;
27import org.bouncycastle.asn1.ASN1EncodableVector;
28import org.bouncycastle.asn1.ASN1InputStream;
29import org.bouncycastle.asn1.ASN1Integer;
30import org.bouncycastle.asn1.ASN1Object;
31import org.bouncycastle.asn1.ASN1Primitive;
32import org.bouncycastle.asn1.ASN1Sequence;
33import org.bouncycastle.asn1.DEROctetString;
34import org.bouncycastle.asn1.DERPrintableString;
35import org.bouncycastle.asn1.DERSequence;
36import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
37import org.bouncycastle.asn1.pkcs.RSAPublicKey;
38import org.bouncycastle.asn1.util.ASN1Dump;
39import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
40import org.bouncycastle.jce.provider.BouncyCastleProvider;
41
42/**
43 * AndroidVerifiedBootKeystore DEFINITIONS ::=
44 * BEGIN
45 *     FormatVersion ::= INTEGER
46 *     KeyBag ::= SEQUENCE {
47 *         Key  ::= SEQUENCE {
48 *             AlgorithmIdentifier  ::=  SEQUENCE {
49 *                 algorithm OBJECT IDENTIFIER,
50 *                 parameters ANY DEFINED BY algorithm OPTIONAL
51 *             }
52 *             KeyMaterial ::= RSAPublicKey
53 *         }
54 *     }
55 *     Signature ::= AndroidVerifiedBootSignature
56 * END
57 */
58
59class BootKey extends ASN1Object
60{
61    private AlgorithmIdentifier algorithmIdentifier;
62    private RSAPublicKey keyMaterial;
63
64    public BootKey(PublicKey key) throws Exception {
65        java.security.interfaces.RSAPublicKey k =
66                (java.security.interfaces.RSAPublicKey) key;
67        this.keyMaterial = new RSAPublicKey(
68                k.getModulus(),
69                k.getPublicExponent());
70        this.algorithmIdentifier = Utils.getSignatureAlgorithmIdentifier(key);
71    }
72
73    public ASN1Primitive toASN1Primitive() {
74        ASN1EncodableVector v = new ASN1EncodableVector();
75        v.add(algorithmIdentifier);
76        v.add(keyMaterial);
77        return new DERSequence(v);
78    }
79
80    public void dump() throws Exception {
81        System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
82    }
83}
84
85class BootKeystore extends ASN1Object
86{
87    private ASN1Integer             formatVersion;
88    private ASN1EncodableVector     keyBag;
89    private BootSignature           signature;
90    private X509Certificate         certificate;
91
92    private static final int FORMAT_VERSION = 0;
93
94    public BootKeystore() {
95        this.formatVersion = new ASN1Integer(FORMAT_VERSION);
96        this.keyBag = new ASN1EncodableVector();
97    }
98
99    public void addPublicKey(byte[] der) throws Exception {
100        PublicKey pubkey = Utils.loadDERPublicKey(der);
101        BootKey k = new BootKey(pubkey);
102        keyBag.add(k);
103    }
104
105    public void setCertificate(X509Certificate cert) {
106        certificate = cert;
107    }
108
109    public byte[] getInnerKeystore() throws Exception {
110        ASN1EncodableVector v = new ASN1EncodableVector();
111        v.add(formatVersion);
112        v.add(new DERSequence(keyBag));
113        return new DERSequence(v).getEncoded();
114    }
115
116    public ASN1Primitive toASN1Primitive() {
117        ASN1EncodableVector v = new ASN1EncodableVector();
118        v.add(formatVersion);
119        v.add(new DERSequence(keyBag));
120        v.add(signature);
121        return new DERSequence(v);
122    }
123
124    public void parse(byte[] input) throws Exception {
125        ASN1InputStream stream = new ASN1InputStream(input);
126        ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
127
128        formatVersion = (ASN1Integer) sequence.getObjectAt(0);
129        if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
130            throw new IllegalArgumentException("Unsupported format version");
131        }
132
133        ASN1Sequence keys = (ASN1Sequence) sequence.getObjectAt(1);
134        Enumeration e = keys.getObjects();
135        while (e.hasMoreElements()) {
136            keyBag.add((ASN1Encodable) e.nextElement());
137        }
138
139        ASN1Object sig = sequence.getObjectAt(2).toASN1Primitive();
140        signature = new BootSignature(sig.getEncoded());
141    }
142
143    public boolean verify() throws Exception {
144        byte[] innerKeystore = getInnerKeystore();
145        return Utils.verify(signature.getPublicKey(), innerKeystore,
146                signature.getSignature(), signature.getAlgorithmIdentifier());
147    }
148
149    public void sign(PrivateKey privateKey) throws Exception {
150        byte[] innerKeystore = getInnerKeystore();
151        byte[] rawSignature = Utils.sign(privateKey, innerKeystore);
152        signature = new BootSignature("keystore", innerKeystore.length);
153        signature.setCertificate(certificate);
154        signature.setSignature(rawSignature,
155                Utils.getSignatureAlgorithmIdentifier(privateKey));
156    }
157
158    public void dump() throws Exception {
159        System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
160    }
161
162    private static void usage() {
163        System.err.println("usage: KeystoreSigner <privatekey.pk8> " +
164                "<certificate.x509.pem> <outfile> <publickey0.der> " +
165                "... <publickeyN-1.der> | -verify <keystore>");
166        System.exit(1);
167    }
168
169    public static void main(String[] args) throws Exception {
170        if (args.length < 2) {
171            usage();
172            return;
173        }
174
175        Security.addProvider(new BouncyCastleProvider());
176        BootKeystore ks = new BootKeystore();
177
178        if ("-verify".equals(args[0])) {
179            ks.parse(Utils.read(args[1]));
180
181            try {
182                if (ks.verify()) {
183                    System.err.println("Signature is VALID");
184                    System.exit(0);
185                } else {
186                    System.err.println("Signature is INVALID");
187                }
188            } catch (Exception e) {
189                e.printStackTrace(System.err);
190            }
191            System.exit(1);
192        } else {
193            String privkeyFname = args[0];
194            String certFname = args[1];
195            String outfileFname = args[2];
196
197            ks.setCertificate(Utils.loadPEMCertificate(certFname));
198
199            for (int i = 3; i < args.length; i++) {
200                ks.addPublicKey(Utils.read(args[i]));
201            }
202
203            ks.sign(Utils.loadDERPrivateKeyFromFile(privkeyFname));
204            Utils.write(ks.getEncoded(), outfileFname);
205        }
206    }
207}
208