1use bc_rand::{
2 RandomNumberGenerator, SecureRandomNumberGenerator, rng_random_data,
3};
4use bc_ur::prelude::*;
5use ssh_key::{
6 Algorithm as SSHAlgorithm,
7 private::{
8 DsaKeypair, EcdsaKeypair, Ed25519Keypair, KeypairData,
9 PrivateKey as SSHPrivateKey, RsaKeypair,
10 },
11};
12use zeroize::ZeroizeOnDrop;
13
14use crate::{
15 Decrypter, ECKey, ECPrivateKey, Ed25519PrivateKey, EncapsulationPrivateKey,
16 EncapsulationPublicKey, Error, HKDFRng, PrivateKeyDataProvider,
17 PrivateKeys, PrivateKeysProvider, PublicKeys, PublicKeysProvider, Result,
18 Signature, Signer, SigningOptions, SigningPrivateKey, Verifier,
19 X25519PrivateKey, tags,
20};
21
22#[derive(Clone, Eq, PartialEq, ZeroizeOnDrop)]
66pub struct PrivateKeyBase(Vec<u8>);
67
68impl Signer for PrivateKeyBase {
69 fn sign_with_options(
70 &self,
71 message: &dyn AsRef<[u8]>,
72 options: Option<SigningOptions>,
73 ) -> Result<Signature> {
74 let schnorr_key = self.schnorr_signing_private_key();
75 schnorr_key.sign_with_options(message, options)
76 }
77}
78
79impl Verifier for PrivateKeyBase {
80 fn verify(&self, signature: &Signature, message: &dyn AsRef<[u8]>) -> bool {
81 let schnorr_key = self
82 .schnorr_signing_private_key()
83 .to_schnorr()
84 .unwrap()
85 .public_key();
86 match signature.to_schnorr() {
87 Some(schnorr_signature) => {
88 schnorr_key.verify(schnorr_signature, message)
89 }
90 None => false,
91 }
92 }
93}
94
95impl Decrypter for PrivateKeyBase {
96 fn encapsulation_private_key(&self) -> EncapsulationPrivateKey {
97 EncapsulationPrivateKey::X25519(self.x25519_private_key())
98 }
99}
100
101impl PrivateKeyBase {
102 pub fn new() -> Self {
104 let mut rng = SecureRandomNumberGenerator;
105 Self::new_using(&mut rng)
106 }
107
108 pub fn from_data(data: impl AsRef<[u8]>) -> Self {
110 Self(data.as_ref().to_vec())
111 }
112
113 pub fn from_optional_data(data: Option<impl AsRef<[u8]>>) -> Self {
118 match data {
119 Some(data) => Self::from_data(data),
120 None => Self::new(),
121 }
122 }
123
124 pub fn new_using(rng: &mut impl RandomNumberGenerator) -> Self {
127 Self::from_data(rng_random_data(rng, 32))
128 }
129
130 pub fn new_with_provider(provider: impl PrivateKeyDataProvider) -> Self {
132 Self::from_data(provider.private_key_data())
133 }
134
135 pub fn ecdsa_signing_private_key(&self) -> SigningPrivateKey {
137 SigningPrivateKey::new_ecdsa(ECPrivateKey::derive_from_key_material(
138 &self.0,
139 ))
140 }
141
142 pub fn schnorr_signing_private_key(&self) -> SigningPrivateKey {
144 SigningPrivateKey::new_schnorr(ECPrivateKey::derive_from_key_material(
145 &self.0,
146 ))
147 }
148
149 pub fn ed25519_signing_private_key(&self) -> SigningPrivateKey {
151 SigningPrivateKey::new_ed25519(
152 Ed25519PrivateKey::derive_from_key_material(&self.0),
153 )
154 }
155
156 pub fn ssh_signing_private_key(
158 &self,
159 algorithm: SSHAlgorithm,
160 comment: impl Into<String>,
161 ) -> Result<SigningPrivateKey> {
162 let mut rng = HKDFRng::new(&self.0, algorithm.as_str());
163 let keypair = match algorithm {
164 SSHAlgorithm::Dsa => {
165 KeypairData::Dsa(DsaKeypair::random(&mut rng)?)
166 }
167 SSHAlgorithm::Ecdsa { curve } => {
168 KeypairData::Ecdsa(EcdsaKeypair::random(&mut rng, curve)?)
169 }
170 SSHAlgorithm::Ed25519 => {
171 KeypairData::Ed25519(Ed25519Keypair::random(&mut rng))
172 }
173 SSHAlgorithm::Rsa { hash: _ } => {
174 KeypairData::Rsa(RsaKeypair::random(&mut rng, 2048)?)
175 }
176 _ => {
177 return Err(Error::ssh(format!(
178 "Unsupported SSH algorithm: {}",
179 algorithm.as_str()
180 )));
181 }
182 };
183 let private_key = SSHPrivateKey::new(keypair, comment)?;
184 Ok(SigningPrivateKey::new_ssh(private_key))
185 }
186
187 pub fn x25519_private_key(&self) -> X25519PrivateKey {
191 X25519PrivateKey::derive_from_key_material(&self.0)
192 }
193
194 pub fn schnorr_private_keys(&self) -> PrivateKeys {
199 PrivateKeys::with_keys(
200 self.schnorr_signing_private_key(),
201 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
202 )
203 }
204
205 pub fn schnorr_public_keys(&self) -> PublicKeys {
210 PublicKeys::new(
211 self.schnorr_signing_private_key().public_key().unwrap(),
212 EncapsulationPublicKey::X25519(
213 self.x25519_private_key().public_key(),
214 ),
215 )
216 }
217
218 pub fn ecdsa_private_keys(&self) -> PrivateKeys {
223 PrivateKeys::with_keys(
224 self.ecdsa_signing_private_key(),
225 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
226 )
227 }
228
229 pub fn ecdsa_public_keys(&self) -> PublicKeys {
234 PublicKeys::new(
235 self.ecdsa_signing_private_key().public_key().unwrap(),
236 EncapsulationPublicKey::X25519(
237 self.x25519_private_key().public_key(),
238 ),
239 )
240 }
241
242 pub fn ssh_private_keys(
247 &self,
248 algorithm: SSHAlgorithm,
249 comment: impl Into<String>,
250 ) -> Result<PrivateKeys> {
251 let private_key = self.ssh_signing_private_key(algorithm, comment)?;
252 Ok(PrivateKeys::with_keys(
253 private_key,
254 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
255 ))
256 }
257
258 pub fn ssh_public_keys(
263 &self,
264 algorithm: SSHAlgorithm,
265 comment: impl Into<String>,
266 ) -> Result<PublicKeys> {
267 let private_key = self.ssh_signing_private_key(algorithm, comment)?;
268 Ok(PublicKeys::new(
269 private_key.public_key().unwrap(),
270 EncapsulationPublicKey::X25519(
271 self.x25519_private_key().public_key(),
272 ),
273 ))
274 }
275
276 pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
278}
279
280impl PrivateKeysProvider for PrivateKeyBase {
281 fn private_keys(&self) -> PrivateKeys {
282 PrivateKeys::with_keys(
283 self.schnorr_signing_private_key(),
284 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
285 )
286 }
287}
288
289impl PublicKeysProvider for PrivateKeyBase {
290 fn public_keys(&self) -> PublicKeys { self.schnorr_public_keys() }
291}
292
293impl Default for PrivateKeyBase {
294 fn default() -> Self { Self::new() }
295}
296
297impl std::fmt::Debug for PrivateKeyBase {
298 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299 write!(f, "PrivateKeyBase")
300 }
301}
302
303impl<'a> From<&'a PrivateKeyBase> for &'a [u8] {
304 fn from(value: &'a PrivateKeyBase) -> Self { &value.0 }
305}
306
307impl AsRef<PrivateKeyBase> for PrivateKeyBase {
308 fn as_ref(&self) -> &PrivateKeyBase { self }
309}
310
311impl AsRef<[u8]> for PrivateKeyBase {
312 fn as_ref(&self) -> &[u8] { &self.0 }
313}
314
315impl CBORTagged for PrivateKeyBase {
316 fn cbor_tags() -> Vec<Tag> {
317 tags_for_values(&[tags::TAG_PRIVATE_KEY_BASE])
318 }
319}
320
321impl From<PrivateKeyBase> for CBOR {
322 fn from(value: PrivateKeyBase) -> Self { value.tagged_cbor() }
323}
324
325impl CBORTaggedEncodable for PrivateKeyBase {
326 fn untagged_cbor(&self) -> CBOR { CBOR::to_byte_string(&self.0) }
327}
328
329impl TryFrom<CBOR> for PrivateKeyBase {
330 type Error = dcbor::Error;
331
332 fn try_from(cbor: CBOR) -> std::result::Result<Self, Self::Error> {
333 Self::from_tagged_cbor(cbor)
334 }
335}
336
337impl CBORTaggedDecodable for PrivateKeyBase {
338 fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
339 let data = CBOR::try_into_byte_string(untagged_cbor)?;
340 let instance = Self::from_data(data);
341 Ok(instance)
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use bc_ur::{URDecodable, UREncodable};
348 use hex_literal::hex;
349
350 use crate::PrivateKeyBase;
351
352 const SEED: [u8; 16] = hex!("59f2293a5bce7d4de59e71b4207ac5d2");
353
354 #[test]
355 fn test_private_key_base() {
356 crate::register_tags();
357 let private_key_base = PrivateKeyBase::from_data(SEED);
358 assert_eq!(
359 private_key_base
360 .ecdsa_signing_private_key()
361 .to_ecdsa()
362 .unwrap()
363 .data(),
364 &hex!(
365 "9505a44aaf385ce633cf0e2bc49e65cc88794213bdfbf8caf04150b9c4905f5a"
366 )
367 );
368 assert_eq!(
369 private_key_base
370 .schnorr_signing_private_key()
371 .public_key()
372 .unwrap()
373 .to_schnorr()
374 .unwrap()
375 .data(),
376 &hex!(
377 "fd4d22f9e8493da52d730aa402ac9e661deca099ef4db5503f519a73c3493e18"
378 )
379 );
380 assert_eq!(
381 private_key_base.x25519_private_key().data(),
382 &hex!(
383 "77ff838285a0403d3618aa8c30491f99f55221be0b944f50bfb371f43b897485"
384 )
385 );
386 assert_eq!(
387 private_key_base.x25519_private_key().public_key().data(),
388 &hex!(
389 "863cf3facee3ba45dc54e5eedecb21d791d64adfb0a1c63bfb6fea366c1ee62b"
390 )
391 );
392
393 let ur = private_key_base.ur_string();
394 assert_eq!(
395 ur,
396 "ur:crypto-prvkey-base/gdhkwzdtfthptokigtvwnnjsqzcxknsktdsfecsbbk"
397 );
398 assert_eq!(
399 PrivateKeyBase::from_ur_string(&ur).unwrap(),
400 private_key_base
401 );
402 }
403}