1use bc_rand::{
2 RandomNumberGenerator, SecureRandomNumberGenerator, rng_random_data,
3};
4use bc_ur::prelude::*;
5#[cfg(feature = "ssh")]
6use ssh_key::{
7 Algorithm as SSHAlgorithm,
8 private::{
9 DsaKeypair, EcdsaKeypair, Ed25519Keypair, KeypairData,
10 PrivateKey as SSHPrivateKey, RsaKeypair,
11 },
12};
13use zeroize::ZeroizeOnDrop;
14
15#[cfg(feature = "ed25519")]
16use crate::Ed25519PrivateKey;
17#[cfg(any(feature = "secp256k1", feature = "ssh"))]
18use crate::Result;
19#[cfg(any(feature = "ed25519", feature = "secp256k1", feature = "ssh"))]
20use crate::SigningPrivateKey;
21use crate::{
22 Decrypter, Digest, EncapsulationPrivateKey, PrivateKeyDataProvider,
23 Reference, ReferenceProvider, X25519PrivateKey, tags,
24};
25#[cfg(feature = "secp256k1")]
26use crate::{ECKey, ECPrivateKey};
27#[cfg(any(feature = "secp256k1", feature = "ssh"))]
28use crate::{EncapsulationPublicKey, PrivateKeys, PublicKeys};
29#[cfg(feature = "ssh")]
30use crate::{Error, HKDFRng};
31#[cfg(feature = "secp256k1")]
32use crate::{
33 PrivateKeysProvider, PublicKeysProvider, Signature, Signer, SigningOptions,
34 Verifier,
35};
36
37#[derive(Clone, Eq, PartialEq, ZeroizeOnDrop)]
82pub struct PrivateKeyBase(Vec<u8>);
83
84#[cfg(feature = "secp256k1")]
85impl Signer for PrivateKeyBase {
86 fn sign_with_options(
87 &self,
88 message: &dyn AsRef<[u8]>,
89 options: Option<SigningOptions>,
90 ) -> Result<Signature> {
91 let schnorr_key = self.schnorr_signing_private_key();
92 schnorr_key.sign_with_options(message, options)
93 }
94}
95
96#[cfg(feature = "secp256k1")]
97impl Verifier for PrivateKeyBase {
98 fn verify(&self, signature: &Signature, message: &dyn AsRef<[u8]>) -> bool {
99 let schnorr_key = self
100 .schnorr_signing_private_key()
101 .to_schnorr()
102 .unwrap()
103 .public_key();
104 match signature.to_schnorr() {
105 Some(schnorr_signature) => {
106 schnorr_key.verify(schnorr_signature, message)
107 }
108 None => false,
109 }
110 }
111}
112
113impl Decrypter for PrivateKeyBase {
114 fn encapsulation_private_key(&self) -> EncapsulationPrivateKey {
115 EncapsulationPrivateKey::X25519(self.x25519_private_key())
116 }
117}
118
119impl PrivateKeyBase {
120 pub fn new() -> Self {
122 let mut rng = SecureRandomNumberGenerator;
123 Self::new_using(&mut rng)
124 }
125
126 pub fn from_data(data: impl AsRef<[u8]>) -> Self {
128 Self(data.as_ref().to_vec())
129 }
130
131 pub fn from_optional_data(data: Option<impl AsRef<[u8]>>) -> Self {
136 match data {
137 Some(data) => Self::from_data(data),
138 None => Self::new(),
139 }
140 }
141
142 pub fn new_using(rng: &mut impl RandomNumberGenerator) -> Self {
145 Self::from_data(rng_random_data(rng, 32))
146 }
147
148 pub fn new_with_provider(provider: impl PrivateKeyDataProvider) -> Self {
150 Self::from_data(provider.private_key_data())
151 }
152
153 #[cfg(feature = "secp256k1")]
155 pub fn ecdsa_signing_private_key(&self) -> SigningPrivateKey {
156 SigningPrivateKey::new_ecdsa(ECPrivateKey::derive_from_key_material(
157 &self.0,
158 ))
159 }
160
161 #[cfg(feature = "secp256k1")]
163 pub fn schnorr_signing_private_key(&self) -> SigningPrivateKey {
164 SigningPrivateKey::new_schnorr(ECPrivateKey::derive_from_key_material(
165 &self.0,
166 ))
167 }
168
169 #[cfg(feature = "ed25519")]
171 pub fn ed25519_signing_private_key(&self) -> SigningPrivateKey {
172 SigningPrivateKey::new_ed25519(
173 Ed25519PrivateKey::derive_from_key_material(&self.0),
174 )
175 }
176
177 #[cfg(feature = "ssh")]
179 pub fn ssh_signing_private_key(
180 &self,
181 algorithm: SSHAlgorithm,
182 comment: impl Into<String>,
183 ) -> Result<SigningPrivateKey> {
184 let mut rng = HKDFRng::new(&self.0, algorithm.as_str());
185 let keypair = match algorithm {
186 SSHAlgorithm::Dsa => {
187 KeypairData::Dsa(DsaKeypair::random(&mut rng)?)
188 }
189 SSHAlgorithm::Ecdsa { curve } => {
190 KeypairData::Ecdsa(EcdsaKeypair::random(&mut rng, curve)?)
191 }
192 SSHAlgorithm::Ed25519 => {
193 KeypairData::Ed25519(Ed25519Keypair::random(&mut rng))
194 }
195 SSHAlgorithm::Rsa { hash: _ } => {
196 KeypairData::Rsa(RsaKeypair::random(&mut rng, 2048)?)
197 }
198 _ => {
199 return Err(Error::ssh(format!(
200 "Unsupported SSH algorithm: {}",
201 algorithm.as_str()
202 )));
203 }
204 };
205 let private_key = SSHPrivateKey::new(keypair, comment)?;
206 Ok(SigningPrivateKey::new_ssh(private_key))
207 }
208
209 pub fn x25519_private_key(&self) -> X25519PrivateKey {
213 X25519PrivateKey::derive_from_key_material(&self.0)
214 }
215
216 #[cfg(feature = "secp256k1")]
221 pub fn schnorr_private_keys(&self) -> PrivateKeys {
222 PrivateKeys::with_keys(
223 self.schnorr_signing_private_key(),
224 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
225 )
226 }
227
228 #[cfg(feature = "secp256k1")]
233 pub fn schnorr_public_keys(&self) -> PublicKeys {
234 PublicKeys::new(
235 self.schnorr_signing_private_key().public_key().unwrap(),
236 EncapsulationPublicKey::X25519(
237 self.x25519_private_key().public_key(),
238 ),
239 )
240 }
241
242 #[cfg(feature = "secp256k1")]
247 pub fn ecdsa_private_keys(&self) -> PrivateKeys {
248 PrivateKeys::with_keys(
249 self.ecdsa_signing_private_key(),
250 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
251 )
252 }
253
254 #[cfg(feature = "secp256k1")]
259 pub fn ecdsa_public_keys(&self) -> PublicKeys {
260 PublicKeys::new(
261 self.ecdsa_signing_private_key().public_key().unwrap(),
262 EncapsulationPublicKey::X25519(
263 self.x25519_private_key().public_key(),
264 ),
265 )
266 }
267
268 #[cfg(feature = "ssh")]
273 pub fn ssh_private_keys(
274 &self,
275 algorithm: SSHAlgorithm,
276 comment: impl Into<String>,
277 ) -> Result<PrivateKeys> {
278 let private_key = self.ssh_signing_private_key(algorithm, comment)?;
279 Ok(PrivateKeys::with_keys(
280 private_key,
281 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
282 ))
283 }
284
285 #[cfg(feature = "ssh")]
290 pub fn ssh_public_keys(
291 &self,
292 algorithm: SSHAlgorithm,
293 comment: impl Into<String>,
294 ) -> Result<PublicKeys> {
295 let private_key = self.ssh_signing_private_key(algorithm, comment)?;
296 Ok(PublicKeys::new(
297 private_key.public_key().unwrap(),
298 EncapsulationPublicKey::X25519(
299 self.x25519_private_key().public_key(),
300 ),
301 ))
302 }
303
304 pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
306}
307
308#[cfg(feature = "secp256k1")]
309impl PrivateKeysProvider for PrivateKeyBase {
310 fn private_keys(&self) -> PrivateKeys {
311 PrivateKeys::with_keys(
312 self.schnorr_signing_private_key(),
313 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
314 )
315 }
316}
317
318#[cfg(feature = "secp256k1")]
319impl PublicKeysProvider for PrivateKeyBase {
320 fn public_keys(&self) -> PublicKeys { self.schnorr_public_keys() }
321}
322
323impl Default for PrivateKeyBase {
324 fn default() -> Self { Self::new() }
325}
326
327impl std::fmt::Debug for PrivateKeyBase {
328 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329 write!(f, "PrivateKeyBase")
330 }
331}
332
333impl<'a> From<&'a PrivateKeyBase> for &'a [u8] {
334 fn from(value: &'a PrivateKeyBase) -> Self { &value.0 }
335}
336
337impl AsRef<PrivateKeyBase> for PrivateKeyBase {
338 fn as_ref(&self) -> &PrivateKeyBase { self }
339}
340
341impl AsRef<[u8]> for PrivateKeyBase {
342 fn as_ref(&self) -> &[u8] { &self.0 }
343}
344
345impl CBORTagged for PrivateKeyBase {
346 fn cbor_tags() -> Vec<Tag> {
347 tags_for_values(&[tags::TAG_PRIVATE_KEY_BASE])
348 }
349}
350
351impl From<PrivateKeyBase> for CBOR {
352 fn from(value: PrivateKeyBase) -> Self { value.tagged_cbor() }
353}
354
355impl CBORTaggedEncodable for PrivateKeyBase {
356 fn untagged_cbor(&self) -> CBOR { CBOR::to_byte_string(&self.0) }
357}
358
359impl TryFrom<CBOR> for PrivateKeyBase {
360 type Error = dcbor::Error;
361
362 fn try_from(cbor: CBOR) -> std::result::Result<Self, Self::Error> {
363 Self::from_tagged_cbor(cbor)
364 }
365}
366
367impl CBORTaggedDecodable for PrivateKeyBase {
368 fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
369 let data = CBOR::try_into_byte_string(untagged_cbor)?;
370 let instance = Self::from_data(data);
371 Ok(instance)
372 }
373}
374
375impl ReferenceProvider for PrivateKeyBase {
376 fn reference(&self) -> Reference {
377 Reference::from_digest(Digest::from_image(
378 self.tagged_cbor().to_cbor_data(),
379 ))
380 }
381}
382
383impl std::fmt::Display for PrivateKeyBase {
384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
385 write!(f, "PrivateKeyBase({})", self.reference().ref_hex_short())
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 #[cfg(feature = "secp256k1")]
392 use bc_ur::{URDecodable, UREncodable};
393 #[cfg(feature = "secp256k1")]
394 use hex_literal::hex;
395
396 #[cfg(feature = "secp256k1")]
397 use crate::PrivateKeyBase;
398
399 #[cfg(feature = "secp256k1")]
400 const SEED: [u8; 16] = hex!("59f2293a5bce7d4de59e71b4207ac5d2");
401
402 #[test]
403 #[cfg(feature = "secp256k1")]
404 fn test_private_key_base() {
405 crate::register_tags();
406 let private_key_base = PrivateKeyBase::from_data(SEED);
407 assert_eq!(
408 private_key_base
409 .ecdsa_signing_private_key()
410 .to_ecdsa()
411 .unwrap()
412 .data(),
413 &hex!(
414 "9505a44aaf385ce633cf0e2bc49e65cc88794213bdfbf8caf04150b9c4905f5a"
415 )
416 );
417 assert_eq!(
418 private_key_base
419 .schnorr_signing_private_key()
420 .public_key()
421 .unwrap()
422 .to_schnorr()
423 .unwrap()
424 .data(),
425 &hex!(
426 "fd4d22f9e8493da52d730aa402ac9e661deca099ef4db5503f519a73c3493e18"
427 )
428 );
429 assert_eq!(
430 private_key_base.x25519_private_key().data(),
431 &hex!(
432 "77ff838285a0403d3618aa8c30491f99f55221be0b944f50bfb371f43b897485"
433 )
434 );
435 assert_eq!(
436 private_key_base.x25519_private_key().public_key().data(),
437 &hex!(
438 "863cf3facee3ba45dc54e5eedecb21d791d64adfb0a1c63bfb6fea366c1ee62b"
439 )
440 );
441
442 let ur = private_key_base.ur_string();
443 assert_eq!(
444 ur,
445 "ur:crypto-prvkey-base/gdhkwzdtfthptokigtvwnnjsqzcxknsktdsfecsbbk"
446 );
447 assert_eq!(
448 PrivateKeyBase::from_ur_string(&ur).unwrap(),
449 private_key_base
450 );
451 }
452}