Skip to content

Import private key support for TPM provider #243

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 23 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ env_logger = "0.7.1"
log = { version = "0.4.8", features = ["serde"] }
pkcs11 = { version = "0.4.0", optional = true }
picky-asn1-der = { version = "0.2.2", optional = true }
picky-asn1 = { version = "0.2.1", optional = true }
tss-esapi = { version = "4.0.8-alpha.1", optional = true }
picky-asn1 = { version = "0.3.0", optional = true }
tss-esapi = { version = "4.0.9-alpha.1", optional = true }
bincode = "1.1.4"
structopt = "0.3.5"
derivative = "2.1.1"
Expand All @@ -42,7 +42,7 @@ hex = "0.4.2"
picky = "5.0.0"
psa-crypto = { version = "0.5.0" , default-features = false, features = ["operations"], optional = true }
zeroize = { version = "1.1.0", features = ["zeroize_derive"] }
picky-asn1-x509 = { version = "0.3.0", optional = true }
picky-asn1-x509 = { version = "0.3.2", optional = true }
users = "0.10.0"
libc = "0.2.72"

Expand Down
22 changes: 17 additions & 5 deletions src/providers/pkcs11_provider/key_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,23 @@ impl Pkcs11Provider {
app_name: ApplicationName,
op: psa_import_key::Operation,
) -> Result<psa_import_key::Result> {
if op.attributes.key_type != Type::RsaPublicKey {
error!("The PKCS 11 provider currently only supports importing RSA public key.");
return Err(ResponseStatus::PsaErrorNotSupported);
match op.attributes.key_type {
Type::RsaPublicKey => self.psa_import_key_internal_rsa_public(app_name, op),
_ => {
error!(
"The pkcs11 provider does not support the {:?} key type.",
op.attributes.key_type
);
Err(ResponseStatus::PsaErrorNotSupported)
}
}
}

pub(super) fn psa_import_key_internal_rsa_public(
&self,
app_name: ApplicationName,
op: psa_import_key::Operation,
) -> Result<psa_import_key::Result> {
let key_name = op.key_name;
let key_attributes = op.attributes;
let key_triple = KeyTriple::new(app_name, ProviderID::Pkcs11, key_name);
Expand Down Expand Up @@ -342,8 +354,8 @@ impl Pkcs11Provider {

// To produce a valid ASN.1 RSAPublicKey structure, 0x00 is put in front of the positive
// integer if highest significant bit is one, to differentiate it from a negative number.
let modulus = IntegerAsn1::from_unsigned_bytes_be(modulus);
let public_exponent = IntegerAsn1::from_unsigned_bytes_be(public_exponent);
let modulus = IntegerAsn1::from_bytes_be_unsigned(modulus);
let public_exponent = IntegerAsn1::from_bytes_be_unsigned(public_exponent);

let key = RSAPublicKey {
modulus,
Expand Down
126 changes: 78 additions & 48 deletions src/providers/tpm_provider/key_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
use super::utils;
use super::utils::PasswordContext;
use super::utils::{validate_private_key, validate_public_key, PUBLIC_EXPONENT};
use super::TpmProvider;
use crate::authenticators::ApplicationName;
use crate::key_info_managers;
Expand All @@ -14,11 +15,10 @@ use parsec_interface::operations::{
};
use parsec_interface::requests::{ProviderID, ResponseStatus, Result};
use parsec_interface::secrecy::ExposeSecret;
use picky_asn1_x509::RSAPublicKey;
use picky_asn1_x509::{RSAPrivateKey, RSAPublicKey};
use tss_esapi::abstraction::transient::RsaExponent;
use zeroize::Zeroize;

// Public exponent value for all RSA keys.
const PUBLIC_EXPONENT: [u8; 3] = [0x01, 0x00, 0x01];
const AUTH_VAL_LEN: usize = 32;

// Inserts a new mapping in the Key Info manager that stores the PasswordContext.
Expand Down Expand Up @@ -117,11 +117,24 @@ impl TpmProvider {
app_name: ApplicationName,
op: psa_import_key::Operation,
) -> Result<psa_import_key::Result> {
if op.attributes.key_type != Type::RsaPublicKey {
error!("The TPM provider currently only supports importing RSA public key.");
return Err(ResponseStatus::PsaErrorNotSupported);
match op.attributes.key_type {
Type::RsaPublicKey => self.psa_import_key_internal_rsa_public(app_name, op),
Type::RsaKeyPair => self.psa_import_key_internal_rsa_keypair(app_name, op),
_ => {
error!(
"The TPM provider does not support importing for the {:?} key type.",
op.attributes.key_type
);
Err(ResponseStatus::PsaErrorNotSupported)
}
}
}

pub(super) fn psa_import_key_internal_rsa_public(
&self,
app_name: ApplicationName,
op: psa_import_key::Operation,
) -> Result<psa_import_key::Result> {
let key_name = op.key_name;
let attributes = op.attributes;
let key_triple = KeyTriple::new(app_name, ProviderID::Tpm, key_name);
Expand All @@ -142,50 +155,9 @@ impl TpmProvider {
ResponseStatus::PsaErrorInvalidArgument
})?;

if public_key.modulus.is_negative() || public_key.public_exponent.is_negative() {
error!("Only positive modulus and public exponent are supported.");
return Err(ResponseStatus::PsaErrorInvalidArgument);
}
validate_public_key(&public_key, &attributes)?;

if public_key.public_exponent.as_unsigned_bytes_be() != PUBLIC_EXPONENT {
if crate::utils::GlobalConfig::log_error_details() {
error!("The TPM Provider only supports 0x101 as public exponent for RSA public keys, {:?} given.", public_key.public_exponent.as_unsigned_bytes_be());
} else {
error!(
"The TPM Provider only supports 0x101 as public exponent for RSA public keys"
);
}
return Err(ResponseStatus::PsaErrorNotSupported);
}
let key_data = public_key.modulus.as_unsigned_bytes_be();
let len = key_data.len();

let key_bits = attributes.bits;
if key_bits != 0 && len * 8 != key_bits {
if crate::utils::GlobalConfig::log_error_details() {
error!(
"`bits` field of key attributes (value: {}) must be either 0 or equal to the size of the key in `data` (value: {}).",
attributes.bits,
len * 8
);
} else {
error!("`bits` field of key attributes must be either 0 or equal to the size of the key in `data`.");
}
return Err(ResponseStatus::PsaErrorInvalidArgument);
}

if len != 128 && len != 256 {
if crate::utils::GlobalConfig::log_error_details() {
error!(
"The TPM provider only supports 1024 and 2048 bits RSA public keys ({} bits given).",
len * 8
);
} else {
error!("The TPM provider only supports 1024 and 2048 bits RSA public keys");
}
return Err(ResponseStatus::PsaErrorNotSupported);
}

let pub_key_context = esapi_context
.load_external_rsa_public_key(&key_data)
.map_err(|e| {
Expand All @@ -206,6 +178,64 @@ impl TpmProvider {
Ok(psa_import_key::Result {})
}

pub(super) fn psa_import_key_internal_rsa_keypair(
&self,
app_name: ApplicationName,
op: psa_import_key::Operation,
) -> Result<psa_import_key::Result> {
let key_name = op.key_name;
let attributes = op.attributes;
let key_triple = KeyTriple::new(app_name, ProviderID::Tpm, key_name);
let key_data = op.data;

let mut store_handle = self
.key_info_store
.write()
.expect("Key store lock poisoned");
let mut esapi_context = self
.esapi_context
.lock()
.expect("ESAPI Context lock poisoned");

let private_key: RSAPrivateKey = picky_asn1_der::from_bytes(key_data.expose_secret())
.map_err(|err| {
format_error!("Could not deserialise key elements", err);
ResponseStatus::PsaErrorInvalidArgument
})?;

// Derive the public key from the keypair.
let public_key = RSAPublicKey {
modulus: private_key.modulus.clone(),
public_exponent: private_key.public_exponent.clone(),
};

// Validate the public and the private key.
validate_public_key(&public_key, &attributes)?;
validate_private_key(&private_key, &attributes)?;

let key_prime = private_key.prime_1.as_unsigned_bytes_be();
let public_modulus = private_key.modulus.as_unsigned_bytes_be();

let keypair_context = esapi_context
.load_external_rsa(key_prime, public_modulus, RsaExponent::new(PUBLIC_EXPONENT))
.map_err(|e| {
format_error!("Error creating a RSA signing key", e);
utils::to_response_status(e)
})?;

insert_password_context(
&mut *store_handle,
key_triple,
PasswordContext {
context: keypair_context,
auth_value: Vec::new(),
},
attributes,
)?;

Ok(psa_import_key::Result {})
}

pub(super) fn psa_export_public_key_internal(
&self,
app_name: ApplicationName,
Expand Down
2 changes: 1 addition & 1 deletion src/providers/tpm_provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ impl TpmProviderBuilder {
.with_root_key_auth_size(ROOT_KEY_AUTH_SIZE)
.with_hierarchy_auth(hierarchy_auth)
.with_hierarchy(tss_esapi::utils::Hierarchy::Owner)
.with_session_hash_alg(HashingAlgorithm::Sha256.into())
.with_session_hash_alg(HashingAlgorithm::Sha256)
.with_default_context_cipher(default_cipher)
.build()
.map_err(|e| {
Expand Down
Loading