1use crate::entropy::{EntropyError, EntropySource};
2use secp256k1::SecretKey;
3use zeroize::{Zeroize, ZeroizeOnDrop};
4
5#[derive(Zeroize, ZeroizeOnDrop)]
13pub struct PrivateKey {
14 bytes: [u8; 32],
15}
16
17impl PrivateKey {
18 pub fn as_bytes(&self) -> &[u8; 32] {
20 &self.bytes
21 }
22
23 pub fn to_secret_key(&self) -> SecretKey {
26 SecretKey::from_slice(&self.bytes).expect("PrivateKey always holds a validated scalar")
27 }
28
29 #[cfg(test)]
33 pub(crate) fn from_bytes(bytes: [u8; 32]) -> Self {
34 Self { bytes }
35 }
36}
37
38pub fn is_valid_key(bytes: &[u8; 32]) -> bool {
42 SecretKey::from_slice(bytes).is_ok()
43}
44
45pub(crate) fn generate_with_entropy(
51 entropy: &dyn EntropySource,
52) -> Result<PrivateKey, EntropyError> {
53 for _ in 0..MAX_RETRIES {
54 let mut bytes = [0u8; 32];
55 entropy.fill_bytes(&mut bytes)?;
56
57 if is_valid_key(&bytes) {
58 return Ok(PrivateKey { bytes });
59 }
60 bytes.zeroize();
62 }
63
64 Err(EntropyError(
65 "failed to generate valid key after maximum retries".into(),
66 ))
67}
68
69pub fn generate() -> Result<PrivateKey, crate::Error> {
85 generate_with_entropy(&crate::entropy::OsEntropy).map_err(crate::Error::from)
86}
87
88const MAX_RETRIES: u32 = 32;
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95 use crate::entropy::{FailingEntropy, FixedEntropy};
96
97 const CURVE_ORDER: [u8; 32] = [
99 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
100 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36,
101 0x41, 0x41,
102 ];
103
104 fn curve_order_minus_one() -> [u8; 32] {
106 let mut bytes = CURVE_ORDER;
107 bytes[31] -= 1;
109 bytes
110 }
111
112 fn curve_order_plus_one() -> [u8; 32] {
114 let mut bytes = CURVE_ORDER;
115 bytes[31] += 1;
117 bytes
118 }
119
120 #[test]
125 fn test_zero_key_rejected() {
126 let zero = [0u8; 32];
127 assert!(!is_valid_key(&zero), "zero must not be a valid private key");
128 }
129
130 #[test]
131 fn test_one_key_valid() {
132 let mut one = [0u8; 32];
133 one[31] = 1;
134 assert!(is_valid_key(&one), "scalar 1 must be a valid private key");
135 }
136
137 #[test]
138 fn test_curve_order_minus_one_valid() {
139 let n_minus_1 = curve_order_minus_one();
140 assert!(
141 is_valid_key(&n_minus_1),
142 "n-1 must be a valid private key (maximum scalar)"
143 );
144 }
145
146 #[test]
147 fn test_curve_order_rejected() {
148 assert!(
149 !is_valid_key(&CURVE_ORDER),
150 "the curve order n itself must not be a valid private key"
151 );
152 }
153
154 #[test]
155 fn test_curve_order_plus_one_rejected() {
156 let n_plus_1 = curve_order_plus_one();
157 assert!(
158 !is_valid_key(&n_plus_1),
159 "n+1 must not be a valid private key"
160 );
161 }
162
163 #[test]
164 fn test_all_ff_rejected() {
165 let all_ff = [0xFF; 32];
166 assert!(
167 !is_valid_key(&all_ff),
168 "all 0xFF bytes exceed curve order and must be rejected"
169 );
170 }
171
172 #[test]
173 fn test_valid_midrange_key() {
174 let mut key = [0u8; 32];
176 key[0] = 0x0A;
177 key[31] = 0x0B;
178 assert!(is_valid_key(&key));
179 }
180
181 #[test]
186 fn test_fixed_entropy_produces_expected_key() {
187 let mut key_bytes = [0u8; 32];
188 key_bytes[31] = 0x01; let entropy = FixedEntropy::new(key_bytes.to_vec());
190
191 let key = generate_with_entropy(&entropy).expect("generation should succeed");
192 assert_eq!(key.as_bytes(), &key_bytes);
193 }
194
195 #[test]
196 fn test_different_entropy_produces_different_keys() {
197 let mut bytes_a = [0u8; 32];
198 bytes_a[31] = 0x01;
199 let mut bytes_b = [0u8; 32];
200 bytes_b[31] = 0x02;
201
202 let key_a = generate_with_entropy(&FixedEntropy::new(bytes_a.to_vec())).unwrap();
203 let key_b = generate_with_entropy(&FixedEntropy::new(bytes_b.to_vec())).unwrap();
204
205 assert_ne!(key_a.as_bytes(), key_b.as_bytes());
206 }
207
208 #[test]
209 fn test_same_entropy_produces_same_key() {
210 let mut key_bytes = [0u8; 32];
211 key_bytes[31] = 0x05;
212
213 let key1 = generate_with_entropy(&FixedEntropy::new(key_bytes.to_vec())).unwrap();
214 let key2 = generate_with_entropy(&FixedEntropy::new(key_bytes.to_vec())).unwrap();
215
216 assert_eq!(key1.as_bytes(), key2.as_bytes());
217 }
218
219 #[test]
220 fn test_invalid_entropy_triggers_retry() {
221 let mut data = CURVE_ORDER.to_vec();
224 let mut valid = [0u8; 32];
225 valid[31] = 0x01;
226 data.extend_from_slice(&valid);
227
228 let entropy = FixedEntropy::new(data);
229 let key = generate_with_entropy(&entropy).expect("should succeed after retry");
230 assert_eq!(key.as_bytes(), &valid);
231 }
232
233 #[test]
234 fn test_entropy_failure_propagates() {
235 let result = generate_with_entropy(&FailingEntropy);
236 assert!(result.is_err(), "entropy failure must propagate as error");
237 }
238
239 #[test]
240 fn test_generated_key_converts_to_secret_key() {
241 let mut key_bytes = [0u8; 32];
242 key_bytes[31] = 0x01;
243 let key = generate_with_entropy(&FixedEntropy::new(key_bytes.to_vec())).unwrap();
244
245 let _sk = key.to_secret_key();
247 }
248}