| Joel Galenson | ca0efb1 | 2020-10-01 14:32:30 -0700 | [diff] [blame] | 1 | // Copyright 2020, The Android Open Source Project | 
 | 2 | // | 
 | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 4 | // you may not use this file except in compliance with the License. | 
 | 5 | // You may obtain a copy of the License at | 
 | 6 | // | 
 | 7 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | 8 | // | 
 | 9 | // Unless required by applicable law or agreed to in writing, software | 
 | 10 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 12 | // See the License for the specific language governing permissions and | 
 | 13 | // limitations under the License. | 
 | 14 |  | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 15 | //! This module implements safe wrappers for some crypto operations required by | 
 | 16 | //! Keystore 2.0. | 
 | 17 |  | 
 | 18 | mod error; | 
| Janis Danisevskis | a16ddf3 | 2021-10-20 09:40:02 -0700 | [diff] [blame] | 19 | pub mod zvec; | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 20 | pub use error::Error; | 
 | 21 | use keystore2_crypto_bindgen::{ | 
| David Drysdale | c97eb9e | 2022-01-26 13:03:48 -0800 | [diff] [blame] | 22 |     extractSubjectFromCertificate, generateKeyFromPassword, hmacSha256, randomBytes, | 
 | 23 |     AES_gcm_decrypt, AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey, | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 24 |     ECKEYParsePrivateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key, | 
 | 25 |     EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE, | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 26 | }; | 
| Shawn Willden | 8fde4c2 | 2021-02-14 13:58:22 -0700 | [diff] [blame] | 27 | use std::convert::TryFrom; | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 28 | use std::convert::TryInto; | 
 | 29 | use std::marker::PhantomData; | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 30 | pub use zvec::ZVec; | 
 | 31 |  | 
 | 32 | /// Length of the expected initialization vector. | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 33 | pub const GCM_IV_LENGTH: usize = 12; | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 34 | /// Length of the expected AEAD TAG. | 
 | 35 | pub const TAG_LENGTH: usize = 16; | 
 | 36 | /// Length of an AES 256 key in bytes. | 
 | 37 | pub const AES_256_KEY_LENGTH: usize = 32; | 
 | 38 | /// Length of an AES 128 key in bytes. | 
 | 39 | pub const AES_128_KEY_LENGTH: usize = 16; | 
 | 40 | /// Length of the expected salt for key from password generation. | 
 | 41 | pub const SALT_LENGTH: usize = 16; | 
| David Drysdale | c97eb9e | 2022-01-26 13:03:48 -0800 | [diff] [blame] | 42 | /// Length of an HMAC-SHA256 tag in bytes. | 
 | 43 | pub const HMAC_SHA256_LEN: usize = 32; | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 44 |  | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 45 | /// Older versions of keystore produced IVs with four extra | 
 | 46 | /// ignored zero bytes at the end; recognise and trim those. | 
 | 47 | pub const LEGACY_IV_LENGTH: usize = 16; | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 48 |  | 
 | 49 | /// Generate an AES256 key, essentially 32 random bytes from the underlying | 
 | 50 | /// boringssl library discretely stuffed into a ZVec. | 
 | 51 | pub fn generate_aes256_key() -> Result<ZVec, Error> { | 
 | 52 |     // Safety: key has the same length as the requested number of random bytes. | 
 | 53 |     let mut key = ZVec::new(AES_256_KEY_LENGTH)?; | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 54 |     if unsafe { randomBytes(key.as_mut_ptr(), AES_256_KEY_LENGTH) } { | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 55 |         Ok(key) | 
 | 56 |     } else { | 
 | 57 |         Err(Error::RandomNumberGenerationFailed) | 
 | 58 |     } | 
 | 59 | } | 
 | 60 |  | 
 | 61 | /// Generate a salt. | 
 | 62 | pub fn generate_salt() -> Result<Vec<u8>, Error> { | 
| David Drysdale | 0e45a61 | 2021-02-25 17:24:36 +0000 | [diff] [blame] | 63 |     generate_random_data(SALT_LENGTH) | 
 | 64 | } | 
 | 65 |  | 
 | 66 | /// Generate random data of the given size. | 
 | 67 | pub fn generate_random_data(size: usize) -> Result<Vec<u8>, Error> { | 
 | 68 |     // Safety: data has the same length as the requested number of random bytes. | 
 | 69 |     let mut data = vec![0; size]; | 
 | 70 |     if unsafe { randomBytes(data.as_mut_ptr(), size) } { | 
 | 71 |         Ok(data) | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 72 |     } else { | 
 | 73 |         Err(Error::RandomNumberGenerationFailed) | 
 | 74 |     } | 
 | 75 | } | 
 | 76 |  | 
