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