use downcast_rs::{impl_downcast, Downcast};
use rand::RngCore;
use ssh_key::{
private::{Ed25519Keypair, Ed25519PrivateKey, KeypairData, OpaqueKeypair},
public::{Ed25519PublicKey, KeyData, OpaquePublicKey},
rand_core::CryptoRng,
Algorithm, AlgorithmName,
};
use tor_error::internal;
use tor_llcrypto::pk::{curve25519, ed25519};
use crate::certs::CertData;
use crate::key_type::CertType;
use crate::{
ssh::{SshKeyData, ED25519_EXPANDED_ALGORITHM_NAME, X25519_ALGORITHM_NAME},
ErasedKey, KeyType, KeystoreItemType, Result,
};
use std::result::Result as StdResult;
pub trait KeygenRng: RngCore + CryptoRng {}
impl<T> KeygenRng for T where T: RngCore + CryptoRng {}
pub trait Keygen {
fn generate(rng: &mut dyn KeygenRng) -> Result<Self>
where
Self: Sized;
}
pub trait ItemType: Downcast {
fn item_type() -> KeystoreItemType
where
Self: Sized;
}
impl_downcast!(ItemType);
pub trait EncodableItem: ItemType + Downcast {
fn as_keystore_item(&self) -> Result<KeystoreItem>;
}
impl_downcast!(EncodableItem);
#[derive(Debug, Clone, derive_more::From)]
#[non_exhaustive]
pub enum KeystoreItem {
Key(SshKeyData),
Cert(CertData),
}
impl KeystoreItem {
pub fn item_type(&self) -> Result<KeystoreItemType> {
match self {
KeystoreItem::Key(ssh_key_data) => ssh_key_data.key_type().map(KeystoreItemType::Key),
KeystoreItem::Cert(cert) => Ok(KeystoreItemType::Cert(cert.cert_type())),
}
}
pub fn into_erased(self) -> Result<ErasedKey> {
match self {
KeystoreItem::Key(ssh_key_data) => ssh_key_data.into_erased(),
KeystoreItem::Cert(cert_data) => cert_data.into_erased(),
}
}
}
pub trait ToEncodableKey: From<Self::KeyPair>
where
Self::Key: From<<Self::KeyPair as ToEncodableKey>::Key>,
{
type Key: EncodableItem + 'static;
type KeyPair: ToEncodableKey;
fn to_encodable_key(self) -> Self::Key;
fn from_encodable_key(key: Self::Key) -> Self;
}
pub trait ToEncodableCert<K: ToEncodableKey>: Clone {
type ParsedCert: ItemType + 'static;
type EncodableCert: EncodableItem + 'static;
type SigningKey: ToEncodableKey;
fn validate(
cert: Self::ParsedCert,
subject: &K,
signed_with: &Self::SigningKey,
) -> StdResult<Self, InvalidCertError>;
fn to_encodable_cert(self) -> Self::EncodableCert;
}
#[derive(thiserror::Error, Debug, Clone)]
#[non_exhaustive]
pub enum InvalidCertError {
#[error("Invalid signature")]
CertSignature(#[from] tor_cert::CertError),
#[error("Certificate is expired or not yet valid")]
TimeValidity(#[from] tor_checkable::TimeValidityError),
#[error("Unexpected subject key algorithm")]
InvalidSubjectKeyAlgorithm,
#[error("Certificate certifies the wrong key")]
SubjectKeyMismatch,
#[error("Unexpected cert type")]
CertType(tor_cert::CertType),
}
impl Keygen for curve25519::StaticKeypair {
fn generate(rng: &mut dyn KeygenRng) -> Result<Self>
where
Self: Sized,
{
let secret = curve25519::StaticSecret::random_from_rng(rng);
let public = curve25519::PublicKey::from(&secret);
Ok(curve25519::StaticKeypair { secret, public })
}
}
impl ItemType for curve25519::StaticKeypair {
fn item_type() -> KeystoreItemType
where
Self: Sized,
{
KeyType::X25519StaticKeypair.into()
}
}
impl EncodableItem for curve25519::StaticKeypair {
fn as_keystore_item(&self) -> Result<KeystoreItem> {
let algorithm_name = AlgorithmName::new(X25519_ALGORITHM_NAME)
.map_err(|_| internal!("invalid algorithm name"))?;
let ssh_public = OpaquePublicKey::new(
self.public.to_bytes().to_vec(),
Algorithm::Other(algorithm_name),
);
let keypair = OpaqueKeypair::new(self.secret.to_bytes().to_vec(), ssh_public);
SshKeyData::try_from_keypair_data(KeypairData::Other(keypair)).map(KeystoreItem::from)
}
}
impl ItemType for curve25519::PublicKey {
fn item_type() -> KeystoreItemType
where
Self: Sized,
{
KeyType::X25519PublicKey.into()
}
}
impl EncodableItem for curve25519::PublicKey {
fn as_keystore_item(&self) -> Result<KeystoreItem> {
let algorithm_name = AlgorithmName::new(X25519_ALGORITHM_NAME)
.map_err(|_| internal!("invalid algorithm name"))?;
let ssh_public =
OpaquePublicKey::new(self.to_bytes().to_vec(), Algorithm::Other(algorithm_name));
SshKeyData::try_from_key_data(KeyData::Other(ssh_public)).map(KeystoreItem::from)
}
}
impl Keygen for ed25519::Keypair {
fn generate(mut rng: &mut dyn KeygenRng) -> Result<Self>
where
Self: Sized,
{
Ok(ed25519::Keypair::generate(&mut rng))
}
}
impl ItemType for ed25519::Keypair {
fn item_type() -> KeystoreItemType
where
Self: Sized,
{
KeyType::Ed25519Keypair.into()
}
}
impl EncodableItem for ed25519::Keypair {
fn as_keystore_item(&self) -> Result<KeystoreItem> {
let keypair = Ed25519Keypair {
public: Ed25519PublicKey(self.verifying_key().to_bytes()),
private: Ed25519PrivateKey::from_bytes(self.as_bytes()),
};
SshKeyData::try_from_keypair_data(KeypairData::Ed25519(keypair)).map(KeystoreItem::from)
}
}
impl ItemType for ed25519::PublicKey {
fn item_type() -> KeystoreItemType
where
Self: Sized,
{
KeyType::Ed25519PublicKey.into()
}
}
impl EncodableItem for ed25519::PublicKey {
fn as_keystore_item(&self) -> Result<KeystoreItem> {
let key_data = Ed25519PublicKey(self.to_bytes());
SshKeyData::try_from_key_data(ssh_key::public::KeyData::Ed25519(key_data))
.map(KeystoreItem::from)
}
}
impl Keygen for ed25519::ExpandedKeypair {
fn generate(rng: &mut dyn KeygenRng) -> Result<Self>
where
Self: Sized,
{
let keypair = <ed25519::Keypair as Keygen>::generate(rng)?;
Ok((&keypair).into())
}
}
impl ItemType for ed25519::ExpandedKeypair {
fn item_type() -> KeystoreItemType
where
Self: Sized,
{
KeyType::Ed25519ExpandedKeypair.into()
}
}
impl EncodableItem for ed25519::ExpandedKeypair {
fn as_keystore_item(&self) -> Result<KeystoreItem> {
let algorithm_name = AlgorithmName::new(ED25519_EXPANDED_ALGORITHM_NAME)
.map_err(|_| internal!("invalid algorithm name"))?;
let ssh_public = OpaquePublicKey::new(
self.public().to_bytes().to_vec(),
Algorithm::Other(algorithm_name),
);
let keypair = OpaqueKeypair::new(self.to_secret_key_bytes().to_vec(), ssh_public);
SshKeyData::try_from_keypair_data(KeypairData::Other(keypair)).map(KeystoreItem::from)
}
}
impl ItemType for crate::EncodedEd25519Cert {
fn item_type() -> KeystoreItemType
where
Self: Sized,
{
CertType::Ed25519TorCert.into()
}
}
impl ItemType for crate::ParsedEd25519Cert {
fn item_type() -> KeystoreItemType
where
Self: Sized,
{
CertType::Ed25519TorCert.into()
}
}
impl EncodableItem for crate::EncodedEd25519Cert {
fn as_keystore_item(&self) -> Result<KeystoreItem> {
Ok(CertData::TorEd25519Cert(self.clone()).into())
}
}