1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! Cryptographic key material.
//!
//! Unlike traditional KDFs and XOFs, HKD32 acts on fixed-sized
//! 256-bit (32-byte) keys.
//!
//! The `KeyMaterial` type is used to represent both input and output key
//! material, and is the primary type useful for deriving other keys.

#[cfg(feature = "mnemonic")]
use crate::mnemonic;
use crate::{path::Path, Error, KEY_SIZE};
#[cfg(feature = "bech32")]
use alloc::string::String;
use core::convert::TryFrom;
#[cfg(feature = "getrandom")]
use getrandom::getrandom;
use hmac::{Hmac, Mac};
use sha2::Sha512;
#[cfg(feature = "bech32")]
use subtle_encoding::bech32::Bech32;
use zeroize::Zeroize;
#[cfg(feature = "bech32")]
use zeroize::Zeroizing;

/// Cryptographic key material: 256-bit (32-byte) uniformly random bytestring
/// generated either via a CSRNG or via hierarchical derivation.
///
/// This type provides the main key derivation functionality and is used to
/// represent both input and output key material.
#[derive(Clone, Zeroize)]
#[zeroize(drop)]
pub struct KeyMaterial([u8; KEY_SIZE]);

impl KeyMaterial {
    /// Create random key material using the operating system CSRNG
    #[cfg(feature = "getrandom")]
    pub fn random() -> Self {
        let mut bytes = [0u8; KEY_SIZE];
        getrandom(&mut bytes).expect("getrandom failure!");
        Self::new(bytes)
    }

    /// Decode key material from a Bech32 representation
    #[cfg(feature = "bech32")]
    pub fn from_bech32<S>(encoded: S) -> Result<(String, Self), Error>
    where
        S: AsRef<str>,
    {
        let (hrp, mut key_bytes) = Bech32::default().decode(encoded).map_err(|_| Error)?;
        let key_result = Self::from_bytes(&key_bytes);
        key_bytes.zeroize();
        key_result.map(|key| (hrp, key))
    }

    /// Create key material from a 24-word BIP39 mnemonic phrase
    #[cfg(feature = "mnemonic")]
    pub fn from_mnemonic<S>(phrase: S, language: mnemonic::Language) -> Result<Self, Error>
    where
        S: AsRef<str>,
    {
        Ok(mnemonic::Phrase::new(phrase, language)?.into())
    }

    /// Create new key material from a byte slice.
    ///
    /// Byte slice is expected to have been generated by a cryptographically
    /// secure random number generator.
    pub fn from_bytes(slice: &[u8]) -> Result<Self, Error> {
        if slice.len() == KEY_SIZE {
            let mut bytes = [0u8; KEY_SIZE];
            bytes.copy_from_slice(slice);
            Ok(Self::new(bytes))
        } else {
            Err(Error)
        }
    }

    /// Import existing key material - must be uniformly random!
    pub fn new(bytes: [u8; KEY_SIZE]) -> KeyMaterial {
        KeyMaterial(bytes)
    }

    /// Borrow the key material as a byte slice
    pub fn as_bytes(&self) -> &[u8] {
        &self.0
    }

    /// Derive an output key from the given input key material
    pub fn derive_subkey<P>(self, path: P) -> Self
    where
        P: AsRef<Path>,
    {
        let component_count = path.as_ref().components().count();

        path.as_ref()
            .components()
            .enumerate()
            .fold(self, |parent_key, (i, component)| {
                let mut hmac = Hmac::<Sha512>::new_varkey(parent_key.as_bytes()).unwrap();
                hmac.input(component.as_bytes());

                let mut hmac_result = hmac.result().code();
                let (secret_key, chain_code) = hmac_result.split_at_mut(KEY_SIZE);
                let mut child_key = [0u8; KEY_SIZE];

                if i < component_count - 1 {
                    // Use chain code for all but the last element
                    child_key.copy_from_slice(chain_code);
                } else {
                    // Use secret key for the last element
                    child_key.copy_from_slice(secret_key);
                }

                secret_key.zeroize();
                chain_code.zeroize();

                KeyMaterial(child_key)
            })
    }

    /// Serialize this `KeyMaterial` as a self-zeroizing Bech32 string
    #[cfg(feature = "bech32")]
    pub fn to_bech32<S>(&self, hrp: S) -> Zeroizing<String>
    where
        S: AsRef<str>,
    {
        let b32 = Bech32::default().encode(hrp, self.as_bytes());
        Zeroizing::new(b32)
    }
}

impl From<[u8; KEY_SIZE]> for KeyMaterial {
    fn from(bytes: [u8; KEY_SIZE]) -> Self {
        Self::new(bytes)
    }
}

impl<'a> TryFrom<&'a [u8]> for KeyMaterial {
    type Error = Error;

    fn try_from(slice: &'a [u8]) -> Result<Self, Error> {
        Self::from_bytes(slice)
    }
}