bssl-crypto: support ECDSA verification of already-hashed messages. Sometimes an ECDSA key is used with the "wrong" hash function. This change adds a function which allows callers to still verify those signatures. Change-Id: I2cc8b9c06c53bbfca51e23ab97302c35c6a02dd9 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/98687 Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Adam Langley <agl@google.com> Reviewed-by: Xiangfei Ding <xfding@google.com> Commit-Queue: Adam Langley <agl@google.com>
diff --git a/rust/bssl-crypto/src/ecdsa.rs b/rust/bssl-crypto/src/ecdsa.rs index 568db33..c103247 100644 --- a/rust/bssl-crypto/src/ecdsa.rs +++ b/rust/bssl-crypto/src/ecdsa.rs
@@ -100,12 +100,32 @@ /// produce the digest if the curve of this public key is P-384. pub fn verify(&self, signed_msg: &[u8], signature: &[u8]) -> Result<(), InvalidSignatureError> { let digest = C::hash(signed_msg); + self.verify_already_hashed(&digest, signature) + } + + /// Verify `signature` as a valid ASN.1-based signature of `hashed_msg` with + /// this public key. `hashed_msg` must already have been hashed with a + /// secure hash function: passing unhashed data here breaks the security + /// properties of the signature scheme. If you have unhashed data, use + /// `verify` instead. + /// + /// This function exists because sometimes the "wrong" hash function is used + /// with a key. A key should not be used with different hash functions and + /// there is an obvious correspondence between NIST's standard elliptic + /// curves and the SHA-2 family of hash functions that implicitly defines + /// the correct hash function to use with a given key. But, if mistakes have + /// been made, this function is here for you. + pub fn verify_already_hashed( + &self, + hashed_msg: &[u8], + signature: &[u8], + ) -> Result<(), InvalidSignatureError> { let result = self.point.with_point_as_ec_key(|ec_key| unsafe { // Safety: `ec_key` is valid per `with_point_as_ec_key`. bssl_sys::ECDSA_verify( /*type=*/ 0, - digest.as_slice().as_ffi_ptr(), - digest.len(), + hashed_msg.as_ffi_ptr(), + hashed_msg.len(), signature.as_ffi_ptr(), signature.len(), ec_key, @@ -373,10 +393,18 @@ ) .unwrap(); assert!(public_key.verify(signed_message, sig.as_slice()).is_ok()); + let mut digest = C::hash(signed_message); + assert!(public_key + .verify_already_hashed(digest.as_slice(), sig.as_slice()) + .is_ok()); assert!(public_key .verify_p1363(signed_message, sig_p1363.as_slice()) .is_ok()); + digest[0] ^= 1; + assert!(public_key + .verify_already_hashed(digest.as_slice(), sig.as_slice()) + .is_err()); sig[10] ^= 1; assert!(public_key.verify(signed_message, sig.as_slice()).is_err()); sig_p1363[10] ^= 1;