minisign/
secret_key.rs

1use std::cmp;
2use std::fmt::Write as fmtWrite;
3use std::fmt::{self, Formatter};
4use std::fs;
5use std::io::{self, Write};
6use std::io::{Cursor, Read};
7use std::path::Path;
8
9use ct_codecs::{Base64, Decoder, Encoder};
10
11use crate::constants::*;
12use crate::crypto::blake2b::Blake2b;
13use crate::crypto::util::fixed_time_eq;
14use crate::errors::*;
15use crate::helpers::*;
16use crate::keynum::*;
17use crate::Result;
18
19/// A secret key and its metadata.
20///
21/// A `SecretKeyBox` represents a raw secret key, along with a key
22/// identifier, an untrusted description, and information required to
23/// decrypt it using a password.
24///
25/// This is what usually gets exported to disk.
26///
27/// A `SecretKeyBox` can be directly converted to/from a single-line string.
28#[derive(Clone, Debug)]
29pub struct SecretKeyBox(String);
30
31impl From<SecretKeyBox> for String {
32    fn from(skb: SecretKeyBox) -> String {
33        skb.0
34    }
35}
36
37impl From<String> for SecretKeyBox {
38    fn from(s: String) -> SecretKeyBox {
39        SecretKeyBox(s)
40    }
41}
42
43impl std::fmt::Display for SecretKeyBox {
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        write!(f, "{}", self.0)
46    }
47}
48
49impl SecretKeyBox {
50    /// Create a new `SecretKeyBox` from a string.
51    pub fn from_string(s: &str) -> Result<SecretKeyBox> {
52        Ok(s.to_string().into())
53    }
54
55    /// Return a `SecretKeyBox` for a string, for storage.
56    pub fn into_string(self) -> String {
57        self.into()
58    }
59
60    /// Convert a `SecretKeyBox` to a string, for storage.
61    /// If `password` is `None`, a password is going to be prompted interactively.
62    pub fn into_secret_key(self, password: Option<String>) -> Result<SecretKey> {
63        SecretKey::from_box(self, password)
64    }
65
66    /// Convert an unencrypted `SecretKeyBox` to a string, for storage.
67    pub fn into_unencrypted_secret_key(self) -> Result<SecretKey> {
68        SecretKey::from_unencrypted_box(self)
69    }
70
71    /// Return a byte representation of the secret key, for storage.
72    pub fn to_bytes(&self) -> Vec<u8> {
73        self.to_string().as_bytes().to_vec()
74    }
75}
76
77/// A `SecretKey` is used to create signatures.
78#[derive(Clone)]
79pub struct SecretKey {
80    pub(crate) sig_alg: [u8; TWOBYTES],
81    pub(crate) kdf_alg: [u8; TWOBYTES],
82    pub(crate) chk_alg: [u8; TWOBYTES],
83    pub(crate) kdf_salt: [u8; KDF_SALTBYTES],
84    pub(crate) kdf_opslimit_le: [u8; KEYNUM_BYTES],
85    pub(crate) kdf_memlimit_le: [u8; KEYNUM_BYTES],
86    pub(crate) keynum_sk: KeynumSK,
87}
88
89impl SecretKey {
90    pub(crate) fn write_checksum(&mut self) -> Result<()> {
91        let h = self.read_checksum()?;
92        self.keynum_sk.chk.copy_from_slice(&h[..]);
93        Ok(())
94    }
95
96    pub(crate) fn read_checksum(&self) -> Result<Vec<u8>> {
97        let mut state = Blake2b::new(CHK_BYTES);
98        state.update(&self.sig_alg);
99        state.update(&self.keynum_sk.keynum);
100        state.update(&self.keynum_sk.sk);
101        let mut h = vec![0u8; CHK_BYTES];
102        state.finalize(&mut h);
103        Ok(h)
104    }
105
106    pub(crate) fn xor_keynum(&mut self, stream: &[u8]) {
107        for (byte, stream) in self.keynum_sk.keynum.iter_mut().zip(stream.iter()) {
108            *byte ^= *stream
109        }
110        let keynum_len = self.keynum_sk.keynum.len();
111        for (byte, stream) in self
112            .keynum_sk
113            .sk
114            .iter_mut()
115            .zip(stream[keynum_len..].iter())
116        {
117            *byte ^= *stream
118        }
119        let sk_len = self.keynum_sk.sk.len();
120        for (byte, stream) in self
121            .keynum_sk
122            .chk
123            .iter_mut()
124            .zip(stream[keynum_len + sk_len..].iter())
125        {
126            *byte ^= *stream
127        }
128    }
129
130    pub(crate) fn encrypt(mut self, password: String) -> Result<SecretKey> {
131        let mut stream = [0u8; CHK_BYTES + SECRETKEY_BYTES + KEYNUM_BYTES];
132        let opslimit = load_u64_le(&self.kdf_opslimit_le);
133        let memlimit = load_u64_le(&self.kdf_memlimit_le) as usize;
134        if memlimit > MEMLIMIT_MAX {
135            return Err(PError::new(ErrorKind::KDF, "scrypt parameters too high"));
136        }
137        let params = raw_scrypt_params(memlimit, opslimit, N_LOG2_MAX)?;
138        scrypt::scrypt(password.as_bytes(), &self.kdf_salt, &params, &mut stream)?;
139        self.xor_keynum(&stream);
140        Ok(self)
141    }
142
143    /// The key identifier of this secret key.
144    pub fn keynum(&self) -> &[u8] {
145        &self.keynum_sk.keynum[..]
146    }
147
148    /// Returns `true` if this secret key is encrypted and requires a password to use.
149    /// This checks both the encryption algorithm and whether the key material has been properly decrypted.
150    pub fn is_encrypted(&self) -> bool {
151        if self.kdf_alg == KDF_NONE {
152            return false;
153        }
154
155        // For encrypted keys, verify that the key material is valid by checking the checksum
156        // If the checksum doesn't match, the key is still encrypted
157        match self.read_checksum() {
158            Ok(checksum_vec) => {
159                let mut expected_chk = [0u8; CHK_BYTES];
160                expected_chk.copy_from_slice(&checksum_vec[..]);
161                expected_chk != self.keynum_sk.chk
162            }
163            Err(_) => true, // If we can't read checksum, assume encrypted
164        }
165    }
166
167    /// Deserialize a `SecretKey`.
168    ///
169    /// For storage, a `SecretKeyBox` is usually what you need instead.
170    pub fn from_bytes(bytes_buf: &[u8]) -> Result<SecretKey> {
171        let mut buf = Cursor::new(bytes_buf);
172        let mut sig_alg = [0u8; TWOBYTES];
173        let mut kdf_alg = [0u8; TWOBYTES];
174        let mut chk_alg = [0u8; TWOBYTES];
175        let mut kdf_salt = [0u8; KDF_SALTBYTES];
176        let mut ops_limit = [0u8; KEYNUM_BYTES];
177        let mut mem_limit = [0u8; KEYNUM_BYTES];
178        let mut keynum = [0u8; KEYNUM_BYTES];
179        let mut sk = [0u8; SECRETKEY_BYTES];
180        let mut chk = [0u8; CHK_BYTES];
181        buf.read_exact(&mut sig_alg)?;
182        buf.read_exact(&mut kdf_alg)?;
183        buf.read_exact(&mut chk_alg)?;
184        buf.read_exact(&mut kdf_salt)?;
185        buf.read_exact(&mut ops_limit)?;
186        buf.read_exact(&mut mem_limit)?;
187        buf.read_exact(&mut keynum)?;
188        buf.read_exact(&mut sk)?;
189        buf.read_exact(&mut chk)?;
190
191        Ok(SecretKey {
192            sig_alg,
193            kdf_alg,
194            chk_alg,
195            kdf_salt,
196            kdf_opslimit_le: ops_limit,
197            kdf_memlimit_le: mem_limit,
198            keynum_sk: KeynumSK { keynum, sk, chk },
199        })
200    }
201
202    /// Serialize a `SecretKey`.
203    ///
204    /// For storage, a `SecretKeyBox` is usually what you need instead.
205    pub fn to_bytes(&self) -> Vec<u8> {
206        let mut iters = Vec::new();
207        iters.push(self.sig_alg.iter());
208        iters.push(self.kdf_alg.iter());
209        iters.push(self.chk_alg.iter());
210        iters.push(self.kdf_salt.iter());
211        iters.push(self.kdf_opslimit_le.iter());
212        iters.push(self.kdf_memlimit_le.iter());
213        iters.push(self.keynum_sk.keynum.iter());
214        iters.push(self.keynum_sk.sk.iter());
215        iters.push(self.keynum_sk.chk.iter());
216        let v: Vec<u8> = iters.iter().flat_map(|b| b.clone().cloned()).collect();
217        v
218    }
219
220    fn from_box_(
221        sk_box: SecretKeyBox,
222        password: Option<String>,
223        unencrypted_key: bool,
224    ) -> Result<SecretKey> {
225        let s = sk_box.0;
226        let mut lines = s.lines();
227        lines.next().ok_or_else(|| {
228            PError::new(ErrorKind::Io, "Missing comment in secret key".to_string())
229        })?;
230        let encoded_sk = lines.next().ok_or_else(|| {
231            PError::new(
232                ErrorKind::Io,
233                "Missing encoded key in secret key".to_string(),
234            )
235        })?;
236        let mut sk = SecretKey::from_base64(encoded_sk)?;
237        if unencrypted_key {
238            if sk.kdf_alg != KDF_NONE {
239                return Err(PError::new(
240                    ErrorKind::Io,
241                    "Key might be encrypted".to_string(),
242                ));
243            }
244        } else {
245            match sk.kdf_alg {
246                KDF_NONE => {
247                    return Err(PError::new(
248                        ErrorKind::Io,
249                        "Key might be encrypted".to_string(),
250                    ))
251                }
252                KDF_ALG => {}
253                _ => {
254                    return Err(PError::new(
255                        ErrorKind::Io,
256                        "Unsupported encryption algorithm".to_string(),
257                    ))
258                }
259            }
260            let interactive = password.is_none();
261            let password = match password {
262                Some(password) => password,
263                None => {
264                    let password = get_password("Password: ")?;
265                    write!(
266                        io::stdout(),
267                        "Deriving a key from the password and decrypting the secret key... "
268                    )
269                    .map_err(|e| PError::new(ErrorKind::Io, e))?;
270                    io::stdout().flush()?;
271                    password
272                }
273            };
274            sk = sk.encrypt(password)?;
275            if interactive {
276                writeln!(io::stdout(), "done").map_err(|e| PError::new(ErrorKind::Io, e))?
277            }
278        }
279        let checksum_vec = sk.read_checksum()?;
280        let mut chk = [0u8; CHK_BYTES];
281        chk.copy_from_slice(&checksum_vec[..]);
282        if chk != sk.keynum_sk.chk {
283            if unencrypted_key {
284                Err(PError::new(ErrorKind::Verify, "Corrupted key"))
285            } else {
286                Err(PError::new(
287                    ErrorKind::Verify,
288                    "Wrong password for that key",
289                ))
290            }
291        } else {
292            Ok(sk)
293        }
294    }
295
296    /// Convert a `SecretKeyBox` to a `SecretKey`.
297    /// If `password` is `None`, a password is going to be prompted interactively.
298    pub fn from_box(sk_box: SecretKeyBox, password: Option<String>) -> Result<SecretKey> {
299        Self::from_box_(sk_box, password, false)
300    }
301
302    /// Convert an unencrypted `SecretKeyBox` to an unencrypted `SecretKey`.
303    pub fn from_unencrypted_box(sk_box: SecretKeyBox) -> Result<SecretKey> {
304        Self::from_box_(sk_box, None, true)
305    }
306
307    /// Convert a `SecretKey` to a `SecretKeyBox`.
308    pub fn to_box(&self, comment: Option<&str>) -> Result<SecretKeyBox> {
309        let mut s = String::new();
310        write!(s, "{COMMENT_PREFIX}")?;
311        if let Some(comment) = comment {
312            writeln!(s, "{comment}")?;
313        } else {
314            writeln!(s, "{SECRETKEY_DEFAULT_COMMENT}")?;
315        }
316        writeln!(s, "{}", self.to_base64())?;
317        Ok(s.into())
318    }
319
320    pub(crate) fn from_base64(s: &str) -> Result<SecretKey> {
321        let bytes = Base64::decode_to_vec(s, None)?;
322        SecretKey::from_bytes(&bytes[..])
323    }
324
325    pub(crate) fn to_base64(&self) -> String {
326        Base64::encode_to_string(self.to_bytes().as_slice()).unwrap()
327    }
328
329    /// Load a `SecretKeyBox` from a file, and returns a `SecretKey` from it.
330    pub fn from_file<P: AsRef<Path>>(sk_path: P, password: Option<String>) -> Result<SecretKey> {
331        let s = fs::read_to_string(sk_path)?;
332        SecretKey::from_box(s.into(), password)
333    }
334}
335
336impl fmt::Debug for SecretKey {
337    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
338        for byte in self.keynum_sk.sk.iter() {
339            write!(f, "{byte:x}")?
340        }
341        Ok(())
342    }
343}
344
345impl cmp::PartialEq for SecretKey {
346    fn eq(&self, other: &SecretKey) -> bool {
347        fixed_time_eq(&self.keynum_sk.sk, &other.keynum_sk.sk)
348    }
349}
350impl cmp::Eq for SecretKey {}