pub(crate) mod arti;
pub(crate) mod ephemeral;
use rand::{CryptoRng, RngCore};
use ssh_key::private::{Ed25519Keypair, Ed25519PrivateKey, KeypairData, OpaqueKeypair};
use ssh_key::public::{Ed25519PublicKey, KeyData, OpaquePublicKey};
use ssh_key::{Algorithm, AlgorithmName, LineEnding, PrivateKey, PublicKey};
use tor_error::{internal, into_internal};
use tor_hscrypto::pk::{
HsBlindIdKey, HsBlindIdKeypair, HsClientDescEncKeypair, HsDescSigningKeypair, HsIdKey,
HsIdKeypair, HsIntroPtSessionIdKeypair, HsSvcNtorKeypair,
};
use tor_llcrypto::pk::{curve25519, ed25519};
use crate::key_type::KeyType;
use crate::ssh::{SshKeyAlgorithm, ED25519_EXPANDED_ALGORITHM_NAME, X25519_ALGORITHM_NAME};
use crate::{Error, KeyPath, KeySpecifier, KeystoreId, Result};
use downcast_rs::{impl_downcast, Downcast};
pub type ErasedKey = Box<dyn EncodableKey>;
pub trait KeygenRng: RngCore + CryptoRng {}
impl<T> KeygenRng for T where T: RngCore + CryptoRng {}
pub trait Keystore: Send + Sync + 'static {
fn id(&self) -> &KeystoreId;
fn contains(&self, key_spec: &dyn KeySpecifier, key_type: &KeyType) -> Result<bool>;
fn get(&self, key_spec: &dyn KeySpecifier, key_type: &KeyType) -> Result<Option<ErasedKey>>;
fn insert(
&self,
key: &dyn EncodableKey,
key_spec: &dyn KeySpecifier,
key_type: &KeyType,
) -> Result<()>;
fn remove(&self, key_spec: &dyn KeySpecifier, key_type: &KeyType) -> Result<Option<()>>;
fn list(&self) -> Result<Vec<(KeyPath, KeyType)>>;
}
pub trait Keygen {
fn generate(rng: &mut dyn KeygenRng) -> Result<Self>
where
Self: Sized;
}
macro_rules! ssh_to_internal_erased {
(PRIVATE $key:expr, $algo:expr) => {{
ssh_to_internal_erased!(
$key,
$algo,
convert_ed25519_kp,
convert_expanded_ed25519_kp,
convert_x25519_kp,
KeypairData
)
}};
(PUBLIC $key:expr, $algo:expr) => {{
ssh_to_internal_erased!(
$key,
$algo,
convert_ed25519_pk,
convert_expanded_ed25519_pk,
convert_x25519_pk,
KeyData
)
}};
($key:expr, $algo:expr, $ed25519_fn:path, $expanded_ed25519_fn:path, $x25519_fn:path, $key_data_ty:tt) => {{
let key = $key;
let algo = SshKeyAlgorithm::from($algo);
match key {
$key_data_ty::Ed25519(key) => Ok($ed25519_fn(&key).map(Box::new)?),
$key_data_ty::Other(other) => match algo {
SshKeyAlgorithm::X25519 => Ok($x25519_fn(&other).map(Box::new)?),
SshKeyAlgorithm::Ed25519Expanded => Ok($expanded_ed25519_fn(&other).map(Box::new)?),
_ => Err(Error::UnsupportedKeyAlgorithm(algo)),
},
_ => Err(Error::UnsupportedKeyAlgorithm(algo)),
}
}};
}
#[allow(clippy::unnecessary_fallible_conversions)]
fn convert_ed25519_kp(key: &ssh_key::private::Ed25519Keypair) -> Result<ed25519::Keypair> {
Ok(ed25519::Keypair::try_from(&key.private.to_bytes())
.map_err(|_| internal!("bad ed25519 keypair"))?)
}
fn convert_x25519_kp(key: &ssh_key::private::OpaqueKeypair) -> Result<curve25519::StaticKeypair> {
let public: [u8; 32] = key
.public
.as_ref()
.try_into()
.map_err(|_| internal!("bad x25519 public key length"))?;
let secret: [u8; 32] = key
.private
.as_ref()
.try_into()
.map_err(|_| internal!("bad x25519 secret key length"))?;
Ok(curve25519::StaticKeypair {
public: public.into(),
secret: secret.into(),
})
}
fn convert_expanded_ed25519_kp(
key: &ssh_key::private::OpaqueKeypair,
) -> Result<ed25519::ExpandedKeypair> {
let public = ed25519::PublicKey::try_from(key.public.as_ref())
.map_err(|_| internal!("bad expanded ed25519 public key "))?;
let keypair = ed25519::ExpandedKeypair::from_secret_key_bytes(
key.private
.as_ref()
.try_into()
.map_err(|_| internal!("bad length on expanded ed25519 secret key ",))?,
)
.ok_or_else(|| internal!("bad expanded ed25519 secret key "))?;
if &public != keypair.public() {
return Err(internal!("mismatched ed25519 keypair",).into());
}
Ok(keypair)
}
fn convert_ed25519_pk(key: &ssh_key::public::Ed25519PublicKey) -> Result<ed25519::PublicKey> {
Ok(ed25519::PublicKey::from_bytes(key.as_ref())
.map_err(|_| internal!("bad ed25519 public key "))?)
}
fn convert_expanded_ed25519_pk(
_key: &ssh_key::public::OpaquePublicKey,
) -> Result<ed25519::PublicKey> {
Err(internal!(
"invalid ed25519 public key (ed25519 public keys should be stored as ssh-ed25519)",
)
.into())
}
fn convert_x25519_pk(key: &ssh_key::public::OpaquePublicKey) -> Result<curve25519::PublicKey> {
let public: [u8; 32] = key
.as_ref()
.try_into()
.map_err(|_| internal!("bad x25519 public key length"))?;
Ok(curve25519::PublicKey::from(public))
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct SshKeyData(SshKeyDataInner);
#[derive(Clone, Debug)]
#[non_exhaustive]
enum SshKeyDataInner {
Public(KeyData),
Private(KeypairData),
}
impl SshKeyData {
pub(crate) fn try_from_key_data(key: KeyData) -> Result<Self> {
let algo = SshKeyAlgorithm::from(key.algorithm());
let () = match key {
KeyData::Ed25519(_) => Ok(()),
KeyData::Other(_) => match algo {
SshKeyAlgorithm::X25519 => Ok(()),
_ => Err(Error::UnsupportedKeyAlgorithm(algo)),
},
_ => Err(Error::UnsupportedKeyAlgorithm(algo)),
}?;
Ok(Self(SshKeyDataInner::Public(key)))
}
pub(crate) fn try_from_keypair_data(key: KeypairData) -> Result<Self> {
let algo = SshKeyAlgorithm::from(
key.algorithm()
.map_err(into_internal!("encrypted keys are not yet supported"))?,
);
let () = match key {
KeypairData::Ed25519(_) => Ok(()),
KeypairData::Other(_) => match algo {
SshKeyAlgorithm::X25519 => Ok(()),
SshKeyAlgorithm::Ed25519Expanded => Ok(()),
_ => Err(Error::UnsupportedKeyAlgorithm(algo)),
},
_ => Err(Error::UnsupportedKeyAlgorithm(algo)),
}?;
Ok(Self(SshKeyDataInner::Private(key)))
}
pub(crate) fn to_openssh_string(&self, comment: &str) -> Result<String> {
let openssh_key = match &self.0 {
SshKeyDataInner::Public(key_data) => {
let openssh_key = PublicKey::new(key_data.clone(), comment);
openssh_key
.to_openssh()
.map_err(|_| tor_error::internal!("failed to encode SSH key"))?
}
SshKeyDataInner::Private(keypair) => {
let openssh_key = PrivateKey::new(keypair.clone(), comment)
.map_err(|_| tor_error::internal!("failed to create SSH private key"))?;
openssh_key
.to_openssh(LineEnding::LF)
.map_err(|_| tor_error::internal!("failed to encode SSH key"))?
.to_string()
}
};
Ok(openssh_key)
}
pub fn into_erased(self) -> Result<ErasedKey> {
match self.0 {
SshKeyDataInner::Private(key) => {
let algorithm = key
.algorithm()
.map_err(into_internal!("unsupported key type"))?;
ssh_to_internal_erased!(PRIVATE key, algorithm)
}
SshKeyDataInner::Public(key) => {
let algorithm = key.algorithm();
ssh_to_internal_erased!(PUBLIC key, algorithm)
}
}
}
pub fn key_type(&self) -> Result<KeyType> {
match &self.0 {
SshKeyDataInner::Public(k) => KeyType::try_from_key_data(k),
SshKeyDataInner::Private(k) => KeyType::try_from_keypair_data(k),
}
}
}
mod sealed {
pub trait Sealed {}
}
#[cfg(test)]
pub(crate) use sealed::Sealed;
#[cfg(not(test))]
use sealed::Sealed;
pub trait EncodableKey: Downcast + Sealed {
fn key_type() -> KeyType
where
Self: Sized;
fn as_ssh_key_data(&self) -> Result<SshKeyData>;
}
impl_downcast!(EncodableKey);
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 Sealed for curve25519::StaticKeypair {}
impl EncodableKey for curve25519::StaticKeypair {
fn key_type() -> KeyType
where
Self: Sized,
{
KeyType::X25519StaticKeypair
}
fn as_ssh_key_data(&self) -> Result<SshKeyData> {
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(ssh_key::private::KeypairData::Other(keypair))
}
}
impl Sealed for curve25519::PublicKey {}
impl EncodableKey for curve25519::PublicKey {
fn key_type() -> KeyType
where
Self: Sized,
{
KeyType::X25519PublicKey
}
fn as_ssh_key_data(&self) -> Result<SshKeyData> {
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))
}
}
impl Keygen for ed25519::Keypair {
fn generate(mut rng: &mut dyn KeygenRng) -> Result<Self>
where
Self: Sized,
{
Ok(ed25519::Keypair::generate(&mut rng))
}
}
impl Sealed for ed25519::Keypair {}
impl EncodableKey for ed25519::Keypair {
fn key_type() -> KeyType
where
Self: Sized,
{
KeyType::Ed25519Keypair
}
fn as_ssh_key_data(&self) -> Result<SshKeyData> {
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))
}
}
impl Sealed for ed25519::PublicKey {}
impl EncodableKey for ed25519::PublicKey {
fn key_type() -> KeyType
where
Self: Sized,
{
KeyType::Ed25519PublicKey
}
fn as_ssh_key_data(&self) -> Result<SshKeyData> {
let key_data = Ed25519PublicKey(self.to_bytes());
SshKeyData::try_from_key_data(ssh_key::public::KeyData::Ed25519(key_data))
}
}
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 Sealed for ed25519::ExpandedKeypair {}
impl EncodableKey for ed25519::ExpandedKeypair {
fn key_type() -> KeyType
where
Self: Sized,
{
KeyType::Ed25519ExpandedKeypair
}
fn as_ssh_key_data(&self) -> Result<SshKeyData> {
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(ssh_key::private::KeypairData::Other(keypair))
}
}
pub trait ToEncodableKey {
type Key: EncodableKey + 'static;
fn to_encodable_key(self) -> Self::Key;
fn from_encodable_key(key: Self::Key) -> Self;
}
impl ToEncodableKey for HsClientDescEncKeypair {
type Key = curve25519::StaticKeypair;
fn to_encodable_key(self) -> Self::Key {
self.into()
}
fn from_encodable_key(key: Self::Key) -> Self {
HsClientDescEncKeypair::new(key.public.into(), key.secret.into())
}
}
impl ToEncodableKey for HsBlindIdKeypair {
type Key = ed25519::ExpandedKeypair;
fn to_encodable_key(self) -> Self::Key {
self.into()
}
fn from_encodable_key(key: Self::Key) -> Self {
HsBlindIdKeypair::from(key)
}
}
impl ToEncodableKey for HsBlindIdKey {
type Key = ed25519::PublicKey;
fn to_encodable_key(self) -> Self::Key {
self.into()
}
fn from_encodable_key(key: Self::Key) -> Self {
HsBlindIdKey::from(key)
}
}
impl ToEncodableKey for HsIdKeypair {
type Key = ed25519::ExpandedKeypair;
fn to_encodable_key(self) -> Self::Key {
self.into()
}
fn from_encodable_key(key: Self::Key) -> Self {
HsIdKeypair::from(key)
}
}
impl ToEncodableKey for HsIdKey {
type Key = ed25519::PublicKey;
fn to_encodable_key(self) -> Self::Key {
self.into()
}
fn from_encodable_key(key: Self::Key) -> Self {
HsIdKey::from(key)
}
}
impl ToEncodableKey for HsDescSigningKeypair {
type Key = ed25519::Keypair;
fn to_encodable_key(self) -> Self::Key {
self.into()
}
fn from_encodable_key(key: Self::Key) -> Self {
HsDescSigningKeypair::from(key)
}
}
impl ToEncodableKey for HsIntroPtSessionIdKeypair {
type Key = ed25519::Keypair;
fn to_encodable_key(self) -> Self::Key {
self.into()
}
fn from_encodable_key(key: Self::Key) -> Self {
key.into()
}
}
impl ToEncodableKey for HsSvcNtorKeypair {
type Key = curve25519::StaticKeypair;
fn to_encodable_key(self) -> Self::Key {
self.into()
}
fn from_encodable_key(key: Self::Key) -> Self {
key.into()
}
}