| David Drysdale | c97eb9e | 2022-01-26 13:03:48 -0800 | [diff] [blame] | 77 | /// Perform HMAC-SHA256. | 
 | 78 | pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> Result<Vec<u8>, Error> { | 
 | 79 |     let mut tag = vec![0; HMAC_SHA256_LEN]; | 
 | 80 |     // Safety: The first two pairs of arguments must point to const buffers with | 
 | 81 |     // size given by the second arg of the pair.  The final pair of arguments | 
 | 82 |     // must point to an output buffer with size given by the second arg of the | 
 | 83 |     // pair. | 
 | 84 |     match unsafe { | 
 | 85 |         hmacSha256(key.as_ptr(), key.len(), msg.as_ptr(), msg.len(), tag.as_mut_ptr(), tag.len()) | 
 | 86 |     } { | 
 | 87 |         true => Ok(tag), | 
 | 88 |         false => Err(Error::HmacSha256Failed), | 
 | 89 |     } | 
 | 90 | } | 
 | 91 |  | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 92 | /// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key. | 
 | 93 | /// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based | 
 | 94 | /// on the key length. | 
 | 95 | /// This function returns the plaintext message in a ZVec because it is assumed that | 
 | 96 | /// it contains sensitive information that should be zeroed from memory before its buffer is | 
 | 97 | /// freed. Input key is taken as a slice for flexibility, but it is recommended that it is held | 
 | 98 | /// in a ZVec as well. | 
 | 99 | pub fn aes_gcm_decrypt(data: &[u8], iv: &[u8], tag: &[u8], key: &[u8]) -> Result<ZVec, Error> { | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 100 |     // Old versions of aes_gcm_encrypt produced 16 byte IVs, but the last four bytes were ignored | 
 | 101 |     // so trim these to the correct size. | 
 | 102 |     let iv = match iv.len() { | 
 | 103 |         GCM_IV_LENGTH => iv, | 
 | 104 |         LEGACY_IV_LENGTH => &iv[..GCM_IV_LENGTH], | 
 | 105 |         _ => return Err(Error::InvalidIvLength), | 
 | 106 |     }; | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 107 |     if tag.len() != TAG_LENGTH { | 
 | 108 |         return Err(Error::InvalidAeadTagLength); | 
 | 109 |     } | 
 | 110 |  | 
 | 111 |     match key.len() { | 
 | 112 |         AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {} | 
 | 113 |         _ => return Err(Error::InvalidKeyLength), | 
 | 114 |     } | 
 | 115 |  | 
 | 116 |     let mut result = ZVec::new(data.len())?; | 
 | 117 |  | 
 | 118 |     // Safety: The first two arguments must point to buffers with a size given by the third | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 119 |     // argument. We pass the length of the key buffer along with the key. | 
 | 120 |     // The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above. | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 121 |     match unsafe { | 
 | 122 |         AES_gcm_decrypt( | 
 | 123 |             data.as_ptr(), | 
 | 124 |             result.as_mut_ptr(), | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 125 |             data.len(), | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 126 |             key.as_ptr(), | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 127 |             key.len(), | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 128 |             iv.as_ptr(), | 
 | 129 |             tag.as_ptr(), | 
 | 130 |         ) | 
 | 131 |     } { | 
 | 132 |         true => Ok(result), | 
 | 133 |         false => Err(Error::DecryptionFailed), | 
 | 134 |     } | 
 | 135 | } | 
 | 136 |  | 
 | 137 | /// Uses AES GCM to encrypt a message given a key. | 
 | 138 | /// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based on | 
 | 139 | /// the key length. The function generates an initialization vector. The return value is a tuple | 
 | 140 | /// of `(ciphertext, iv, tag)`. | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 141 | pub fn aes_gcm_encrypt(plaintext: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> { | 
 | 142 |     let mut iv = vec![0; GCM_IV_LENGTH]; | 
 | 143 |     // Safety: iv is GCM_IV_LENGTH bytes long. | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 144 |     if !unsafe { randomBytes(iv.as_mut_ptr(), GCM_IV_LENGTH) } { | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 145 |         return Err(Error::RandomNumberGenerationFailed); | 
 | 146 |     } | 
 | 147 |  | 
 | 148 |     match key.len() { | 
 | 149 |         AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {} | 
 | 150 |         _ => return Err(Error::InvalidKeyLength), | 
 | 151 |     } | 
 | 152 |  | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 153 |     let mut ciphertext: Vec<u8> = vec![0; plaintext.len()]; | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 154 |     let mut tag: Vec<u8> = vec![0; TAG_LENGTH]; | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 155 |     // Safety: The first two arguments must point to buffers with a size given by the third | 
 | 156 |     // argument. We pass the length of the key buffer along with the key. | 
 | 157 |     // The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above. | 
 | 158 |     if unsafe { | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 159 |         AES_gcm_encrypt( | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 160 |             plaintext.as_ptr(), | 
 | 161 |             ciphertext.as_mut_ptr(), | 
 | 162 |             plaintext.len(), | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 163 |             key.as_ptr(), | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 164 |             key.len(), | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 165 |             iv.as_ptr(), | 
 | 166 |             tag.as_mut_ptr(), | 
 | 167 |         ) | 
 | 168 |     } { | 
| Paul Crowley | 9a7f5a5 | 2021-04-23 16:12:08 -0700 | [diff] [blame] | 169 |         Ok((ciphertext, iv, tag)) | 
 | 170 |     } else { | 
 | 171 |         Err(Error::EncryptionFailed) | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 172 |     } | 
 | 173 | } | 
 | 174 |  | 
