summaryrefslogtreecommitdiff
path: root/utils/kernel_image_fw.c
blob: 734111c6840e75acb8be72adaf721a586f443f25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Functions for verifying a verified boot kernel image.
 * (Firmware portion)
 */

#include "kernel_image_fw.h"

#include "cryptolib.h"
#include "rollback_index.h"
#include "utility.h"

/* Macro to determine the size of a field structure in the KernelImage
 * structure. */
#define FIELD_LEN(field) (sizeof(((KernelImage*)0)->field))
#define KERNEL_CONFIG_FIELD_LEN (FIELD_LEN(kernel_version) + FIELD_LEN(options.version) + \
                                 FIELD_LEN(options.cmd_line) + \
                                 FIELD_LEN(options.kernel_len) + \
                                 FIELD_LEN(options.kernel_load_addr) + \
                                 FIELD_LEN(options.kernel_entry_addr))

char* kVerifyKernelErrors[VERIFY_KERNEL_MAX] = {
  "Success.",
  "Invalid Image.",
  "Kernel Key Signature Failed.",
  "Invalid Kernel Verification Algorithm.",
  "Config Signature Failed.",
  "Kernel Signature Failed.",
  "Wrong Kernel Magic.",
};

int VerifyKernelHeader(const uint8_t* firmware_key_blob,
                       const uint8_t* header_blob,
                       const int dev_mode,
                       int* firmware_algorithm,
                       int* kernel_algorithm,
                       int* kernel_header_len) {
  int kernel_sign_key_len;
  int firmware_sign_key_len;
  uint16_t header_version, header_len;
  uint16_t firmware_sign_algorithm, kernel_sign_algorithm;
  uint8_t* header_checksum = NULL;

  /* Base Offset for the header_checksum field. Actual offset is
   * this + kernel_sign_key_len. */
  int base_header_checksum_offset = (FIELD_LEN(header_version) +
                                     FIELD_LEN(header_len) +
                                     FIELD_LEN(firmware_sign_algorithm) +
                                     FIELD_LEN(kernel_sign_algorithm) +
                                     FIELD_LEN(kernel_key_version));

  Memcpy(&header_version, header_blob, sizeof(header_version));
  Memcpy(&header_len, header_blob + FIELD_LEN(header_version),
         sizeof(header_len));
  Memcpy(&firmware_sign_algorithm,
         header_blob + (FIELD_LEN(header_version) +
                        FIELD_LEN(header_len)),
         sizeof(firmware_sign_algorithm));
  Memcpy(&kernel_sign_algorithm,
         header_blob + (FIELD_LEN(header_version) +
                        FIELD_LEN(header_len) +
                        FIELD_LEN(firmware_sign_algorithm)),
         sizeof(kernel_sign_algorithm));

  /* TODO(gauravsh): Make this return two different error types depending
   * on whether the firmware or kernel signing algorithm is invalid. */
  if (firmware_sign_algorithm >= kNumAlgorithms)
    return VERIFY_KERNEL_INVALID_ALGORITHM;
  if (kernel_sign_algorithm >= kNumAlgorithms)
    return VERIFY_KERNEL_INVALID_ALGORITHM;

  *firmware_algorithm = (int) firmware_sign_algorithm;
  *kernel_algorithm = (int) kernel_sign_algorithm;
  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
  firmware_sign_key_len = RSAProcessedKeySize(firmware_sign_algorithm);


  /* Verify if header len is correct? */
  if (header_len != (base_header_checksum_offset +
                     kernel_sign_key_len +
                     FIELD_LEN(header_checksum))) {
    debug("VerifyKernelHeader: Header length mismatch\n");
    return VERIFY_KERNEL_INVALID_IMAGE;
  }
  *kernel_header_len = (int) header_len;

  /* Verify if the hash of the header is correct. */
  header_checksum = DigestBuf(header_blob,
                              header_len - FIELD_LEN(header_checksum),
                              SHA512_DIGEST_ALGORITHM);
  if (SafeMemcmp(header_checksum,
                 header_blob + (base_header_checksum_offset +
                                kernel_sign_key_len),
                 FIELD_LEN(header_checksum))) {
    Free(header_checksum);
    debug("VerifyKernelHeader: Invalid header hash\n");
    return VERIFY_KERNEL_INVALID_IMAGE;
  }
  Free(header_checksum);

  /* Verify kernel key signature unless we are in dev mode. */
  if (!dev_mode) {
    if (!RSAVerifyBinary_f(firmware_key_blob, NULL,  /* Key to use */
                           header_blob,  /* Data to verify */
                           header_len, /* Length of data */
                           header_blob + header_len,  /* Expected Signature */
                           firmware_sign_algorithm))
      return VERIFY_KERNEL_KEY_SIGNATURE_FAILED;
  }
  return 0;
}

