tpfs_krypt 7.1.8

An interface for accessing secrets
Documentation
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};

/// Key pair types used across the Xand platform
#[derive(Clone, Debug, EnumDiscriminants)]
#[strum_discriminants(name(XandKeyType))]
#[strum_discriminants(derive(Display, EnumIter, EnumString))]
pub enum AnyXandKeyPair {
    /// Keys used to authorize creations and redemptions.
    Trust(TrustKeyPair),
    /// Keys used to control funds. Could be held by a member/user/validator.
    Wallet(WalletKeyPair),
    /// The key that validators use to control their own settings, including changing their
    /// session keys.
    ValidatorAuthority(ValidatorAuthorityKeyPair),
    /// This key is used by validators to testify they voted for finalizing blocks
    ValidatorSessionFinalizeBlocks(ValidatorSessionFinalizeBlocksKeyPair),
    /// This key is used by validators to produce blocks
    ValidatorSessionProduceBlocks(ValidatorSessionProduceBlocksKeyPair),
    /// This key is used by validators to identify peer nodes and encrypt gossip
    ValidatorLibp2p(ValidatorLibp2pKeyPair),
    /// Encryption and decryption key for confidentiality
    Encryption(EncryptionKeyPair),
    /// Key which authorizes Limited Agent actions
    LimitedAgent(LimitedAgentKeyPair),
}

impl XandKeyType {
    /// Generates random key pair of the desired type
    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)?)
            }
        })
    }

    /// Generates random key pair of the desired type
    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),
        }
    }
}

/// Keys used to authorize creations and redemptions.
#[derive(Clone, Debug)]
pub struct TrustKeyPair(SubstrateSr25519KeyPair);

impl TrustKeyPair {
    /// Generates random key pair
    pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
        Ok(Self(RawKeyPair::generate_with(rng)?))
    }

    /// Generates random key pair
    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())
    }
}

/// Keys used to control funds. Could be held by a member/user/validator
#[derive(Clone, Debug)]
pub struct WalletKeyPair(SubstrateSr25519KeyPair);

impl WalletKeyPair {
    /// Generates random key pair
    pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
        Ok(Self(RawKeyPair::generate_with(rng)?))
    }

    /// Generates random key pair
    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())
    }
}

/// The key that validators use to control their own settings, including changing their
/// session keys.
#[derive(Clone, Debug)]
pub struct ValidatorAuthorityKeyPair(SubstrateSr25519KeyPair);

impl ValidatorAuthorityKeyPair {
    /// Generates random key pair
    pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
        Ok(Self(RawKeyPair::generate_with(rng)?))
    }

    /// Generates random key pair
    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())
    }
}

/// This key is used by validators to testify they voted for finalizing blocks
#[derive(Clone, Debug)]
pub struct ValidatorSessionFinalizeBlocksKeyPair(SubstrateEd25519KeyPair);

impl ValidatorSessionFinalizeBlocksKeyPair {
    /// Generates random key pair
    pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
        Ok(Self(RawKeyPair::generate_with(rng)?))
    }

    /// Generates random key pair
    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)
    }
}

/// This key is used by validators to produce blocks
#[derive(Clone, Debug)]
pub struct ValidatorSessionProduceBlocksKeyPair(SubstrateSr25519KeyPair);

impl ValidatorSessionProduceBlocksKeyPair {
    /// Generates random key pair
    pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
        Ok(Self(RawKeyPair::generate_with(rng)?))
    }

    /// Generates random key pair
    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)
    }
}

/// This key is used by validators to identify peer nodes and encrypt gossip
///
/// Libp2p keys get special treatment with regards to how their address/pubkey is represented,
/// as well as the way their file format works. Ideally this can be reconciled with the types
/// inside krypt, since ultimately this is still an ed25519 key.
#[derive(Clone, Debug)]
pub struct ValidatorLibp2pKeyPair(libp2p_core::identity::ed25519::Keypair);

impl ValidatorLibp2pKeyPair {
    /// Generates random key pair
    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()))
    }

    /// Generates random key pair
    pub fn generate() -> Result<Self, KeyManagementError> {
        Self::generate_with(&mut rand::thread_rng())
    }

    /// Returns the secret portion of this keypair as a hex-encoded string
    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())
    }

    /// Writes out the libp2p key into the provided directory with the name
    /// `validator-lp2p-key.env` and with the format:
    ///
    /// ```text
    /// NODE_KEY=<hex encoded private key>
    /// ```
    ///
    /// Returns the complete path the file was written to
    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 {
    /// Generates random key pair
    pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
        Ok(Self(RawKeyPair::generate_with(rng)?))
    }

    /// Generates random key pair
    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())
    }
}

/// Key which authorizes Limited Agent actions
#[derive(Clone, Debug)]
pub struct LimitedAgentKeyPair(SubstrateSr25519KeyPair);

impl LimitedAgentKeyPair {
    /// Generates random key pair
    pub fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self, KeyManagementError> {
        Ok(Self(RawKeyPair::generate_with(rng)?))
    }

    /// Generates random key pair
    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())
    }
}

/// Interface all Xand key pair types must implement
pub trait XandKeyPair {
    /// Address of the key pair
    ///
    /// # Note
    /// Not all key types have an address.
    fn address(&self) -> Option<String>;

    /// Writes key pair to directory and return file path
    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());
        }
    }
}