1 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4 // option. This file may not be copied, modified, or distributed
5 // except according to those terms.
6 
7 use crate::constants::{Cipher, Version};
8 use crate::err::{Error, Res};
9 use crate::p11::{random, SymKey};
10 use crate::{hkdf, Aead};
11 
12 use neqo_common::{hex, qinfo, qtrace, Encoder};
13 
14 use std::mem;
15 
16 #[derive(Debug)]
17 pub struct SelfEncrypt {
18     version: Version,
19     cipher: Cipher,
20     key_id: u8,
21     key: SymKey,
22     old_key: Option<SymKey>,
23 }
24 
25 impl SelfEncrypt {
26     const VERSION: u8 = 1;
27     const SALT_LENGTH: usize = 16;
28 
29     /// # Errors
30     /// Failure to generate a new HKDF key using NSS results in an error.
new(version: Version, cipher: Cipher) -> Res<Self>31     pub fn new(version: Version, cipher: Cipher) -> Res<Self> {
32         let key = hkdf::generate_key(version, cipher)?;
33         Ok(Self {
34             version,
35             cipher,
36             key_id: 0,
37             key,
38             old_key: None,
39         })
40     }
41 
make_aead(&self, k: &SymKey, salt: &[u8]) -> Res<Aead>42     fn make_aead(&self, k: &SymKey, salt: &[u8]) -> Res<Aead> {
43         debug_assert_eq!(salt.len(), Self::SALT_LENGTH);
44         let salt = hkdf::import_key(self.version, salt)?;
45         let secret = hkdf::extract(self.version, self.cipher, Some(&salt), k)?;
46         Aead::new(self.version, self.cipher, &secret, "neqo self")
47     }
48 
49     /// Rotate keys.  This causes any previous key that is being held to be replaced by the current key.
50     ///
51     /// # Errors
52     /// Failure to generate a new HKDF key using NSS results in an error.
rotate(&mut self) -> Res<()>53     pub fn rotate(&mut self) -> Res<()> {
54         let new_key = hkdf::generate_key(self.version, self.cipher)?;
55         self.old_key = Some(mem::replace(&mut self.key, new_key));
56         let (kid, _) = self.key_id.overflowing_add(1);
57         self.key_id = kid;
58         qinfo!(["SelfEncrypt"], "Rotated keys to {}", self.key_id);
59         Ok(())
60     }
61 
62     /// Seal an item using the underlying key.  This produces a single buffer that contains
63     /// the encrypted `plaintext`, plus a version number and salt.
64     /// `aad` is only used as input to the AEAD, it is not included in the output; the
65     /// caller is responsible for carrying the AAD as appropriate.
66     ///
67     /// # Errors
68     /// Failure to protect using NSS AEAD APIs produces an error.
seal(&self, aad: &[u8], plaintext: &[u8]) -> Res<Vec<u8>>69     pub fn seal(&self, aad: &[u8], plaintext: &[u8]) -> Res<Vec<u8>> {
70         // Format is:
71         // struct {
72         //   uint8 version;
73         //   uint8 key_id;
74         //   uint8 salt[16];
75         //   opaque aead_encrypted(plaintext)[length as expanded];
76         // };
77         // AAD covers the entire header, plus the value of the AAD parameter that is provided.
78         let salt = random(Self::SALT_LENGTH);
79         let cipher = self.make_aead(&self.key, &salt)?;
80         let encoded_len = 2 + salt.len() + plaintext.len() + cipher.expansion();
81 
82         let mut enc = Encoder::with_capacity(encoded_len);
83         enc.encode_byte(Self::VERSION);
84         enc.encode_byte(self.key_id);
85         enc.encode(&salt);
86 
87         let mut extended_aad = enc.clone();
88         extended_aad.encode(aad);
89 
90         let offset = enc.len();
91         let mut output: Vec<u8> = enc.into();
92         output.resize(encoded_len, 0);
93         cipher.encrypt(0, &extended_aad, plaintext, &mut output[offset..])?;
94         qtrace!(
95             ["SelfEncrypt"],
96             "seal {} {} -> {}",
97             hex(aad),
98             hex(plaintext),
99             hex(&output)
100         );
101         Ok(output)
102     }
103 
select_key(&self, kid: u8) -> Option<&SymKey>104     fn select_key(&self, kid: u8) -> Option<&SymKey> {
105         if kid == self.key_id {
106             Some(&self.key)
107         } else {
108             let (prev_key_id, _) = self.key_id.overflowing_sub(1);
109             if kid == prev_key_id {
110                 self.old_key.as_ref()
111             } else {
112                 None
113             }
114         }
115     }
116 
117     /// Open the protected `ciphertext`.
118     ///
119     /// # Errors
120     /// Returns an error when the self-encrypted object is invalid;
121     /// when the keys have been rotated; or when NSS fails.
122     #[allow(clippy::similar_names)] // aad is similar to aead
open(&self, aad: &[u8], ciphertext: &[u8]) -> Res<Vec<u8>>123     pub fn open(&self, aad: &[u8], ciphertext: &[u8]) -> Res<Vec<u8>> {
124         if ciphertext[0] != Self::VERSION {
125             return Err(Error::SelfEncryptFailure);
126         }
127         let key = if let Some(k) = self.select_key(ciphertext[1]) {
128             k
129         } else {
130             return Err(Error::SelfEncryptFailure);
131         };
132         let offset = 2 + Self::SALT_LENGTH;
133 
134         let mut extended_aad = Encoder::with_capacity(offset + aad.len());
135         extended_aad.encode(&ciphertext[0..offset]);
136         extended_aad.encode(aad);
137 
138         let aead = self.make_aead(key, &ciphertext[2..offset])?;
139         // NSS insists on having extra space available for decryption.
140         let padded_len = ciphertext.len() - offset;
141         let mut output = vec![0; padded_len];
142         let decrypted = aead.decrypt(0, &extended_aad, &ciphertext[offset..], &mut output)?;
143         let final_len = decrypted.len();
144         output.truncate(final_len);
145         qtrace!(
146             ["SelfEncrypt"],
147             "open {} {} -> {}",
148             hex(aad),
149             hex(ciphertext),
150             hex(&output)
151         );
152         Ok(output)
153     }
154 }
155