bc_components/ec_key/
ec_private_key.rs

1use anyhow::{Result, bail};
2use bc_rand::{RandomNumberGenerator, SecureRandomNumberGenerator};
3use bc_ur::prelude::*;
4
5use crate::{ECKey, ECKeyBase, ECPublicKey, SchnorrPublicKey, tags};
6
7/// The size of an ECDSA private key in bytes (32 bytes).
8pub const ECDSA_PRIVATE_KEY_SIZE: usize = bc_crypto::ECDSA_PRIVATE_KEY_SIZE;
9
10/// A private key for elliptic curve digital signature algorithms.
11///
12/// An `ECPrivateKey` is a 32-byte secret value that can be used to:
13///
14/// - Generate its corresponding public key
15/// - Sign messages using the ECDSA signature scheme
16/// - Sign messages using the Schnorr signature scheme (BIP-340)
17///
18/// These keys use the secp256k1 curve, which is the same curve used in Bitcoin
19/// and other cryptocurrencies. The secp256k1 curve is defined by the Standards
20/// for Efficient Cryptography Group (SECG).
21///
22/// # Security
23///
24/// Private keys should be kept secret and never exposed. They represent
25/// proof of ownership and control over any associated assets or identities.
26///
27/// # Examples
28///
29/// Creating a new random private key:
30///
31/// ```
32/// use bc_components::ECPrivateKey;
33///
34/// // Generate a random private key
35/// let private_key = ECPrivateKey::new();
36/// ```
37///
38/// Signing a message with ECDSA:
39///
40/// ```
41/// use bc_components::ECPrivateKey;
42///
43/// // Generate a random private key
44/// let private_key = ECPrivateKey::new();
45///
46/// // Sign a message
47/// let message = b"Hello, world!";
48/// let signature = private_key.ecdsa_sign(message);
49/// ```
50#[derive(Clone, PartialEq, Eq, Hash)]
51pub struct ECPrivateKey([u8; ECDSA_PRIVATE_KEY_SIZE]);
52
53impl ECPrivateKey {
54    /// Creates a new random ECDSA private key.
55    ///
56    /// Uses a secure random number generator to generate the key.
57    pub fn new() -> Self {
58        let mut rng = SecureRandomNumberGenerator;
59        Self::new_using(&mut rng)
60    }
61
62    /// Creates a new random ECDSA private key using the given random number
63    /// generator.
64    ///
65    /// This allows for deterministic key generation when using a seeded RNG.
66    pub fn new_using(rng: &mut impl RandomNumberGenerator) -> Self {
67        let mut key = [0u8; ECDSA_PRIVATE_KEY_SIZE];
68        rng.fill_random_data(&mut key);
69        Self::from_data(key)
70    }
71
72    /// Returns the ECDSA private key as an array of bytes.
73    pub fn data(&self) -> &[u8; ECDSA_PRIVATE_KEY_SIZE] { &self.0 }
74
75    /// Get the ECDSA private key as a byte slice.
76    pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
77
78    /// Restores an ECDSA private key from an array of bytes.
79    ///
80    /// This method performs no validation on the input data.
81    pub const fn from_data(data: [u8; ECDSA_PRIVATE_KEY_SIZE]) -> Self {
82        Self(data)
83    }
84
85    /// Restores an ECDSA private key from a reference to an array of bytes.
86    ///
87    /// Returns an error if the data is not exactly 32 bytes.
88    pub fn from_data_ref(data: impl AsRef<[u8]>) -> Result<Self> {
89        let data = data.as_ref();
90        if data.len() != ECDSA_PRIVATE_KEY_SIZE {
91            bail!("Invalid EC private key size");
92        }
93        let mut arr = [0u8; ECDSA_PRIVATE_KEY_SIZE];
94        arr.copy_from_slice(data);
95        Ok(Self::from_data(arr))
96    }
97
98    /// Derives a new private key from the given key material.
99    ///
100    /// This method uses the provided key material to deterministically
101    /// generate a valid private key for the secp256k1 curve.
102    pub fn derive_from_key_material(key_material: impl AsRef<[u8]>) -> Self {
103        Self::from_data(bc_crypto::derive_signing_private_key(key_material))
104    }
105}
106
107impl ECPrivateKey {
108    /// Derives the Schnorr public key from this ECDSA private key.
109    ///
110    /// Schnorr public keys are used with the BIP-340 Schnorr signature scheme.
111    /// Unlike ECDSA public keys, Schnorr public keys are 32 bytes ("x-only")
112    /// rather than 33 bytes.
113    pub fn schnorr_public_key(&self) -> SchnorrPublicKey {
114        bc_crypto::schnorr_public_key_from_private_key(self.into()).into()
115    }
116
117    /// Signs a message using the ECDSA signature scheme.
118    ///
119    /// Returns a 70-72 byte signature in DER format.
120    pub fn ecdsa_sign(
121        &self,
122        message: impl AsRef<[u8]>,
123    ) -> [u8; bc_crypto::ECDSA_SIGNATURE_SIZE] {
124        bc_crypto::ecdsa_sign(&self.0, message.as_ref())
125    }
126
127    /// Signs a message using the Schnorr signature scheme with a custom random
128    /// number generator.
129    ///
130    /// This method implements the BIP-340 Schnorr signature scheme, which
131    /// provides several advantages over ECDSA including linearity (allowing
132    /// for signature aggregation) and non-malleability.
133    ///
134    /// Returns a 64-byte signature.
135    pub fn schnorr_sign_using(
136        &self,
137        message: impl AsRef<[u8]>,
138        rng: &mut dyn RandomNumberGenerator,
139    ) -> [u8; bc_crypto::SCHNORR_SIGNATURE_SIZE] {
140        bc_crypto::schnorr_sign_using(&self.0, message, rng)
141    }
142
143    /// Signs a message using the Schnorr signature scheme.
144    ///
145    /// Uses a secure random number generator for nonce generation.
146    ///
147    /// Returns a 64-byte signature.
148    pub fn schnorr_sign(
149        &self,
150        message: impl AsRef<[u8]>,
151    ) -> [u8; bc_crypto::SCHNORR_SIGNATURE_SIZE] {
152        let mut rng = SecureRandomNumberGenerator;
153        self.schnorr_sign_using(message, &mut rng)
154    }
155}
156
157/// Converts a fixed-size byte array to an `ECPrivateKey`.
158impl From<[u8; ECDSA_PRIVATE_KEY_SIZE]> for ECPrivateKey {
159    /// Converts a 32-byte array into an EC private key.
160    fn from(data: [u8; ECDSA_PRIVATE_KEY_SIZE]) -> Self {
161        Self::from_data(data)
162    }
163}
164
165/// Provides a reference to the key data as a byte slice.
166impl AsRef<[u8]> for ECPrivateKey {
167    /// Returns a reference to the key as a byte slice.
168    fn as_ref(&self) -> &[u8] { self.0.as_ref() }
169}
170
171/// Formats the key as a hexadecimal string.
172impl std::fmt::Display for ECPrivateKey {
173    /// Displays the key as a hexadecimal string.
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        write!(f, "{}", self.hex())
176    }
177}
178
179/// Formats the key for debugging, showing type name and hexadecimal value.
180impl std::fmt::Debug for ECPrivateKey {
181    /// Displays the key with type information and hexadecimal value.
182    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183        write!(f, "ECPrivateKey({})", self.hex())
184    }
185}
186
187/// Implements the `Default` trait, creating a random key.
188impl Default for ECPrivateKey {
189    /// Creates a new random key as the default value.
190    fn default() -> Self { Self::new() }
191}
192
193/// Converts a reference to an `ECPrivateKey` to a reference to a fixed-size
194/// byte array.
195impl<'a> From<&'a ECPrivateKey> for &'a [u8; ECDSA_PRIVATE_KEY_SIZE] {
196    /// Returns a reference to the underlying byte array.
197    fn from(value: &'a ECPrivateKey) -> Self { &value.0 }
198}
199
200/// Converts a reference to an `ECPrivateKey` to a reference to a byte slice.
201impl<'a> From<&'a ECPrivateKey> for &'a [u8] {
202    /// Returns a reference to the key as a byte slice.
203    fn from(value: &'a ECPrivateKey) -> Self { value.as_ref() }
204}
205
206/// Implements the `ECKeyBase` trait methods.
207impl ECKeyBase for ECPrivateKey {
208    /// The size of an EC private key (32 bytes).
209    const KEY_SIZE: usize = bc_crypto::ECDSA_PRIVATE_KEY_SIZE;
210
211    /// Creates a key from a byte slice, with validation.
212    fn from_data_ref(data: impl AsRef<[u8]>) -> Result<Self>
213    where
214        Self: Sized,
215    {
216        let data = data.as_ref();
217        if data.len() != ECDSA_PRIVATE_KEY_SIZE {
218            bail!("Invalid EC private key size");
219        }
220        let mut key = [0u8; ECDSA_PRIVATE_KEY_SIZE];
221        key.copy_from_slice(data);
222        Ok(Self(key))
223    }
224
225    /// Returns the key as a byte slice.
226    fn data(&self) -> &[u8] { self.0.as_ref() }
227}
228
229/// Implements the `ECKey` trait for deriving public keys.
230impl ECKey for ECPrivateKey {
231    /// Derives the corresponding ECDSA compressed public key.
232    fn public_key(&self) -> ECPublicKey {
233        bc_crypto::ecdsa_public_key_from_private_key(&self.0).into()
234    }
235}
236
237/// Defines CBOR tags for EC keys.
238impl CBORTagged for ECPrivateKey {
239    /// Returns the CBOR tags for EC keys.
240    fn cbor_tags() -> Vec<Tag> {
241        tags_for_values(&[tags::TAG_EC_KEY, tags::TAG_EC_KEY_V1])
242    }
243}
244
245/// Converts an `ECPrivateKey` to CBOR.
246impl From<ECPrivateKey> for CBOR {
247    /// Converts to tagged CBOR.
248    fn from(value: ECPrivateKey) -> Self { value.tagged_cbor() }
249}
250
251/// Implements CBOR encoding for EC private keys.
252impl CBORTaggedEncodable for ECPrivateKey {
253    /// Creates the untagged CBOR representation.
254    ///
255    /// The format is a map with:
256    /// - Key 2: boolean true (indicates private key)
257    /// - Key 3: byte string of the key data
258    fn untagged_cbor(&self) -> CBOR {
259        let mut m = Map::new();
260        m.insert(2, true);
261        m.insert(3, CBOR::to_byte_string(self.0));
262        m.into()
263    }
264}