1use anyhow::{Result, bail};
2use bc_rand::{
3 RandomNumberGenerator, SecureRandomNumberGenerator, rng_random_data,
4};
5use bc_ur::prelude::*;
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
15use crate::{
16 Decrypter, ECKey, ECPrivateKey, Ed25519PrivateKey, EncapsulationPrivateKey,
17 EncapsulationPublicKey, HKDFRng, PrivateKeyDataProvider, PrivateKeys,
18 PrivateKeysProvider, PublicKeys, PublicKeysProvider, Signature, Signer,
19 SigningOptions, SigningPrivateKey, Verifier, 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 _ => bail!("Unsupported SSH algorithm: {:?}", algorithm.as_str()),
177 };
178 let private_key = SSHPrivateKey::new(keypair, comment)?;
179 Ok(SigningPrivateKey::new_ssh(private_key))
180 }
181
182 pub fn x25519_private_key(&self) -> X25519PrivateKey {
186 X25519PrivateKey::derive_from_key_material(&self.0)
187 }
188
189 pub fn schnorr_private_keys(&self) -> PrivateKeys {
194 PrivateKeys::with_keys(
195 self.schnorr_signing_private_key(),
196 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
197 )
198 }
199
200 pub fn schnorr_public_keys(&self) -> PublicKeys {
205 PublicKeys::new(
206 self.schnorr_signing_private_key().public_key().unwrap(),
207 EncapsulationPublicKey::X25519(
208 self.x25519_private_key().public_key(),
209 ),
210 )
211 }
212
213 pub fn ecdsa_private_keys(&self) -> PrivateKeys {
218 PrivateKeys::with_keys(
219 self.ecdsa_signing_private_key(),
220 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
221 )
222 }
223
224 pub fn ecdsa_public_keys(&self) -> PublicKeys {
229 PublicKeys::new(
230 self.ecdsa_signing_private_key().public_key().unwrap(),
231 EncapsulationPublicKey::X25519(
232 self.x25519_private_key().public_key(),
233 ),
234 )
235 }
236
237 pub fn ssh_private_keys(
242 &self,
243 algorithm: SSHAlgorithm,
244 comment: impl Into<String>,
245 ) -> Result<PrivateKeys> {
246 let private_key = self.ssh_signing_private_key(algorithm, comment)?;
247 Ok(PrivateKeys::with_keys(
248 private_key,
249 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
250 ))
251 }
252
253 pub fn ssh_public_keys(
258 &self,
259 algorithm: SSHAlgorithm,
260 comment: impl Into<String>,
261 ) -> Result<PublicKeys> {
262 let private_key = self.ssh_signing_private_key(algorithm, comment)?;
263 Ok(PublicKeys::new(
264 private_key.public_key().unwrap(),
265 EncapsulationPublicKey::X25519(
266 self.x25519_private_key().public_key(),
267 ),
268 ))
269 }
270
271 pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
273}
274
275impl PrivateKeysProvider for PrivateKeyBase {
276 fn private_keys(&self) -> PrivateKeys {
277 PrivateKeys::with_keys(
278 self.schnorr_signing_private_key(),
279 EncapsulationPrivateKey::X25519(self.x25519_private_key()),
280 )
281 }
282}
283
284impl PublicKeysProvider for PrivateKeyBase {
285 fn public_keys(&self) -> PublicKeys { self.schnorr_public_keys() }
286}
287
288impl Default for PrivateKeyBase {
289 fn default() -> Self { Self::new() }
290}
291
292impl std::fmt::Debug for PrivateKeyBase {
293 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294 write!(f, "PrivateKeyBase")
295 }
296}
297
298impl<'a> From<&'a PrivateKeyBase> for &'a [u8] {
299 fn from(value: &'a PrivateKeyBase) -> Self { &value.0 }
300}
301
302impl AsRef<PrivateKeyBase> for PrivateKeyBase {
303 fn as_ref(&self) -> &PrivateKeyBase { self }
304}
305
306impl AsRef<[u8]> for PrivateKeyBase {
307 fn as_ref(&self) -> &[u8] { &self.0 }
308}
309
310impl CBORTagged for PrivateKeyBase {
311 fn cbor_tags() -> Vec<Tag> {
312 tags_for_values(&[tags::TAG_PRIVATE_KEY_BASE])
313 }
314}
315
316impl From<PrivateKeyBase> for CBOR {
317 fn from(value: PrivateKeyBase) -> Self { value.tagged_cbor() }
318}
319
320impl CBORTaggedEncodable for PrivateKeyBase {
321 fn untagged_cbor(&self) -> CBOR { CBOR::to_byte_string(&self.0) }
322}
323
324impl TryFrom<CBOR> for PrivateKeyBase {
325 type Error = dcbor::Error;
326
327 fn try_from(cbor: CBOR) -> Result<Self, Self::Error> {
328 Self::from_tagged_cbor(cbor)
329 }
330}
331
332impl CBORTaggedDecodable for PrivateKeyBase {
333 fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
334 let data = CBOR::try_into_byte_string(untagged_cbor)?;
335 let instance = Self::from_data(data);
336 Ok(instance)
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use bc_ur::{URDecodable, UREncodable};
343 use hex_literal::hex;
344
345 use crate::PrivateKeyBase;
346
347 const SEED: [u8; 16] = hex!("59f2293a5bce7d4de59e71b4207ac5d2");
348
349 #[test]
350 fn test_private_key_base() {
351 crate::register_tags();
352 let private_key_base = PrivateKeyBase::from_data(SEED);
353 assert_eq!(
354 private_key_base
355 .ecdsa_signing_private_key()
356 .to_ecdsa()
357 .unwrap()
358 .data(),
359 &hex!(
360 "9505a44aaf385ce633cf0e2bc49e65cc88794213bdfbf8caf04150b9c4905f5a"
361 )
362 );
363 assert_eq!(
364 private_key_base
365 .schnorr_signing_private_key()
366 .public_key()
367 .unwrap()
368 .to_schnorr()
369 .unwrap()
370 .data(),
371 &hex!(
372 "fd4d22f9e8493da52d730aa402ac9e661deca099ef4db5503f519a73c3493e18"
373 )
374 );
375 assert_eq!(
376 private_key_base.x25519_private_key().data(),
377 &hex!(
378 "77ff838285a0403d3618aa8c30491f99f55221be0b944f50bfb371f43b897485"
379 )
380 );
381 assert_eq!(
382 private_key_base.x25519_private_key().public_key().data(),
383 &hex!(
384 "863cf3facee3ba45dc54e5eedecb21d791d64adfb0a1c63bfb6fea366c1ee62b"
385 )
386 );
387
388 let ur = private_key_base.ur_string();
389 assert_eq!(
390 ur,
391 "ur:crypto-prvkey-base/gdhkwzdtfthptokigtvwnnjsqzcxknsktdsfecsbbk"
392 );
393 assert_eq!(
394 PrivateKeyBase::from_ur_string(&ur).unwrap(),
395 private_key_base
396 );
397 }
398}