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