int VerifyKernelConfig(RSAPublicKey* kernel_sign_key,
                       const uint8_t* config_blob,
                       int algorithm,
                       uint64_t* kernel_len) {
  uint64_t len;
  if (!RSAVerifyBinary_f(NULL, kernel_sign_key,  /* Key to use */
                         config_blob,  /* Data to verify */
                         KERNEL_CONFIG_FIELD_LEN,  /* Length of data */
                         config_blob + KERNEL_CONFIG_FIELD_LEN,  /* Expected
                                                                  * Signature */
                         algorithm))
    return VERIFY_KERNEL_CONFIG_SIGNATURE_FAILED;

  Memcpy(&len,
         config_blob + (FIELD_LEN(kernel_version) + FIELD_LEN(options.version) +
                              FIELD_LEN(options.cmd_line)),
         sizeof(len));
  *kernel_len = len;
  return 0;
}

int VerifyKernelData(RSAPublicKey* kernel_sign_key,
                     const uint8_t* kernel_config_start,
                     const uint8_t* kernel_data_start,
                     uint64_t kernel_len,
                     int algorithm) {
  int signature_len = siglen_map[algorithm];
  uint8_t* digest;
  DigestContext ctx;

  /* Since the kernel signature is computed over the kernel version, options
   * and data, which does not form a contiguous region of memory, we calculate
   * the message digest ourselves. */
  DigestInit(&ctx, algorithm);
  DigestUpdate(&ctx, kernel_config_start, KERNEL_CONFIG_FIELD_LEN);
  DigestUpdate(&ctx, kernel_data_start + signature_len, kernel_len);
  digest = DigestFinal(&ctx);
  if (!RSAVerifyBinaryWithDigest_f(
          NULL, kernel_sign_key,  /* Key to use. */
          digest, /* Digest of the data to verify. */
          kernel_data_start,  /* Expected Signature */
          algorithm)) {
    Free(digest);
    return VERIFY_KERNEL_SIGNATURE_FAILED;
  }
  Free(digest);
  return 0;
}

