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