yubihsm/authentication/
key.rs

1//! `YubiHSM 2` authentication keys (2 * AES-128 symmetric PSK) from which session keys are derived
2
3use super::{Error, ErrorKind};
4use rand_core::{OsRng, RngCore};
5use std::fmt::{self, Debug};
6use zeroize::Zeroize;
7
8#[cfg(feature = "pbkdf2")]
9use pbkdf2::pbkdf2_hmac;
10
11#[cfg(feature = "sha2")]
12use sha2::Sha256;
13
14/// Auth keys are 2 * AES-128 keys
15pub const SIZE: usize = 32;
16
17/// Password from which the default auth key is derived
18pub const DEFAULT_PASSWORD: &[u8] = b"password";
19
20/// Salt value to use with PBKDF2 when deriving auth keys from a password.
21/// This salt is designed to be compatible with the password functionality in
22/// yubihsm-shell (otherwise using a static salt is not best practice).
23pub const PBKDF2_SALT: &[u8] = b"Yubico";
24
25/// Number of PBKDF2 iterations to perform when deriving auth keys.
26/// This number of iterations matches what is performed by yubihsm-shell.
27pub const PBKDF2_ITERATIONS: u32 = 10_000;
28
29/// `YubiHSM 2` authentication keys (2 * AES-128 symmetric PSK) from which
30/// session keys are derived.c
31#[derive(Clone)]
32pub struct Key(pub(crate) [u8; SIZE]);
33
34impl Key {
35    /// Generate a random `Key` using `OsRng`.
36    pub fn random() -> Self {
37        let mut challenge = [0u8; SIZE];
38        OsRng.fill_bytes(&mut challenge);
39        Key(challenge)
40    }
41
42    /// Derive an auth key from a password (using PBKDF2 + static salt).
43    /// This method is designed to be compatible with yubihsm-shell. Ensure
44    /// you use a long, random password when using this method as the key
45    /// derivation algorithm used does little to prevent brute force attacks.
46    #[cfg(feature = "passwords")]
47    pub fn derive_from_password(password: &[u8]) -> Self {
48        let mut kdf_output = [0u8; SIZE];
49        pbkdf2_hmac::<Sha256>(password, PBKDF2_SALT, PBKDF2_ITERATIONS, &mut kdf_output);
50        Self::new(kdf_output)
51    }
52
53    /// Create an `authentication::Key` from a 32-byte slice, returning an
54    /// error if the key is the wrong length
55    pub fn from_slice(key_slice: &[u8]) -> Result<Self, Error> {
56        ensure!(
57            key_slice.len() == SIZE,
58            ErrorKind::KeySizeInvalid,
59            "expected {}-byte key, got {}",
60            SIZE,
61            key_slice.len()
62        );
63
64        let mut key_bytes = [0u8; SIZE];
65        key_bytes.copy_from_slice(key_slice);
66
67        Ok(Key(key_bytes))
68    }
69
70    /// Create a new Key from the given byte array
71    pub fn new(key_bytes: [u8; SIZE]) -> Self {
72        Key(key_bytes)
73    }
74
75    /// Borrow the secret authentication keys
76    pub fn as_secret_slice(&self) -> &[u8] {
77        &self.0
78    }
79
80    /// Obtain the encryption key portion of this auth key
81    pub(crate) fn enc_key(&self) -> &[u8] {
82        &self.0[..16]
83    }
84
85    /// Obtain the MAC key portion of this auth key
86    pub(crate) fn mac_key(&self) -> &[u8] {
87        &self.0[16..]
88    }
89}
90
91impl Debug for Key {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        // Avoid leaking secrets in debug messages
94        write!(f, "yubihsm::authentication::Key(...)")
95    }
96}
97
98/// Derive the default authentication key for all YubiHSM 2s
99#[cfg(feature = "passwords")]
100impl Default for Key {
101    fn default() -> Self {
102        Key::derive_from_password(DEFAULT_PASSWORD)
103    }
104}
105
106impl Drop for Key {
107    fn drop(&mut self) {
108        self.0.zeroize();
109    }
110}
111
112impl From<[u8; SIZE]> for Key {
113    fn from(key_bytes: [u8; SIZE]) -> Key {
114        Key::new(key_bytes)
115    }
116}
117
118impl_array_serializers!(Key, SIZE);