bc_crypto/
ecdsa_keys.rs

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