| Paul Crowley | f61fee7 | 2021-03-17 14:38:44 -0700 | [diff] [blame] | 175 | /// Represents a "password" that can be used to key the PBKDF2 algorithm. | 
 | 176 | pub enum Password<'a> { | 
 | 177 |     /// Borrow an existing byte array | 
 | 178 |     Ref(&'a [u8]), | 
 | 179 |     /// Use an owned ZVec to store the key | 
 | 180 |     Owned(ZVec), | 
 | 181 | } | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 182 |  | 
| Paul Crowley | f61fee7 | 2021-03-17 14:38:44 -0700 | [diff] [blame] | 183 | impl<'a> From<&'a [u8]> for Password<'a> { | 
 | 184 |     fn from(pw: &'a [u8]) -> Self { | 
 | 185 |         Self::Ref(pw) | 
 | 186 |     } | 
 | 187 | } | 
 | 188 |  | 
 | 189 | impl<'a> Password<'a> { | 
 | 190 |     fn get_key(&'a self) -> &'a [u8] { | 
 | 191 |         match self { | 
 | 192 |             Self::Ref(b) => b, | 
| Chris Wailes | 263de9f | 2022-08-11 15:00:51 -0700 | [diff] [blame] | 193 |             Self::Owned(z) => z, | 
| Paul Crowley | f61fee7 | 2021-03-17 14:38:44 -0700 | [diff] [blame] | 194 |         } | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 195 |     } | 
 | 196 |  | 
| Paul Crowley | f61fee7 | 2021-03-17 14:38:44 -0700 | [diff] [blame] | 197 |     /// Generate a key from the given password and salt. | 
 | 198 |     /// The salt must be exactly 16 bytes long. | 
 | 199 |     /// Two key sizes are accepted: 16 and 32 bytes. | 
| David Drysdale | 6a0ec2c | 2022-04-19 08:11:18 +0100 | [diff] [blame] | 200 |     pub fn derive_key(&self, salt: &[u8], key_length: usize) -> Result<ZVec, Error> { | 
 | 201 |         if salt.len() != SALT_LENGTH { | 
 | 202 |             return Err(Error::InvalidSaltLength); | 
 | 203 |         } | 
| Paul Crowley | f61fee7 | 2021-03-17 14:38:44 -0700 | [diff] [blame] | 204 |         match key_length { | 
 | 205 |             AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {} | 
 | 206 |             _ => return Err(Error::InvalidKeyLength), | 
 | 207 |         } | 
 | 208 |  | 
| David Drysdale | 6a0ec2c | 2022-04-19 08:11:18 +0100 | [diff] [blame] | 209 |         let pw = self.get_key(); | 
| Paul Crowley | f61fee7 | 2021-03-17 14:38:44 -0700 | [diff] [blame] | 210 |         let mut result = ZVec::new(key_length)?; | 
 | 211 |  | 
 | 212 |         unsafe { | 
 | 213 |             generateKeyFromPassword( | 
 | 214 |                 result.as_mut_ptr(), | 
 | 215 |                 result.len(), | 
 | 216 |                 pw.as_ptr() as *const std::os::raw::c_char, | 
 | 217 |                 pw.len(), | 
| David Drysdale | 6a0ec2c | 2022-04-19 08:11:18 +0100 | [diff] [blame] | 218 |                 salt.as_ptr(), | 
| Paul Crowley | f61fee7 | 2021-03-17 14:38:44 -0700 | [diff] [blame] | 219 |             ) | 
 | 220 |         }; | 
 | 221 |  | 
 | 222 |         Ok(result) | 
 | 223 |     } | 
 | 224 |  | 
 | 225 |     /// Try to make another Password object with the same data. | 
 | 226 |     pub fn try_clone(&self) -> Result<Password<'static>, Error> { | 
 | 227 |         Ok(Password::Owned(ZVec::try_from(self.get_key())?)) | 
 | 228 |     } | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 229 | } | 
