Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

P256 ECDSA Signing does not match other implementations #187

Closed
alistair23 opened this issue Sep 16, 2020 · 2 comments
Closed

P256 ECDSA Signing does not match other implementations #187

alistair23 opened this issue Sep 16, 2020 · 2 comments
Labels
p256 NIST P-256 crate

Comments

@alistair23
Copy link

When running the following code with the Python3 cryptography library:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives import hashes
attest = ec.derive_private_key(0x32c9f75d73b5c5ce890daa575ad508429acd95d5d3d7773ff2d09cd0a5f3d6cd, ec.SECP256R1(), default_backend())
data = bytes([163, 121, 166, 246, 238, 175, 185, 165, 94, 55, 140, 17, 128, 52, 226, 117, 30, 104, 47, 171, 159, 45, 48, 171, 19, 210, 18, 85, 134, 206, 25, 71, 65, 0, 0, 0, 0, 248, 160, 17, 243, 140, 10, 77, 21, 128, 6, 23, 17, 31, 158, 220, 125, 0, 40, 32, 27, 13, 94, 181, 116, 99, 237, 92, 107, 88, 61, 77, 89, 195, 249, 80, 51, 218, 175, 2, 206, 23, 3, 198, 183, 94, 25, 106, 48, 166, 10, 0, 0, 0, 0, 0, 0, 0, 0, 165, 1, 2, 3, 38, 32, 1, 33, 88, 32, 135, 246, 4, 67, 55, 184, 84, 105, 81, 144, 148, 109, 22, 144, 253, 104, 167, 81, 89, 93, 84, 187, 187, 77, 134, 73, 77, 206, 241, 144, 133, 211, 34, 88, 32, 86, 111, 67, 255, 4, 111, 211, 242, 129, 66, 116, 151, 203, 35, 108, 24, 37, 217, 200, 244, 230, 38, 93, 112, 230, 168, 131, 232, 207, 157, 65, 16, 78, 187, 155, 59, 173, 254, 35, 48, 209, 67, 146, 109, 243, 103, 182, 99, 76, 244, 154, 222, 205, 14, 4, 197, 222, 176, 30, 4, 143, 68, 110, 132])
sig = attest.sign(data, ec.ECDSA(hashes.SHA256()))
output = utils.decode_dss_signature(sig)
print("Signature: r: " + str(hex(output[0])) + " s: " + str(hex(output[1])))

I get this output:

Signature: r: 0x85e35de0bad8c6ffc347f58bebc051701ad08d8a6bc662401250e08d32334c48 s: 0x176743776c0ea6f8aa0d3ff87a17be98a4d31c2d069f982374fb2ce56b7eafa7

When running this code

const ATTESTATION_KEY: [u8; 32] = [
    0x32, 0xc9, 0xf7, 0x5d, 0x73, 0xb5, 0xc5, 0xce, 0x89, 0x0d, 0xaa, 0x57, 0x5a, 0xd5, 0x08, 0x42,
    0x9a, 0xcd, 0x95, 0xd5, 0xd3, 0xd7, 0x77, 0x3f, 0xf2, 0xd0, 0x9c, 0xd0, 0xa5, 0xf3, 0xd6, 0xcd,
]

