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