| Joel Galenson | 46d6fd0 | 2020-11-19 17:58:33 -0800 | [diff] [blame] | 230 |  | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 231 | /// Calls the boringssl HKDF_extract function. | 
 | 232 | pub fn hkdf_extract(secret: &[u8], salt: &[u8]) -> Result<ZVec, Error> { | 
 | 233 |     let max_size: usize = EVP_MAX_MD_SIZE.try_into().unwrap(); | 
 | 234 |     let mut buf = ZVec::new(max_size)?; | 
 | 235 |     let mut out_len = 0; | 
 | 236 |     // Safety: HKDF_extract writes at most EVP_MAX_MD_SIZE bytes. | 
 | 237 |     // Secret and salt point to valid buffers. | 
 | 238 |     let result = unsafe { | 
 | 239 |         HKDFExtract( | 
 | 240 |             buf.as_mut_ptr(), | 
 | 241 |             &mut out_len, | 
 | 242 |             secret.as_ptr(), | 
 | 243 |             secret.len(), | 
 | 244 |             salt.as_ptr(), | 
 | 245 |             salt.len(), | 
 | 246 |         ) | 
 | 247 |     }; | 
 | 248 |     if !result { | 
 | 249 |         return Err(Error::HKDFExtractFailed); | 
 | 250 |     } | 
 | 251 |     // According to the boringssl API, this should never happen. | 
 | 252 |     if out_len > max_size { | 
 | 253 |         return Err(Error::HKDFExtractFailed); | 
 | 254 |     } | 
 | 255 |     // HKDF_extract may write fewer than the maximum number of bytes, so we | 
 | 256 |     // truncate the buffer. | 
 | 257 |     buf.reduce_len(out_len); | 
 | 258 |     Ok(buf) | 
 | 259 | } | 
 | 260 |  | 
 | 261 | /// Calls the boringssl HKDF_expand function. | 
 | 262 | pub fn hkdf_expand(out_len: usize, prk: &[u8], info: &[u8]) -> Result<ZVec, Error> { | 
 | 263 |     let mut buf = ZVec::new(out_len)?; | 
 | 264 |     // Safety: HKDF_expand writes out_len bytes to the buffer. | 
 | 265 |     // prk and info are valid buffers. | 
 | 266 |     let result = unsafe { | 
 | 267 |         HKDFExpand(buf.as_mut_ptr(), out_len, prk.as_ptr(), prk.len(), info.as_ptr(), info.len()) | 
 | 268 |     }; | 
 | 269 |     if !result { | 
 | 270 |         return Err(Error::HKDFExpandFailed); | 
 | 271 |     } | 
 | 272 |     Ok(buf) | 
 | 273 | } | 
 | 274 |  | 
 | 275 | /// A wrapper around the boringssl EC_KEY type that frees it on drop. | 
 | 276 | pub struct ECKey(*mut EC_KEY); | 
 | 277 |  | 
 | 278 | impl Drop for ECKey { | 
 | 279 |     fn drop(&mut self) { | 
 | 280 |         // Safety: We only create ECKey objects for valid EC_KEYs | 
 | 281 |         // and they are the sole owners of those keys. | 
 | 282 |         unsafe { EC_KEY_free(self.0) }; | 
 | 283 |     } | 
 | 284 | } | 
 | 285 |  | 
 | 286 | // Wrappers around the boringssl EC_POINT type. | 
 | 287 | // The EC_POINT can either be owned (and therefore mutable) or a pointer to an | 
 | 288 | // EC_POINT owned by someone else (and thus immutable).  The former are freed | 
 | 289 | // on drop. | 
 | 290 |  | 
 | 291 | /// An owned EC_POINT object. | 
 | 292 | pub struct OwnedECPoint(*mut EC_POINT); | 
 | 293 |  | 
 | 294 | /// A pointer to an EC_POINT object. | 
 | 295 | pub struct BorrowedECPoint<'a> { | 
 | 296 |     data: *const EC_POINT, | 
 | 297 |     phantom: PhantomData<&'a EC_POINT>, | 
 | 298 | } | 
 | 299 |  | 
 | 300 | impl OwnedECPoint { | 
 | 301 |     /// Get the wrapped EC_POINT object. | 
 | 302 |     pub fn get_point(&self) -> &EC_POINT { | 
 | 303 |         // Safety: We only create OwnedECPoint objects for valid EC_POINTs. | 
 | 304 |         unsafe { self.0.as_ref().unwrap() } | 
 | 305 |     } | 
 | 306 | } | 
 | 307 |  | 
 | 308 | impl<'a> BorrowedECPoint<'a> { | 
 | 309 |     /// Get the wrapped EC_POINT object. | 
 | 310 |     pub fn get_point(&self) -> &EC_POINT { | 
 | 311 |         // Safety: We only create BorrowedECPoint objects for valid EC_POINTs. | 
 | 312 |         unsafe { self.data.as_ref().unwrap() } | 
 | 313 |     } | 
 | 314 | } | 
 | 315 |  | 
 | 316 | impl Drop for OwnedECPoint { | 
 | 317 |     fn drop(&mut self) { | 
 | 318 |         // Safety: We only create OwnedECPoint objects for valid | 
 | 319 |         // EC_POINTs and they are the sole owners of those points. | 
 | 320 |         unsafe { EC_POINT_free(self.0) }; | 
 | 321 |     } | 
 | 322 | } | 
 | 323 |  | 
 | 324 | /// Calls the boringssl ECDH_compute_key function. | 
 | 325 | pub fn ecdh_compute_key(pub_key: &EC_POINT, priv_key: &ECKey) -> Result<ZVec, Error> { | 
 | 326 |     let mut buf = ZVec::new(EC_MAX_BYTES)?; | 
 | 327 |     // Safety: Our ECDHComputeKey wrapper passes EC_MAX_BYES to ECDH_compute_key, which | 
 | 328 |     // writes at most that many bytes to the output. | 
 | 329 |     // The two keys are valid objects. | 
 | 330 |     let result = | 
 | 331 |         unsafe { ECDHComputeKey(buf.as_mut_ptr() as *mut std::ffi::c_void, pub_key, priv_key.0) }; | 
 | 332 |     if result == -1 { | 
 | 333 |         return Err(Error::ECDHComputeKeyFailed); | 
 | 334 |     } | 
 | 335 |     let out_len = result.try_into().unwrap(); | 
 | 336 |     // According to the boringssl API, this should never happen. | 
 | 337 |     if out_len > buf.len() { | 
 | 338 |         return Err(Error::ECDHComputeKeyFailed); | 
 | 339 |     } | 
 | 340 |     // ECDH_compute_key may write fewer than the maximum number of bytes, so we | 
 | 341 |     // truncate the buffer. | 
 | 342 |     buf.reduce_len(out_len); | 
 | 343 |     Ok(buf) | 
 | 344 | } | 
 | 345 |  | 
 | 346 | /// Calls the boringssl EC_KEY_generate_key function. | 
 | 347 | pub fn ec_key_generate_key() -> Result<ECKey, Error> { | 
 | 348 |     // Safety: Creates a new key on its own. | 
 | 349 |     let key = unsafe { ECKEYGenerateKey() }; | 
 | 350 |     if key.is_null() { | 
 | 351 |         return Err(Error::ECKEYGenerateKeyFailed); | 
 | 352 |     } | 
 | 353 |     Ok(ECKey(key)) | 
 | 354 | } | 
 | 355 |  | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 356 | /// Calls the boringssl EC_KEY_marshal_private_key function. | 
 | 357 | pub fn ec_key_marshal_private_key(key: &ECKey) -> Result<ZVec, Error> { | 
| Paul Crowley | 52f017f | 2021-06-22 08:16:01 -0700 | [diff] [blame] | 358 |     let len = 73; // Empirically observed length of private key | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 359 |     let mut buf = ZVec::new(len)?; | 
 | 360 |     // Safety: the key is valid. | 
 | 361 |     // This will not write past the specified length of the buffer; if the | 
 | 362 |     // len above is too short, it returns 0. | 
| Charisee | 03e0084 | 2023-01-25 01:41:23 +0000 | [diff] [blame] | 363 |     let written_len = unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) }; | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 364 |     if written_len == len { | 
 | 365 |         Ok(buf) | 
 | 366 |     } else { | 
 | 367 |         Err(Error::ECKEYMarshalPrivateKeyFailed) | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 368 |     } | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 369 | } | 
 | 370 |  | 
 | 371 | /// Calls the boringssl EC_KEY_parse_private_key function. | 
 | 372 | pub fn ec_key_parse_private_key(buf: &[u8]) -> Result<ECKey, Error> { | 
 | 373 |     // Safety: this will not read past the specified length of the buffer. | 
 | 374 |     // It fails if less than the whole buffer is consumed. | 
 | 375 |     let key = unsafe { ECKEYParsePrivateKey(buf.as_ptr(), buf.len()) }; | 
 | 376 |     if key.is_null() { | 
 | 377 |         Err(Error::ECKEYParsePrivateKeyFailed) | 
 | 378 |     } else { | 
 | 379 |         Ok(ECKey(key)) | 
 | 380 |     } | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 381 | } | 
 | 382 |  | 
 | 383 | /// Calls the boringssl EC_KEY_get0_public_key function. | 
 | 384 | pub fn ec_key_get0_public_key(key: &ECKey) -> BorrowedECPoint { | 
 | 385 |     // Safety: The key is valid. | 
 | 386 |     // This returns a pointer to a key, so we create an immutable variant. | 
 | 387 |     BorrowedECPoint { data: unsafe { EC_KEY_get0_public_key(key.0) }, phantom: PhantomData } | 
 | 388 | } | 
 | 389 |  | 
 | 390 | /// Calls the boringssl EC_POINT_point2oct. | 
 | 391 | pub fn ec_point_point_to_oct(point: &EC_POINT) -> Result<Vec<u8>, Error> { | 
| Paul Crowley | 52f017f | 2021-06-22 08:16:01 -0700 | [diff] [blame] | 392 |     // We fix the length to 133 (1 + 2 * field_elem_size), as we get an error if it's too small. | 
 | 393 |     let len = 133; | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 394 |     let mut buf = vec![0; len]; | 
 | 395 |     // Safety: EC_POINT_point2oct writes at most len bytes. The point is valid. | 
 | 396 |     let result = unsafe { ECPOINTPoint2Oct(point, buf.as_mut_ptr(), len) }; | 
 | 397 |     if result == 0 { | 
 | 398 |         return Err(Error::ECPoint2OctFailed); | 
 | 399 |     } | 
 | 400 |     // According to the boringssl API, this should never happen. | 
 | 401 |     if result > len { | 
 | 402 |         return Err(Error::ECPoint2OctFailed); | 
 | 403 |     } | 
 | 404 |     buf.resize(result, 0); | 
 | 405 |     Ok(buf) | 
 | 406 | } | 
 | 407 |  | 
 | 408 | /// Calls the boringssl EC_POINT_oct2point function. | 
 | 409 | pub fn ec_point_oct_to_point(buf: &[u8]) -> Result<OwnedECPoint, Error> { | 
 | 410 |     // Safety: The buffer is valid. | 
 | 411 |     let result = unsafe { ECPOINTOct2Point(buf.as_ptr(), buf.len()) }; | 
 | 412 |     if result.is_null() { | 
 | 413 |         return Err(Error::ECPoint2OctFailed); | 
 | 414 |     } | 
 | 415 |     // Our C wrapper creates a new EC_POINT, so we mark this mutable and free | 
 | 416 |     // it on drop. | 
 | 417 |     Ok(OwnedECPoint(result)) | 
 | 418 | } | 
 | 419 |  | 
