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#define _LARGEFILE64_SOURCE
18
19#include <endian.h>
20#include <stddef.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <fcntl.h>
27#include <unistd.h>
28#include <errno.h>
29
30#include <openssl/asn1.h>
31#include <openssl/asn1t.h>
32#include <openssl/crypto.h>
33#include <openssl/err.h>
34#include <openssl/evp.h>
35#include <openssl/pem.h>
36#include <openssl/rsa.h>
37#include <openssl/x509.h>
38
39#include "bootimg.h"
40
41#define FORMAT_VERSION 1
42#define BUFFER_SIZE (1024 * 1024)
43
44typedef struct {
45    ASN1_STRING *target;
46    ASN1_INTEGER *length;
47} AuthAttrs;
48
49ASN1_SEQUENCE(AuthAttrs) = {
50    ASN1_SIMPLE(AuthAttrs, target, ASN1_PRINTABLE),
51    ASN1_SIMPLE(AuthAttrs, length, ASN1_INTEGER)
52} ASN1_SEQUENCE_END(AuthAttrs)
53
54IMPLEMENT_ASN1_FUNCTIONS(AuthAttrs)
55
56typedef struct {
57    ASN1_INTEGER *formatVersion;
58    X509 *certificate;
59    X509_ALGOR *algorithmIdentifier;
60    AuthAttrs *authenticatedAttributes;
61    ASN1_OCTET_STRING *signature;
62} BootSignature;
63
64ASN1_SEQUENCE(BootSignature) = {
65    ASN1_SIMPLE(BootSignature, formatVersion, ASN1_INTEGER),
66    ASN1_SIMPLE(BootSignature, certificate, X509),
67    ASN1_SIMPLE(BootSignature, algorithmIdentifier, X509_ALGOR),
68    ASN1_SIMPLE(BootSignature, authenticatedAttributes, AuthAttrs),
69    ASN1_SIMPLE(BootSignature, signature, ASN1_OCTET_STRING)
70} ASN1_SEQUENCE_END(BootSignature)
71
72IMPLEMENT_ASN1_FUNCTIONS(BootSignature)
73
74static BIO *g_error = NULL;
75
76/**
77 * Rounds n up to the nearest multiple of page_size
78 * @param n The value to round
79 * @param page_size Page size
80 */
81static uint64_t page_align(uint64_t n, uint64_t page_size)
82{
83    return (((n + page_size - 1) / page_size) * page_size);
84}
85
86/**
87 * Calculates the offset to the beginning of the BootSignature block
88 * based on the boot image header. The signature will start after the
89 * the boot image contents.
90 * @param fd File descriptor to the boot image
91 * @param offset Receives the offset in bytes
92 */
93static int get_signature_offset(int fd, off64_t *offset)
94{
95    int i;
96    struct boot_img_hdr hdr;
97
98    if (!offset) {
99        return -1;
100    }
101
102    if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
103        return -1;
104    }
105
106    if (memcmp(BOOT_MAGIC, hdr.magic, BOOT_MAGIC_SIZE) != 0) {
107        printf("Invalid boot image: missing magic\n");
108        return -1;
109    }
110
111    if (!hdr.page_size) {
112        printf("Invalid boot image: page size must be non-zero\n");
113        return -1;
114    }
115
116    *offset = page_align(hdr.page_size
117                    + page_align(hdr.kernel_size,  hdr.page_size)
118                    + page_align(hdr.ramdisk_size, hdr.page_size)
119                    + page_align(hdr.second_size,  hdr.page_size),
120                hdr.page_size);
121
122    return 0;
123}
124
125/**
126 * Reads and parses the ASN.1 BootSignature block from the given offset
127 * @param fd File descriptor to the boot image
128 * @param offset Offset from the beginning of file to the signature
129 * @param bs Pointer to receive the BootImage structure
130 */
131static int read_signature(int fd, off64_t offset, BootSignature **bs)
132{
133    BIO *in = NULL;
134
135    if (!bs) {
136        return -1;
137    }
138
139    if (lseek64(fd, offset, SEEK_SET) == -1) {
140        return -1;
141    }
142
143    if ((in = BIO_new_fd(fd, BIO_NOCLOSE)) == NULL) {
144        ERR_print_errors(g_error);
145        return -1;
146    }
147
148    if ((*bs = ASN1_item_d2i_bio(ASN1_ITEM_rptr(BootSignature), in, bs)) == NULL) {
149        ERR_print_errors(g_error);
150        BIO_free(in);
151        return -1;
152    }
153
154    BIO_free(in);
155    return 0;
156}
157
158/**
159 * Validates the format of the boot signature block, and checks that
160 * the length in authenticated attributes matches the actual length of
161 * the image.
162 * @param bs The boot signature block to validate
163 * @param length The actual length of the boot image without the signature
164 */
165static int validate_signature_block(const BootSignature *bs, uint64_t length)
166{
167    BIGNUM expected;
168    BIGNUM value;
169    int rc = -1;
170
171    if (!bs) {
172        return -1;
173    }
174
175    BN_init(&expected);
176    BN_init(&value);
177
178    /* Confirm that formatVersion matches our supported version */
179    if (!BN_set_word(&expected, FORMAT_VERSION)) {
180        ERR_print_errors(g_error);
181        goto vsb_done;
182    }
183
184    ASN1_INTEGER_to_BN(bs->formatVersion, &value);
185
186    if (BN_cmp(&expected, &value) != 0) {
187        printf("Unsupported signature version\n");
188        goto vsb_done;
189    }
190
191    BN_clear(&expected);
192    BN_clear(&value);
193
194    /* Confirm that the length of the image matches with the length in
195        the authenticated attributes */
196    length = htobe64(length);
197    BN_bin2bn((const unsigned char *) &length, sizeof(length), &expected);
198
199    ASN1_INTEGER_to_BN(bs->authenticatedAttributes->length, &value);
200
201    if (BN_cmp(&expected, &value) != 0) {
202        printf("Image length doesn't match signature attributes\n");
203        goto vsb_done;
204    }
205
206    rc = 0;
207
208vsb_done:
209    BN_free(&expected);
210    BN_free(&value);
211
212    return rc;
213}
214
215/**
216 * Creates a SHA-256 hash from the boot image contents and the encoded
217 * authenticated attributes.
218 * @param fd File descriptor to the boot image
219 * @param length Length of the boot image without the signature block
220 * @param aa Pointer to AuthAttrs
221 * @param digest Pointer to a buffer where the hash is written
222 */
223static int hash_image(int fd, uint64_t length, const AuthAttrs *aa,
224        unsigned char *digest)
225{
226    EVP_MD_CTX *ctx = NULL;
227    int rc = -1;
228
229    ssize_t bytes = 0;
230    unsigned char *attrs = NULL;
231    unsigned char *buffer = NULL;
232    unsigned char *p = NULL;
233    uint64_t total = 0;
234
235    if (!aa || !digest) {
236        goto hi_done;
237    }
238
239    if ((buffer = malloc(BUFFER_SIZE)) == NULL) {
240        goto hi_done;
241    }
242
243    if (lseek64(fd, 0, SEEK_SET) != 0) {
244        goto hi_done;
245    }
246
247    if ((ctx = EVP_MD_CTX_create()) == NULL) {
248        ERR_print_errors(g_error);
249        goto hi_done;
250    }
251
252    EVP_DigestInit(ctx, EVP_sha256());
253
254    do {
255        bytes = BUFFER_SIZE;
256
257        if ((length - total) < BUFFER_SIZE) {
258            bytes = length - total;
259        }
260
261        if ((bytes = read(fd, buffer, bytes)) == -1) {
262            printf("%s\n", strerror(errno));
263            goto hi_done;
264        }
265
266        EVP_DigestUpdate(ctx, buffer, bytes);
267        total += bytes;
268    } while (total < length);
269
270    if ((bytes = i2d_AuthAttrs((AuthAttrs *) aa, NULL)) < 0) {
271        ERR_print_errors(g_error);
272        goto hi_done;
273    }
274
275    if ((attrs = OPENSSL_malloc(bytes)) == NULL) {
276        ERR_print_errors(g_error);
277        goto hi_done;
278    }
279
280    p = attrs;
281
282    if (i2d_AuthAttrs((AuthAttrs *) aa, &p) < 0) {
283        ERR_print_errors(g_error);
284        goto hi_done;
285    }
286
287    EVP_DigestUpdate(ctx, attrs, bytes);
288    EVP_DigestFinal(ctx, digest, NULL);
289
290    rc = 0;
291
292hi_done:
293    if (buffer) {
294        free(buffer);
295    }
296
297    if (ctx) {
298        EVP_MD_CTX_destroy(ctx);
299    }
300
301    if (attrs) {
302        OPENSSL_free(attrs);
303    }
304
305    return rc;
306}
307
308/**
309 * Verifies the RSA signature against the pubkey (certificate) in the
310 * BootSignature, and additionally against the pubkey file if provided.
311 * @param fd File descriptor to the boot image
312 * @param length Length of the boot image without the signature block
313 * @param bs The boot signature block
314 * @param pkey The external pubkey file
315 */
316static int verify_signature(int fd, uint64_t length, const BootSignature *bs,
317        const char *pkey)
318{
319    int rc = -1;
320    EVP_PKEY *pkey_bs = NULL;
321    RSA *rsa_bs = NULL;
322    RSA *rsa_pkey = NULL;
323    BIO *bio_pkey = NULL;
324    unsigned char digest[SHA256_DIGEST_LENGTH];
325
326    if (!bs) {
327        goto vs_done;
328    }
329
330    if (hash_image(fd, length, bs->authenticatedAttributes, digest) == -1) {
331        goto vs_done;
332    }
333
334    if ((pkey_bs = X509_get_pubkey(bs->certificate)) == NULL) {
335        ERR_print_errors(g_error);
336        goto vs_done;
337    }
338
339    if ((rsa_bs = EVP_PKEY_get1_RSA(pkey_bs)) == NULL) {
340        ERR_print_errors(g_error);
341        goto vs_done;
342    }
343
344    if (!RSA_verify(NID_sha256, digest, SHA256_DIGEST_LENGTH,
345                bs->signature->data, bs->signature->length, rsa_bs)) {
346        ERR_print_errors(g_error);
347        goto vs_done;
348    }
349
350    if (pkey) {
351        if ((bio_pkey = BIO_new_file(pkey, "r")) == NULL) {
352            ERR_print_errors(g_error);
353            goto vs_done;
354         }
355
356        if ((rsa_pkey = PEM_read_bio_RSA_PUBKEY(bio_pkey, NULL, NULL, NULL)) == NULL) {
357            ERR_print_errors(g_error);
358            goto vs_done;
359        }
360
361        if (!RSA_verify(NID_sha256, digest, SHA256_DIGEST_LENGTH,
362                    bs->signature->data, bs->signature->length, rsa_pkey)) {
363            ERR_print_errors(g_error);
364            goto vs_done;
365        }
366    }
367
368    rc = 0;
369
370vs_done:
371    if (pkey_bs) {
372        EVP_PKEY_free(pkey_bs);
373    }
374
375    if (rsa_bs) {
376        RSA_free(rsa_bs);
377    }
378
379    if (bio_pkey) {
380        BIO_free_all(bio_pkey);
381    }
382
383    if (rsa_pkey) {
384        RSA_free(rsa_pkey);
385    }
386
387    return rc;
388}
389
390/**
391 * Given the file name of a signed boot image, verifies the signature
392 * @param image_file Name of the boot image file
393 */
394static int verify(const char *image_file, const char *pkey)
395{
396    BootSignature *bs = NULL;
397    int fd = -1;
398    int rc = 1;
399    off64_t offset = 0;
400
401    if (!image_file) {
402        return rc;
403    }
404
405    if ((fd = open(image_file, O_RDONLY | O_LARGEFILE)) == -1) {
406        return rc;
407    }
408
409    if (get_signature_offset(fd, &offset) == -1) {
410        goto out;
411    }
412
413    if (read_signature(fd, offset, &bs) == -1) {
414        goto out;
415    }
416
417    if (validate_signature_block(bs, offset) == -1) {
418        goto out;
419    }
420
421    if (verify_signature(fd, offset, bs, pkey) == -1) {
422        goto out;
423    }
424
425    printf("Signature is VALID\n");
426    rc = 0;
427
428out:
429    if (bs) {
430        BootSignature_free(bs);
431    }
432
433    if (fd != -1) {
434        close(fd);
435    }
436
437    return rc;
438}
439
440static void usage()
441{
442    printf("Usage: verify_boot_signature <path-to-boot-image>\n"
443           "       verify_boot_signature <path-to-boot-image> <pubkey>\n");
444}
445
446int main(int argc, char *argv[])
447{
448    if (argc != 2 && argc != 3) {
449        usage();
450        return 1;
451    }
452
453    /* BIO descriptor for logging OpenSSL errors to stderr */
454    if ((g_error = BIO_new_fd(STDERR_FILENO, BIO_NOCLOSE)) == NULL) {
455        printf("Failed to allocate a BIO handle for error output\n");
456        return 1;
457    }
458
459    ERR_load_crypto_strings();
460
461    const char *pkey = (argc == 2) ? NULL : argv[2];
462
463    return verify(argv[1], pkey);
464}
465