paulmillr / noble-secp256k1

Fastest 4KB JS implementation of secp256k1 signatures and ECDH
https://paulmillr.com/noble
MIT License
757 stars 114 forks source link

[Question] Compatibility to other library #35

Closed 0x79756b69 closed 2 years ago

0x79756b69 commented 2 years ago

Hi. I'm new at cryptography. I'm trying to check compatibility to other library ( https://docs.rs/secp256k1/ ).

const public_key_js = secp.getPublicKey("f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994");
let public_key_rust = PublicKey::from_str(public_key_js);

and

let secp = Secp256k1::new();
let secret_key = SecretKey::from_str("f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994").unwrap();
let public_key_rust = PublicKey::from_secret_key(&secp, &secret_key);

is the same public_key_rust output.

But

const sig = secp.sign("9e71e1c1f3f5d45d42669dc0c191d58ee5c90ff69a8011fc34c66f375ba46a7e", "f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994");
// sig = 30450220617dd27ff185e0b8d0904e3ccb4215a4ba2e22d165439365a4a3516b400e8a3a022100e20c366399e11322a1e56b897c339e6d764fca5c9cee9f3fc0370d597c8eb680
// This signature will not valid to https://docs.rs/secp256k1/

and

let mut hasher = Sha256::new();
hasher.update(b"madoka");
let hashed_value = hasher.finalize();
// sha2-256("madoka") == 9e71e1c1f3f5d45d42669dc0c191d58ee5c90ff69a8011fc34c66f375ba46a7e

let secp = Secp256k1::new();
let sig = secp.sign(&Message::from_slice(&*hashed_value).unwrap(), &SecretKey::from_str("f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994").unwrap());
// sig = 30440220617dd27ff185e0b8d0904e3ccb4215a4ba2e22d165439365a4a3516b400e8a3a02201df3c99c661eecdd5e1a947683cc6191445f128a125a00fbff9b513353a78ac1
// This signature will valid to noble-secp256k1

is not the same.

What makes this difference? Is this a normal behavior of crypto library?

Sorry for my not good English and Code.

paulmillr commented 2 years ago

Instead of using Message from slice in rust, can you try this:

let message = Message::from_hashed_data::("madoka".as_bytes());

0x79756b69 commented 2 years ago

Thank you for reply. Still can't match the value.


    let secp = Secp256k1::new();

    let secret_key = SecretKey::from_str("f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994").unwrap();
    let message = Message::from_hashed_data::<sha256::Hash>("madoka".as_bytes());
    let js_public_key = PublicKey::from_str("04458eb1d8af007f821f7f50405d844171c0c55c389a87a3944ddda5dbe4d51b62d8cb182e2243750458a42894abb2e6f91bff6415523650adf45fadf2e01543c3").unwrap();
    let js_sig = Signature::from_str("30450220617dd27ff185e0b8d0904e3ccb4215a4ba2e22d165439365a4a3516b400e8a3a022100e20c366399e11322a1e56b897c339e6d764fca5c9cee9f3fc0370d597c8eb680").expect("compact signatures are 64 bytes; DER signatures are 68-72 bytes");
    let rust_public_key = PublicKey::from_secret_key(&secp, &secret_key);
    let rust_sig = secp.sign(&message, &secret_key);
    println!("{:?}", message);
    println!("{:?}", secret_key);
    println!("{:?}", js_public_key);
    println!("{:?}", js_sig);
    println!("-------");
    println!("{:?}", rust_public_key);
    println!("{:?}", rust_sig);

output:

Message(9e71e1c1f3f5d45d42669dc0c191d58ee5c90ff69a8011fc34c66f375ba46a7e)
SecretKey(f7ea2341e4b1f1dc438c43725e8bebdb8d3815a47651d2312f431793b66f6994)
PublicKey(621bd5e4dba5dd4d94a3879a385cc5c07141845d40507f1f827f00afd8b18e45c34315e0f2ad5ff4ad5036521564ff1bf9e6b2ab9428a458047543222e18cbd8)
30450220617dd27ff185e0b8d0904e3ccb4215a4ba2e22d165439365a4a3516b400e8a3a022100e20c366399e11322a1e56b897c339e6d764fca5c9cee9f3fc0370d597c8eb680
-------
PublicKey(621bd5e4dba5dd4d94a3879a385cc5c07141845d40507f1f827f00afd8b18e45c34315e0f2ad5ff4ad5036521564ff1bf9e6b2ab9428a458047543222e18cbd8)
30440220617dd27ff185e0b8d0904e3ccb4215a4ba2e22d165439365a4a3516b400e8a3a02201df3c99c661eecdd5e1a947683cc6191445f128a125a00fbff9b513353a78ac1
paulmillr commented 2 years ago

Noble-secp is tested through tons of test vectors from different libraries, so my guess is that somewhere you're doing something differently between node.js and Rust.

vae520283995 commented 2 years ago

Although you seem to have different values, both are legal results

if (s > HIGH_NUMBER && canonical) {
    adjustedS = CURVE.n - s;
    recovery ^= 1;
  }

https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures https://github.com/paulmillr/noble-secp256k1/blob/57cdd6df471c8869a7786bc1c500b21aac6c3e8c/index.ts#L975

paulmillr commented 2 years ago

Yeah, maybe switch canonical flag of sign

0x79756b69 commented 2 years ago

Switching to the canonical flag was successful. Thank you for your warm help.