use bc_rand::{
RandomNumberGenerator, SecureRandomNumberGenerator, rng_random_data,
};
use bc_ur::prelude::*;
#[cfg(feature = "ssh")]
use ssh_key::{
Algorithm as SSHAlgorithm,
private::{
DsaKeypair, EcdsaKeypair, Ed25519Keypair, KeypairData,
PrivateKey as SSHPrivateKey, RsaKeypair,
},
};
use zeroize::ZeroizeOnDrop;
#[cfg(feature = "ed25519")]
use crate::Ed25519PrivateKey;
#[cfg(any(feature = "secp256k1", feature = "ssh"))]
use crate::Result;
#[cfg(any(feature = "ed25519", feature = "secp256k1", feature = "ssh"))]
use crate::SigningPrivateKey;
use crate::{
Decrypter, Digest, EncapsulationPrivateKey, PrivateKeyDataProvider,
Reference, ReferenceProvider, X25519PrivateKey, tags,
};
#[cfg(feature = "secp256k1")]
use crate::{ECKey, ECPrivateKey};
#[cfg(any(feature = "secp256k1", feature = "ssh"))]
use crate::{EncapsulationPublicKey, PrivateKeys, PublicKeys};
#[cfg(feature = "ssh")]
use crate::{Error, HKDFRng};
#[cfg(feature = "secp256k1")]
use crate::{
PrivateKeysProvider, PublicKeysProvider, Signature, Signer, SigningOptions,
Verifier,
};
#[derive(Clone, Eq, PartialEq, ZeroizeOnDrop)]
pub struct PrivateKeyBase(Vec<u8>);
#[cfg(feature = "secp256k1")]
impl Signer for PrivateKeyBase {
fn sign_with_options(
&self,
message: &dyn AsRef<[u8]>,
options: Option<SigningOptions>,
) -> Result<Signature> {
let schnorr_key = self.schnorr_signing_private_key();
schnorr_key.sign_with_options(message, options)
}
}
#[cfg(feature = "secp256k1")]
impl Verifier for PrivateKeyBase {
fn verify(&self, signature: &Signature, message: &dyn AsRef<[u8]>) -> bool {
let schnorr_key = self
.schnorr_signing_private_key()
.to_schnorr()
.unwrap()
.public_key();
match signature.to_schnorr() {
Some(schnorr_signature) => {
schnorr_key.verify(schnorr_signature, message)
}
None => false,
}
}
}
impl Decrypter for PrivateKeyBase {
fn encapsulation_private_key(&self) -> EncapsulationPrivateKey {
EncapsulationPrivateKey::X25519(self.x25519_private_key())
}
}
impl PrivateKeyBase {
pub fn new() -> Self {
let mut rng = SecureRandomNumberGenerator;
Self::new_using(&mut rng)
}
pub fn from_data(data: impl AsRef<[u8]>) -> Self {
Self(data.as_ref().to_vec())
}
pub fn from_optional_data(data: Option<impl AsRef<[u8]>>) -> Self {
match data {
Some(data) => Self::from_data(data),
None => Self::new(),
}
}
pub fn new_using(rng: &mut impl RandomNumberGenerator) -> Self {
Self::from_data(rng_random_data(rng, 32))
}
pub fn new_with_provider(provider: impl PrivateKeyDataProvider) -> Self {
Self::from_data(provider.private_key_data())
}
#[cfg(feature = "secp256k1")]
pub fn ecdsa_signing_private_key(&self) -> SigningPrivateKey {
SigningPrivateKey::new_ecdsa(ECPrivateKey::derive_from_key_material(
&self.0,
))
}
#[cfg(feature = "secp256k1")]
pub fn schnorr_signing_private_key(&self) -> SigningPrivateKey {
SigningPrivateKey::new_schnorr(ECPrivateKey::derive_from_key_material(
&self.0,
))
}
#[cfg(feature = "ed25519")]
pub fn ed25519_signing_private_key(&self) -> SigningPrivateKey {
SigningPrivateKey::new_ed25519(
Ed25519PrivateKey::derive_from_key_material(&self.0),
)
}
#[cfg(feature = "ssh")]
pub fn ssh_signing_private_key(
&self,
algorithm: SSHAlgorithm,
comment: impl Into<String>,
) -> Result<SigningPrivateKey> {
let mut rng = HKDFRng::new(&self.0, algorithm.as_str());
let keypair = match algorithm {
SSHAlgorithm::Dsa => {
KeypairData::Dsa(DsaKeypair::random(&mut rng)?)
}
SSHAlgorithm::Ecdsa { curve } => {
KeypairData::Ecdsa(EcdsaKeypair::random(&mut rng, curve)?)
}
SSHAlgorithm::Ed25519 => {
KeypairData::Ed25519(Ed25519Keypair::random(&mut rng))
}
SSHAlgorithm::Rsa { hash: _ } => {
KeypairData::Rsa(RsaKeypair::random(&mut rng, 2048)?)
}
_ => {
return Err(Error::ssh(format!(
"Unsupported SSH algorithm: {}",
algorithm.as_str()
)));
}
};
let private_key = SSHPrivateKey::new(keypair, comment)?;
Ok(SigningPrivateKey::new_ssh(private_key))
}
pub fn x25519_private_key(&self) -> X25519PrivateKey {
X25519PrivateKey::derive_from_key_material(&self.0)
}
#[cfg(feature = "secp256k1")]
pub fn schnorr_private_keys(&self) -> PrivateKeys {
PrivateKeys::with_keys(
self.schnorr_signing_private_key(),
EncapsulationPrivateKey::X25519(self.x25519_private_key()),
)
}
#[cfg(feature = "secp256k1")]
pub fn schnorr_public_keys(&self) -> PublicKeys {
PublicKeys::new(
self.schnorr_signing_private_key().public_key().unwrap(),
EncapsulationPublicKey::X25519(
self.x25519_private_key().public_key(),
),
)
}
#[cfg(feature = "secp256k1")]
pub fn ecdsa_private_keys(&self) -> PrivateKeys {
PrivateKeys::with_keys(
self.ecdsa_signing_private_key(),
EncapsulationPrivateKey::X25519(self.x25519_private_key()),
)
}
#[cfg(feature = "secp256k1")]
pub fn ecdsa_public_keys(&self) -> PublicKeys {
PublicKeys::new(
self.ecdsa_signing_private_key().public_key().unwrap(),
EncapsulationPublicKey::X25519(
self.x25519_private_key().public_key(),
),
)
}
#[cfg(feature = "ssh")]
pub fn ssh_private_keys(
&self,
algorithm: SSHAlgorithm,
comment: impl Into<String>,
) -> Result<PrivateKeys> {
let private_key = self.ssh_signing_private_key(algorithm, comment)?;
Ok(PrivateKeys::with_keys(
private_key,
EncapsulationPrivateKey::X25519(self.x25519_private_key()),
))
}
#[cfg(feature = "ssh")]
pub fn ssh_public_keys(
&self,
algorithm: SSHAlgorithm,
comment: impl Into<String>,
) -> Result<PublicKeys> {
let private_key = self.ssh_signing_private_key(algorithm, comment)?;
Ok(PublicKeys::new(
private_key.public_key().unwrap(),
EncapsulationPublicKey::X25519(
self.x25519_private_key().public_key(),
),
))
}
pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
}
#[cfg(feature = "secp256k1")]
impl PrivateKeysProvider for PrivateKeyBase {
fn private_keys(&self) -> PrivateKeys {
PrivateKeys::with_keys(
self.schnorr_signing_private_key(),
EncapsulationPrivateKey::X25519(self.x25519_private_key()),
)
}
}
#[cfg(feature = "secp256k1")]
impl PublicKeysProvider for PrivateKeyBase {
fn public_keys(&self) -> PublicKeys { self.schnorr_public_keys() }
}
impl Default for PrivateKeyBase {
fn default() -> Self { Self::new() }
}
impl std::fmt::Debug for PrivateKeyBase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PrivateKeyBase")
}
}
impl<'a> From<&'a PrivateKeyBase> for &'a [u8] {
fn from(value: &'a PrivateKeyBase) -> Self { &value.0 }
}
impl AsRef<PrivateKeyBase> for PrivateKeyBase {
fn as_ref(&self) -> &PrivateKeyBase { self }
}
impl AsRef<[u8]> for PrivateKeyBase {
fn as_ref(&self) -> &[u8] { &self.0 }
}
impl CBORTagged for PrivateKeyBase {
fn cbor_tags() -> Vec<Tag> {
tags_for_values(&[tags::TAG_PRIVATE_KEY_BASE])
}
}
impl From<PrivateKeyBase> for CBOR {
fn from(value: PrivateKeyBase) -> Self { value.tagged_cbor() }
}
impl CBORTaggedEncodable for PrivateKeyBase {
fn untagged_cbor(&self) -> CBOR { CBOR::to_byte_string(&self.0) }
}
impl TryFrom<CBOR> for PrivateKeyBase {
type Error = dcbor::Error;
fn try_from(cbor: CBOR) -> std::result::Result<Self, Self::Error> {
Self::from_tagged_cbor(cbor)
}
}
impl CBORTaggedDecodable for PrivateKeyBase {
fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
let data = CBOR::try_into_byte_string(untagged_cbor)?;
let instance = Self::from_data(data);
Ok(instance)
}
}
impl ReferenceProvider for PrivateKeyBase {
fn reference(&self) -> Reference {
Reference::from_digest(Digest::from_image(
self.tagged_cbor().to_cbor_data(),
))
}
}
impl std::fmt::Display for PrivateKeyBase {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PrivateKeyBase({})", self.reference().ref_hex_short())
}
}
#[cfg(test)]
mod tests {
#[cfg(feature = "secp256k1")]
use bc_ur::{URDecodable, UREncodable};
#[cfg(feature = "secp256k1")]
use hex_literal::hex;
#[cfg(feature = "secp256k1")]
use crate::PrivateKeyBase;
#[cfg(feature = "secp256k1")]
const SEED: [u8; 16] = hex!("59f2293a5bce7d4de59e71b4207ac5d2");
#[test]
#[cfg(feature = "secp256k1")]
fn test_private_key_base() {
crate::register_tags();
let private_key_base = PrivateKeyBase::from_data(SEED);
assert_eq!(
private_key_base
.ecdsa_signing_private_key()
.to_ecdsa()
.unwrap()
.data(),
&hex!(
"9505a44aaf385ce633cf0e2bc49e65cc88794213bdfbf8caf04150b9c4905f5a"
)
);
assert_eq!(
private_key_base
.schnorr_signing_private_key()
.public_key()
.unwrap()
.to_schnorr()
.unwrap()
.data(),
&hex!(
"fd4d22f9e8493da52d730aa402ac9e661deca099ef4db5503f519a73c3493e18"
)
);
assert_eq!(
private_key_base.x25519_private_key().data(),
&hex!(
"77ff838285a0403d3618aa8c30491f99f55221be0b944f50bfb371f43b897485"
)
);
assert_eq!(
private_key_base.x25519_private_key().public_key().data(),
&hex!(
"863cf3facee3ba45dc54e5eedecb21d791d64adfb0a1c63bfb6fea366c1ee62b"
)
);
let ur = private_key_base.ur_string();
assert_eq!(
ur,
"ur:crypto-prvkey-base/gdhkwzdtfthptokigtvwnnjsqzcxknsktdsfecsbbk"
);
assert_eq!(
PrivateKeyBase::from_ur_string(&ur).unwrap(),
private_key_base
);
}
}