blob: 92b257c2de59996af3f1bf5eb04f660a38a9e5fd [file] [log] [blame]
Joel Galensonca0efb12020-10-01 14:32:30 -07001// 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 Danisevskis9d90b812020-11-25 21:02:11 -080015//! This module implements safe wrappers for some crypto operations required by
16//! Keystore 2.0.
17
18mod error;
19mod zvec;
20pub use error::Error;
21use keystore2_crypto_bindgen::{
Joel Galenson05914582021-01-08 09:30:41 -080022 generateKeyFromPassword, randomBytes, AES_gcm_decrypt, AES_gcm_encrypt, ECDHComputeKey,
23 ECKEYDeriveFromSecret, ECKEYGenerateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free,
24 EC_KEY_get0_public_key, EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT,
25 EVP_MAX_MD_SIZE,
Janis Danisevskis9d90b812020-11-25 21:02:11 -080026};
Joel Galenson05914582021-01-08 09:30:41 -080027use std::convert::TryInto;
28use std::marker::PhantomData;
Janis Danisevskis9d90b812020-11-25 21:02:11 -080029pub use zvec::ZVec;
30
31/// Length of the expected initialization vector.
32pub const IV_LENGTH: usize = 16;
33/// Length of the expected AEAD TAG.
34pub const TAG_LENGTH: usize = 16;
35/// Length of an AES 256 key in bytes.
36pub const AES_256_KEY_LENGTH: usize = 32;
37/// Length of an AES 128 key in bytes.
38pub const AES_128_KEY_LENGTH: usize = 16;
39/// Length of the expected salt for key from password generation.
40pub const SALT_LENGTH: usize = 16;
41
42// This is the number of bytes of the GCM IV that is expected to be initialized
43// with random bytes.
44const GCM_IV_LENGTH: usize = 12;
45
46/// Generate an AES256 key, essentially 32 random bytes from the underlying
47/// boringssl library discretely stuffed into a ZVec.
48pub fn generate_aes256_key() -> Result<ZVec, Error> {
49 // Safety: key has the same length as the requested number of random bytes.
50 let mut key = ZVec::new(AES_256_KEY_LENGTH)?;
Joel Galenson05914582021-01-08 09:30:41 -080051 if unsafe { randomBytes(key.as_mut_ptr(), AES_256_KEY_LENGTH) } {
Janis Danisevskis9d90b812020-11-25 21:02:11 -080052 Ok(key)
53 } else {
54 Err(Error::RandomNumberGenerationFailed)
55 }
56}
57
58/// Generate a salt.
59pub fn generate_salt() -> Result<Vec<u8>, Error> {
60 // Safety: salt has the same length as the requested number of random bytes.
61 let mut salt = vec![0; SALT_LENGTH];
Joel Galenson05914582021-01-08 09:30:41 -080062 if unsafe { randomBytes(salt.as_mut_ptr(), SALT_LENGTH) } {
Janis Danisevskis9d90b812020-11-25 21:02:11 -080063 Ok(salt)
64 } else {
65 Err(Error::RandomNumberGenerationFailed)
66 }
67}
68
69/// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key.
70/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based
71/// on the key length.
72/// This function returns the plaintext message in a ZVec because it is assumed that
73/// it contains sensitive information that should be zeroed from memory before its buffer is
74/// freed. Input key is taken as a slice for flexibility, but it is recommended that it is held
75/// in a ZVec as well.
76pub fn aes_gcm_decrypt(data: &[u8], iv: &[u8], tag: &[u8], key: &[u8]) -> Result<ZVec, Error> {
77 if iv.len() != IV_LENGTH {
78 return Err(Error::InvalidIvLength);
79 }
80
81 if tag.len() != TAG_LENGTH {
82 return Err(Error::InvalidAeadTagLength);
83 }
84
85 match key.len() {
86 AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
87 _ => return Err(Error::InvalidKeyLength),
88 }
89
90 let mut result = ZVec::new(data.len())?;
91
92 // Safety: The first two arguments must point to buffers with a size given by the third
93 // argument. The key must have a size of 16 or 32 bytes which we check above.
94 // The iv and tag arguments must be 16 bytes, which we also check above.
95 match unsafe {
96 AES_gcm_decrypt(
97 data.as_ptr(),
98 result.as_mut_ptr(),
Joel Galenson05914582021-01-08 09:30:41 -080099 data.len(),
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800100 key.as_ptr(),
Joel Galenson05914582021-01-08 09:30:41 -0800101 key.len(),
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800102 iv.as_ptr(),
103 tag.as_ptr(),
104 )
105 } {
106 true => Ok(result),
107 false => Err(Error::DecryptionFailed),
108 }
109}
110
111/// Uses AES GCM to encrypt a message given a key.
112/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based on
113/// the key length. The function generates an initialization vector. The return value is a tuple
114/// of `(ciphertext, iv, tag)`.
115pub fn aes_gcm_encrypt(data: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
116 let mut iv = vec![0; IV_LENGTH];
117 // Safety: iv is longer than GCM_IV_LENGTH, which is 12 while IV_LENGTH is 16.
118 // The iv needs to be 16 bytes long, but the last 4 bytes remain zeroed.
Joel Galenson05914582021-01-08 09:30:41 -0800119 if !unsafe { randomBytes(iv.as_mut_ptr(), GCM_IV_LENGTH) } {
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800120 return Err(Error::RandomNumberGenerationFailed);
121 }
122
123 match key.len() {
124 AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
125 _ => return Err(Error::InvalidKeyLength),
126 }
127
128 let mut result: Vec<u8> = vec![0; data.len()];
129 let mut tag: Vec<u8> = vec![0; TAG_LENGTH];
130 match unsafe {
131 AES_gcm_encrypt(
132 data.as_ptr(),
133 result.as_mut_ptr(),
Joel Galenson05914582021-01-08 09:30:41 -0800134 data.len(),
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800135 key.as_ptr(),
Joel Galenson05914582021-01-08 09:30:41 -0800136 key.len(),
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800137 iv.as_ptr(),
138 tag.as_mut_ptr(),
139 )
140 } {
141 true => Ok((result, iv, tag)),
142 false => Err(Error::EncryptionFailed),
143 }
144}
145
146/// Generates a key from the given password and salt.
147/// The salt must be exactly 16 bytes long.
148/// Two key sizes are accepted: 16 and 32 bytes.
149pub fn derive_key_from_password(
150 pw: &[u8],
151 salt: Option<&[u8]>,
152 key_length: usize,
153) -> Result<ZVec, Error> {
154 let salt: *const u8 = match salt {
155 Some(s) => {
156 if s.len() != SALT_LENGTH {
157 return Err(Error::InvalidSaltLength);
158 }
159 s.as_ptr()
160 }
161 None => std::ptr::null(),
162 };
163
164 match key_length {
165 AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
166 _ => return Err(Error::InvalidKeyLength),
167 }
168
169 let mut result = ZVec::new(key_length)?;
170
171 unsafe {
172 generateKeyFromPassword(
173 result.as_mut_ptr(),
Joel Galenson05914582021-01-08 09:30:41 -0800174 result.len(),
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800175 pw.as_ptr() as *const std::os::raw::c_char,
Joel Galenson05914582021-01-08 09:30:41 -0800176 pw.len(),
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800177 salt,
178 )
179 };
180
181 Ok(result)
182}
Joel Galenson46d6fd02020-11-19 17:58:33 -0800183
Joel Galenson05914582021-01-08 09:30:41 -0800184/// Calls the boringssl HKDF_extract function.
185pub fn hkdf_extract(secret: &[u8], salt: &[u8]) -> Result<ZVec, Error> {
186 let max_size: usize = EVP_MAX_MD_SIZE.try_into().unwrap();
187 let mut buf = ZVec::new(max_size)?;
188 let mut out_len = 0;
189 // Safety: HKDF_extract writes at most EVP_MAX_MD_SIZE bytes.
190 // Secret and salt point to valid buffers.
191 let result = unsafe {
192 HKDFExtract(
193 buf.as_mut_ptr(),
194 &mut out_len,
195 secret.as_ptr(),
196 secret.len(),
197 salt.as_ptr(),
198 salt.len(),
199 )
200 };
201 if !result {
202 return Err(Error::HKDFExtractFailed);
203 }
204 // According to the boringssl API, this should never happen.
205 if out_len > max_size {
206 return Err(Error::HKDFExtractFailed);
207 }
208 // HKDF_extract may write fewer than the maximum number of bytes, so we
209 // truncate the buffer.
210 buf.reduce_len(out_len);
211 Ok(buf)
212}
213
214/// Calls the boringssl HKDF_expand function.
215pub fn hkdf_expand(out_len: usize, prk: &[u8], info: &[u8]) -> Result<ZVec, Error> {
216 let mut buf = ZVec::new(out_len)?;
217 // Safety: HKDF_expand writes out_len bytes to the buffer.
218 // prk and info are valid buffers.
219 let result = unsafe {
220 HKDFExpand(buf.as_mut_ptr(), out_len, prk.as_ptr(), prk.len(), info.as_ptr(), info.len())
221 };
222 if !result {
223 return Err(Error::HKDFExpandFailed);
224 }
225 Ok(buf)
226}
227
228/// A wrapper around the boringssl EC_KEY type that frees it on drop.
229pub struct ECKey(*mut EC_KEY);
230
231impl Drop for ECKey {
232 fn drop(&mut self) {
233 // Safety: We only create ECKey objects for valid EC_KEYs
234 // and they are the sole owners of those keys.
235 unsafe { EC_KEY_free(self.0) };
236 }
237}
238
239// Wrappers around the boringssl EC_POINT type.
240// The EC_POINT can either be owned (and therefore mutable) or a pointer to an
241// EC_POINT owned by someone else (and thus immutable). The former are freed
242// on drop.
243
244/// An owned EC_POINT object.
245pub struct OwnedECPoint(*mut EC_POINT);
246
247/// A pointer to an EC_POINT object.
248pub struct BorrowedECPoint<'a> {
249 data: *const EC_POINT,
250 phantom: PhantomData<&'a EC_POINT>,
251}
252
253impl OwnedECPoint {
254 /// Get the wrapped EC_POINT object.
255 pub fn get_point(&self) -> &EC_POINT {
256 // Safety: We only create OwnedECPoint objects for valid EC_POINTs.
257 unsafe { self.0.as_ref().unwrap() }
258 }
259}
260
261impl<'a> BorrowedECPoint<'a> {
262 /// Get the wrapped EC_POINT object.
263 pub fn get_point(&self) -> &EC_POINT {
264 // Safety: We only create BorrowedECPoint objects for valid EC_POINTs.
265 unsafe { self.data.as_ref().unwrap() }
266 }
267}
268
269impl Drop for OwnedECPoint {
270 fn drop(&mut self) {
271 // Safety: We only create OwnedECPoint objects for valid
272 // EC_POINTs and they are the sole owners of those points.
273 unsafe { EC_POINT_free(self.0) };
274 }
275}
276
277/// Calls the boringssl ECDH_compute_key function.
278pub fn ecdh_compute_key(pub_key: &EC_POINT, priv_key: &ECKey) -> Result<ZVec, Error> {
279 let mut buf = ZVec::new(EC_MAX_BYTES)?;
280 // Safety: Our ECDHComputeKey wrapper passes EC_MAX_BYES to ECDH_compute_key, which
281 // writes at most that many bytes to the output.
282 // The two keys are valid objects.
283 let result =
284 unsafe { ECDHComputeKey(buf.as_mut_ptr() as *mut std::ffi::c_void, pub_key, priv_key.0) };
285 if result == -1 {
286 return Err(Error::ECDHComputeKeyFailed);
287 }
288 let out_len = result.try_into().unwrap();
289 // According to the boringssl API, this should never happen.
290 if out_len > buf.len() {
291 return Err(Error::ECDHComputeKeyFailed);
292 }
293 // ECDH_compute_key may write fewer than the maximum number of bytes, so we
294 // truncate the buffer.
295 buf.reduce_len(out_len);
296 Ok(buf)
297}
298
299/// Calls the boringssl EC_KEY_generate_key function.
300pub fn ec_key_generate_key() -> Result<ECKey, Error> {
301 // Safety: Creates a new key on its own.
302 let key = unsafe { ECKEYGenerateKey() };
303 if key.is_null() {
304 return Err(Error::ECKEYGenerateKeyFailed);
305 }
306 Ok(ECKey(key))
307}
308
309/// Calls the boringssl EC_KEY_derive_from_secret function.
310pub fn ec_key_derive_from_secret(secret: &[u8]) -> Result<ECKey, Error> {
311 // Safety: secret is a valid buffer.
312 let result = unsafe { ECKEYDeriveFromSecret(secret.as_ptr(), secret.len()) };
313 if result.is_null() {
314 return Err(Error::ECKEYDeriveFailed);
315 }
316 Ok(ECKey(result))
317}
318
319/// Calls the boringssl EC_KEY_get0_public_key function.
320pub fn ec_key_get0_public_key(key: &ECKey) -> BorrowedECPoint {
321 // Safety: The key is valid.
322 // This returns a pointer to a key, so we create an immutable variant.
323 BorrowedECPoint { data: unsafe { EC_KEY_get0_public_key(key.0) }, phantom: PhantomData }
324}
325
326/// Calls the boringssl EC_POINT_point2oct.
327pub fn ec_point_point_to_oct(point: &EC_POINT) -> Result<Vec<u8>, Error> {
328 // We fix the length to 65 (1 + 2 * field_elem_size), as we get an error if it's too small.
329 let len = 65;
330 let mut buf = vec![0; len];
331 // Safety: EC_POINT_point2oct writes at most len bytes. The point is valid.
332 let result = unsafe { ECPOINTPoint2Oct(point, buf.as_mut_ptr(), len) };
333 if result == 0 {
334 return Err(Error::ECPoint2OctFailed);
335 }
336 // According to the boringssl API, this should never happen.
337 if result > len {
338 return Err(Error::ECPoint2OctFailed);
339 }
340 buf.resize(result, 0);
341 Ok(buf)
342}
343
344/// Calls the boringssl EC_POINT_oct2point function.
345pub fn ec_point_oct_to_point(buf: &[u8]) -> Result<OwnedECPoint, Error> {
346 // Safety: The buffer is valid.
347 let result = unsafe { ECPOINTOct2Point(buf.as_ptr(), buf.len()) };
348 if result.is_null() {
349 return Err(Error::ECPoint2OctFailed);
350 }
351 // Our C wrapper creates a new EC_POINT, so we mark this mutable and free
352 // it on drop.
353 Ok(OwnedECPoint(result))
354}
355
Joel Galensonca0efb12020-10-01 14:32:30 -0700356#[cfg(test)]
357mod tests {
358
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800359 use super::*;
Joel Galensonca0efb12020-10-01 14:32:30 -0700360 use keystore2_crypto_bindgen::{
361 generateKeyFromPassword, AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId,
362 };
363
364 #[test]
Janis Danisevskis9d90b812020-11-25 21:02:11 -0800365 fn test_wrapper_roundtrip() {
366 let key = generate_aes256_key().unwrap();
367 let message = b"totally awesome message";
368 let (cipher_text, iv, tag) = aes_gcm_encrypt(message, &key).unwrap();
369 let message2 = aes_gcm_decrypt(&cipher_text, &iv, &tag, &key).unwrap();
370 assert_eq!(message[..], message2[..])
371 }
372
373 #[test]
Joel Galensonca0efb12020-10-01 14:32:30 -0700374 fn test_encrypt_decrypt() {
375 let input = vec![0; 16];
376 let mut out = vec![0; 16];
377 let mut out2 = vec![0; 16];
378 let key = vec![0; 16];
379 let iv = vec![0; 12];
380 let mut tag = vec![0; 16];
381 unsafe {
382 let res = AES_gcm_encrypt(
383 input.as_ptr(),
384 out.as_mut_ptr(),
385 16,
386 key.as_ptr(),
387 16,
388 iv.as_ptr(),
389 tag.as_mut_ptr(),
390 );
391 assert!(res);
392 assert_ne!(out, input);
393 assert_ne!(tag, input);
394 let res = AES_gcm_decrypt(
395 out.as_ptr(),
396 out2.as_mut_ptr(),
397 16,
398 key.as_ptr(),
399 16,
400 iv.as_ptr(),
401 tag.as_ptr(),
402 );
403 assert!(res);
404 assert_eq!(out2, input);
405 }
406 }
407
408 #[test]
409 fn test_create_key_id() {
410 let blob = vec![0; 16];
411 let mut out: u64 = 0;
412 unsafe {
413 let res = CreateKeyId(blob.as_ptr(), 16, &mut out);
414 assert!(res);
415 assert_ne!(out, 0);
416 }
417 }
418
419 #[test]
420 fn test_generate_key_from_password() {
421 let mut key = vec![0; 16];
422 let pw = vec![0; 16];
423 let mut salt = vec![0; 16];
424 unsafe {
425 generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_mut_ptr());
426 }
427 assert_ne!(key, vec![0; 16]);
428 }
Joel Galenson05914582021-01-08 09:30:41 -0800429
430 #[test]
431 fn test_hkdf() {
432 let result = hkdf_extract(&[0; 16], &[0; 16]);
433 assert!(result.is_ok());
434 for out_len in 4..=8 {
435 let result = hkdf_expand(out_len, &[0; 16], &[0; 16]);
436 assert!(result.is_ok());
437 assert_eq!(result.unwrap().len(), out_len);
438 }
439 }
440
441 #[test]
442 fn test_ec() {
443 let key = ec_key_generate_key();
444 assert!(key.is_ok());
445 assert!(!key.unwrap().0.is_null());
446
447 let key = ec_key_derive_from_secret(&[42; 16]);
448 assert!(key.is_ok());
449 let key = key.unwrap();
450 assert!(!key.0.is_null());
451
452 let point = ec_key_get0_public_key(&key);
453
454 let result = ecdh_compute_key(point.get_point(), &key);
455 assert!(result.is_ok());
456
457 let oct = ec_point_point_to_oct(point.get_point());
458 assert!(oct.is_ok());
459 let oct = oct.unwrap();
460
461 let point2 = ec_point_oct_to_point(oct.as_slice());
462 assert!(point2.is_ok());
463 }
Joel Galensonca0efb12020-10-01 14:32:30 -0700464}