int VerifyKernel(const uint8_t* firmware_key_blob,
                 const uint8_t* kernel_blob,
                 const int dev_mode) {
  int error_code;
  int firmware_sign_algorithm;  /* Firmware signing key algorithm. */
  int kernel_sign_algorithm;  /* Kernel Signing key algorithm. */
  RSAPublicKey* kernel_sign_key;
  int kernel_sign_key_len, kernel_key_signature_len, kernel_signature_len,
      header_len;
  uint64_t kernel_len;
  const uint8_t* header_ptr;  /* Pointer to header. */
  const uint8_t* kernel_sign_key_ptr;  /* Pointer to signing key. */
  const uint8_t* config_ptr;  /* Pointer to kernel config block. */
  const uint8_t* kernel_ptr;  /* Pointer to kernel signature/data. */

  /* Note: All the offset calculations are based on struct FirmwareImage which
   * is defined in include/firmware_image.h. */

  /* Compare magic bytes. */
  if (SafeMemcmp(kernel_blob, KERNEL_MAGIC, KERNEL_MAGIC_SIZE))
    return VERIFY_KERNEL_WRONG_MAGIC;
  header_ptr = kernel_blob + KERNEL_MAGIC_SIZE;

  /* Only continue if header verification succeeds. */
  if ((error_code = VerifyKernelHeader(firmware_key_blob, header_ptr, dev_mode,
                                       &firmware_sign_algorithm,
                                       &kernel_sign_algorithm, &header_len))) {
    debug("VerifyKernel: Kernel header verification failed.\n");
    return error_code;  /* AKA jump to recovery. */
  }
  /* Parse signing key into RSAPublicKey structure since it is required multiple
   * times. */
  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
  kernel_sign_key_ptr = header_ptr + (FIELD_LEN(header_version) +
                                      FIELD_LEN(header_len) +
                                      FIELD_LEN(firmware_sign_algorithm) +
                                      FIELD_LEN(kernel_sign_algorithm) +
                                      FIELD_LEN(kernel_key_version));
  kernel_sign_key = RSAPublicKeyFromBuf(kernel_sign_key_ptr,
                                        kernel_sign_key_len);
  kernel_signature_len = siglen_map[kernel_sign_algorithm];
  kernel_key_signature_len = siglen_map[firmware_sign_algorithm];

  /* Only continue if config verification succeeds. */
  config_ptr = (header_ptr + header_len + kernel_key_signature_len);
  if ((error_code = VerifyKernelConfig(kernel_sign_key, config_ptr,
                                       kernel_sign_algorithm,
                                       &kernel_len))) {
    RSAPublicKeyFree(kernel_sign_key);
    return error_code;  /* AKA jump to recovery. */
  }
  /* Only continue if kernel data verification succeeds. */
  kernel_ptr = (config_ptr +
                KERNEL_CONFIG_FIELD_LEN +  /* Skip config block/signature. */
                kernel_signature_len);

  if ((error_code = VerifyKernelData(kernel_sign_key, config_ptr, kernel_ptr,
                                     kernel_len,
                                     kernel_sign_algorithm))) {
    RSAPublicKeyFree(kernel_sign_key);
    return error_code;  /* AKA jump to recovery. */
  }
  RSAPublicKeyFree(kernel_sign_key);
  return 0;  /* Success! */
}

uint32_t GetLogicalKernelVersion(uint8_t* kernel_blob) {
  uint8_t* kernel_ptr;
  uint16_t kernel_key_version;
  uint16_t kernel_version;
  uint16_t firmware_sign_algorithm;
  uint16_t kernel_sign_algorithm;
  int kernel_key_signature_len;
  int kernel_sign_key_len;
  kernel_ptr = kernel_blob + (FIELD_LEN(magic) +
                              FIELD_LEN(header_version) +
                              FIELD_LEN(header_len));
  Memcpy(&firmware_sign_algorithm, kernel_ptr, sizeof(firmware_sign_algorithm));
  kernel_ptr += FIELD_LEN(firmware_sign_algorithm);
  Memcpy(&kernel_sign_algorithm, kernel_ptr, sizeof(kernel_sign_algorithm));
  kernel_ptr += FIELD_LEN(kernel_sign_algorithm);
  Memcpy(&kernel_key_version, kernel_ptr, sizeof(kernel_key_version));

  if (firmware_sign_algorithm >= kNumAlgorithms)
    return 0;
  if (kernel_sign_algorithm >= kNumAlgorithms)
    return 0;
  kernel_key_signature_len = siglen_map[firmware_sign_algorithm];
  kernel_sign_key_len = RSAProcessedKeySize(kernel_sign_algorithm);
  kernel_ptr += (FIELD_LEN(kernel_key_version) +
                 kernel_sign_key_len +
                 FIELD_LEN(header_checksum) +
                 kernel_key_signature_len);
  Memcpy(&kernel_version, kernel_ptr, sizeof(kernel_version));
  return CombineUint16Pair(kernel_key_version, kernel_version);
}

