use crate::{
crypto::{
shared_encryption::x25519::SharedEncryptionX25519KeyPair,
substrate::{ed25519::SubstrateEd25519KeyPair, sr25519::SubstrateSr25519KeyPair},
KeyPairValue, RawKeyPair,
},
errors::{IoError, KeyManagementError},
KeyPairInstance,
};
use libp2p_core::PeerId;
use rand::{CryptoRng, Rng, RngCore};
use secrecy::{ExposeSecret, Secret};
use snafu::ResultExt;
use std::{
fs,
path::{Path, PathBuf},
};
use strum_macros::{Display, EnumDiscriminants, EnumIter, EnumString};
#[derive(Clone, Debug, EnumDiscriminants)]
#[strum_discriminants(name(XandKeyType))]
#[strum_discriminants(derive(Display, EnumIter, EnumString))]
pub enum AnyXandKeyPair {
Trust(TrustKeyPair),
Wallet(WalletKeyPair),
ValidatorAuthority(ValidatorAuthorityKeyPair),
ValidatorSessionFinalizeBlocks(ValidatorSessionFinalizeBlocksKeyPair),
ValidatorSessionProduceBlocks(ValidatorSessionProduceBlocksKeyPair),
ValidatorLibp2p(ValidatorLibp2pKeyPair),
Encryption(EncryptionKeyPair),
LimitedAgent(LimitedAgentKeyPair),
}
impl XandKeyType {
pub fn generate_with<R>(self, rng: &mut R) -> Result<AnyXandKeyPair, KeyManagementError>
where
R: RngCore + CryptoRng,
{
Ok(match self {
XandKeyType::Trust => AnyXandKeyPair::Trust(TrustKeyPair::generate_with(rng)?),
XandKeyType::Wallet => AnyXandKeyPair::Wallet(WalletKeyPair::generate_with(rng)?),
XandKeyType::ValidatorAuthority => {
AnyXandKeyPair::ValidatorAuthority(ValidatorAuthorityKeyPair::generate_with(rng)?)
}
XandKeyType::ValidatorSessionFinalizeBlocks => {
AnyXandKeyPair::ValidatorSessionFinalizeBlocks(
ValidatorSessionFinalizeBlocksKeyPair::generate_with(rng)?,
)
}
XandKeyType::ValidatorSessionProduceBlocks => {
AnyXandKeyPair::ValidatorSessionProduceBlocks(
ValidatorSessionProduceBlocksKeyPair::generate_with(rng)?,
)
}
XandKeyType::ValidatorLibp2p => {
AnyXandKeyPair::ValidatorLibp2p(ValidatorLibp2pKeyPair::generate_with(rng)?)
}
XandKeyType::Encryption => {
AnyXandKeyPair::Encryption(EncryptionKeyPair::generate_with(rng)?)
}
XandKeyType::LimitedAgent => {
AnyXandKeyPair::LimitedAgent(LimitedAgentKeyPair::generate_with(rng)?)
}
})
}
pub fn generate(self) -> Result<AnyXandKeyPair, KeyManagementError> {
self.generate_with(&mut rand::thread_rng())
}
}
impl XandKeyPair for AnyXandKeyPair {
fn address(&self) -> Option<String> {
match self {
AnyXandKeyPair::Trust(x) => x.address(),
AnyXandKeyPair::Wallet(x) => x.address(),
AnyXandKeyPair::ValidatorAuthority(x) => x.address(),
AnyXandKeyPair::ValidatorSessionFinalizeBlocks(x) => x.address(),
AnyXandKeyPair::ValidatorSessionProduceBlocks(x) => x.address(),
AnyXandKeyPair::ValidatorLibp2p(x) => x.address(),
AnyXandKeyPair::Encryption(x) => x.address(),
AnyXandKeyPair::LimitedAgent(x) => x.address(),
}
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
match self {
AnyXandKeyPair::Trust(x) => x.write_to_directory(dir),
AnyXandKeyPair::Wallet(x) => x.write_to_directory(dir),
AnyXandKeyPair::ValidatorAuthority(x) => x.write_to_directory(dir),
AnyXandKeyPair::ValidatorSessionFinalizeBlocks(x) => x.write_to_directory(dir),
AnyXandKeyPair::ValidatorSessionProduceBlocks(x) => x.write_to_directory(dir),
AnyXandKeyPair::ValidatorLibp2p(x) => x.write_to_directory(dir),
AnyXandKeyPair::Encryption(x) => x.write_to_directory(dir),
AnyXandKeyPair::LimitedAgent(x) => x.write_to_directory(dir),
}
}
}
#[derive(Clone, Debug)]
pub struct TrustKeyPair(SubstrateSr25519KeyPair);
impl TrustKeyPair {
pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
Ok(Self(RawKeyPair::generate_with(rng)?))
}
pub fn generate() -> Result<Self, KeyManagementError> {
Self::generate_with(&mut rand::thread_rng())
}
}
impl XandKeyPair for TrustKeyPair {
fn address(&self) -> Option<String> {
Some(self.0.to_address().value)
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
KeyPairInstance(KeyPairValue::SubstrateSr25519(self.0.clone()))
.write_to_directory(dir.as_ref())
}
}
#[derive(Clone, Debug)]
pub struct WalletKeyPair(SubstrateSr25519KeyPair);
impl WalletKeyPair {
pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
Ok(Self(RawKeyPair::generate_with(rng)?))
}
pub fn generate() -> Result<Self, KeyManagementError> {
Self::generate_with(&mut rand::thread_rng())
}
}
impl XandKeyPair for WalletKeyPair {
fn address(&self) -> Option<String> {
Some(self.0.to_address().value)
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
KeyPairInstance(KeyPairValue::SubstrateSr25519(self.0.clone()))
.write_to_directory(dir.as_ref())
}
}
#[derive(Clone, Debug)]
pub struct ValidatorAuthorityKeyPair(SubstrateSr25519KeyPair);
impl ValidatorAuthorityKeyPair {
pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
Ok(Self(RawKeyPair::generate_with(rng)?))
}
pub fn generate() -> Result<Self, KeyManagementError> {
Self::generate_with(&mut rand::thread_rng())
}
}
impl XandKeyPair for ValidatorAuthorityKeyPair {
fn address(&self) -> Option<String> {
Some(self.0.to_address().value)
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
KeyPairInstance(KeyPairValue::SubstrateSr25519(self.0.clone()))
.write_to_directory(dir.as_ref())
}
}
#[derive(Clone, Debug)]
pub struct ValidatorSessionFinalizeBlocksKeyPair(SubstrateEd25519KeyPair);
impl ValidatorSessionFinalizeBlocksKeyPair {
pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
Ok(Self(RawKeyPair::generate_with(rng)?))
}
pub fn generate() -> Result<Self, KeyManagementError> {
Self::generate_with(&mut rand::thread_rng())
}
}
impl XandKeyPair for ValidatorSessionFinalizeBlocksKeyPair {
fn address(&self) -> Option<String> {
Some(self.0.to_address().value)
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
let path = dir.as_ref().join("validator-session-finalize-blocks");
fs::write(&path, self.0.to_secret()?.expose_secret()).context(IoError {
message: "Creating/writing to key file",
})?;
Ok(path)
}
}
#[derive(Clone, Debug)]
pub struct ValidatorSessionProduceBlocksKeyPair(SubstrateSr25519KeyPair);
impl ValidatorSessionProduceBlocksKeyPair {
pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
Ok(Self(RawKeyPair::generate_with(rng)?))
}
pub fn generate() -> Result<Self, KeyManagementError> {
Self::generate_with(&mut rand::thread_rng())
}
}
impl XandKeyPair for ValidatorSessionProduceBlocksKeyPair {
fn address(&self) -> Option<String> {
Some(self.0.to_address().value)
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
let path = dir.as_ref().join("validator-session-produce-blocks");
fs::write(&path, self.0.to_secret()?.expose_secret()).context(IoError {
message: "Creating/writing to key file",
})?;
Ok(path)
}
}
#[derive(Clone, Debug)]
pub struct ValidatorLibp2pKeyPair(libp2p_core::identity::ed25519::Keypair);
impl ValidatorLibp2pKeyPair {
pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
let key = rng.gen::<[u8; 32]>();
let secret_key = libp2p_core::identity::ed25519::SecretKey::from_bytes(key)
.expect("The length is correct and that's the only possible error");
Ok(Self(secret_key.into()))
}
pub fn generate() -> Result<Self, KeyManagementError> {
Self::generate_with(&mut rand::thread_rng())
}
pub fn get_secret_string(&self) -> Secret<String> {
Secret::new(hex::encode(self.0.secret().as_ref()))
}
}
impl XandKeyPair for ValidatorLibp2pKeyPair {
fn address(&self) -> Option<String> {
Some(PeerId::from(libp2p_core::PublicKey::Ed25519(self.0.public())).to_base58())
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
let secret_str = hex::encode(self.0.secret().as_ref());
let path = dir.as_ref().join("validator-lp2p-key.env");
fs::write(&path, format!("NODE_KEY={}", secret_str)).context(IoError {
message: "Creating/writing to key file",
})?;
Ok(path)
}
}
#[derive(Clone, Debug)]
pub struct EncryptionKeyPair(SharedEncryptionX25519KeyPair);
impl EncryptionKeyPair {
pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
Ok(Self(RawKeyPair::generate_with(rng)?))
}
pub fn generate() -> Result<Self, KeyManagementError> {
Self::generate_with(&mut rand::thread_rng())
}
}
impl XandKeyPair for EncryptionKeyPair {
fn address(&self) -> Option<String> {
None
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
KeyPairInstance(KeyPairValue::SharedEncryptionX25519(self.0.clone()))
.write_to_directory(dir.as_ref())
}
}
#[derive(Clone, Debug)]
pub struct LimitedAgentKeyPair(SubstrateSr25519KeyPair);
impl LimitedAgentKeyPair {
pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
Ok(Self(RawKeyPair::generate_with(rng)?))
}
pub fn generate() -> Result<Self, KeyManagementError> {
Self::generate_with(&mut rand::thread_rng())
}
}
impl XandKeyPair for LimitedAgentKeyPair {
fn address(&self) -> Option<String> {
Some(self.0.to_address().value)
}
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError> {
KeyPairInstance(KeyPairValue::SubstrateSr25519(self.0.clone()))
.write_to_directory(dir.as_ref())
}
}
pub trait XandKeyPair {
fn address(&self) -> Option<String>;
fn write_to_directory<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, KeyManagementError>;
}
#[cfg(test)]
mod tests {
use super::{XandKeyPair, XandKeyType};
use std::path::Path;
use strum::IntoEnumIterator;
fn generation_succeeds(key_type: XandKeyType, dir: &Path) {
let key_pair = key_type.generate().unwrap();
key_pair.write_to_directory(dir).unwrap();
}
#[test]
fn generation_of_all_key_types_succeeds() {
let dir = tempfile::tempdir().unwrap();
for key_type in XandKeyType::iter() {
generation_succeeds(key_type, dir.path());
}
}
}