bc_crypto/
ecdsa_keys.rs

1use bc_rand::RandomNumberGenerator;
2use secp256k1::{Secp256k1, SecretKey, PublicKey, Keypair, constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, UNCOMPRESSED_PUBLIC_KEY_SIZE}};
3
4pub const ECDSA_PRIVATE_KEY_SIZE: usize = 32;
5pub const ECDSA_PUBLIC_KEY_SIZE: usize = 33;
6pub const ECDSA_UNCOMPRESSED_PUBLIC_KEY_SIZE: usize = 65;
7pub const ECDSA_MESSAGE_HASH_SIZE: usize = 32;
8pub const ECDSA_SIGNATURE_SIZE : usize = 64;
9pub const SCHNORR_PUBLIC_KEY_SIZE: usize = 32;
10
11use crate::hash::hkdf_hmac_sha256;
12
13/// Generate a new ECDSA private key using the given random number generator.
14pub fn ecdsa_new_private_key_using(rng: &mut impl RandomNumberGenerator) -> [u8; SECRET_KEY_SIZE] {
15    rng.random_data(ECDSA_PRIVATE_KEY_SIZE).try_into().unwrap()
16}
17
18/// Derives the ECDSA public key from the given private key.
19pub fn ecdsa_public_key_from_private_key(private_key: &[u8; ECDSA_PRIVATE_KEY_SIZE]) -> [u8; PUBLIC_KEY_SIZE] {
20    let secp = Secp256k1::new();
21    let private_key = SecretKey::from_slice(private_key)
22        .expect("32 bytes, within curve order");
23    let public_key = PublicKey::from_secret_key(&secp, &private_key);
24    public_key.serialize()
25}
26
27/// Decompresses the given ECDSA public key.
28///
29/// This format is generally deprecated, but is still used in some places.
30pub fn ecdsa_decompress_public_key(compressed_public_key: &[u8; PUBLIC_KEY_SIZE]) -> [u8; UNCOMPRESSED_PUBLIC_KEY_SIZE] {
31    let public_key = PublicKey::from_slice(compressed_public_key)
32        .expect("65 bytes, serialized according to the spec");
33    public_key.serialize_uncompressed()
34}
35
36/// Compresses the given ECDSA public key.
37pub fn ecdsa_compress_public_key(uncompressed_public_key: &[u8; UNCOMPRESSED_PUBLIC_KEY_SIZE]) -> [u8; PUBLIC_KEY_SIZE] {
38    let public_key = PublicKey::from_slice(uncompressed_public_key.as_ref())
39        .expect("33 bytes, serialized according to the spec");
40    public_key.serialize()
41}
42
43/// Derives the ECDSA private key from the given key material.
44///
45/// Uses the HKDF algorithm to derive the private key from the given key material.
46pub fn ecdsa_derive_private_key(key_material: impl AsRef<[u8]>) -> Vec<u8> {
47    hkdf_hmac_sha256(key_material, "signing".as_bytes(), 32)
48}
49
50/// Derives the Schnorr public key from the given private key.
51pub fn schnorr_public_key_from_private_key(private_key: &[u8; ECDSA_PRIVATE_KEY_SIZE]) -> [u8; SCHNORR_PUBLIC_KEY_SIZE] {
52    let secp = Secp256k1::new();
53    let kp: Keypair = Keypair::from_seckey_slice(&secp, private_key).unwrap();
54    let (x, _) = kp.x_only_public_key();
55    x.serialize()
56}
57
58#[cfg(test)]
59mod tests {
60    use crate::{ecdsa_public_key_from_private_key, ecdsa_new_private_key_using, ecdsa_decompress_public_key, ecdsa_compress_public_key, schnorr_public_key_from_private_key, ecdsa_derive_private_key};
61    use bc_rand::make_fake_random_number_generator;
62    use hex_literal::hex;
63
64    #[test]
65    fn test_ecdsa_keys() {
66        let mut rng = make_fake_random_number_generator();
67        let private_key = ecdsa_new_private_key_using(&mut rng);
68        assert_eq!(private_key, hex!("7eb559bbbf6cce2632cf9f194aeb50943de7e1cbad54dcfab27a42759f5e2fed"));
69        let public_key = ecdsa_public_key_from_private_key(&private_key);
70        assert_eq!(public_key, hex!("0271b92b6212a79b9215f1d24efb9e6294a1bedc95b6c8cf187cb94771ca02626b"));
71        let decompressed = ecdsa_decompress_public_key(&public_key);
72        assert_eq!(decompressed, hex!("0471b92b6212a79b9215f1d24efb9e6294a1bedc95b6c8cf187cb94771ca02626b72325f1f3bb69a44d3f1cb6d1fd488220dd502f49c0b1a46cb91ce3718d8334a"));
73        let compressed = ecdsa_compress_public_key(&decompressed);
74        assert_eq!(compressed, public_key);
75        let x_only_public_key = schnorr_public_key_from_private_key(&private_key);
76        assert_eq!(x_only_public_key, hex!("71b92b6212a79b9215f1d24efb9e6294a1bedc95b6c8cf187cb94771ca02626b"));
77
78        let private_key = ecdsa_derive_private_key(b"password");
79        assert_eq!(private_key, hex!("05cc550daa75058e613e606d9898fedf029e395911c43273a208b7e0e88e271b"));
80    }
81}