diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c index 0c4c788ff..9cfa1bebb 100644 --- a/grub-core/commands/appendedsig/appendedsig.c +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -521,6 +521,83 @@ extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize, return grub_pkcs7_data_parse (signed_data, appendedsig_pkcs7_size, &sig->pkcs7); } +static grub_err_t +get_binary_hash (const grub_size_t binary_hash_size, const grub_uint8_t *data, + const grub_size_t data_size, grub_uint8_t *hash, grub_size_t *hash_size) +{ + grub_packed_guid_t guid = { 0 }; + + /* support SHA256, SHA384 and SHA512 for binary hash */ + if (binary_hash_size == SHA256_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_SHA256_GUID, GRUB_PACKED_GUID_SIZE); + else if (binary_hash_size == SHA384_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_SHA384_GUID, GRUB_PACKED_GUID_SIZE); + else if (binary_hash_size == SHA512_HASH_SIZE) + grub_memcpy (&guid, &GRUB_PKS_CERT_SHA512_GUID, GRUB_PACKED_GUID_SIZE); + else + { + grub_dprintf ("appendedsig", "unsupported hash type (%" PRIuGRUB_SIZE ") and " + "skipped\n", binary_hash_size); + return GRUB_ERR_UNKNOWN_COMMAND; + } + + return get_hash (&guid, data, data_size, hash, hash_size); +} + +/* + * Verify binary hash against the db and dbx list. + * The following errors can occur: + * - GRUB_ERR_BAD_SIGNATURE: indicates that the hash is in dbx list. + * - GRUB_ERR_EOF: the hash could not be found in the db and dbx list. + * - GRUB_ERR_NONE: the hash is found in db list. + */ +static grub_err_t +verify_binary_hash (const grub_uint8_t *data, const grub_size_t data_size) +{ + grub_err_t rc = GRUB_ERR_NONE; + grub_uint32_t i; + grub_size_t hash_size = 0; + grub_uint8_t hash[GRUB_MAX_HASH_LEN] = { 0 }; + + for (i = 0; i < dbx.hash_entries; i++) + { + if (dbx.hashes[i] == NULL) + continue; + + rc = get_binary_hash (dbx.hash_sizes[i], data, data_size, hash, &hash_size); + if (rc != GRUB_ERR_NONE) + continue; + + if (hash_size == dbx.hash_sizes[i] && + grub_memcmp (dbx.hashes[i], hash, hash_size) == 0) + { + grub_dprintf ("appendedsig", "the hash (%02x%02x%02x%02x) is present in the dbx list\n", + hash[0], hash[1], hash[2], hash[3]); + return GRUB_ERR_BAD_SIGNATURE; + } + } + + for (i = 0; i < db.hash_entries; i++) + { + if (db.hashes[i] == NULL) + continue; + + rc = get_binary_hash (db.hash_sizes[i], data, data_size, hash, &hash_size); + if (rc != GRUB_ERR_NONE) + continue; + + if (hash_size == db.hash_sizes[i] && + grub_memcmp (db.hashes[i], hash, hash_size) == 0) + { + grub_dprintf ("appendedsig", "verified with a trusted hash (%02x%02x%02x%02x)\n", + hash[0], hash[1], hash[2], hash[3]); + return GRUB_ERR_NONE; + } + } + + return GRUB_ERR_EOF; +} + /* * Given a hash value 'hval', of hash specification 'hash', prepare the * S-expressions (sexp) and perform the signature verification. @@ -565,7 +642,7 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) grub_pkcs7_signer_t *si; grub_int32_t i; - if (!db.cert_entries) + if (!db.cert_entries && !db.hash_entries) return grub_error (GRUB_ERR_BAD_SIGNATURE, "no trusted keys to verify against"); err = extract_appended_signature (buf, bufsize, &sig); @@ -574,6 +651,21 @@ grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize) datasize = bufsize - sig.signature_len; + /* + * If signature verification is enabled with dynamic key management mode, + * Verify binary hash against the db and dbx list. + */ + if (append_key_mgmt == true) + { + err = verify_binary_hash (buf, datasize); + if (err == GRUB_ERR_BAD_SIGNATURE) + { + grub_pkcs7_data_release (&sig.pkcs7); + return grub_error (err, + "failed to verify the binary hash against a trusted binary hash"); + } + } + /* Verify signature using trusted keys from db list. */ for (i = 0; i < sig.pkcs7.signer_count; i++) {