| Shawn Willden | 3412087 | 2021-02-24 21:56:30 -0700 | [diff] [blame] | 420 | /// Uses BoringSSL to extract the DER-encoded subject from a DER-encoded X.509 certificate. | 
 | 421 | pub fn parse_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error> { | 
| Shawn Willden | 8fde4c2 | 2021-02-14 13:58:22 -0700 | [diff] [blame] | 422 |     // Try with a 200-byte output buffer, should be enough in all but bizarre cases. | 
 | 423 |     let mut retval = vec![0; 200]; | 
| Shawn Willden | 3412087 | 2021-02-24 21:56:30 -0700 | [diff] [blame] | 424 |  | 
 | 425 |     // Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf and | 
 | 426 |     // writes at most retval.len() bytes to retval. | 
| Shawn Willden | 8fde4c2 | 2021-02-14 13:58:22 -0700 | [diff] [blame] | 427 |     let mut size = unsafe { | 
 | 428 |         extractSubjectFromCertificate( | 
 | 429 |             cert_buf.as_ptr(), | 
 | 430 |             cert_buf.len(), | 
 | 431 |             retval.as_mut_ptr(), | 
 | 432 |             retval.len(), | 
 | 433 |         ) | 
 | 434 |     }; | 
 | 435 |  | 
 | 436 |     if size == 0 { | 
 | 437 |         return Err(Error::ExtractSubjectFailed); | 
 | 438 |     } | 
 | 439 |  | 
 | 440 |     if size < 0 { | 
 | 441 |         // Our buffer wasn't big enough.  Make one that is just the right size and try again. | 
| Shawn Willden | 3412087 | 2021-02-24 21:56:30 -0700 | [diff] [blame] | 442 |         let negated_size = usize::try_from(-size).map_err(|_e| Error::ExtractSubjectFailed)?; | 
 | 443 |         retval = vec![0; negated_size]; | 
| Shawn Willden | 8fde4c2 | 2021-02-14 13:58:22 -0700 | [diff] [blame] | 444 |  | 
| Shawn Willden | 3412087 | 2021-02-24 21:56:30 -0700 | [diff] [blame] | 445 |         // Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf | 
 | 446 |         // and writes at most retval.len() bytes to retval. | 
| Shawn Willden | 8fde4c2 | 2021-02-14 13:58:22 -0700 | [diff] [blame] | 447 |         size = unsafe { | 
 | 448 |             extractSubjectFromCertificate( | 
 | 449 |                 cert_buf.as_ptr(), | 
 | 450 |                 cert_buf.len(), | 
 | 451 |                 retval.as_mut_ptr(), | 
 | 452 |                 retval.len(), | 
 | 453 |             ) | 
 | 454 |         }; | 
 | 455 |  | 
 | 456 |         if size <= 0 { | 
 | 457 |             return Err(Error::ExtractSubjectFailed); | 
 | 458 |         } | 
 | 459 |     } | 
 | 460 |  | 
 | 461 |     // Reduce buffer size to the amount written. | 
| Shawn Willden | 3412087 | 2021-02-24 21:56:30 -0700 | [diff] [blame] | 462 |     let safe_size = usize::try_from(size).map_err(|_e| Error::ExtractSubjectFailed)?; | 
 | 463 |     retval.truncate(safe_size); | 
| Shawn Willden | 8fde4c2 | 2021-02-14 13:58:22 -0700 | [diff] [blame] | 464 |  | 
 | 465 |     Ok(retval) | 
 | 466 | } | 
 | 467 |  | 
