Skip to content

Commit d164d5d

Browse files
author
Joe Ellis
committed
Add support for importing private keys with TPM provider
Signed-off-by: Joe Ellis <[email protected]>
1 parent dad5792 commit d164d5d

File tree

3 files changed

+145
-47
lines changed

3 files changed

+145
-47
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ log = { version = "0.4.8", features = ["serde"] }
3333
pkcs11 = { version = "0.4.0", optional = true }
3434
picky-asn1-der = { version = "0.2.2", optional = true }
3535
picky-asn1 = { version = "0.2.1", optional = true }
36-
tss-esapi = { version = "4.0.6-alpha.1", optional = true }
36+
tss-esapi = { version = "4.0.9-alpha.1", optional = true }
3737
bincode = "1.1.4"
3838
structopt = "0.3.5"
3939
derivative = "2.1.1"

src/providers/tpm_provider/key_management.rs

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33
use super::utils;
44
use super::utils::PasswordContext;
5+
use super::utils::{validate_private_key, validate_public_key};
56
use super::TpmProvider;
67
use crate::authenticators::ApplicationName;
78
use crate::key_info_managers;
@@ -14,10 +15,10 @@ use parsec_interface::operations::{
1415
};
1516
use parsec_interface::requests::{ProviderID, ResponseStatus, Result};
1617
use parsec_interface::secrecy::ExposeSecret;
17-
use picky_asn1_x509::RSAPublicKey;
18+
use picky_asn1_x509::{RSAPrivateKey, RSAPublicKey};
19+
use std::convert::TryInto;
20+
use tss_esapi::abstraction::transient::RsaExponent;
1821

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

