1cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra/*
2cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Copyright (C) 2014 The Android Open Source Project
3cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *
4cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Licensed under the Apache License, Version 2.0 (the "License");
5cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * you may not use this file except in compliance with the License.
6cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * You may obtain a copy of the License at
7cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *
8cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *      http://www.apache.org/licenses/LICENSE-2.0
9cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *
10cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * Unless required by applicable law or agreed to in writing, software
11cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * distributed under the License is distributed on an "AS IS" BASIS,
12cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * See the License for the specific language governing permissions and
14cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * limitations under the License.
15cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra */
16cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
17cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condrapackage com.android.verity;
18cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
19cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.io.IOException;
20cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.PrivateKey;
21cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.PublicKey;
22f0f33adb7ce6557459306ce03576af4d79c0c9efSami Tolvanenimport java.security.Security;
23cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.Signature;
24241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanenimport java.security.cert.X509Certificate;
25241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanenimport java.util.Enumeration;
26cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Encodable;
27cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1EncodableVector;
28241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanenimport org.bouncycastle.asn1.ASN1InputStream;
29cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Integer;
30cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Object;
31cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Primitive;
32241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanenimport org.bouncycastle.asn1.ASN1Sequence;
33cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DEROctetString;
34cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DERPrintableString;
35cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DERSequence;
36cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
37cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.pkcs.RSAPublicKey;
38cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.util.ASN1Dump;
39cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.x509.AlgorithmIdentifier;
40f0f33adb7ce6557459306ce03576af4d79c0c9efSami Tolvanenimport org.bouncycastle.jce.provider.BouncyCastleProvider;
41cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
42cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra/**
43cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * AndroidVerifiedBootKeystore DEFINITIONS ::=
44cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * BEGIN
45cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *     FormatVersion ::= INTEGER
46cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *     KeyBag ::= SEQUENCE {
47cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *         Key  ::= SEQUENCE {
48cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *             AlgorithmIdentifier  ::=  SEQUENCE {
49cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *                 algorithm OBJECT IDENTIFIER,
50cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *                 parameters ANY DEFINED BY algorithm OPTIONAL
51cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *             }
52cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *             KeyMaterial ::= RSAPublicKey
53cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *         }
54cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *     }
55cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *     Signature ::= AndroidVerifiedBootSignature
56cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra * END
57cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra */
58cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
59cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraclass BootKey extends ASN1Object
60cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra{
61cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    private AlgorithmIdentifier algorithmIdentifier;
62cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    private RSAPublicKey keyMaterial;
63cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
64cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public BootKey(PublicKey key) throws Exception {
65cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        java.security.interfaces.RSAPublicKey k =
66cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra                (java.security.interfaces.RSAPublicKey) key;
67cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        this.keyMaterial = new RSAPublicKey(
68cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra                k.getModulus(),
69cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra                k.getPublicExponent());
70241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        this.algorithmIdentifier = Utils.getSignatureAlgorithmIdentifier(key);
71cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
72cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
73cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public ASN1Primitive toASN1Primitive() {
74cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        ASN1EncodableVector v = new ASN1EncodableVector();
75cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(algorithmIdentifier);
76cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(keyMaterial);
77cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        return new DERSequence(v);
78cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
79cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
80cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public void dump() throws Exception {
81cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
82cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
83cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra}
84cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
85cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraclass BootKeystore extends ASN1Object
86cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra{
87241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    private ASN1Integer             formatVersion;
88241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    private ASN1EncodableVector     keyBag;
89241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    private BootSignature           signature;
90241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    private X509Certificate         certificate;
91241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
92241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    private static final int FORMAT_VERSION = 0;
93cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
94cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public BootKeystore() {
95241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        this.formatVersion = new ASN1Integer(FORMAT_VERSION);
96cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        this.keyBag = new ASN1EncodableVector();
97cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
98cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
99cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public void addPublicKey(byte[] der) throws Exception {
100cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        PublicKey pubkey = Utils.loadDERPublicKey(der);
101cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        BootKey k = new BootKey(pubkey);
102cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        keyBag.add(k);
103cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
104cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
105241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    public void setCertificate(X509Certificate cert) {
106241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        certificate = cert;
107241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    }
108241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
109cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public byte[] getInnerKeystore() throws Exception {
110cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        ASN1EncodableVector v = new ASN1EncodableVector();
111cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(formatVersion);
112cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(new DERSequence(keyBag));
113cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        return new DERSequence(v).getEncoded();
114cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
115cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
116cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public ASN1Primitive toASN1Primitive() {
117cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        ASN1EncodableVector v = new ASN1EncodableVector();
118cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(formatVersion);
119cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(new DERSequence(keyBag));
120cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(signature);
121cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        return new DERSequence(v);
122cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
123cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
124241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    public void parse(byte[] input) throws Exception {
125241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        ASN1InputStream stream = new ASN1InputStream(input);
126241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
127241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
128241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        formatVersion = (ASN1Integer) sequence.getObjectAt(0);
129241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
130241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            throw new IllegalArgumentException("Unsupported format version");
131241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        }
132241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
133241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        ASN1Sequence keys = (ASN1Sequence) sequence.getObjectAt(1);
134241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        Enumeration e = keys.getObjects();
135241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        while (e.hasMoreElements()) {
136241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            keyBag.add((ASN1Encodable) e.nextElement());
137241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        }
138241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
139241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        ASN1Object sig = sequence.getObjectAt(2).toASN1Primitive();
140241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        signature = new BootSignature(sig.getEncoded());
141241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    }
142241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
143241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    public boolean verify() throws Exception {
144241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        byte[] innerKeystore = getInnerKeystore();
145241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        return Utils.verify(signature.getPublicKey(), innerKeystore,
146241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                signature.getSignature(), signature.getAlgorithmIdentifier());
147241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    }
148241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
149cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public void sign(PrivateKey privateKey) throws Exception {
150cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        byte[] innerKeystore = getInnerKeystore();
151cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        byte[] rawSignature = Utils.sign(privateKey, innerKeystore);
152cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        signature = new BootSignature("keystore", innerKeystore.length);
153241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        signature.setCertificate(certificate);
15429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        signature.setSignature(rawSignature,
155f0f33adb7ce6557459306ce03576af4d79c0c9efSami Tolvanen                Utils.getSignatureAlgorithmIdentifier(privateKey));
156cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
157cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
158cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public void dump() throws Exception {
159cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        System.out.println(ASN1Dump.dumpAsString(toASN1Primitive()));
160cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
161cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
162241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    private static void usage() {
163241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        System.err.println("usage: KeystoreSigner <privatekey.pk8> " +
164241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                "<certificate.x509.pem> <outfile> <publickey0.der> " +
165241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                "... <publickeyN-1.der> | -verify <keystore>");
166241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        System.exit(1);
167241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    }
168241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
169cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public static void main(String[] args) throws Exception {
170241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        if (args.length < 2) {
171241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            usage();
172241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            return;
173241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        }
174241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
175f0f33adb7ce6557459306ce03576af4d79c0c9efSami Tolvanen        Security.addProvider(new BouncyCastleProvider());
176cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        BootKeystore ks = new BootKeystore();
177241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
178241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        if ("-verify".equals(args[0])) {
179241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            ks.parse(Utils.read(args[1]));
180241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
181241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            try {
182241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                if (ks.verify()) {
183241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                    System.err.println("Signature is VALID");
184241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                    System.exit(0);
185241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                } else {
186241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                    System.err.println("Signature is INVALID");
187241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                }
188241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            } catch (Exception e) {
189241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                e.printStackTrace(System.err);
190241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            }
191241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            System.exit(1);
192241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        } else {
193241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            String privkeyFname = args[0];
194241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            String certFname = args[1];
195241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            String outfileFname = args[2];
196241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
197241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            ks.setCertificate(Utils.loadPEMCertificate(certFname));
198241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
199241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            for (int i = 3; i < args.length; i++) {
200241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen                ks.addPublicKey(Utils.read(args[i]));
201241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            }
202241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
203241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            ks.sign(Utils.loadDERPrivateKeyFromFile(privkeyFname));
204241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen            Utils.write(ks.getEncoded(), outfileFname);
205cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        }
206cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
20729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence}
208