Search code examples
rustcryptographydigital-signatureecdsaecdsasignature

Why is my ECDSA P-384 signature not different for the same message?


ECDSA P-384 is supposed to have signatures which are different even if the message being signed are the exact same.

Why does ECDSA produce different signatures for the same data, whereas RSA doesn't?

That seems to work in JavaScript using the Crypto Subtle library which gives different signature even for same message.

But in rust, that's not working. I keep getting the same signature for the same message. Please see below. (the keys and message are dummy data for testing only)

use p384::{elliptic_curve::SecretKey, ecdsa::{SigningKey, Signature, signature::Signer}};

fn main() {
    let jwk_string = format!(r#"{{"kty": "EC","crv": "P-384","x": "w6CbiBS2SKN4Y4hov7BqJbq3ZVZeEks_3PEUFEb281nYHWZYpCd8BrC1M7CfJYi4","y": "yYugPHaZMaOo_BKWaIMDDXDauZD9tKGGg444dY-tJ-0lmsZI3jJ99fw5LlXkCJMp","d": "zO5NR7o0T5jJc3aqr2mwHVjQeDAfzMovpc3-9uYeKxBAfZ4_07G0_jQDaiuyKNPM"}}"#);

    let key = match SecretKey::from_jwk_str(&jwk_string) {
        Ok(p) => SigningKey::from(p),
        Err(e) => {
            println!("Not valid secret key: {}",e);
            return;
        },
    };

    let to_sign = "public.xvRyWGu5CjC-ufl18xIQFUHN9XlLQoXFkU8RCLfRMooi63ftX-nAHXt5tuNP_ZYC.wjRSUmKCVY_yL8ZjpoHWCOaB7NOc2yEt-VtrsqQJHGrYr1uCT_m6Qbrg84BCkHMF";
    let to_sign_bytes = to_sign.as_bytes();
    println!("to_sign_bytes: {:?}",to_sign_bytes);
    let signature : Signature = key.sign(to_sign_bytes);
    println!("Signature: {:x?}",hex::encode(signature.to_bytes()));

}

Output:

to_sign_bytes: [112, 117, 98, 108, 105, 99, 46, 120, 118, 82, 121, 87, 71, 117, 53, 67, 106, 67, 45, 117, 102, 108, 49, 56, 120, 73, 81, 70, 85, 72, 78, 57, 88, 108, 76, 81, 111, 88, 70, 107, 85, 56, 82, 67, 76, 102, 82, 77, 111, 111, 105, 54, 51, 102, 116, 88, 45, 110, 65, 72, 88, 116, 53, 116, 117, 78, 80, 95, 90, 89, 67, 46, 119, 106, 82, 83, 85, 109, 75, 67, 86, 89, 95, 121, 76, 56, 90, 106, 112, 111, 72, 87, 67, 79, 97, 66, 55, 78, 79, 99, 50, 121, 69, 116, 45, 86, 116, 114, 115, 113, 81, 74, 72, 71, 114, 89, 114, 49, 117, 67, 84, 95, 109, 54, 81, 98, 114, 103, 56, 52, 66, 67, 107, 72, 77, 70]

Signature: "f4ff418ceece0b6d276e75938ea825ada59885249da48d492ed671af0f77f53b8e9ddf2c9401f848239279acf717b4f7ca158721d2c9a80078312ae02e76e239fafb3bd117099aee37d41a239ac6b305cfed009ab8b75641e44a227752077bd7"

Is this a bug in the p384 crate I am using?

EDIT: Could this be related to this Github issue about "sm2 sign method seems got same result when use same secret_key"?:

https://github.com/RustCrypto/elliptic-curves/issues/883


Solution

  • RFC 6979 specifies an approach for deterministic DSA and ECDSA, which takes the private key and the hash of the message, feeds them into an HMAC-DRBG, and generates the value k that way. The p384 crate uses that by default.

    The advantage of deterministic ECDSA is that it is secure even without a CSPRNG once the key is generated. This can be advantageous in some environments and guards against the risk that the CSPRNG in use is broken or insufficiently seeded (which is common on embedded devices and on some types of hardware without a TRNG). Some people also like the fact that the same message and key produce identical signatures, which can be practical in some cases.

    You can, of course, use the variant in the crate that uses an RNG. However, you must be careful, because you must always use a secure CSPRNG. For example, some Rust CSPRNGs do not reseed immediately after a fork, which would allow for duplicate randomness and therefore a security vulnerability leaking the entire secret key.