blob: 14bdf045e014fdff5e56a9c10146afd5ec0aa3b4 [file] [log] [blame]
// Copyright 2020, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! This module implements safe wrappers for some crypto operations required by
//! Keystore 2.0.
mod error;
pub mod zvec;
pub use error::Error;
use keystore2_crypto_bindgen::{
extractSubjectFromCertificate, generateKeyFromPassword, hmacSha256, randomBytes,
AES_gcm_decrypt, AES_gcm_encrypt, ECDHComputeKey, ECKEYGenerateKey, ECKEYMarshalPrivateKey,
ECKEYParsePrivateKey, ECPOINTOct2Point, ECPOINTPoint2Oct, EC_KEY_free, EC_KEY_get0_public_key,
EC_POINT_free, HKDFExpand, HKDFExtract, EC_KEY, EC_MAX_BYTES, EC_POINT, EVP_MAX_MD_SIZE,
};
use std::convert::TryFrom;
use std::convert::TryInto;
use std::marker::PhantomData;
pub use zvec::ZVec;
/// Length of the expected initialization vector.
pub const GCM_IV_LENGTH: usize = 12;
/// Length of the expected AEAD TAG.
pub const TAG_LENGTH: usize = 16;
/// Length of an AES 256 key in bytes.
pub const AES_256_KEY_LENGTH: usize = 32;
/// Length of an AES 128 key in bytes.
pub const AES_128_KEY_LENGTH: usize = 16;
/// Length of the expected salt for key from password generation.
pub const SALT_LENGTH: usize = 16;
/// Length of an HMAC-SHA256 tag in bytes.
pub const HMAC_SHA256_LEN: usize = 32;
/// Older versions of keystore produced IVs with four extra
/// ignored zero bytes at the end; recognise and trim those.
pub const LEGACY_IV_LENGTH: usize = 16;
/// Generate an AES256 key, essentially 32 random bytes from the underlying
/// boringssl library discretely stuffed into a ZVec.
pub fn generate_aes256_key() -> Result<ZVec, Error> {
// Safety: key has the same length as the requested number of random bytes.
let mut key = ZVec::new(AES_256_KEY_LENGTH)?;
if unsafe { randomBytes(key.as_mut_ptr(), AES_256_KEY_LENGTH) } {
Ok(key)
} else {
Err(Error::RandomNumberGenerationFailed)
}
}
/// Generate a salt.
pub fn generate_salt() -> Result<Vec<u8>, Error> {
generate_random_data(SALT_LENGTH)
}
/// Generate random data of the given size.
pub fn generate_random_data(size: usize) -> Result<Vec<u8>, Error> {
// Safety: data has the same length as the requested number of random bytes.
let mut data = vec![0; size];
if unsafe { randomBytes(data.as_mut_ptr(), size) } {
Ok(data)
} else {
Err(Error::RandomNumberGenerationFailed)
}
}
/// Perform HMAC-SHA256.
pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> Result<Vec<u8>, Error> {
let mut tag = vec![0; HMAC_SHA256_LEN];
// Safety: The first two pairs of arguments must point to const buffers with
// size given by the second arg of the pair. The final pair of arguments
// must point to an output buffer with size given by the second arg of the
// pair.
match unsafe {
hmacSha256(key.as_ptr(), key.len(), msg.as_ptr(), msg.len(), tag.as_mut_ptr(), tag.len())
} {
true => Ok(tag),
false => Err(Error::HmacSha256Failed),
}
}
/// Uses AES GCM to decipher a message given an initialization vector, aead tag, and key.
/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based
/// on the key length.
/// This function returns the plaintext message in a ZVec because it is assumed that
/// it contains sensitive information that should be zeroed from memory before its buffer is
/// freed. Input key is taken as a slice for flexibility, but it is recommended that it is held
/// in a ZVec as well.
pub fn aes_gcm_decrypt(data: &[u8], iv: &[u8], tag: &[u8], key: &[u8]) -> Result<ZVec, Error> {
// Old versions of aes_gcm_encrypt produced 16 byte IVs, but the last four bytes were ignored
// so trim these to the correct size.
let iv = match iv.len() {
GCM_IV_LENGTH => iv,
LEGACY_IV_LENGTH => &iv[..GCM_IV_LENGTH],
_ => return Err(Error::InvalidIvLength),
};
if tag.len() != TAG_LENGTH {
return Err(Error::InvalidAeadTagLength);
}
match key.len() {
AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
_ => return Err(Error::InvalidKeyLength),
}
let mut result = ZVec::new(data.len())?;
// Safety: The first two arguments must point to buffers with a size given by the third
// argument. We pass the length of the key buffer along with the key.
// The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above.
match unsafe {
AES_gcm_decrypt(
data.as_ptr(),
result.as_mut_ptr(),
data.len(),
key.as_ptr(),
key.len(),
iv.as_ptr(),
tag.as_ptr(),
)
} {
true => Ok(result),
false => Err(Error::DecryptionFailed),
}
}
/// Uses AES GCM to encrypt a message given a key.
/// This function accepts 128 and 256-bit keys and uses AES128 and AES256 respectively based on
/// the key length. The function generates an initialization vector. The return value is a tuple
/// of `(ciphertext, iv, tag)`.
pub fn aes_gcm_encrypt(plaintext: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>, Vec<u8>), Error> {
let mut iv = vec![0; GCM_IV_LENGTH];
// Safety: iv is GCM_IV_LENGTH bytes long.
if !unsafe { randomBytes(iv.as_mut_ptr(), GCM_IV_LENGTH) } {
return Err(Error::RandomNumberGenerationFailed);
}
match key.len() {
AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
_ => return Err(Error::InvalidKeyLength),
}
let mut ciphertext: Vec<u8> = vec![0; plaintext.len()];
let mut tag: Vec<u8> = vec![0; TAG_LENGTH];
// Safety: The first two arguments must point to buffers with a size given by the third
// argument. We pass the length of the key buffer along with the key.
// The `iv` buffer must be 12 bytes and the `tag` buffer 16, which we check above.
if unsafe {
AES_gcm_encrypt(
plaintext.as_ptr(),
ciphertext.as_mut_ptr(),
plaintext.len(),
key.as_ptr(),
key.len(),
iv.as_ptr(),
tag.as_mut_ptr(),
)
} {
Ok((ciphertext, iv, tag))
} else {
Err(Error::EncryptionFailed)
}
}
/// Represents a "password" that can be used to key the PBKDF2 algorithm.
pub enum Password<'a> {
/// Borrow an existing byte array
Ref(&'a [u8]),
/// Use an owned ZVec to store the key
Owned(ZVec),
}
impl<'a> From<&'a [u8]> for Password<'a> {
fn from(pw: &'a [u8]) -> Self {
Self::Ref(pw)
}
}
impl<'a> Password<'a> {
fn get_key(&'a self) -> &'a [u8] {
match self {
Self::Ref(b) => b,
Self::Owned(z) => &*z,
}
}
/// Generate a key from the given password and salt.
/// The salt must be exactly 16 bytes long.
/// Two key sizes are accepted: 16 and 32 bytes.
pub fn derive_key(&self, salt: Option<&[u8]>, key_length: usize) -> Result<ZVec, Error> {
let pw = self.get_key();
let salt: *const u8 = match salt {
Some(s) => {
if s.len() != SALT_LENGTH {
return Err(Error::InvalidSaltLength);
}
s.as_ptr()
}
None => std::ptr::null(),
};
match key_length {
AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
_ => return Err(Error::InvalidKeyLength),
}
let mut result = ZVec::new(key_length)?;
unsafe {
generateKeyFromPassword(
result.as_mut_ptr(),
result.len(),
pw.as_ptr() as *const std::os::raw::c_char,
pw.len(),
salt,
)
};
Ok(result)
}
/// Try to make another Password object with the same data.
pub fn try_clone(&self) -> Result<Password<'static>, Error> {
Ok(Password::Owned(ZVec::try_from(self.get_key())?))
}
}
/// Calls the boringssl HKDF_extract function.
pub fn hkdf_extract(secret: &[u8], salt: &[u8]) -> Result<ZVec, Error> {
let max_size: usize = EVP_MAX_MD_SIZE.try_into().unwrap();
let mut buf = ZVec::new(max_size)?;
let mut out_len = 0;
// Safety: HKDF_extract writes at most EVP_MAX_MD_SIZE bytes.
// Secret and salt point to valid buffers.
let result = unsafe {
HKDFExtract(
buf.as_mut_ptr(),
&mut out_len,
secret.as_ptr(),
secret.len(),
salt.as_ptr(),
salt.len(),
)
};
if !result {
return Err(Error::HKDFExtractFailed);
}
// According to the boringssl API, this should never happen.
if out_len > max_size {
return Err(Error::HKDFExtractFailed);
}
// HKDF_extract may write fewer than the maximum number of bytes, so we
// truncate the buffer.
buf.reduce_len(out_len);
Ok(buf)
}
/// Calls the boringssl HKDF_expand function.
pub fn hkdf_expand(out_len: usize, prk: &[u8], info: &[u8]) -> Result<ZVec, Error> {
let mut buf = ZVec::new(out_len)?;
// Safety: HKDF_expand writes out_len bytes to the buffer.
// prk and info are valid buffers.
let result = unsafe {
HKDFExpand(buf.as_mut_ptr(), out_len, prk.as_ptr(), prk.len(), info.as_ptr(), info.len())
};
if !result {
return Err(Error::HKDFExpandFailed);
}
Ok(buf)
}
/// A wrapper around the boringssl EC_KEY type that frees it on drop.
pub struct ECKey(*mut EC_KEY);
impl Drop for ECKey {
fn drop(&mut self) {
// Safety: We only create ECKey objects for valid EC_KEYs
// and they are the sole owners of those keys.
unsafe { EC_KEY_free(self.0) };
}
}
// Wrappers around the boringssl EC_POINT type.
// The EC_POINT can either be owned (and therefore mutable) or a pointer to an
// EC_POINT owned by someone else (and thus immutable). The former are freed
// on drop.
/// An owned EC_POINT object.
pub struct OwnedECPoint(*mut EC_POINT);
/// A pointer to an EC_POINT object.
pub struct BorrowedECPoint<'a> {
data: *const EC_POINT,
phantom: PhantomData<&'a EC_POINT>,
}
impl OwnedECPoint {
/// Get the wrapped EC_POINT object.
pub fn get_point(&self) -> &EC_POINT {
// Safety: We only create OwnedECPoint objects for valid EC_POINTs.
unsafe { self.0.as_ref().unwrap() }
}
}
impl<'a> BorrowedECPoint<'a> {
/// Get the wrapped EC_POINT object.
pub fn get_point(&self) -> &EC_POINT {
// Safety: We only create BorrowedECPoint objects for valid EC_POINTs.
unsafe { self.data.as_ref().unwrap() }
}
}
impl Drop for OwnedECPoint {
fn drop(&mut self) {
// Safety: We only create OwnedECPoint objects for valid
// EC_POINTs and they are the sole owners of those points.
unsafe { EC_POINT_free(self.0) };
}
}
/// Calls the boringssl ECDH_compute_key function.
pub fn ecdh_compute_key(pub_key: &EC_POINT, priv_key: &ECKey) -> Result<ZVec, Error> {
let mut buf = ZVec::new(EC_MAX_BYTES)?;
// Safety: Our ECDHComputeKey wrapper passes EC_MAX_BYES to ECDH_compute_key, which
// writes at most that many bytes to the output.
// The two keys are valid objects.
let result =
unsafe { ECDHComputeKey(buf.as_mut_ptr() as *mut std::ffi::c_void, pub_key, priv_key.0) };
if result == -1 {
return Err(Error::ECDHComputeKeyFailed);
}
let out_len = result.try_into().unwrap();
// According to the boringssl API, this should never happen.
if out_len > buf.len() {
return Err(Error::ECDHComputeKeyFailed);
}
// ECDH_compute_key may write fewer than the maximum number of bytes, so we
// truncate the buffer.
buf.reduce_len(out_len);
Ok(buf)
}
/// Calls the boringssl EC_KEY_generate_key function.
pub fn ec_key_generate_key() -> Result<ECKey, Error> {
// Safety: Creates a new key on its own.
let key = unsafe { ECKEYGenerateKey() };
if key.is_null() {
return Err(Error::ECKEYGenerateKeyFailed);
}
Ok(ECKey(key))
}
/// Calls the boringssl EC_KEY_marshal_private_key function.
pub fn ec_key_marshal_private_key(key: &ECKey) -> Result<ZVec, Error> {
let len = 73; // Empirically observed length of private key
let mut buf = ZVec::new(len)?;
// Safety: the key is valid.
// This will not write past the specified length of the buffer; if the
// len above is too short, it returns 0.
let written_len =
unsafe { ECKEYMarshalPrivateKey(key.0, buf.as_mut_ptr(), buf.len()) } as usize;
if written_len == len {
Ok(buf)
} else {
Err(Error::ECKEYMarshalPrivateKeyFailed)
}
}
/// Calls the boringssl EC_KEY_parse_private_key function.
pub fn ec_key_parse_private_key(buf: &[u8]) -> Result<ECKey, Error> {
// Safety: this will not read past the specified length of the buffer.
// It fails if less than the whole buffer is consumed.
let key = unsafe { ECKEYParsePrivateKey(buf.as_ptr(), buf.len()) };
if key.is_null() {
Err(Error::ECKEYParsePrivateKeyFailed)
} else {
Ok(ECKey(key))
}
}
/// Calls the boringssl EC_KEY_get0_public_key function.
pub fn ec_key_get0_public_key(key: &ECKey) -> BorrowedECPoint {
// Safety: The key is valid.
// This returns a pointer to a key, so we create an immutable variant.
BorrowedECPoint { data: unsafe { EC_KEY_get0_public_key(key.0) }, phantom: PhantomData }
}
/// Calls the boringssl EC_POINT_point2oct.
pub fn ec_point_point_to_oct(point: &EC_POINT) -> Result<Vec<u8>, Error> {
// We fix the length to 133 (1 + 2 * field_elem_size), as we get an error if it's too small.
let len = 133;
let mut buf = vec![0; len];
// Safety: EC_POINT_point2oct writes at most len bytes. The point is valid.
let result = unsafe { ECPOINTPoint2Oct(point, buf.as_mut_ptr(), len) };
if result == 0 {
return Err(Error::ECPoint2OctFailed);
}
// According to the boringssl API, this should never happen.
if result > len {
return Err(Error::ECPoint2OctFailed);
}
buf.resize(result, 0);
Ok(buf)
}
/// Calls the boringssl EC_POINT_oct2point function.
pub fn ec_point_oct_to_point(buf: &[u8]) -> Result<OwnedECPoint, Error> {
// Safety: The buffer is valid.
let result = unsafe { ECPOINTOct2Point(buf.as_ptr(), buf.len()) };
if result.is_null() {
return Err(Error::ECPoint2OctFailed);
}
// Our C wrapper creates a new EC_POINT, so we mark this mutable and free
// it on drop.
Ok(OwnedECPoint(result))
}
/// Uses BoringSSL to extract the DER-encoded subject from a DER-encoded X.509 certificate.
pub fn parse_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error> {
// Try with a 200-byte output buffer, should be enough in all but bizarre cases.
let mut retval = vec![0; 200];
// Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf and
// writes at most retval.len() bytes to retval.
let mut size = unsafe {
extractSubjectFromCertificate(
cert_buf.as_ptr(),
cert_buf.len(),
retval.as_mut_ptr(),
retval.len(),
)
};
if size == 0 {
return Err(Error::ExtractSubjectFailed);
}
if size < 0 {
// Our buffer wasn't big enough. Make one that is just the right size and try again.
let negated_size = usize::try_from(-size).map_err(|_e| Error::ExtractSubjectFailed)?;
retval = vec![0; negated_size];
// Safety: extractSubjectFromCertificate reads at most cert_buf.len() bytes from cert_buf
// and writes at most retval.len() bytes to retval.
size = unsafe {
extractSubjectFromCertificate(
cert_buf.as_ptr(),
cert_buf.len(),
retval.as_mut_ptr(),
retval.len(),
)
};
if size <= 0 {
return Err(Error::ExtractSubjectFailed);
}
}
// Reduce buffer size to the amount written.
let safe_size = usize::try_from(size).map_err(|_e| Error::ExtractSubjectFailed)?;
retval.truncate(safe_size);
Ok(retval)
}
#[cfg(test)]
mod tests {
use super::*;
use keystore2_crypto_bindgen::{
generateKeyFromPassword, AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId,
};
#[test]
fn test_wrapper_roundtrip() {
let key = generate_aes256_key().unwrap();
let message = b"totally awesome message";
let (cipher_text, iv, tag) = aes_gcm_encrypt(message, &key).unwrap();
let message2 = aes_gcm_decrypt(&cipher_text, &iv, &tag, &key).unwrap();
assert_eq!(message[..], message2[..])
}
#[test]
fn test_encrypt_decrypt() {
let input = vec![0; 16];
let mut out = vec![0; 16];
let mut out2 = vec![0; 16];
let key = vec![0; 16];
let iv = vec![0; 12];
let mut tag = vec![0; 16];
unsafe {
let res = AES_gcm_encrypt(
input.as_ptr(),
out.as_mut_ptr(),
16,
key.as_ptr(),
16,
iv.as_ptr(),
tag.as_mut_ptr(),
);
assert!(res);
assert_ne!(out, input);
assert_ne!(tag, input);
let res = AES_gcm_decrypt(
out.as_ptr(),
out2.as_mut_ptr(),
16,
key.as_ptr(),
16,
iv.as_ptr(),
tag.as_ptr(),
);
assert!(res);
assert_eq!(out2, input);
}
}
#[test]
fn test_create_key_id() {
let blob = vec![0; 16];
let mut out: u64 = 0;
unsafe {
let res = CreateKeyId(blob.as_ptr(), 16, &mut out);
assert!(res);
assert_ne!(out, 0);
}
}
#[test]
fn test_generate_key_from_password() {
let mut key = vec![0; 16];
let pw = vec![0; 16];
let mut salt = vec![0; 16];
unsafe {
generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_mut_ptr());
}
assert_ne!(key, vec![0; 16]);
}
#[test]
fn test_hkdf() {
let result = hkdf_extract(&[0; 16], &[0; 16]);
assert!(result.is_ok());
for out_len in 4..=8 {
let result = hkdf_expand(out_len, &[0; 16], &[0; 16]);
assert!(result.is_ok());
assert_eq!(result.unwrap().len(), out_len);
}
}
#[test]
fn test_ec() -> Result<(), Error> {
let priv0 = ec_key_generate_key()?;
assert!(!priv0.0.is_null());
let pub0 = ec_key_get0_public_key(&priv0);
let priv1 = ec_key_generate_key()?;
let pub1 = ec_key_get0_public_key(&priv1);
let priv0s = ec_key_marshal_private_key(&priv0)?;
let pub0s = ec_point_point_to_oct(pub0.get_point())?;
let pub1s = ec_point_point_to_oct(pub1.get_point())?;
let priv0 = ec_key_parse_private_key(&priv0s)?;
let pub0 = ec_point_oct_to_point(&pub0s)?;
let pub1 = ec_point_oct_to_point(&pub1s)?;
let left_key = ecdh_compute_key(pub0.get_point(), &priv1)?;
let right_key = ecdh_compute_key(pub1.get_point(), &priv0)?;
assert_eq!(left_key, right_key);
Ok(())
}
#[test]
fn test_hmac_sha256() {
let key = b"This is the key";
let msg1 = b"This is a message";
let msg2 = b"This is another message";
let tag1a = hmac_sha256(key, msg1).unwrap();
assert_eq!(tag1a.len(), HMAC_SHA256_LEN);
let tag1b = hmac_sha256(key, msg1).unwrap();
assert_eq!(tag1a, tag1b);
let tag2 = hmac_sha256(key, msg2).unwrap();
assert_eq!(tag2.len(), HMAC_SHA256_LEN);
assert_ne!(tag1a, tag2);
}
}