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