const data: [u8] = [163, 121, 166, 246, 238, 175, 185, 165, 94, 55, 140, 17, 128, 52, 226, 117, 30, 104, 47, 171, 159, 45, 48, 171, 19, 210, 18, 85, 134, 206, 25, 71, 65, 0, 0, 0, 0, 248, 160, 17, 243, 140, 10, 77, 21, 128, 6, 23, 17, 31, 158, 220, 125, 0, 40, 32, 27, 13, 94, 181, 116, 99, 237, 92, 107, 88, 61, 77, 89, 195, 249, 80, 51, 218, 175, 2, 206, 23, 3, 198, 183, 94, 25, 106, 48, 166, 10, 0, 0, 0, 0, 0, 0, 0, 0, 165, 1, 2, 3, 38, 32, 1, 33, 88, 32, 135, 246, 4, 67, 55, 184, 84, 105, 81, 144, 148, 109, 22, 144, 253, 104, 167, 81, 89, 93, 84, 187, 187, 77, 134, 73, 77, 206, 241, 144, 133, 211, 34, 88, 32, 86, 111, 67, 255, 4, 111, 211, 242, 129, 66, 116, 151, 203, 35, 108, 24, 37, 217, 200, 244, 230, 38, 93, 112, 230, 168, 131, 232, 207, 157, 65, 16, 78, 187, 155, 59, 173, 254, 35, 48, 209, 67, 146, 109, 243, 103, 182, 99, 76, 244, 154, 222, 205, 14, 4, 197, 222, 176, 30, 4, 143, 68, 110, 132]

            print!("ATTESTATION_KEY: 0x");
            for d in ATTESTATION_KEY.iter() {
                print!("{:02x}", d);
            }
            println!("");

            let secret_key = SecretKey::from_bytes(&ATTESTATION_KEY).unwrap();
            let signer = SigningKey::from(&secret_key);
            let sig = signer.sign(data);

            println!("sig: {:?}", sig);

            print!("r: 0x");
            for d in sig.r().as_ref().to_bytes().iter() {
                print!("{:02x}", d);
            }
            println!("");

            print!("s: 0x");
            for d in sig.s().as_ref().to_bytes().iter() {
                print!("{:02x}", d);
            }
            println!("");

I see this:

ATTESTATION_KEY: 0x32c9f75d73b5c5ce890daa575ad508429acd95d5d3d7773ff2d09cd0a5f3d6cd
sig: ecdsa::Signature<NistP256>([67, 125, 8, 103, 193, 16, 122, 186, 244, 233, 1, 154, 248, 252, 223, 74, 146, 92, 138, 232, 168, 162, 59, 76, 245, 85, 132, 94, 77, 161, 236, 163, 176, 12, 147, 133, 176, 234, 179, 216, 31, 177, 101, 31, 175, 34, 140, 130, 27, 149, 176, 187, 198, 2, 110, 27, 157, 165, 86, 234, 55, 245, 44, 143])
r: 0x437d0867c1107abaf4e9019af8fcdf4a925c8ae8a8a23b4cf555845e4da1eca3
s: 0xb00c9385b0eab3d81fb1651faf228c821b95b0bbc6026e1b9da556ea37f52c8f

What what I can tell the Rust and Python code should be doing the same thing, but I get different r and s values at the end.

@tarcieri
Copy link
Member

tarcieri commented Sep 16, 2020

Note that the implementation in the p256 crate passes this test vector from RFC6979:

https://tools.ietf.org/html/rfc6979#appendix-A.2.5

curve: NIST P-256
private key:
   x = C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721

With SHA-256, message = "sample":
   k = A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60
   r = EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716
   s = F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8

You can find the test for this vector against the p256 implementation here:

https://github.com/RustCrypto/elliptic-curves/blob/cf65586/p256/src/ecdsa.rs#L146-L158

The signature from Python cryptography does not match this test vector:

from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec, utils
from cryptography.hazmat.primitives import hashes
attest = ec.derive_private_key(0xc9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721, ec.SECP256R1(), default_backend())
data = bytes([115, 97, 109, 112, 108, 101])
sig = attest.sign(data, ec.ECDSA(hashes.SHA256()))
output = utils.decode_dss_signature(sig)
print("Signature: r: " + str(hex(output[0])) + " s: " + str(hex(output[1])))

Prints:

r: 0xcfed9cfefbd61caf24b3d04f56c94c215af3597be11d27cd646786bb5360512c
s: 0x295c05ecf488ac7df05ca71bd332f868a663793aa749b622f470cc401b5f0b0f

(it's possible I adapted the example incorrectly, please double check me on that)

ECDSA signing implementations are free to choose k as they please. You might want to check if cryptography implements RFC6979. Whatever they're doing appears to be deterministic, but may not necessarily match RFC6979.

@tarcieri tarcieri added the p256 NIST P-256 crate label Sep 16, 2020
@tarcieri
Copy link
Member

@alistair23 please reopen if you think there’s still an issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
p256 NIST P-256 crate
Projects
None yet
Development

No branches or pull requests

2 participants