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}