int vb2_check_keyblock(const struct vb2_keyblock *block, uint32_t size, const struct vb2_signature *sig) { if(size < sizeof(*block)) { VB2_DEBUG("Not enough space for key block header.\n"); return VB2_ERROR_KEYBLOCK_TOO_SMALL_FOR_HEADER; } if (memcmp(block->magic, KEY_BLOCK_MAGIC, KEY_BLOCK_MAGIC_SIZE)) { VB2_DEBUG("Not a valid verified boot key block.\n"); return VB2_ERROR_KEYBLOCK_MAGIC; } if (block->header_version_major != KEY_BLOCK_HEADER_VERSION_MAJOR) { VB2_DEBUG("Incompatible key block header version.\n"); return VB2_ERROR_KEYBLOCK_HEADER_VERSION; } if (size < block->keyblock_size) { VB2_DEBUG("Not enough data for key block.\n"); return VB2_ERROR_KEYBLOCK_SIZE; } if (vb2_verify_signature_inside(block, block->keyblock_size, sig)) { VB2_DEBUG("Key block signature off end of block\n"); return VB2_ERROR_KEYBLOCK_SIG_OUTSIDE; } /* Make sure advertised signature data sizes are sane. */ if (block->keyblock_size < sig->data_size) { VB2_DEBUG("Signature calculated past end of block\n"); return VB2_ERROR_KEYBLOCK_SIGNED_TOO_MUCH; } /* Verify we signed enough data */ if (sig->data_size < sizeof(struct vb2_keyblock)) { VB2_DEBUG("Didn't sign enough data\n"); return VB2_ERROR_KEYBLOCK_SIGNED_TOO_LITTLE; } /* Verify data key is inside the block and inside signed data */ if (vb2_verify_packed_key_inside(block, block->keyblock_size, &block->data_key)) { VB2_DEBUG("Data key off end of key block\n"); return VB2_ERROR_KEYBLOCK_DATA_KEY_OUTSIDE; } if (vb2_verify_packed_key_inside(block, sig->data_size, &block->data_key)) { VB2_DEBUG("Data key off end of signed data\n"); return VB2_ERROR_KEYBLOCK_DATA_KEY_UNSIGNED; } return VB2_SUCCESS; }
VbError_t VbExTpmOpen(void) { char* device_path; struct timespec delay; int retries, saved_errno; if (tpm_fd >= 0) return VBERROR_SUCCESS; /* Already open */ device_path = getenv("TPM_DEVICE_PATH"); if (device_path == NULL) { device_path = TPM_DEVICE_PATH; } /* Retry TPM opens on EBUSY failures. */ for (retries = 0; retries < OPEN_RETRY_MAX_NUM; ++ retries) { errno = 0; tpm_fd = open(device_path, O_RDWR | O_CLOEXEC); saved_errno = errno; if (tpm_fd >= 0) return VBERROR_SUCCESS; if (saved_errno != EBUSY) break; VB2_DEBUG("TPM: retrying %s: %s\n", device_path, strerror(errno)); /* Stall until TPM comes back. */ delay.tv_sec = 0; delay.tv_nsec = OPEN_RETRY_DELAY_NS; nanosleep(&delay, NULL); } return DoError(TPM_E_NO_DEVICE, "TPM: Cannot open TPM device %s: %s\n", device_path, strerror(saved_errno)); }
void vb2_check_recovery(struct vb2_context *ctx) { struct vb2_shared_data *sd = vb2_get_sd(ctx); uint32_t reason = vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST); uint32_t subcode = vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE); VB2_DEBUG("Recovery reason from previous boot: %#x / %#x\n", reason, subcode); /* * Sets the current recovery request, unless there's already been a * failure earlier in the boot process. */ if (!sd->recovery_reason) sd->recovery_reason = reason; /* Clear request and subcode so we don't get stuck in recovery mode */ vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, VB2_RECOVERY_NOT_REQUESTED); vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, VB2_RECOVERY_NOT_REQUESTED); if (ctx->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE) { VB2_DEBUG("Recovery was requested manually\n"); if (subcode && !sd->recovery_reason) /* * Recovery was requested at 'broken' screen. * Promote subcode to reason. */ sd->recovery_reason = subcode; else /* Recovery was forced. Override recovery reason */ sd->recovery_reason = VB2_RECOVERY_RO_MANUAL; sd->flags |= VB2_SD_FLAG_MANUAL_RECOVERY; } /* If recovery reason is non-zero, tell caller we need recovery mode */ if (sd->recovery_reason) { ctx->flags |= VB2_CONTEXT_RECOVERY_MODE; VB2_DEBUG("We have a recovery request: %#x / %#x\n", sd->recovery_reason, vb2_nv_get(ctx, VB2_NV_RECOVERY_SUBCODE)); } }
int vb2_verify_keyblock(struct vb2_keyblock *block, uint32_t size, const struct vb2_public_key *key, const struct vb2_workbuf *wb) { struct vb2_signature *sig = &block->keyblock_signature; int rv; /* Sanity check keyblock before attempting signature check of data */ rv = vb2_check_keyblock(block, size, sig); if (rv) return rv; VB2_DEBUG("Checking key block signature...\n"); rv = vb2_verify_data((const uint8_t *)block, size, sig, key, wb); if (rv) { VB2_DEBUG("Invalid key block signature.\n"); return VB2_ERROR_KEYBLOCK_SIG_INVALID; } /* Success */ return VB2_SUCCESS; }
VbError_t VbGbbReadHWID(struct vb2_context *ctx, char *hwid, uint32_t max_size) { struct vb2_shared_data *sd = vb2_get_sd(ctx); if (!max_size) return VBERROR_INVALID_PARAMETER; *hwid = '\0'; StrnAppend(hwid, "{INVALID}", max_size); if (!ctx) return VBERROR_INVALID_GBB; if (0 == sd->gbb->hwid_size) { VB2_DEBUG("VbHWID(): invalid hwid size\n"); return VBERROR_SUCCESS; /* oddly enough! */ } if (sd->gbb->hwid_size > max_size) { VB2_DEBUG("VbDisplayDebugInfo(): invalid hwid offset/size\n"); return VBERROR_INVALID_PARAMETER; } return VbGbbReadData(ctx, sd->gbb->hwid_offset, sd->gbb->hwid_size, hwid); }
void vb2_fail(struct vb2_context *ctx, uint8_t reason, uint8_t subcode) { struct vb2_shared_data *sd = vb2_get_sd(ctx); /* If NV data hasn't been initialized, initialize it now */ if (!(sd->status & VB2_SD_STATUS_NV_INIT)) vb2_nv_init(ctx); /* See if we were far enough in the boot process to choose a slot */ if (sd->status & VB2_SD_STATUS_CHOSE_SLOT) { /* Boot failed */ vb2_nv_set(ctx, VB2_NV_FW_RESULT, VB2_FW_RESULT_FAILURE); /* Use up remaining tries */ vb2_nv_set(ctx, VB2_NV_TRY_COUNT, 0); /* * Try the other slot next time. We'll alternate * between slots, which may help if one or both slots is * flaky. */ vb2_nv_set(ctx, VB2_NV_TRY_NEXT, 1 - sd->fw_slot); /* * If we didn't try the other slot last boot, or we tried it * and it didn't fail, try it next boot. */ if (sd->last_fw_slot != 1 - sd->fw_slot || sd->last_fw_result != VB2_FW_RESULT_FAILURE) return; } /* * If we're still here, we failed before choosing a slot, or both * this slot and the other slot failed in successive boots. So we * need to go to recovery. * * Set a recovery reason and subcode only if they're not already set. * If recovery is already requested, it's a more specific error code * than later code is providing and we shouldn't overwrite it. */ VB2_DEBUG("Need recovery, reason: %#x / %#x\n", reason, subcode); if (!vb2_nv_get(ctx, VB2_NV_RECOVERY_REQUEST)) { vb2_nv_set(ctx, VB2_NV_RECOVERY_REQUEST, reason); vb2_nv_set(ctx, VB2_NV_RECOVERY_SUBCODE, subcode); } }
int vb2_verify_data(const uint8_t *data, uint32_t size, struct vb2_signature *sig, const struct vb2_public_key *key, const struct vb2_workbuf *wb) { struct vb2_workbuf wblocal = *wb; struct vb2_digest_context *dc; uint8_t *digest; uint32_t digest_size; int rv; if (sig->data_size > size) { VB2_DEBUG("Data buffer smaller than length of signed data.\n"); return VB2_ERROR_VDATA_NOT_ENOUGH_DATA; } /* Digest goes at start of work buffer */ digest_size = vb2_digest_size(key->hash_alg); if (!digest_size) return VB2_ERROR_VDATA_DIGEST_SIZE; digest = vb2_workbuf_alloc(&wblocal, digest_size); if (!digest) return VB2_ERROR_VDATA_WORKBUF_DIGEST; /* Hashing requires temp space for the context */ dc = vb2_workbuf_alloc(&wblocal, sizeof(*dc)); if (!dc) return VB2_ERROR_VDATA_WORKBUF_HASHING; rv = vb2_digest_init(dc, key->hash_alg); if (rv) return rv; rv = vb2_digest_extend(dc, data, sig->data_size); if (rv) return rv; rv = vb2_digest_finalize(dc, digest, digest_size); if (rv) return rv; vb2_workbuf_free(&wblocal, sizeof(*dc)); return vb2_verify_digest(key, sig, digest, &wblocal); }
int vb2_verify_digest(const struct vb2_public_key *key, struct vb2_signature *sig, const uint8_t *digest, const struct vb2_workbuf *wb) { uint8_t *sig_data = vb2_signature_data(sig); if (sig->sig_size != vb2_rsa_sig_size(key->sig_alg)) { VB2_DEBUG("Wrong data signature size for algorithm, " "sig_size=%d, expected %d for algorithm %d.\n", sig->sig_size, vb2_rsa_sig_size(key->sig_alg), key->sig_alg); return VB2_ERROR_VDATA_SIG_SIZE; } return vb2_rsa_verify_digest(key, sig_data, digest, wb); }
int vb2_verify_fw_preamble(struct vb2_fw_preamble *preamble, uint32_t size, const struct vb2_public_key *key, const struct vb2_workbuf *wb) { struct vb2_signature *sig = &preamble->preamble_signature; VB2_DEBUG("Verifying preamble.\n"); /* Sanity checks before attempting signature of data */ if(size < sizeof(*preamble)) { VB2_DEBUG("Not enough data for preamble header\n"); return VB2_ERROR_PREAMBLE_TOO_SMALL_FOR_HEADER; } if (preamble->header_version_major != FIRMWARE_PREAMBLE_HEADER_VERSION_MAJOR) { VB2_DEBUG("Incompatible firmware preamble header version.\n"); return VB2_ERROR_PREAMBLE_HEADER_VERSION; } if (preamble->header_version_minor < 1) { VB2_DEBUG("Only preamble header 2.1+ supported\n"); return VB2_ERROR_PREAMBLE_HEADER_OLD; } if (size < preamble->preamble_size) { VB2_DEBUG("Not enough data for preamble.\n"); return VB2_ERROR_PREAMBLE_SIZE; } /* Check signature */ if (vb2_verify_signature_inside(preamble, preamble->preamble_size, sig)) { VB2_DEBUG("Preamble signature off end of preamble\n"); return VB2_ERROR_PREAMBLE_SIG_OUTSIDE; } /* Make sure advertised signature data sizes are sane. */ if (preamble->preamble_size < sig->data_size) { VB2_DEBUG("Signature calculated past end of the block\n"); return VB2_ERROR_PREAMBLE_SIGNED_TOO_MUCH; } if (vb2_verify_data((const uint8_t *)preamble, size, sig, key, wb)) { VB2_DEBUG("Preamble signature validation failed\n"); return VB2_ERROR_PREAMBLE_SIG_INVALID; } /* Verify we signed enough data */ if (sig->data_size < sizeof(struct vb2_fw_preamble)) { VB2_DEBUG("Didn't sign enough data\n"); return VB2_ERROR_PREAMBLE_SIGNED_TOO_LITTLE; } /* Verify body signature is inside the signed data */ if (vb2_verify_signature_inside(preamble, sig->data_size, &preamble->body_signature)) { VB2_DEBUG("Firmware body signature off end of preamble\n"); return VB2_ERROR_PREAMBLE_BODY_SIG_OUTSIDE; } /* Verify kernel subkey is inside the signed data */ if (vb2_verify_packed_key_inside(preamble, sig->data_size, &preamble->kernel_subkey)) { VB2_DEBUG("Kernel subkey off end of preamble\n"); return VB2_ERROR_PREAMBLE_KERNEL_SUBKEY_OUTSIDE; } /* Success */ return VB2_SUCCESS; }
int vb2_verify_keyblock(struct vb2_keyblock *block, uint32_t size, const struct vb2_public_key *key, const struct vb2_workbuf *wb) { struct vb2_signature *sig; int rv; /* Sanity checks before attempting signature of data */ if(size < sizeof(*block)) { VB2_DEBUG("Not enough space for key block header.\n"); return VB2_ERROR_KEYBLOCK_TOO_SMALL_FOR_HEADER; } if (memcmp(block->magic, KEY_BLOCK_MAGIC, KEY_BLOCK_MAGIC_SIZE)) { VB2_DEBUG("Not a valid verified boot key block.\n"); return VB2_ERROR_KEYBLOCK_MAGIC; } if (block->header_version_major != KEY_BLOCK_HEADER_VERSION_MAJOR) { VB2_DEBUG("Incompatible key block header version.\n"); return VB2_ERROR_KEYBLOCK_HEADER_VERSION; } if (size < block->keyblock_size) { VB2_DEBUG("Not enough data for key block.\n"); return VB2_ERROR_KEYBLOCK_SIZE; } /* Check signature */ sig = &block->keyblock_signature; if (vb2_verify_signature_inside(block, block->keyblock_size, sig)) { VB2_DEBUG("Key block signature off end of block\n"); return VB2_ERROR_KEYBLOCK_SIG_OUTSIDE; } /* Make sure advertised signature data sizes are sane. */ if (block->keyblock_size < sig->data_size) { VB2_DEBUG("Signature calculated past end of block\n"); return VB2_ERROR_KEYBLOCK_SIGNED_TOO_MUCH; } VB2_DEBUG("Checking key block signature...\n"); rv = vb2_verify_data((const uint8_t *)block, size, sig, key, wb); if (rv) { VB2_DEBUG("Invalid key block signature.\n"); return VB2_ERROR_KEYBLOCK_SIG_INVALID; } /* Verify we signed enough data */ if (sig->data_size < sizeof(struct vb2_keyblock)) { VB2_DEBUG("Didn't sign enough data\n"); return VB2_ERROR_KEYBLOCK_SIGNED_TOO_LITTLE; } /* Verify data key is inside the block and inside signed data */ if (vb2_verify_packed_key_inside(block, block->keyblock_size, &block->data_key)) { VB2_DEBUG("Data key off end of key block\n"); return VB2_ERROR_KEYBLOCK_DATA_KEY_OUTSIDE; } if (vb2_verify_packed_key_inside(block, sig->data_size, &block->data_key)) { VB2_DEBUG("Data key off end of signed data\n"); return VB2_ERROR_KEYBLOCK_DATA_KEY_UNSIGNED; } /* Success */ return VB2_SUCCESS; }
int vb2api_init_hash2(struct vb2_context *ctx, const struct vb2_guid *guid, uint32_t *size) { struct vb2_shared_data *sd = vb2_get_sd(ctx); const struct vb2_fw_preamble *pre; const struct vb2_signature *sig = NULL; struct vb2_digest_context *dc; struct vb2_workbuf wb; uint32_t hash_offset; int i, rv; vb2_workbuf_from_ctx(ctx, &wb); /* Get preamble pointer */ if (!sd->workbuf_preamble_size) return VB2_ERROR_API_INIT_HASH_PREAMBLE; pre = (const struct vb2_fw_preamble *) (ctx->workbuf + sd->workbuf_preamble_offset); /* Find the matching signature */ hash_offset = pre->hash_offset; for (i = 0; i < pre->hash_count; i++) { sig = (const struct vb2_signature *) ((uint8_t *)pre + hash_offset); if (!memcmp(guid, &sig->guid, sizeof(*guid))) break; hash_offset += sig->c.total_size; } if (i >= pre->hash_count) return VB2_ERROR_API_INIT_HASH_GUID; /* No match */ /* Allocate workbuf space for the hash */ if (sd->workbuf_hash_size) { dc = (struct vb2_digest_context *) (ctx->workbuf + sd->workbuf_hash_offset); } else { uint32_t dig_size = sizeof(*dc); dc = vb2_workbuf_alloc(&wb, dig_size); if (!dc) return VB2_ERROR_API_INIT_HASH_WORKBUF; sd->workbuf_hash_offset = vb2_offset_of(ctx->workbuf, dc); sd->workbuf_hash_size = dig_size; ctx->workbuf_used = sd->workbuf_hash_offset + dig_size; } sd->hash_tag = vb2_offset_of(ctx->workbuf, sig); sd->hash_remaining_size = sig->data_size; if (size) *size = sig->data_size; if (!(pre->flags & VB2_FIRMWARE_PREAMBLE_DISALLOW_HWCRYPTO)) { rv = vb2ex_hwcrypto_digest_init(sig->hash_alg, sig->data_size); if (!rv) { VB2_DEBUG("Using HW crypto engine for hash_alg %d\n", sig->hash_alg); dc->hash_alg = sig->hash_alg; dc->using_hwcrypto = 1; return VB2_SUCCESS; } if (rv != VB2_ERROR_EX_HWCRYPTO_UNSUPPORTED) return rv; VB2_DEBUG("HW crypto for hash_alg %d not supported, using SW\n", sig->hash_alg); } else { VB2_DEBUG("HW crypto forbidden by preamble, using SW\n"); } return vb2_digest_init(dc, sig->hash_alg); }
/* Executes a command on the TPM. */ static VbError_t TpmExecute(const uint8_t *in, const uint32_t in_len, uint8_t *out, uint32_t *pout_len) { uint8_t response[TPM_MAX_COMMAND_SIZE]; if (in_len <= 0) { return DoError(TPM_E_INPUT_TOO_SMALL, "invalid command length %d for command 0x%x\n", in_len, in[9]); } else if (tpm_fd < 0) { return DoError(TPM_E_NO_DEVICE, "the TPM device was not opened. " \ "Forgot to call TlclLibInit?\n"); } else { int n; int retries = 0; int first_errno = 0; /* Write command. Retry in case of communication errors. */ for ( ; retries < COMM_RETRY_MAX_NUM; ++retries) { n = write(tpm_fd, in, in_len); if (n >= 0) { break; } if (retries == 0) { first_errno = errno; } VB2_DEBUG("TPM: write attempt %d failed: %s\n", retries + 1, strerror(errno)); } if (n < 0) { return DoError(TPM_E_WRITE_FAILURE, "write failure to TPM device: %s " "(first error %d)\n", strerror(errno), first_errno); } else if (n != in_len) { return DoError(TPM_E_WRITE_FAILURE, "bad write size to TPM device: %d vs %u " "(%d retries, first error %d)\n", n, in_len, retries, first_errno); } /* Read response. Retry in case of communication errors. */ for (retries = 0, first_errno = 0; retries < COMM_RETRY_MAX_NUM; ++retries) { n = read(tpm_fd, response, sizeof(response)); if (n >= 0) { break; } if (retries == 0) { first_errno = errno; } VB2_DEBUG("TPM: read attempt %d failed: %s\n", retries + 1, strerror(errno)); } if (n == 0) { return DoError(TPM_E_READ_EMPTY, "null read from TPM device\n"); } else if (n < 0) { return DoError(TPM_E_READ_FAILURE, "read failure from TPM device: %s " "(first error %d)\n", strerror(errno), first_errno); } else { if (n > *pout_len) { return DoError(TPM_E_RESPONSE_TOO_LARGE, "TPM response too long for " "output buffer\n"); } else { *pout_len = n; memcpy(out, response, n); } } } return VBERROR_SUCCESS; }
VbError_t VbExTpmSendReceive(const uint8_t* request, uint32_t request_length, uint8_t* response, uint32_t* response_length) { /* * In a real firmware implementation, this function should contain * the equivalent API call for the firmware TPM driver which takes a * raw sequence of bytes as input command and a pointer to the * output buffer for putting in the results. * * For EFI firmwares, this can make use of the EFI TPM driver as * follows (based on page 16, of TCG EFI Protocol Specs Version 1.20 * availaible from the TCG website): * * EFI_STATUS status; * status = TcgProtocol->EFI_TCG_PASS_THROUGH_TO_TPM( * TpmCommandSize(request), * request, * max_length, * response); * // Error checking depending on the value of the status above */ #ifndef NDEBUG int tag, response_tag; #endif VbError_t result; #ifdef VBOOT_DEBUG struct timeval before, after; VB2_DEBUG("request (%d bytes):\n", request_length); DbgPrintBytes(request, request_length); gettimeofday(&before, NULL); #endif result = TpmExecute(request, request_length, response, response_length); if (result != VBERROR_SUCCESS) return result; #ifdef VBOOT_DEBUG gettimeofday(&after, NULL); VB2_DEBUG("response (%d bytes):\n", *response_length); DbgPrintBytes(response, *response_length); VB2_DEBUG("execution time: %dms\n", (int) ((after.tv_sec - before.tv_sec) * VB_MSEC_PER_SEC + (after.tv_usec - before.tv_usec) / VB_USEC_PER_MSEC)); #endif #ifndef NDEBUG /* sanity checks */ tag = TpmTag(request); response_tag = TpmTag(response); assert( (tag == TPM_TAG_RQU_COMMAND && response_tag == TPM_TAG_RSP_COMMAND) || (tag == TPM_TAG_RQU_AUTH1_COMMAND && response_tag == TPM_TAG_RSP_AUTH1_COMMAND) || (tag == TPM_TAG_RQU_AUTH2_COMMAND && response_tag == TPM_TAG_RSP_AUTH2_COMMAND)); assert(*response_length == TpmResponseSize(response)); #endif return VBERROR_SUCCESS; }