Home | History | Annotate | Download | only in verity
      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.IOException;
     20 import java.security.PrivateKey;
     21 import java.security.PublicKey;
     22 import java.security.Security;
     23 import java.security.Signature;
     24 import java.security.cert.X509Certificate;
     25 import java.util.Enumeration;
     26 import org.bouncycastle.asn1.ASN1Encodable;
     27 import org.bouncycastle.asn1.ASN1EncodableVector;
     28 import org.bouncycastle.asn1.ASN1InputStream;
     29 import org.bouncycastle.asn1.ASN1Integer;
     30 import org.bouncycastle.asn1.ASN1Object;
     31 import org.bouncycastle.asn1.ASN1Primitive;
     32 import org.bouncycastle.asn1.ASN1Sequence;
     33 import org.bouncycastle.asn1.DEROctetString;
     34 import org.bouncycastle.asn1.DERPrintableString;
     35 import org.bouncycastle.asn1.DERSequence;
     36 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
     37 import org.bouncycastle.asn1.pkcs.RSAPublicKey;
     38 import org.bouncycastle.asn1.util.ASN1Dump;
     39 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
     40 import 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 
     59 class 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 
     85 class 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