| Joel Galenson | ca0efb1 | 2020-10-01 14:32:30 -0700 | [diff] [blame] | 468 | #[cfg(test)] | 
 | 469 | mod tests { | 
 | 470 |  | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 471 |     use super::*; | 
| Joel Galenson | ca0efb1 | 2020-10-01 14:32:30 -0700 | [diff] [blame] | 472 |     use keystore2_crypto_bindgen::{ | 
 | 473 |         generateKeyFromPassword, AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId, | 
 | 474 |     }; | 
 | 475 |  | 
 | 476 |     #[test] | 
| Janis Danisevskis | 9d90b81 | 2020-11-25 21:02:11 -0800 | [diff] [blame] | 477 |     fn test_wrapper_roundtrip() { | 
 | 478 |         let key = generate_aes256_key().unwrap(); | 
 | 479 |         let message = b"totally awesome message"; | 
 | 480 |         let (cipher_text, iv, tag) = aes_gcm_encrypt(message, &key).unwrap(); | 
 | 481 |         let message2 = aes_gcm_decrypt(&cipher_text, &iv, &tag, &key).unwrap(); | 
 | 482 |         assert_eq!(message[..], message2[..]) | 
 | 483 |     } | 
 | 484 |  | 
 | 485 |     #[test] | 
| Joel Galenson | ca0efb1 | 2020-10-01 14:32:30 -0700 | [diff] [blame] | 486 |     fn test_encrypt_decrypt() { | 
 | 487 |         let input = vec![0; 16]; | 
 | 488 |         let mut out = vec![0; 16]; | 
 | 489 |         let mut out2 = vec![0; 16]; | 
 | 490 |         let key = vec![0; 16]; | 
 | 491 |         let iv = vec![0; 12]; | 
 | 492 |         let mut tag = vec![0; 16]; | 
 | 493 |         unsafe { | 
 | 494 |             let res = AES_gcm_encrypt( | 
 | 495 |                 input.as_ptr(), | 
 | 496 |                 out.as_mut_ptr(), | 
 | 497 |                 16, | 
 | 498 |                 key.as_ptr(), | 
 | 499 |                 16, | 
 | 500 |                 iv.as_ptr(), | 
 | 501 |                 tag.as_mut_ptr(), | 
 | 502 |             ); | 
 | 503 |             assert!(res); | 
 | 504 |             assert_ne!(out, input); | 
 | 505 |             assert_ne!(tag, input); | 
 | 506 |             let res = AES_gcm_decrypt( | 
 | 507 |                 out.as_ptr(), | 
 | 508 |                 out2.as_mut_ptr(), | 
 | 509 |                 16, | 
 | 510 |                 key.as_ptr(), | 
 | 511 |                 16, | 
 | 512 |                 iv.as_ptr(), | 
 | 513 |                 tag.as_ptr(), | 
 | 514 |             ); | 
 | 515 |             assert!(res); | 
 | 516 |             assert_eq!(out2, input); | 
 | 517 |         } | 
 | 518 |     } | 
 | 519 |  | 
 | 520 |     #[test] | 
 | 521 |     fn test_create_key_id() { | 
 | 522 |         let blob = vec![0; 16]; | 
 | 523 |         let mut out: u64 = 0; | 
 | 524 |         unsafe { | 
 | 525 |             let res = CreateKeyId(blob.as_ptr(), 16, &mut out); | 
 | 526 |             assert!(res); | 
 | 527 |             assert_ne!(out, 0); | 
 | 528 |         } | 
 | 529 |     } | 
 | 530 |  | 
 | 531 |     #[test] | 
 | 532 |     fn test_generate_key_from_password() { | 
 | 533 |         let mut key = vec![0; 16]; | 
 | 534 |         let pw = vec![0; 16]; | 
| David Drysdale | 6a0ec2c | 2022-04-19 08:11:18 +0100 | [diff] [blame] | 535 |         let salt = vec![0; 16]; | 
| Joel Galenson | ca0efb1 | 2020-10-01 14:32:30 -0700 | [diff] [blame] | 536 |         unsafe { | 
| David Drysdale | 6a0ec2c | 2022-04-19 08:11:18 +0100 | [diff] [blame] | 537 |             generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_ptr()); | 
| Joel Galenson | ca0efb1 | 2020-10-01 14:32:30 -0700 | [diff] [blame] | 538 |         } | 
 | 539 |         assert_ne!(key, vec![0; 16]); | 
 | 540 |     } | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 541 |  | 
 | 542 |     #[test] | 
 | 543 |     fn test_hkdf() { | 
 | 544 |         let result = hkdf_extract(&[0; 16], &[0; 16]); | 
 | 545 |         assert!(result.is_ok()); | 
 | 546 |         for out_len in 4..=8 { | 
 | 547 |             let result = hkdf_expand(out_len, &[0; 16], &[0; 16]); | 
 | 548 |             assert!(result.is_ok()); | 
 | 549 |             assert_eq!(result.unwrap().len(), out_len); | 
 | 550 |         } | 
 | 551 |     } | 
 | 552 |  | 
 | 553 |     #[test] | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 554 |     fn test_ec() -> Result<(), Error> { | 
 | 555 |         let priv0 = ec_key_generate_key()?; | 
 | 556 |         assert!(!priv0.0.is_null()); | 
 | 557 |         let pub0 = ec_key_get0_public_key(&priv0); | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 558 |  | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 559 |         let priv1 = ec_key_generate_key()?; | 
 | 560 |         let pub1 = ec_key_get0_public_key(&priv1); | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 561 |  | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 562 |         let priv0s = ec_key_marshal_private_key(&priv0)?; | 
 | 563 |         let pub0s = ec_point_point_to_oct(pub0.get_point())?; | 
 | 564 |         let pub1s = ec_point_point_to_oct(pub1.get_point())?; | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 565 |  | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 566 |         let priv0 = ec_key_parse_private_key(&priv0s)?; | 
 | 567 |         let pub0 = ec_point_oct_to_point(&pub0s)?; | 
 | 568 |         let pub1 = ec_point_oct_to_point(&pub1s)?; | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 569 |  | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 570 |         let left_key = ecdh_compute_key(pub0.get_point(), &priv1)?; | 
 | 571 |         let right_key = ecdh_compute_key(pub1.get_point(), &priv0)?; | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 572 |  | 
