blob: dfad74132c209912e4e160fcee9d3b481c3170c3 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
mod boringssl_wrapper;
mod keysafe;
use self::boringssl_wrapper::*;
use self::keysafe::*;
use crate::crypto_provider::{
AsymmetricProviderKey, CryptoProvider, CryptoProviderError, ProviderKey, SealingProviderKey,
};
use crate::tee::*;
use boringssl_sys::{NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1};
use byteorder::{ByteOrder, LittleEndian};
use fidl_fuchsia_kms::{AsymmetricKeyAlgorithm, KeyProvider};
/// We use 1024 for default output buffer size since all outputs from Keysafe is
/// currently less than 1024 bytes.
const OUTPUT_BUFFER_SIZE: usize = 1024;
/// OpteeProvider is a CryptoProvider that talks with Keysafe Trusted App in OPTEE.
///
/// It wraps the key blob with a sealing key that is only known to the Keysafe App in OPTEE so that
/// the key material itself would never be exposed to rich OS side in plain text. OPTEE is
/// responsible for all crypto operations that is supported by this provider, thus this provider
/// provides a stronger protection for the keys than a software provider.
#[derive(Debug)]
pub struct OpteeProvider {}
#[derive(Debug)]
pub struct OpteeAsymmetricPrivateKey {
key_data: Vec<u8>,
key_algorithm: AsymmetricKeyAlgorithm,
}
impl ProviderKey for OpteeAsymmetricPrivateKey {
fn delete(&mut self) -> Result<(), CryptoProviderError> {
Ok(())
}
fn get_key_data(&self) -> Vec<u8> {
self.key_data.clone()
}
fn get_key_provider(&self) -> KeyProvider {
(OpteeProvider {}).get_name()
}
}
#[derive(Debug)]
pub struct OpteeSealingKey {
key_data: Vec<u8>,
}
impl ProviderKey for OpteeSealingKey {
fn delete(&mut self) -> Result<(), CryptoProviderError> {
Ok(())
}
fn get_key_data(&self) -> Vec<u8> {
self.key_data.clone()
}
fn get_key_provider(&self) -> KeyProvider {
(OpteeProvider {}).get_name()
}
}
fn map_command_error(error_code: u32) -> CryptoProviderError {
CryptoProviderError::new(&format!("Operation error, error code: {:}", error_code))
}
fn map_openssl_error(error_status: &'static str) -> CryptoProviderError {
CryptoProviderError::new(error_status)
}
impl CryptoProvider for OpteeProvider {
fn supported_asymmetric_algorithms(&self) -> &'static [AsymmetricKeyAlgorithm] {
&[
AsymmetricKeyAlgorithm::EcdsaSha256P256,
AsymmetricKeyAlgorithm::EcdsaSha512P384,
AsymmetricKeyAlgorithm::EcdsaSha512P521,
AsymmetricKeyAlgorithm::RsaSsaPssSha2562048,
AsymmetricKeyAlgorithm::RsaSsaPssSha2563072,
AsymmetricKeyAlgorithm::RsaSsaPssSha5124096,
AsymmetricKeyAlgorithm::RsaSsaPkcs1Sha2562048,
AsymmetricKeyAlgorithm::RsaSsaPkcs1Sha2563072,
AsymmetricKeyAlgorithm::RsaSsaPkcs1Sha5124096,
]
}
fn get_name(&self) -> KeyProvider {
KeyProvider::OpteeProvider
}
fn box_clone(&self) -> Box<dyn CryptoProvider> {
Box::new(OpteeProvider {})
}
fn generate_asymmetric_key(
&self,
key_algorithm: AsymmetricKeyAlgorithm,
key_name: &str,
) -> Result<Box<dyn AsymmetricProviderKey>, CryptoProviderError> {
let ta_algorithm = translate_algorithm(key_algorithm);
let key_data = optee_generate_key(ta_algorithm, key_name)?;
Ok(Box::new(OpteeAsymmetricPrivateKey { key_data, key_algorithm }))
}
fn import_asymmetric_key(
&self,
key_data: &[u8],
key_algorithm: AsymmetricKeyAlgorithm,
key_name: &str,
) -> Result<Box<dyn AsymmetricProviderKey>, CryptoProviderError> {
let imported_key_data = optee_import_key(key_algorithm, key_data, key_name)?;
Ok(Box::new(OpteeAsymmetricPrivateKey { key_data: imported_key_data, key_algorithm }))
}
fn parse_asymmetric_key(
&self,
key_data: &[u8],
key_algorithm: AsymmetricKeyAlgorithm,
) -> Result<Box<dyn AsymmetricProviderKey>, CryptoProviderError> {
let ta_algorithm = translate_algorithm(key_algorithm);
optee_parse_key(key_data, ta_algorithm)?;
Ok(Box::new(OpteeAsymmetricPrivateKey { key_data: key_data.to_vec(), key_algorithm }))
}
fn generate_sealing_key(
&self,
key_name: &str,
) -> Result<Box<dyn SealingProviderKey>, CryptoProviderError> {
let ta_algorithm = TA_KEYSAFE_ALG_AES_GCM_256;
let key_data = optee_generate_key(ta_algorithm, key_name)?;
Ok(Box::new(OpteeSealingKey { key_data }))
}
fn parse_sealing_key(
&self,
key_data: &[u8],
) -> Result<Box<dyn SealingProviderKey>, CryptoProviderError> {
let ta_algorithm = TA_KEYSAFE_ALG_AES_GCM_256;
optee_parse_key(key_data, ta_algorithm)?;
Ok(Box::new(OpteeSealingKey { key_data: key_data.to_vec() }))
}
fn calculate_sealed_data_size(
&self,
original_data_size: u64,
) -> Result<u64, CryptoProviderError> {
// overhead is GCM IV size + GCM Tag size.
let overhead: u64 = 12 + 16;
original_data_size
.checked_add(overhead)
.ok_or(CryptoProviderError::new("original data size too large, overflow"))
}
}
fn translate_algorithm(key_algorithm: AsymmetricKeyAlgorithm) -> u32 {
match key_algorithm {
AsymmetricKeyAlgorithm::RsaSsaPssSha2562048 => TA_KEYSAFE_ALG_RSA_SSA_PSS_SHA256_2048,
AsymmetricKeyAlgorithm::RsaSsaPssSha2563072 => TA_KEYSAFE_ALG_RSA_SSA_PSS_SHA256_3072,
AsymmetricKeyAlgorithm::RsaSsaPssSha5124096 => TA_KEYSAFE_ALG_RSA_SSA_PSS_SHA512_4096,
AsymmetricKeyAlgorithm::RsaSsaPkcs1Sha2562048 => TA_KEYSAFE_ALG_RSA_SSA_PKCS1_SHA256_2048,
AsymmetricKeyAlgorithm::RsaSsaPkcs1Sha2563072 => TA_KEYSAFE_ALG_RSA_SSA_PKCS1_SHA256_3072,
AsymmetricKeyAlgorithm::RsaSsaPkcs1Sha5124096 => TA_KEYSAFE_ALG_RSA_SSA_PKCS1_SHA512_4096,
AsymmetricKeyAlgorithm::EcdsaSha256P256 => TA_KEYSAFE_ALG_ECDSA_SHA256_P256,
AsymmetricKeyAlgorithm::EcdsaSha512P384 => TA_KEYSAFE_ALG_ECDSA_SHA512_P384,
AsymmetricKeyAlgorithm::EcdsaSha512P521 => TA_KEYSAFE_ALG_ECDSA_SHA512_P521,
}
}
fn get_ec_curve_nid(key_algorithm: AsymmetricKeyAlgorithm) -> i32 {
match key_algorithm {
AsymmetricKeyAlgorithm::EcdsaSha256P256 => NID_X9_62_prime256v1 as i32,
AsymmetricKeyAlgorithm::EcdsaSha512P384 => NID_secp384r1 as i32,
AsymmetricKeyAlgorithm::EcdsaSha512P521 => NID_secp521r1 as i32,
_ => unreachable!(),
}
}
/// This is the same macro definition as TEEC_PARAM_TYPES in tee-client-types.h
fn teec_param_types(param0_type: u32, param1_type: u32, param2_type: u32, param3_type: u32) -> u32 {
((param0_type & 0xF) << 0)
| ((param1_type & 0xF) << 4)
| ((param2_type & 0xF) << 8)
| ((param3_type & 0xF) << 12)
}
/// Uses Keysafe TA in OPTEE to parse a key blob previously returned by Keysafe.
///
/// Issues TA_KEYSAFE_CMD_PARSE_KEY command.CryptoProviderError
///
/// Params should be:
/// * algorithm as value input.
/// * key_name as memref input.
/// * unused
/// * unused.
fn optee_parse_key(key_data: &[u8], ta_algorithm: u32) -> Result<(), CryptoProviderError> {
let param_type =
teec_param_types(TEEC_VALUE_INPUT, TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE);
let params = [
get_value_parameter(ta_algorithm, 0),
get_memref_input_parameter(key_data),
get_zero_parameter(),
get_zero_parameter(),
];
let mut op = create_operation(param_type, params);
call_command(&mut op, TA_KEYSAFE_CMD_PARSE_KEY).map_err(map_command_error)
}
/// Uses Keysafe TA in OPTEE to generate key.
///
/// Issues TA_KEYSAFE_CMD_GENERATE_KEY command.
///
/// Params should be:
/// * algorithm as value input.
/// * key_name as memref input.
/// * unused
/// * output key data as memref output.
fn optee_generate_key(ta_algorithm: u32, key_name: &str) -> Result<Vec<u8>, CryptoProviderError> {
let key_name_bytes = key_name.as_bytes();
let mut output_buffer = vec![0; OUTPUT_BUFFER_SIZE];
let param_type = teec_param_types(
TEEC_VALUE_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_NONE,
TEEC_MEMREF_TEMP_OUTPUT,
);
let params = [
get_value_parameter(ta_algorithm, 0),
get_memref_input_parameter(&key_name_bytes),
get_zero_parameter(),
get_memref_output_parameter(&mut output_buffer),
];
let mut op = create_operation(param_type, params);
call_command(&mut op, TA_KEYSAFE_CMD_GENERATE_KEY).map_err(map_command_error)?;
let output_size = unsafe { op.params[3].tmpref.size };
if output_size > OUTPUT_BUFFER_SIZE {
return Err(CryptoProviderError::new("invalid returned output buffer size"));
}
output_buffer.truncate(output_size);
Ok(output_buffer)
}
/// Import key and uses OPTEE Keysafe TA to create a key object.
///
/// Parses the key data and read key information out.
///
/// * public_key_x, public_key_y, private_key for ECDSA
/// * public_exponent, private_exponent, modulus for RSA
///
/// Encode the key information and issues TA_KEYSAFE_CMD_IMPORT_KEY command. We have to encode
/// key information because OPTEE could not parse ASN1 format and we do not have enough fields
/// to pass each information separately.
///
/// Params should be:
/// * algorithm as value input.
/// * key_name as memref input.
/// * encoded key data as memref input.
/// * output key data as memref output.
fn optee_import_key(
key_algorithm: AsymmetricKeyAlgorithm,
key_data: &[u8],
key_name: &str,
) -> Result<Vec<u8>, CryptoProviderError> {
let ta_algorithm = translate_algorithm(key_algorithm);
let encoded_buffer = {
if key_algorithm == AsymmetricKeyAlgorithm::EcdsaSha256P256
|| key_algorithm == AsymmetricKeyAlgorithm::EcdsaSha512P384
|| key_algorithm == AsymmetricKeyAlgorithm::EcdsaSha512P521
{
let ec_key = EcPrivateKey::new(get_ec_curve_nid(key_algorithm), key_data)
.map_err(map_openssl_error)?;
let public_key_x = ec_key.get_public_key_x().map_err(map_openssl_error)?;
let public_key_y = ec_key.get_public_key_y().map_err(map_openssl_error)?;
let private_key = ec_key.get_private_key().map_err(map_openssl_error)?;
let encoded_buffer = encode_private_key(public_key_x, public_key_y, private_key);
encoded_buffer
} else {
let rsa_key = RsaPrivateKey::new(key_data).map_err(map_openssl_error)?;
let modulus = rsa_key.get_modulus().map_err(map_openssl_error)?;
let public_exponent = rsa_key.get_public_exponent().map_err(map_openssl_error)?;
let private_exponent = rsa_key.get_private_exponent().map_err(map_openssl_error)?;
let encoded_buffer = encode_private_key(modulus, public_exponent, private_exponent);
encoded_buffer
}
};
let key_name_bytes = key_name.as_bytes();
let mut output_buffer = vec![0; OUTPUT_BUFFER_SIZE];
let param_type = teec_param_types(
TEEC_VALUE_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_OUTPUT,
);
let params = [
get_value_parameter(ta_algorithm, 0),
get_memref_input_parameter(&key_name_bytes),
get_memref_input_parameter(&encoded_buffer),
get_memref_output_parameter(&mut output_buffer),
];
let mut op = create_operation(param_type, params);
call_command(&mut op, TA_KEYSAFE_CMD_IMPORT_KEY).map_err(map_command_error)?;
let output_size = unsafe { op.params[3].tmpref.size };
if output_size > OUTPUT_BUFFER_SIZE {
return Err(CryptoProviderError::new("invalid returned output buffer size"));
}
output_buffer.truncate(output_size);
Ok(output_buffer)
}
/// Encode private key information into one string.
///
/// Because we only have limited fields for input, we need to encode the key information
/// in one field. We use the following format:
///
/// RSA:
/// | public_key_x_size | public_key_x | public_key_y_size | public_key_y | private_key_size |
/// private_key |
///
/// ECDSA:
/// | modulus_size | modulus | public_exponent_size | public_exponent | private_exponent_size |
/// private_exponent |
fn encode_private_key(field_1: Vec<u8>, field_2: Vec<u8>, field_3: Vec<u8>) -> Vec<u8> {
let mut encoded_buffer: Vec<u8> = vec![];
let mut length_buffer = [0; 4];
LittleEndian::write_u32(&mut length_buffer, field_1.len() as u32);
encoded_buffer.extend_from_slice(&length_buffer);
encoded_buffer.extend_from_slice(&field_1);
LittleEndian::write_u32(&mut length_buffer, field_2.len() as u32);
encoded_buffer.extend_from_slice(&length_buffer);
encoded_buffer.extend_from_slice(&field_2);
LittleEndian::write_u32(&mut length_buffer, field_3.len() as u32);
encoded_buffer.extend_from_slice(&length_buffer);
encoded_buffer.extend_from_slice(&field_3);
encoded_buffer
}
impl AsymmetricProviderKey for OpteeAsymmetricPrivateKey {
/// Issues TA_KEYSAFE_CMD_SIGN command.
///
/// Params should be:
/// * algorithm as value input.
/// * key_data as memref input.
/// * data_to_be_signed as memref input.
/// * output signature as memref output.
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, CryptoProviderError> {
let ta_algorithm = translate_algorithm(self.key_algorithm);
let mut output_buffer = vec![0; OUTPUT_BUFFER_SIZE];
let param_type = teec_param_types(
TEEC_VALUE_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_OUTPUT,
);
let params = [
get_value_parameter(ta_algorithm, 0),
get_memref_input_parameter(&self.key_data),
get_memref_input_parameter(data),
get_memref_output_parameter(&mut output_buffer),
];
let mut op = create_operation(param_type, params);
call_command(&mut op, TA_KEYSAFE_CMD_SIGN).map_err(map_command_error)?;
let output_size = unsafe { op.params[3].tmpref.size };
if output_size > OUTPUT_BUFFER_SIZE {
return Err(CryptoProviderError::new("invalid returned output buffer size"));
}
output_buffer.truncate(output_size);
Ok(output_buffer)
}
/// Issues TA_KEYSAFE_CMD_GET_PUBLIC_KEY command.
///
/// Params should be:
/// * algorithm as value input.
/// * key_data as memref input.
/// * first public value as memref output.
/// * second public value as memref output.
fn get_der_public_key(&self) -> Result<Vec<u8>, CryptoProviderError> {
let ta_algorithm = translate_algorithm(self.key_algorithm);
let mut first_output = [0; OUTPUT_BUFFER_SIZE];
let mut second_output = [0; OUTPUT_BUFFER_SIZE];
let param_type = teec_param_types(
TEEC_VALUE_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_OUTPUT,
TEEC_MEMREF_TEMP_OUTPUT,
);
let params = [
get_value_parameter(ta_algorithm, 0),
get_memref_input_parameter(&self.key_data),
get_memref_output_parameter(&mut first_output),
get_memref_output_parameter(&mut second_output),
];
let mut op = create_operation(param_type, params);
call_command(&mut op, TA_KEYSAFE_CMD_GET_PUBLIC_KEY).map_err(map_command_error)?;
if self.key_algorithm == AsymmetricKeyAlgorithm::EcdsaSha256P256
|| self.key_algorithm == AsymmetricKeyAlgorithm::EcdsaSha512P384
|| self.key_algorithm == AsymmetricKeyAlgorithm::EcdsaSha512P521
{
let (public_x_size, public_y_size) = unsafe {
let public_x_size = op.params[2].tmpref.size;
if public_x_size > OUTPUT_BUFFER_SIZE {
return Err(CryptoProviderError::new("invalid returned output buffer size"));
}
let public_y_size = op.params[3].tmpref.size;
if public_y_size > OUTPUT_BUFFER_SIZE {
return Err(CryptoProviderError::new("invalid returned output buffer size"));
}
Ok((public_x_size, public_y_size))
}?;
let mut ec_key = EcPublicKey::new(
get_ec_curve_nid(self.key_algorithm),
&first_output[0..public_x_size],
&second_output[0..public_y_size],
)
.map_err(map_openssl_error)?;
Ok(ec_key.marshal_public_key().map_err(map_openssl_error)?)
} else {
let (modulus_size, public_exponent_size) = unsafe {
let modulus_size = op.params[2].tmpref.size;
if modulus_size > OUTPUT_BUFFER_SIZE {
return Err(CryptoProviderError::new("invalid returned output buffer size"));
}
let public_exponent_size = op.params[3].tmpref.size;
if public_exponent_size > OUTPUT_BUFFER_SIZE {
return Err(CryptoProviderError::new("invalid returned output buffer size"));
}
(modulus_size, public_exponent_size)
};
let mut rsa = RsaPublicKey::new(
&first_output[0..modulus_size],
&second_output[0..public_exponent_size],
)
.map_err(map_openssl_error)?;
Ok(rsa.marshal_public_key().map_err(map_openssl_error)?)
}
}
fn get_key_algorithm(&self) -> AsymmetricKeyAlgorithm {
self.key_algorithm
}
}
impl SealingProviderKey for OpteeSealingKey {
fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>, CryptoProviderError> {
self.encrypt_decrypt(data, true)
}
fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, CryptoProviderError> {
self.encrypt_decrypt(data, false)
}
}
impl OpteeSealingKey {
/// Issues TA_KEYSAFE_CMD_ENCRYPT_DATA / TA_KEYSAFE_CMD_DECRYPT_DATA command.
///
/// Params should be:
/// * algorithm as value input.
/// * key_data as memref input.
/// * data as memref input.
/// * encrypted/decrypted data as memref output.
fn encrypt_decrypt(&self, data: &[u8], encrypt: bool) -> Result<Vec<u8>, CryptoProviderError> {
// IV is 16 byte, tag is 12 byte, 64 byte is large enough to hold them.
let output_buffer_size = data.len() + 64;
let mut output_buffer = vec![0; output_buffer_size];
let param_type = teec_param_types(
TEEC_VALUE_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_OUTPUT,
);
let params = [
get_value_parameter(TA_KEYSAFE_ALG_AES_GCM_256, 0),
get_memref_input_parameter(&self.key_data),
get_memref_input_parameter(data),
get_memref_output_parameter(&mut output_buffer),
];
let mut op = create_operation(param_type, params);
let command_id =
if encrypt { TA_KEYSAFE_CMD_ENCRYPT_DATA } else { TA_KEYSAFE_CMD_DECRYPT_DATA };
call_command(&mut op, command_id).map_err(map_command_error)?;
let output_size = unsafe { op.params[3].tmpref.size };
if output_size > output_buffer_size {
return Err(CryptoProviderError::new("invalid returned output buffer size"));
}
output_buffer.truncate(output_size);
Ok(output_buffer)
}
}