tpfs_krypt 7.1.8

An interface for accessing secrets
Documentation
pub(crate) mod ed25519;
pub(crate) mod sr25519;

use crate::errors::{Bip39EntropyConversionError, Result, SubstrateSs58ConversionError};
use regex::Regex;
use secrecy::{ExposeSecret, Secret};
use snafu::ResultExt;

lazy_static! {
    static ref SECRET_RE: Regex = Regex::new(r"^(?P<phrase>[\d\w ]+)(?P<path>(//?[^/]+)*)$")
        .expect("Constructed from known-good static regex value.");
    static ref DERIVE_PATHS_RE: Regex =
        Regex::new(r"/(/?[^/]+)").expect("Constructed from known-good static regex value.");
}

/// A substrate-styled ss58-check address
#[derive(derive_more::From, Debug, Eq, PartialEq, Hash)]
pub struct Address {
    pub value: String,
    pub key_type: KeyType,
}
impl AddressValidation for Address {
    fn validate(&self) -> Result<()> {
        match self.key_type {
            KeyType::SubstrateSr25519 => {
                sr25519::Public::from_string(&self.value).context(
                    SubstrateSs58ConversionError {
                        address: Some(self.value.clone()),
                    },
                )?;
                Ok(())
            }
            KeyType::SubstrateEd25519 => {
                ed25519::Public::from_string(&self.value).context(
                    SubstrateSs58ConversionError {
                        address: Some(self.value.clone()),
                    },
                )?;
                Ok(())
            }
            // Addresses are not meaningful for shared encryption
            KeyType::SharedEncryptionX25519 => Ok(()),
        }
    }
}

// Since const generics still aren't a thing - this macro exists to provide 16 and 32 byte versions
// of these methods
macro_rules! decl_fixed_size_methods {
    ($modname: ident, $size: literal) => {
        pub(crate) mod $modname {
            use super::*;
            // Do ya macro boi
            type EntropySize = [u8; $size];
            // First part of the tuple is the entropy, and the second part is the
            // derived paths.
            type DeconstructedTuple = (Secret<EntropySize>, Vec<Secret<String>>);

            pub(crate) fn deconstruct_secret_string(
                secret: Secret<String>,
            ) -> Result<DeconstructedTuple> {
                let cap = SECRET_RE
                    .captures(secret.expose_secret())
                    .ok_or(sp_core::crypto::SecretStringError::InvalidFormat)?;
                let phrase = cap
                    .name("phrase")
                    .map(|r| Secret::new(r.as_str().to_string()))
                    .ok_or(sp_core::crypto::SecretStringError::InvalidFormat)?;
                let paths = DERIVE_PATHS_RE
                    .captures_iter(&cap["path"])
                    .map(|path| Secret::new(path[1].to_string()))
                    .collect();

                Ok((to_entropy(phrase)?, paths))
            }

            /// A consistent function that happens in the substrate modules
            /// for retrieving the mnemonic string from entropy.
            fn to_mnemonic_string(entropy: &Secret<EntropySize>) -> Result<Secret<String>> {
                // The entropy expected here is just $size bytes of entropy since
                // a 12 word mnemonic phrase is 128 bits of entropy.
                // 12 words is hard coded in sp_core::crypto's implementation.
                let mnemonic = bip39::Mnemonic::from_entropy(
                    &entropy.expose_secret()[..],
                    bip39::Language::English,
                )
                .map_err(|err| err.compat())
                .context(Bip39EntropyConversionError {
                    message: "bytes weren't able to recreate the mnemonic for key.",
                })?;

                Ok(Secret::new(String::from(mnemonic.phrase())))
            }

            pub(crate) fn to_secret_string(
                entropy: &Secret<EntropySize>,
                derive_paths: &[Secret<String>],
            ) -> Result<Secret<String>> {
                let phrase = to_mnemonic_string(&entropy)?;
                let paths: Vec<&str> = derive_paths
                    .iter()
                    .map(|path| path.expose_secret().as_str())
                    .collect();
                let secret = if !paths.is_empty() {
                    Secret::new(format!("{}/{}", phrase.expose_secret(), paths.join("/")))
                } else {
                    phrase
                };

                Ok(secret)
            }

            /// A consistent function that happens in the substrate modules
            /// for retrieving the entropy from a mnemonic string.
            fn to_entropy(private_string: Secret<String>) -> Result<Secret<EntropySize>> {
                let mnemonic = bip39::Mnemonic::from_phrase(
                    private_string.expose_secret(),
                    bip39::Language::English,
                )
                .map_err(|err| err.compat())
                .context(Bip39EntropyConversionError {
                    message: "Mnemonic wasn't able to load for private key from bip39 library.",
                })?;
                let entropy = mnemonic.entropy();

                // The entropy expected here is just $size bytes of entropy since
                // a 12 word mnemonic phrase is 128 bits of entropy.
                // 12 words is hard coded in sp_core::crypto's implementation.
                let mut secret = [0u8; $size];
                secret.copy_from_slice(&entropy[..$size]);

                Ok(Secret::new(secret))
            }
        }
    };
}

decl_fixed_size_methods!(siz_sixteen, 16);
// 16 bytes is used more commonly, so they're exported by default.
use crate::{AddressValidation, KeyType};
pub(crate) use siz_sixteen::*;
use sp_core::crypto::Ss58Codec;
decl_fixed_size_methods!(siz_thirytwo, 32);