tpfs_krypt 7.1.8

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

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

/// A struct for working with the Sr25519KeyPair
/// 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 SubstrateSr25519KeyPair {
    // 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: While the underlying secrets / scalars implement zeroize, we can't make this secret until
    // the substrate newtype wrapper for the schnorkel type implements Zeroize as well:
    //  https://dev.azure.com/transparentsystems/xand-network/_workitems/edit/4725
    //  https://github.com/paritytech/substrate/issues/8130
    pub key: sr25519::Pair,
}

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

fn make_sr25519_pair(
    entropy: &Secret<[u8; 16]>,
    derive_paths: &[Secret<String>],
) -> Result<sr25519::Pair> {
    let (kp, _mini_key) = sr25519::Pair::from_entropy(entropy.expose_secret(), None);

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

    let (pair, _seed) = kp.derive(derivations, None)?;

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

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

impl RawKeyPair for SubstrateSr25519KeyPair {
    type PublicKey = Public;
    type Signature = sr25519::Signature;
    const KEY_TYPE: KeyType = KeyType::SubstrateSr25519;

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

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

    fn from_secret(secret: Secret<String>) -> Result<Self> {
        let (entropy, derive_paths) = super::deconstruct_secret_string(secret)?;
        let pair = make_sr25519_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, _) = sr25519::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> {
        // The signing here uses substrate libraries, and the only reason that it needs to
        // is due SIGNING_CTX for the schnorrkel library is set to b"substrate" which is in the
        // substrate module: https://github.com/paritytech/substrate/blob/24a18eabda157a262429695cf4c07712a888381b/core/primitives/src/sr25519.rs#L50
        Ok(self.key.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()
    }
}