| Paul Crowley | 7bb5edd | 2021-03-20 20:26:43 -0700 | [diff] [blame] | 573 |         assert_eq!(left_key, right_key); | 
 | 574 |         Ok(()) | 
| Joel Galenson | 0591458 | 2021-01-08 09:30:41 -0800 | [diff] [blame] | 575 |     } | 
| David Drysdale | c97eb9e | 2022-01-26 13:03:48 -0800 | [diff] [blame] | 576 |  | 
 | 577 |     #[test] | 
 | 578 |     fn test_hmac_sha256() { | 
 | 579 |         let key = b"This is the key"; | 
 | 580 |         let msg1 = b"This is a message"; | 
 | 581 |         let msg2 = b"This is another message"; | 
 | 582 |         let tag1a = hmac_sha256(key, msg1).unwrap(); | 
 | 583 |         assert_eq!(tag1a.len(), HMAC_SHA256_LEN); | 
 | 584 |         let tag1b = hmac_sha256(key, msg1).unwrap(); | 
 | 585 |         assert_eq!(tag1a, tag1b); | 
 | 586 |         let tag2 = hmac_sha256(key, msg2).unwrap(); | 
 | 587 |         assert_eq!(tag2.len(), HMAC_SHA256_LEN); | 
 | 588 |         assert_ne!(tag1a, tag2); | 
 | 589 |     } | 
| Joel Galenson | ca0efb1 | 2020-10-01 14:32:30 -0700 | [diff] [blame] | 590 | } |