tpfs_krypt 7.1.8

An interface for accessing secrets
Documentation
use crate::crypto::substrate::Address;
use crate::{crypto::RawKeyPair, errors::Result, KeyIdentifier, KeyType, Signature};
use bip39::{Mnemonic, MnemonicType};
use rand::{CryptoRng, Rng, RngCore};
use secrecy::{ExposeSecret, Secret};
use sp_core::{crypto::Ss58Codec, ed25519, DeriveJunction, Pair};
use std::fmt::{Debug, Formatter};

pub(crate) type Public = sp_core::ed25519::Public;
impl Signature for ed25519::Signature {}

/// A struct for working with the Ed25519KeyPair
/// types directly that has some substrate details in here,
/// and should have all the implementations that could
/// be needed for a key manager even though a manager may
/// not use it locally.
#[derive(Clone, Constructor)]
pub struct SubstrateEd25519KeyPair {
    // Stores the entropy bits since substrate uses a salt from these
    // bits to generate the private key. Which means retrieving back the
    // original mnemonic wouldn't be possible.
    pub entropy: Secret<[u8; 16]>,
    pub derive_paths: Vec<Secret<String>>,
    // Stores the constructed key, since recovering it from entropy is expensive.
    // TODO: This should be made a `Secret` once it implements zeroize. There's not really any
    //  point in wrapping it to do that now, since it's not being zeroed anywhere it's being
    //  created in the libraries below us we don't control.
    //  https://dev.azure.com/transparentsystems/xand-network/_workitems/edit/4725
    pub(crate) key: ed25519::Pair,
}

impl Debug for SubstrateEd25519KeyPair {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Ed25519({})", &self.key.public())
    }
}

fn make_ed25519_pair(
    entropy: &Secret<[u8; 16]>,
    derive_paths: &[Secret<String>],
) -> Result<ed25519::Pair> {
    let seed = substrate_bip39::seed_from_entropy(&entropy.expose_secret()[..], "")?;

    let derivations = derive_paths
        .iter()
        .map(|path| DeriveJunction::from(path.expose_secret()));

    let pair = ed25519::Pair::from_seed_slice(&seed[0..32])?;
    let (pair, _seed) = pair.derive(derivations, None)?;

    // This just converts it directly to 32 bytes which is how long the seed should be.
    Ok(pair)
}

impl SubstrateEd25519KeyPair {
    fn get_ed25519_pair(&self) -> Result<ed25519::Pair> {
        make_ed25519_pair(&self.entropy, &self.derive_paths)
    }

    pub fn to_address(&self) -> Address {
        Address {
            value: self.key.public().to_ss58check(),
            key_type: KeyType::SubstrateEd25519,
        }
    }
}

impl RawKeyPair for SubstrateEd25519KeyPair {
    type PublicKey = Public;
    type Signature = ed25519::Signature;
    const KEY_TYPE: KeyType = KeyType::SubstrateEd25519;

    fn from_strings(secret: Secret<String>) -> Result<Self> {
        let (entropy, derive_paths) = super::deconstruct_secret_string(secret)?;
        let pair = make_ed25519_pair(&entropy, &derive_paths)?;

        Ok(Self::new(entropy, derive_paths, pair))
    }

    fn from_secret(secret: Secret<String>) -> Result<Self> {
        let (entropy, derive_paths) = super::deconstruct_secret_string(secret)?;
        let pair = make_ed25519_pair(&entropy, &derive_paths)?;

        Ok(Self::new(entropy, derive_paths, pair))
    }

    fn generate_with<R: RngCore + CryptoRng>(rng: &mut R) -> Result<Self> {
        let mut entropy = vec![0; MnemonicType::Words12.entropy_bits() / 8];
        rng.fill(&mut *entropy);
        let mnemonic = Mnemonic::from_entropy(&entropy, bip39::Language::English)
            .expect("Mnemonic length is valid by construction");
        let phrase = mnemonic.phrase();
        let (key, _) = ed25519::Pair::from_phrase(phrase, None)
            .expect("All phrases generated by Mnemonic are valid");
        let secret_phrase = phrase.to_owned();
        let phrase = Secret::new(secret_phrase);
        let (entropy, derive_paths) = super::deconstruct_secret_string(phrase)?;

        Ok(Self::new(entropy, derive_paths, key))
    }

    fn sign(&self, message: &[u8]) -> Result<Self::Signature> {
        let pair = self.get_ed25519_pair()?;

        // The signing here uses substrate libraries, but doesn't need to as its
        // just a combination of ed25519-dalek library and expanding the key using
        // SHA512.
        Ok(pair.sign(message))
    }

    fn identifier(&self) -> KeyIdentifier {
        KeyIdentifier {
            value: self.public().to_ss58check(),
            key_type: Self::KEY_TYPE,
        }
    }

    fn to_secret(&self) -> Result<Secret<String>> {
        super::to_secret_string(&self.entropy, &self.derive_paths)
    }

    fn public(&self) -> Self::PublicKey {
        self.key.public()
    }
}