core/crypto/signature_verification.c (114 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <string.h> #include "signature_verification.h" #include "common/buffer_util.h" /** * Verify a specified message with a digital signature. * * @param sig_verify The context to use for signature verification. * @param hash The hash engine that will be used to calculate the message digest. * @param hash_algo Algorithm to use for message hashing. * @param message The raw message data that should be verified. * @param msg_length Length of the message. * @param key Optional public key to use for signature verification. If this is provided, the * verification context will be loaded with this key prior to verification. This key will be erased * from the verification context prior to returning. If this is null, the key currently present in * the verification context will be used for verification and will be left unchanged upon returning. * @param key_length Length of the optional public key. This should be 0 if no key is provided. * @param signature The digital signature for the message data. * @param sig_length Length of the digital signature. * * @return 0 if the message was verified successfully or an error code. */ int signature_verification_verify_message (const struct signature_verification *sig_verify, const struct hash_engine *hash, enum hash_type hash_algo, const uint8_t *message, size_t msg_length, const uint8_t *key, size_t key_length, const uint8_t *signature, size_t sig_length) { int status; if ((sig_verify == NULL) || (hash == NULL) || (signature == NULL) || (sig_length == 0)) { return SIG_VERIFICATION_INVALID_ARGUMENT; } status = hash_start_new_hash (hash, hash_algo); if (status != 0) { return status; } status = hash->update (hash, message, msg_length); if (status != 0) { hash->cancel (hash); return status; } return signature_verification_verify_hash_and_finish (sig_verify, hash, key, key_length, signature, sig_length); } /** * Verify a calculated digest against the provided digital signature. * * @param sig_verify The context to use for signature verification. * @param digest The digest to verify. * @param digest_length Length of the digest to verify. * @param key Optional public key to use for signature verification. If this is provided, the * verification context will be loaded with this key prior to verification. This key will be erased * from the verification context prior to returning. If this is null, the key currently present in * the verification context will be used for verification and will be left unchanged upon returning. * @param key_length Length of the optional public key. This should be 0 if no key is provided. * @param signature The digital signature for the digest. * @param sig_length Length of the digital signature. * * @return 0 if the digest was verified successfully or an error code. */ static int signature_verification_verify_digest (const struct signature_verification *sig_verify, uint8_t digest[HASH_MAX_HASH_LEN], size_t digest_length, const uint8_t *key, size_t key_length, const uint8_t *signature, size_t sig_length) { int status; if (((key != NULL) && (key_length == 0)) || ((key == NULL) && (key_length != 0))) { return SIG_VERIFICATION_INCONSISTENT_KEY; } if (key != NULL) { status = sig_verify->set_verification_key (sig_verify, key, key_length); if (status != 0) { return status; } } status = sig_verify->verify_signature (sig_verify, digest, digest_length, signature, sig_length); if (key != NULL) { /* Wipe the public key from the verification context. Errors here are ignored. */ sig_verify->set_verification_key (sig_verify, NULL, 0); } return status; } /** * Verify an active hash context with a digital signature. * * The hash context will remain active after signature verification, allowing additional updates to * be made. Not all hash implementations support this type of behavior, so it should only be used * in scenarios which require it. Most scenarios should use dsa_verify_hash_and_finish instead. * * @param sig_verify The context to use for signature verification. * @param hash The hash engine that will be used to calculate the message digest. * @param key Optional public key to use for signature verification. If this is provided, the * verification context will be loaded with this key prior to verification. This key will be erased * from the verification context prior to returning. If this is null, the key currently present in * the verification context will be used for verification and will be left unchanged upon returning. * @param key_length Length of the optional public key. This should be 0 if no key is provided. * @param signature The digital signature for the hash context * @param sig_length Length of the digital signature. * * @return 0 if the hash was verified successfully or an error code. */ int signature_verification_verify_hash (const struct signature_verification *sig_verify, const struct hash_engine *hash, const uint8_t *key, size_t key_length, const uint8_t *signature, size_t sig_length) { uint8_t digest[HASH_MAX_HASH_LEN] = {0}; size_t digest_length; int status; if ((sig_verify == NULL) || (hash == NULL) || (signature == NULL) || (sig_length == 0)) { return SIG_VERIFICATION_INVALID_ARGUMENT; } digest_length = hash_get_active_hash_length (hash); if (digest_length == 0) { return SIG_VERIFICATION_NO_ACTVE_HASH; } status = hash->get_hash (hash, digest, sizeof (digest)); if (status != 0) { goto exit; } status = signature_verification_verify_digest (sig_verify, digest, digest_length, key, key_length, signature, sig_length); exit: buffer_zeroize (digest, HASH_MAX_HASH_LEN); return status; } /** * Verify an active hash context with a digital signature. * * The hash context will be finished as part of signature verification. No additional updates can * be made to the hash context, regardless of whether the signature verification was successful or * not. * * @param sig_verify The context to use for signature verification. * @param hash The hash engine that will be used to calculate the message digest. The active * context will always be terminated upon returning from this call. * @param key Optional public key to use for signature verification. If this is provided, the * verification context will be loaded with this key prior to verification. This key will be erased * from the verification context prior to returning. If this is null, the key currently present in * the verification context will be used for verification and will be left unchanged upon returning. * @param key_length Length of the optional public key. This should be 0 if no key is provided. * @param signature The digital signature for the hash context. * @param sig_length Length of the digital signature. * * @return 0 if the hash was verified successfully or an error code. */ int signature_verification_verify_hash_and_finish (const struct signature_verification *sig_verify, const struct hash_engine *hash, const uint8_t *key, size_t key_length, const uint8_t *signature, size_t sig_length) { uint8_t digest[HASH_MAX_HASH_LEN] = {0}; int status; status = signature_verification_verify_hash_and_finish_save_digest (sig_verify, hash, key, key_length, signature, sig_length, digest, sizeof (digest), NULL); buffer_zeroize (digest, sizeof (digest)); return status; } /** * Verify an active hash context with a digital signature. The digest that was calculated for * verification will be returned. * * The hash context will be finished as part of signature verification. No additional updates can * be made to the hash context, regardless of whether the signature verification was successful or * not. * * @param sig_verify The context to use for signature verification. * @param hash The hash engine that will be used to calculate the message digest. The active * context will always be terminated upon returning from this call. * @param key Optional public key to use for signature verification. If this is provided, the * verification context will be loaded with this key prior to verification. This key will be erased * from the verification context prior to returning. If this is null, the key currently present in * the verification context will be used for verification and will be left unchanged upon returning. * @param key_length Length of the optional public key. This should be 0 if no key is provided. * @param signature The digital signature for the hash context. * @param sig_length Length of the digital signature. * @param digest Output buffer for the calculated digest used for signature verification. The * caller is expected to know the length of the generated digest. This may contain valid data even * if the verification operation overall was not successful. * @param digest_length Length of the output digest buffer. * @param digest_valid Optional output to indicate if the digest output buffer contains valid data. * This could be used to determine situations where the hash finish completed successfully but the * verification of the digest did not. This can be null if this information is not needed. * * @return 0 if the hash was verified successfully or an error code. */ int signature_verification_verify_hash_and_finish_save_digest ( const struct signature_verification *sig_verify, const struct hash_engine *hash, const uint8_t *key, size_t key_length, const uint8_t *signature, size_t sig_length, uint8_t *digest, size_t digest_length, bool *digest_valid) { size_t active_length; int status; if (digest_valid != NULL) { *digest_valid = false; } if (hash == NULL) { return SIG_VERIFICATION_INVALID_ARGUMENT; } active_length = hash_get_active_hash_length (hash); if (active_length == 0) { return SIG_VERIFICATION_NO_ACTVE_HASH; } if ((sig_verify == NULL) || (signature == NULL) || (sig_length == 0) || (digest == NULL)) { status = SIG_VERIFICATION_INVALID_ARGUMENT; goto hash_cancel; } status = hash->finish (hash, digest, digest_length); if (status != 0) { goto hash_cancel; } if (digest_valid != NULL) { *digest_valid = true; } return signature_verification_verify_digest (sig_verify, digest, active_length, key, key_length, signature, sig_length); hash_cancel: hash->cancel (hash); return status; }