int VerifyKernelDriver_f(uint8_t* firmware_key_blob,
                         kernel_entry* kernelA,
                         kernel_entry* kernelB,
                         int dev_mode) {
  int i;
  /* Contains the logical kernel version (32-bit) which is calculated as
   * (kernel_key_version << 16 | kernel_version) where
   * [kernel_key_version], [firmware_version] are both 16-bit.
   */
  uint32_t kernelA_lversion, kernelB_lversion;
  uint32_t min_lversion;  /* Minimum of kernel A and kernel B lversion. */
  uint32_t stored_lversion;  /* Stored logical version in the TPM. */
  kernel_entry* try_kernel[2];  /* Kernel in try order. */
  int try_kernel_which[2];  /* Which corresponding kernel in the try order */
  uint32_t try_kernel_lversion[2];  /* Their logical versions. */

  /* [kernel_to_boot] will eventually contain the boot path to follow
   * and is returned to the caller. Initially, we set it to recovery. If
   * a valid bootable kernel is found, it will be set to that. */
  int kernel_to_boot = BOOT_KERNEL_RECOVERY_CONTINUE;


  /* The TPM must already have be initialized, so no need to call SetupTPM(). */

  /* We get the key versions by reading directly from the image blobs without
   * any additional (expensive) sanity checking on the blob since it's faster to
   * outright reject a kernel with an older kernel key version. A malformed
   * or corrupted kernel blob will still fail when VerifyKernel() is called
   * on it.
   */
  kernelA_lversion = GetLogicalKernelVersion(kernelA->kernel_blob);
  kernelB_lversion = GetLogicalKernelVersion(kernelB->kernel_blob);
  min_lversion  = Min(kernelA_lversion, kernelB_lversion);
  stored_lversion = CombineUint16Pair(GetStoredVersion(KERNEL_KEY_VERSION),
                                      GetStoredVersion(KERNEL_VERSION));

  /* TODO(gauravsh): The kernel entries kernelA and kernelB come from the
   * partition table - verify its signature/checksum before proceeding
   * further. */

  /* The logic for deciding which kernel to boot from is taken from the
   * the Chromium OS Drive Map design document.
   *
   * We went to consider the kernels in their according to their boot
   * priority attribute value.
   */

  if (kernelA->boot_priority >= kernelB->boot_priority) {
    try_kernel[0] = kernelA;
    try_kernel_which[0] = BOOT_KERNEL_A_CONTINUE;
    try_kernel_lversion[0] = kernelA_lversion;
    try_kernel[1] = kernelB;
    try_kernel_which[1] = BOOT_KERNEL_B_CONTINUE;
    try_kernel_lversion[1] = kernelB_lversion;
  } else {
    try_kernel[0] = kernelB;
    try_kernel_which[0] = BOOT_KERNEL_B_CONTINUE;
    try_kernel_lversion[0] = kernelB_lversion;
    try_kernel[1] = kernelA;
    try_kernel_which[1] = BOOT_KERNEL_A_CONTINUE;
    try_kernel_lversion[1] = kernelA_lversion;
  }

  /* TODO(gauravsh): Changes to boot_tries_remaining and boot_priority
   * below should be propagated to partition table. This will be added
   * once the firmware parition table parsing code is in. */
  for (i = 0; i < 2; i++) {
    if ((try_kernel[i]->boot_success_flag ||
         try_kernel[i]->boot_tries_remaining) &&
        (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
                                               try_kernel[i]->kernel_blob,
                                               dev_mode))) {
      if (try_kernel[i]->boot_tries_remaining > 0)
        try_kernel[i]->boot_tries_remaining--;
      if (stored_lversion > try_kernel_lversion[i])
        continue;  /* Rollback: I am afraid I can't let you do that Dave. */
      if (i == 0 && (stored_lversion < try_kernel_lversion[1])) {
        /* The higher priority kernel is valid and bootable, See if we
         * need to update the stored version for rollback prevention. */
        if (VERIFY_KERNEL_SUCCESS == VerifyKernel(firmware_key_blob,
                                                  try_kernel[1]->kernel_blob,
                                                  dev_mode)) {
          WriteStoredVersion(KERNEL_KEY_VERSION,
                             (uint16_t) (min_lversion >> 16));
          WriteStoredVersion(KERNEL_VERSION,
                             (uint16_t) (min_lversion & 0xFFFF));
          stored_lversion = min_lversion;  /* Update stored version as it's
                                            * used later. */
        }
      }
      kernel_to_boot = try_kernel_which[i];
      break;  /* We found a valid kernel. */
    }
    try_kernel[i]->boot_priority = 0;
    }  /* for loop. */

  /* Lock Kernel TPM rollback indices from further writes.
   * TODO(gauravsh): Figure out if these can be combined into one
   * 32-bit location since we seem to always use them together. This can help
   * us minimize the number of NVRAM writes/locks (which are limited over flash
   * memory lifetimes.
   */
  LockStoredVersion(KERNEL_KEY_VERSION);
  LockStoredVersion(KERNEL_VERSION);
  return kernel_to_boot;
}