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