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
193380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport java.io.ByteArrayInputStream;
20cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.io.IOException;
2129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.nio.ByteBuffer;
2229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.nio.ByteOrder;
23cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.security.PrivateKey;
243380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport java.security.PublicKey;
2529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.security.Security;
2629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.security.cert.X509Certificate;
273380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport java.security.cert.Certificate;
283380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport java.security.cert.CertificateFactory;
2929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport java.security.cert.CertificateEncodingException;
30cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport java.util.Arrays;
31cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Encodable;
32cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1EncodableVector;
33cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Integer;
34cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Object;
353380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport org.bouncycastle.asn1.ASN1ObjectIdentifier;
363380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport org.bouncycastle.asn1.ASN1OctetString;
37cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.ASN1Primitive;
383380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanenimport org.bouncycastle.asn1.ASN1Sequence;
3929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport org.bouncycastle.asn1.ASN1InputStream;
40cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DEROctetString;
41cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DERPrintableString;
42cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.DERSequence;
43cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.util.ASN1Dump;
44cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condraimport org.bouncycastle.asn1.x509.AlgorithmIdentifier;
4529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrenceimport org.bouncycastle.jce.provider.BouncyCastleProvider;
46cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
47cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra/**
48cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *    AndroidVerifiedBootSignature DEFINITIONS ::=
49cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *    BEGIN
5029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence *        formatVersion ::= INTEGER
5129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence *        certificate ::= Certificate
5229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence *        algorithmIdentifier ::= SEQUENCE {
53cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *            algorithm OBJECT IDENTIFIER,
54cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *            parameters ANY DEFINED BY algorithm OPTIONAL
55cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *        }
5629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence *        authenticatedAttributes ::= SEQUENCE {
57cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *            target CHARACTER STRING,
58cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *            length INTEGER
59cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *        }
6029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence *        signature ::= OCTET STRING
61cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra *     END
62cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra */
63cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
64cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condrapublic class BootSignature extends ASN1Object
65cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra{
66cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    private ASN1Integer             formatVersion;
6729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence    private ASN1Encodable           certificate;
68cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    private AlgorithmIdentifier     algorithmIdentifier;
69cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    private DERPrintableString      target;
70cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    private ASN1Integer             length;
71cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    private DEROctetString          signature;
723380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen    private PublicKey               publicKey;
73cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
7429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence    private static final int FORMAT_VERSION = 1;
7529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
763380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen    /**
773380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen     * Initializes the object for signing an image file
783380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen     * @param target Target name, included in the signed data
793380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen     * @param length Length of the image, included in the signed data
803380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen     */
81cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public BootSignature(String target, int length) {
8229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        this.formatVersion = new ASN1Integer(FORMAT_VERSION);
83cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        this.target = new DERPrintableString(target);
84cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        this.length = new ASN1Integer(length);
85cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
86cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
873380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen    /**
883380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen     * Initializes the object for verifying a signed image file
893380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen     * @param signature Signature footer
903380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen     */
913380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen    public BootSignature(byte[] signature)
923380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            throws Exception {
933380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        ASN1InputStream stream = new ASN1InputStream(signature);
943380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        ASN1Sequence sequence = (ASN1Sequence) stream.readObject();
953380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
963380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        formatVersion = (ASN1Integer) sequence.getObjectAt(0);
973380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        if (formatVersion.getValue().intValue() != FORMAT_VERSION) {
983380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            throw new IllegalArgumentException("Unsupported format version");
993380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        }
1003380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
1013380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        certificate = sequence.getObjectAt(1);
1023380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        byte[] encoded = ((ASN1Object) certificate).getEncoded();
1033380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        ByteArrayInputStream bis = new ByteArrayInputStream(encoded);
1043380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
1053380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        CertificateFactory cf = CertificateFactory.getInstance("X.509");
1063380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        X509Certificate c = (X509Certificate) cf.generateCertificate(bis);
1073380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        publicKey = c.getPublicKey();
1083380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
1093380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        ASN1Sequence algId = (ASN1Sequence) sequence.getObjectAt(2);
1103380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        algorithmIdentifier = new AlgorithmIdentifier(
1113380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            (ASN1ObjectIdentifier) algId.getObjectAt(0));
1123380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
1133380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        ASN1Sequence attrs = (ASN1Sequence) sequence.getObjectAt(3);
1143380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        target = (DERPrintableString) attrs.getObjectAt(0);
1153380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        length = (ASN1Integer) attrs.getObjectAt(1);
1163380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
1173380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        this.signature = (DEROctetString) sequence.getObjectAt(4);
1183380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen    }
1193380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
120cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public ASN1Object getAuthenticatedAttributes() {
121cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        ASN1EncodableVector attrs = new ASN1EncodableVector();
122cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        attrs.add(target);
123cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        attrs.add(length);
124cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        return new DERSequence(attrs);
125cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
126cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
127cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public byte[] getEncodedAuthenticatedAttributes() throws IOException {
128cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        return getAuthenticatedAttributes().getEncoded();
129cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
130cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
131241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    public AlgorithmIdentifier getAlgorithmIdentifier() {
132241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        return algorithmIdentifier;
133241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    }
134241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
135241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    public PublicKey getPublicKey() {
136241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        return publicKey;
137241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    }
138241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
139241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    public byte[] getSignature() {
140241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen        return signature.getOctets();
141241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen    }
142241f964e10ce8bc6c401073854fdaf1662013daeSami Tolvanen
14329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence    public void setSignature(byte[] sig, AlgorithmIdentifier algId) {
14429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        algorithmIdentifier = algId;
145cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        signature = new DEROctetString(sig);
146cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
147cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
14829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence    public void setCertificate(X509Certificate cert)
14929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence            throws Exception, IOException, CertificateEncodingException {
15029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        ASN1InputStream s = new ASN1InputStream(cert.getEncoded());
15129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        certificate = s.readObject();
15253e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen        publicKey = cert.getPublicKey();
15329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence    }
15429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
155cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public byte[] generateSignableImage(byte[] image) throws IOException {
156cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        byte[] attrs = getEncodedAuthenticatedAttributes();
157cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        byte[] signable = Arrays.copyOf(image, image.length + attrs.length);
158cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        for (int i=0; i < attrs.length; i++) {
159cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra            signable[i+image.length] = attrs[i];
160cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        }
161cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        return signable;
162cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
163cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
164cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public byte[] sign(byte[] image, PrivateKey key) throws Exception {
165cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        byte[] signable = generateSignableImage(image);
166d66cefd9d9ef9aab181bdf6d429a0f7938cf0cb5Geremy Condra        return Utils.sign(key, signable);
167cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
168cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
1693380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen    public boolean verify(byte[] image) throws Exception {
1703380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        if (length.getValue().intValue() != image.length) {
1713380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            throw new IllegalArgumentException("Invalid image length");
1723380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        }
1733380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
1743380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        byte[] signable = generateSignableImage(image);
1753380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        return Utils.verify(publicKey, signable, signature.getOctets(),
1763380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen                    algorithmIdentifier);
1773380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen    }
1783380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
179cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public ASN1Primitive toASN1Primitive() {
180cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        ASN1EncodableVector v = new ASN1EncodableVector();
181cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(formatVersion);
18229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        v.add(certificate);
183cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(algorithmIdentifier);
184cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(getAuthenticatedAttributes());
185cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        v.add(signature);
186cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        return new DERSequence(v);
187cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
188cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
18929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence    public static int getSignableImageSize(byte[] data) throws Exception {
19029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        if (!Arrays.equals(Arrays.copyOfRange(data, 0, 8),
19129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence                "ANDROID!".getBytes("US-ASCII"))) {
19229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence            throw new IllegalArgumentException("Invalid image header: missing magic");
19329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        }
19429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
19529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        ByteBuffer image = ByteBuffer.wrap(data);
19629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        image.order(ByteOrder.LITTLE_ENDIAN);
19729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
19829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        image.getLong(); // magic
19929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        int kernelSize = image.getInt();
20029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        image.getInt(); // kernel_addr
20129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        int ramdskSize = image.getInt();
20229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        image.getInt(); // ramdisk_addr
20329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        int secondSize = image.getInt();
20429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        image.getLong(); // second_addr + tags_addr
20529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        int pageSize = image.getInt();
20629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
20729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        int length = pageSize // include the page aligned image header
20829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence                + ((kernelSize + pageSize - 1) / pageSize) * pageSize
20929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence                + ((ramdskSize + pageSize - 1) / pageSize) * pageSize
21029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence                + ((secondSize + pageSize - 1) / pageSize) * pageSize;
21129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
21229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        length = ((length + pageSize - 1) / pageSize) * pageSize;
21329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
21429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        if (length <= 0) {
21529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence            throw new IllegalArgumentException("Invalid image header: invalid length");
21629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        }
21729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
21829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        return length;
21929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence    }
22029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
221cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public static void doSignature( String target,
222cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra                                    String imagePath,
223cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra                                    String keyPath,
22429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence                                    String certPath,
225cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra                                    String outPath) throws Exception {
22629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
227cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        byte[] image = Utils.read(imagePath);
22829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        int signableSize = getSignableImageSize(image);
22929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
23029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        if (signableSize < image.length) {
23129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence            System.err.println("NOTE: truncating file " + imagePath +
23229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence                    " from " + image.length + " to " + signableSize + " bytes");
23329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence            image = Arrays.copyOf(image, signableSize);
23429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        } else if (signableSize > image.length) {
23529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence            throw new IllegalArgumentException("Invalid image: too short, expected " +
23629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence                    signableSize + " bytes");
23729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        }
23829131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
239cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra        BootSignature bootsig = new BootSignature(target, image.length);
24029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
24129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        X509Certificate cert = Utils.loadPEMCertificate(certPath);
24229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        bootsig.setCertificate(cert);
24329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
24429131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        PrivateKey key = Utils.loadDERPrivateKeyFromFile(keyPath);
24529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        bootsig.setSignature(bootsig.sign(image, key),
24629131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence            Utils.getSignatureAlgorithmIdentifier(key));
24729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
248d66cefd9d9ef9aab181bdf6d429a0f7938cf0cb5Geremy Condra        byte[] encoded_bootsig = bootsig.getEncoded();
249d66cefd9d9ef9aab181bdf6d429a0f7938cf0cb5Geremy Condra        byte[] image_with_metadata = Arrays.copyOf(image, image.length + encoded_bootsig.length);
25029131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
25129131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        System.arraycopy(encoded_bootsig, 0, image_with_metadata,
25229131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence                image.length, encoded_bootsig.length);
25329131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence
254d66cefd9d9ef9aab181bdf6d429a0f7938cf0cb5Geremy Condra        Utils.write(image_with_metadata, outPath);
255cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
256cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra
25753e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen    public static void verifySignature(String imagePath, String certPath) throws Exception {
2583380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        byte[] image = Utils.read(imagePath);
2593380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        int signableSize = getSignableImageSize(image);
2603380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
2613380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        if (signableSize >= image.length) {
2623380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            throw new IllegalArgumentException("Invalid image: not signed");
2633380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        }
2643380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
2653380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        byte[] signature = Arrays.copyOfRange(image, signableSize, image.length);
2663380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        BootSignature bootsig = new BootSignature(signature);
2673380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
26853e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen        if (!certPath.isEmpty()) {
26953e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen            System.err.println("NOTE: verifying using public key from " + certPath);
27053e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen            bootsig.setCertificate(Utils.loadPEMCertificate(certPath));
27153e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen        }
27253e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen
2733380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        try {
2743380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            if (bootsig.verify(Arrays.copyOf(image, signableSize))) {
2753380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen                System.err.println("Signature is VALID");
2763380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen                System.exit(0);
2773380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            } else {
2783380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen                System.err.println("Signature is INVALID");
2793380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            }
2803380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        } catch (Exception e) {
2813380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            e.printStackTrace(System.err);
2823380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        }
2833380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        System.exit(1);
2843380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen    }
2853380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
28640193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen    /* Example usage for signing a boot image using dev keys:
28740193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen        java -cp \
28840193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            ../../../out/host/common/obj/JAVA_LIBRARIES/BootSignature_intermediates/ \
28940193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen                classes/com.android.verity.BootSignature \
29040193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            /boot \
29140193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            ../../../out/target/product/$PRODUCT/boot.img \
29240193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            ../../../build/target/product/security/verity.pk8 \
29340193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            ../../../build/target/product/security/verity.x509.pem \
29440193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            /tmp/boot.img.signed
29529131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence    */
296cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    public static void main(String[] args) throws Exception {
29729131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence        Security.addProvider(new BouncyCastleProvider());
2983380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen
2993380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        if ("-verify".equals(args[0])) {
30053e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen            String certPath = "";
30153e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen
30253e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen            if (args.length >= 4 && "-certificate".equals(args[2])) {
30353e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen                /* args[3] is the path to a public key certificate */
30453e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen                certPath = args[3];
30553e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen            }
30653e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen
30740193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            /* args[1] is the path to a signed boot image */
30853e790468c3ce7d094f1003648e7766395912ddbSami Tolvanen            verifySignature(args[1], certPath);
3093380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        } else {
31040193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            /* args[0] is the target name, typically /boot
31140193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen               args[1] is the path to a boot image to sign
31240193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen               args[2] is the path to a private key
31340193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen               args[3] is the path to the matching public key certificate
31440193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen               args[4] is the path where to output the signed boot image
31540193d94182934b37f4b2ae00fde2402583e59e1Sami Tolvanen            */
3163380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen            doSignature(args[0], args[1], args[2], args[3], args[4]);
3173380f2fea1b9a18f26ae95b60a01eeb55565eb1bSami Tolvanen        }
318cee5bfdf119104b8ebce56d54dfcdcca1f537075Geremy Condra    }
31929131b97ed091bb2b10917036a64f3403c507eb7Paul Lawrence}
320