2324
// Inserts a new mapping in the Key Info manager that stores the PasswordContext.
@@ -116,6 +117,7 @@ impl TpmProvider {
116117
) -> Result<psa_import_key::Result> {
117118
match op.attributes.key_type {
118119
Type::RsaPublicKey => self.psa_import_key_internal_rsa_public(app_name, op),
120+
Type::RsaKeyPair => self.psa_import_key_internal_rsa_keypair(app_name, op),
119121
_ => {
120122
error!(
121123
"The TPM provider does not support the {:?} key type.",
@@ -151,50 +153,9 @@ impl TpmProvider {
151153
ResponseStatus::PsaErrorInvalidArgument
152154
})?;
153155

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

159-
if public_key.public_exponent.as_unsigned_bytes_be() != PUBLIC_EXPONENT {
160-
if crate::utils::GlobalConfig::log_error_details() {
161-
error!("The TPM Provider only supports 0x101 as public exponent for RSA public keys, {:?} given.", public_key.public_exponent.as_unsigned_bytes_be());
162-
} else {
163-
error!(
164-
"The TPM Provider only supports 0x101 as public exponent for RSA public keys"
165-
);
166-
}
167-
return Err(ResponseStatus::PsaErrorNotSupported);
168-
}
169158
let key_data = public_key.modulus.as_unsigned_bytes_be();
170-
let len = key_data.len();
171-
172-
let key_bits = attributes.bits;
173-
if key_bits != 0 && len * 8 != key_bits {
174-
if crate::utils::GlobalConfig::log_error_details() {
175-
error!(
176-
"`bits` field of key attributes (value: {}) must be either 0 or equal to the size of the key in `data` (value: {}).",
177-
attributes.bits,
178-
len * 8
179-
);
180-
} else {
181-
error!("`bits` field of key attributes must be either 0 or equal to the size of the key in `data`.");
182-
}
183-
return Err(ResponseStatus::PsaErrorInvalidArgument);
184-
}
185-
186-
if len != 128 && len != 256 {
187-
if crate::utils::GlobalConfig::log_error_details() {
188-
error!(
189-
"The TPM provider only supports 1024 and 2048 bits RSA public keys ({} bits given).",
190-
len * 8
191-
);
192-
} else {
193-
error!("The TPM provider only supports 1024 and 2048 bits RSA public keys");
194-
}
195-
return Err(ResponseStatus::PsaErrorNotSupported);
196-
}
197-
198159
let pub_key_context = esapi_context
199160
.load_external_rsa_public_key(&key_data)
200161
.map_err(|e| {
@@ -215,6 +176,77 @@ impl TpmProvider {
215176
Ok(psa_import_key::Result {})
216177
}
217178

179+
pub(super) fn psa_import_key_internal_rsa_keypair(
180+
&self,
181+
app_name: ApplicationName,
182+
op: psa_import_key::Operation,
183+
) -> Result<psa_import_key::Result> {
184+
let key_name = op.key_name;
185+
let attributes = op.attributes;
186+
let key_triple = KeyTriple::new(app_name, ProviderID::Tpm, key_name);
187+
let key_data = op.data;
188+
189+
let mut store_handle = self
190+
.key_info_store
191+
.write()
192+
.expect("Key store lock poisoned");
193+
let mut esapi_context = self
194+
.esapi_context
195+
.lock()
196+
.expect("ESAPI Context lock poisoned");
197+
198+
let private_key: RSAPrivateKey = picky_asn1_der::from_bytes(key_data.expose_secret())
199+
.map_err(|err| {
200+
format_error!("Could not deserialise key elements", err);
201+
ResponseStatus::PsaErrorInvalidArgument
202+
})?;
203+
204+
// Derive the public key from the keypair.
205+
let public_key = RSAPublicKey {
206+
modulus: private_key.modulus().clone(),
207+
public_exponent: private_key.public_exponent().clone(),
208+
};
209+
210+
// Validate the public and the private key.
211+
validate_public_key(&public_key, &attributes)?;
212+
validate_private_key(&private_key, &attributes)?;
213+
214+
let key_prime = private_key.primes()[0].as_unsigned_bytes_be();
215+
let public_modulus = private_key.modulus().as_unsigned_bytes_be();
216+
217+
let (int_bytes, _) = private_key
218+
.public_exponent()
219+
.split_at(std::mem::size_of::<u32>());
220+
let int_bytes: [u8; 4] = int_bytes.try_into().map_err(|err| {
221+
format_error!("Could not convert public exponent into u32", err);
222+
ResponseStatus::PsaErrorInvalidArgument
223+
})?;
224+
225+
let public_exponent_u32 = u32::from_be_bytes(int_bytes);
226+
let keypair_context = esapi_context
227+
.load_external_rsa(
228+
key_prime,
229+
public_modulus,
230+
RsaExponent::new(public_exponent_u32),
231+
)
232+
.map_err(|e| {
233+
format_error!("Error creating a RSA signing key", e);
234+
utils::to_response_status(e)
235+
})?;
236+
237+
insert_password_context(
238+
&mut *store_handle,
239+
key_triple,
240+
PasswordContext {
241+
context: keypair_context,
242+
auth_value: Vec::new(),
243+
},
244+
attributes,
245+
)?;
246+
247+
Ok(psa_import_key::Result {})
248+
}
249+
218250
pub(super) fn psa_export_public_key_internal(
219251
&self,
220252
app_name: ApplicationName,

src/providers/tpm_provider/utils.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use parsec_interface::operations::psa_algorithm::*;
66
use parsec_interface::operations::psa_key_attributes::*;
77
use parsec_interface::requests::{ResponseStatus, Result};
88
use picky_asn1::wrapper::IntegerAsn1;
9-
use picky_asn1_x509::RSAPublicKey;
9+
use picky_asn1_x509::{RSAPrivateKey, RSAPublicKey};
1010
use serde::{Deserialize, Serialize};
1111
use std::convert::TryInto;
1212
use tss_esapi::abstraction::transient::KeyParams;
@@ -243,6 +243,72 @@ pub fn parsec_to_tpm_signature(
243243
})
244244
}
245245

246+
/// Validates an RSAPublicKey against the attributes we expect. Returns ok on success, otherwise
247+
/// returns an error.
248+
pub fn validate_public_key(public_key: &RSAPublicKey, attributes: &Attributes) -> Result<()> {
249+
if public_key.modulus.is_negative() || public_key.public_exponent.is_negative() {
250+
error!("Only positive modulus and public exponent are supported.");
251+
return Err(ResponseStatus::PsaErrorInvalidArgument);
252+
}
253+
254+
if public_key.public_exponent.as_unsigned_bytes_be() != PUBLIC_EXPONENT {
255+
if crate::utils::GlobalConfig::log_error_details() {
256+
error!("The TPM Provider only supports 0x101 as public exponent for RSA public keys, {:?} given.", public_key.public_exponent.as_unsigned_bytes_be());
257+
} else {
258+
error!("The TPM Provider only supports 0x101 as public exponent for RSA public keys");
259+
}
260+
return Err(ResponseStatus::PsaErrorNotSupported);
261+
}
262+
let key_data = public_key.modulus.as_unsigned_bytes_be();
263+
let len = key_data.len();
264+
265+
let key_bits = attributes.bits;
266+
if key_bits != 0 && len * 8 != key_bits {
267+
if crate::utils::GlobalConfig::log_error_details() {
268+
error!(
269+
"`bits` field of key attributes (value: {}) must be either 0 or equal to the size of the key in `data` (value: {}).",
270+
attributes.bits,
271+
len * 8
272+
);
273+
} else {
274+
error!("`bits` field of key attributes must be either 0 or equal to the size of the key in `data`.");
275+
}
276+
return Err(ResponseStatus::PsaErrorInvalidArgument);
277+
}
278+
279+
if len != 128 && len != 256 {
280+
if crate::utils::GlobalConfig::log_error_details() {
281+
error!(
282+
"The TPM provider only supports 1024 and 2048 bits RSA public keys ({} bits given).",
283+
len * 8
284+
);
285+
} else {
286+
error!("The TPM provider only supports 1024 and 2048 bits RSA public keys");
287+
}
288+
return Err(ResponseStatus::PsaErrorNotSupported);
289+
}
290+
291+
Ok(())
292+
}
293+
294+
/// Validates an RSAPrivateKey against the attributes we expect. Returns ok on success, otherwise
295+
/// returns an error.
296+
pub fn validate_private_key(private_key: &RSAPrivateKey, attributes: &Attributes) -> Result<()> {
297+
// NOTE: potentially incomplete, but any errors that aren't caught here should be caught
298+
// further down the stack (i.e. in the tss crate).
299+
let key_prime = &private_key.primes()[0];
300+
let key_len = key_prime.len();
301+
if key_len != attributes.bits / 2 {
302+
error!(
303+
"The key prime is not of the expected size (expected {}, got {})",
304+
attributes.bits / 2,
305+
key_len,
306+
);
307+
return Err(ResponseStatus::PsaErrorInvalidArgument);
308+
}
309+
Ok(())
310+
}
311+
246312
fn bytes_to_signature_data(
247313
data: Zeroizing<Vec<u8>>,
248314
key_attributes: Attributes,

0 commit comments

Comments
 (0)