|
@@ -13,7 +13,7 @@ use std::{
|
|
|
mem::size_of,
|
|
|
};
|
|
|
use openssl::{
|
|
|
- bn::BigNum,
|
|
|
+ bn::{BigNum, BigNumRef},
|
|
|
hash::Hasher,
|
|
|
nid::Nid,
|
|
|
};
|
|
@@ -30,7 +30,7 @@ use tss_esapi::{
|
|
|
session_type::SessionType,
|
|
|
response_code::Tss2ResponseCode,
|
|
|
tss::{TPM2_RH_NULL, TPM2_PERSISTENT_FIRST},
|
|
|
- CapabilityType, Tss2ResponseCodeKind,
|
|
|
+ CapabilityType, Tss2ResponseCodeKind, CommandCode,
|
|
|
},
|
|
|
tcti_ldr::{TctiNameConf, TabrmdConfig},
|
|
|
interface_types::{
|
|
@@ -38,7 +38,7 @@ use tss_esapi::{
|
|
|
algorithm::HashingAlgorithm,
|
|
|
key_bits::RsaKeyBits,
|
|
|
dynamic_handles::Persistent,
|
|
|
- session_handles::AuthSession,
|
|
|
+ session_handles::{AuthSession, PolicySession},
|
|
|
},
|
|
|
structures::{
|
|
|
Auth,
|
|
@@ -55,13 +55,20 @@ use tss_esapi::{
|
|
|
SignatureScheme,
|
|
|
RsaDecryptionScheme,
|
|
|
Data,
|
|
|
- CapabilityData,
|
|
|
+ CapabilityData, EncryptedSecret,
|
|
|
},
|
|
|
attributes::{
|
|
|
object::ObjectAttributes, SessionAttributesBuilder,
|
|
|
},
|
|
|
- handles::{KeyHandle, PersistentTpmHandle, TpmHandle},
|
|
|
+ handles::{
|
|
|
+ KeyHandle,
|
|
|
+ PersistentTpmHandle,
|
|
|
+ TpmHandle,
|
|
|
+ ObjectHandle
|
|
|
+ },
|
|
|
+ traits::{Marshall, UnMarshall},
|
|
|
};
|
|
|
+use serde::ser;
|
|
|
|
|
|
impl From<tss_esapi::Error> for Error {
|
|
|
fn from(err: tss_esapi::Error) -> Self {
|
|
@@ -96,7 +103,11 @@ trait ContextExt {
|
|
|
|
|
|
fn key_handle(&mut self, tpm_handle: TpmHandle) -> Result<KeyHandle>;
|
|
|
|
|
|
+ fn set_encrypt_decrypt(&mut self, session: AuthSession) -> Result<()>;
|
|
|
+
|
|
|
fn start_default_auth_session(&mut self) -> Result<AuthSession>;
|
|
|
+
|
|
|
+ fn start_policy_session(&mut self, is_trial: IsTrial) -> Result<PolicySession>;
|
|
|
}
|
|
|
|
|
|
impl ContextExt for Context {
|
|
@@ -206,6 +217,15 @@ impl ContextExt for Context {
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
+ fn set_encrypt_decrypt(&mut self, session: AuthSession) -> Result<()> {
|
|
|
+ let (attributes, mask) = SessionAttributesBuilder::new()
|
|
|
+ .with_decrypt(true)
|
|
|
+ .with_encrypt(true)
|
|
|
+ .build();
|
|
|
+ self.tr_sess_set_attributes(session, attributes, mask)?;
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
fn start_default_auth_session(&mut self) -> Result<AuthSession> {
|
|
|
let session = self.start_auth_session(
|
|
|
None,
|
|
@@ -216,16 +236,24 @@ impl ContextExt for Context {
|
|
|
HashingAlgorithm::Sha256,
|
|
|
)?
|
|
|
.ok_or_else(|| Error::custom("empty session handle received from TPM"))?;
|
|
|
-
|
|
|
- let (attributes, mask) = SessionAttributesBuilder::new()
|
|
|
- .with_decrypt(true)
|
|
|
- .with_encrypt(true)
|
|
|
- .build();
|
|
|
- self.tr_sess_set_attributes(session, attributes, mask)?;
|
|
|
-
|
|
|
+ self.set_encrypt_decrypt(session)?;
|
|
|
Ok(session)
|
|
|
}
|
|
|
|
|
|
+ fn start_policy_session(&mut self, is_trial: IsTrial) -> Result<PolicySession> {
|
|
|
+ let session = self.start_auth_session(
|
|
|
+ None,
|
|
|
+ None,
|
|
|
+ None,
|
|
|
+ is_trial.into(),
|
|
|
+ SymmetricDefinition::AES_256_CFB,
|
|
|
+ HashingAlgorithm::Sha256,
|
|
|
+ )?
|
|
|
+ .ok_or_else(|| Error::custom("empty session handle received from TPM"))?;
|
|
|
+ self.set_encrypt_decrypt(session)?;
|
|
|
+ Ok(session.try_into()?)
|
|
|
+ }
|
|
|
+
|
|
|
/// Loads the public information from the persistent object stored in the TPM referred to by
|
|
|
/// `tpm_handle` and returns the `KeyHandle` for this object. If the TPM handle refers to an
|
|
|
/// object which is not actually a key, then an error is returned.
|
|
@@ -237,6 +265,20 @@ impl ContextExt for Context {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+enum IsTrial {
|
|
|
+ True,
|
|
|
+ False,
|
|
|
+}
|
|
|
+
|
|
|
+impl From<IsTrial> for SessionType {
|
|
|
+ fn from(is_trial: IsTrial) -> Self {
|
|
|
+ match is_trial {
|
|
|
+ IsTrial::True => SessionType::Trial,
|
|
|
+ IsTrial::False => SessionType::Policy,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
trait DigestExt {
|
|
|
fn empty() -> Digest;
|
|
|
}
|
|
@@ -281,6 +323,82 @@ impl Cookie {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+pub struct ExportedKeyPair<S: Scheme> {
|
|
|
+ scheme: S,
|
|
|
+ public: Public,
|
|
|
+ private: tss_esapi::structures::Private,
|
|
|
+ secret: EncryptedSecret,
|
|
|
+}
|
|
|
+
|
|
|
+impl<S: Scheme> ExportedKeyPair<S> {
|
|
|
+ const FIELDS: &'static [&'static str] = &[
|
|
|
+ "scheme",
|
|
|
+ "public",
|
|
|
+ "private",
|
|
|
+ "secret",
|
|
|
+ ];
|
|
|
+}
|
|
|
+
|
|
|
+impl<S: Scheme> Serialize for ExportedKeyPair<S> {
|
|
|
+ fn serialize<T: Serializer>(&self, ser: T) -> std::result::Result<T::Ok, T::Error> {
|
|
|
+ let vec = self.public.marshall().map_err(ser::Error::custom)?;
|
|
|
+ let mut struct_ser = ser.serialize_struct(stringify!(ExportedKey), Self::FIELDS.len())?;
|
|
|
+ struct_ser.serialize_field(Self::FIELDS[0], &self.scheme)?;
|
|
|
+ struct_ser.serialize_field(Self::FIELDS[1], &vec)?;
|
|
|
+ struct_ser.serialize_field(Self::FIELDS[2], self.private.value())?;
|
|
|
+ struct_ser.serialize_field(Self::FIELDS[3], self.secret.value())?;
|
|
|
+ struct_ser.end()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<'de, S: Scheme> Deserialize<'de> for ExportedKeyPair<S> {
|
|
|
+ fn deserialize<D: Deserializer<'de>>(de: D) -> std::result::Result<Self, D::Error> {
|
|
|
+ struct StructVisitor<S: Scheme>(PhantomData<S>);
|
|
|
+
|
|
|
+ impl<'de, S: Scheme> Visitor<'de> for StructVisitor<S> {
|
|
|
+ type Value = ExportedKeyPair<S>;
|
|
|
+
|
|
|
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
|
+ formatter.write_fmt(format_args!("struct {}", stringify!(ExportedKey)))
|
|
|
+ }
|
|
|
+
|
|
|
+ fn visit_seq<V: SeqAccess<'de>>(
|
|
|
+ self, mut seq: V
|
|
|
+ ) -> std::result::Result<Self::Value, V::Error> {
|
|
|
+ fn conv_err<T, E: de::Error>(
|
|
|
+ result: std::result::Result<T, tss_esapi::Error>
|
|
|
+ ) -> std::result::Result<T, E> {
|
|
|
+ result.map_err(de::Error::custom)
|
|
|
+ }
|
|
|
+
|
|
|
+ let scheme: S = seq.next_element()?
|
|
|
+ .ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[0]))?;
|
|
|
+ let public: Box<[u8]> = seq.next_element()?
|
|
|
+ .ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[1]))?;
|
|
|
+ let public = conv_err(Public::unmarshall(public.as_ref()))?;
|
|
|
+ let private: Box<[u8]> = seq.next_element()?
|
|
|
+ .ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[2]))?;
|
|
|
+ let secret: Box<[u8]> = seq.next_element()?
|
|
|
+ .ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[3]))?;
|
|
|
+ Ok(ExportedKeyPair {
|
|
|
+ scheme,
|
|
|
+ public,
|
|
|
+ private: conv_err(private.as_ref().try_into())?,
|
|
|
+ secret: conv_err(secret.as_ref().try_into())?,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ de.deserialize_struct(stringify!(ExportedKey), Self::FIELDS, StructVisitor(PhantomData))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Serialize, Deserialize)]
|
|
|
+pub struct ExportedCreds {
|
|
|
+ sign: ExportedKeyPair<Sign>,
|
|
|
+ enc: ExportedKeyPair<Encrypt>,
|
|
|
+}
|
|
|
+
|
|
|
/// A public/private key pair in a form which can be serialized and deserialized.
|
|
|
#[derive(Serialize, Clone)]
|
|
|
struct StoredKeyPair<S: Scheme> {
|
|
@@ -288,6 +406,8 @@ struct StoredKeyPair<S: Scheme> {
|
|
|
private: TPM2_HANDLE,
|
|
|
}
|
|
|
|
|
|
+// I was unable to use `derive(Deserialize)` on `StoredKeyPair`, so I resorted to implementing it
|
|
|
+// manually.
|
|
|
impl<'de, S: Scheme> Deserialize<'de> for StoredKeyPair<S> {
|
|
|
fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
|
|
|
const FIELDS: &[&str] = &["public", "private"];
|
|
@@ -442,6 +562,7 @@ struct KeyBuilder<'a, S: Scheme> {
|
|
|
parent: Parent,
|
|
|
restricted: bool,
|
|
|
symmetric: SymmetricDefinitionObject,
|
|
|
+ rsa_exponent: u32,
|
|
|
}
|
|
|
|
|
|
impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
@@ -456,9 +577,17 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
|
parent: Parent::Seed(Hierarchy::Owner),
|
|
|
restricted: false,
|
|
|
symmetric: SymmetricDefinitionObject::Null,
|
|
|
+ rsa_exponent: Rsa::EXP,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ fn for_storage_key(scheme: S, unique: &'a [u8]) -> KeyBuilder<'a, S> {
|
|
|
+ KeyBuilder::new(scheme, unique)
|
|
|
+ .with_allow_dup(false)
|
|
|
+ .with_restricted(true)
|
|
|
+ .with_symmetric(SymmetricDefinitionObject::AES_256_CFB)
|
|
|
+ }
|
|
|
+
|
|
|
fn with_allow_dup(mut self, allow_dup: bool) -> Self {
|
|
|
self.allow_dup = allow_dup;
|
|
|
self
|
|
@@ -494,6 +623,11 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
|
self
|
|
|
}
|
|
|
|
|
|
+ fn with_rsa_exponent(mut self, rsa_exponent: u32) -> Self {
|
|
|
+ self.rsa_exponent = rsa_exponent;
|
|
|
+ self
|
|
|
+ }
|
|
|
+
|
|
|
fn rsa_template(&self) -> Result<Public> {
|
|
|
let scheme_enum = self.scheme.as_enum();
|
|
|
let decrypt = match scheme_enum {
|
|
@@ -514,7 +648,7 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
|
self.symmetric,
|
|
|
if self.restricted { RsaScheme::Null } else { scheme_enum.try_into()? },
|
|
|
self.scheme.key_len().try_into()?,
|
|
|
- Rsa::EXP.try_into()?,
|
|
|
+ self.rsa_exponent.try_into()?,
|
|
|
);
|
|
|
let unique = PublicKeyRsa::try_from(self.unique)?;
|
|
|
let public = Public::Rsa {
|
|
@@ -637,8 +771,8 @@ pub(crate) struct TpmCredStore {
|
|
|
}
|
|
|
|
|
|
impl TpmCredStore {
|
|
|
- const SIGN_SCHEME: Sign = Sign::RSA_PSS_3072_SHA_256;
|
|
|
- const ENCRYPT_SCHEME: Encrypt = Encrypt::RSA_OAEP_3072_SHA_256;
|
|
|
+ const SIGN_SCHEME: Sign = Sign::RSA_PSS_2048_SHA_256;
|
|
|
+ const ENCRYPT_SCHEME: Encrypt = Encrypt::RSA_OAEP_2048_SHA_256;
|
|
|
|
|
|
pub(crate) fn new<P: AsRef<Path>>(mut context: Context, state_path: P) -> Result<TpmCredStore> {
|
|
|
let storage = Storage::load_or_init(state_path.as_ref())?;
|
|
@@ -698,21 +832,30 @@ impl TpmCredStore {
|
|
|
{
|
|
|
let guard = self.state.read()?;
|
|
|
if let Some(storage_key) = &guard.storage_key {
|
|
|
+ // We take this path if the storage key was generated and loaded.
|
|
|
return Ok(storage_key.clone());
|
|
|
}
|
|
|
}
|
|
|
- let params = KeyBuilder::new(Self::ENCRYPT_SCHEME, self.cookie.as_slice())
|
|
|
+ {
|
|
|
+ let mut guard = self.state.write()?;
|
|
|
+ if let Some(storage_key) = guard.storage.storage_key.take() {
|
|
|
+ let result = KeyPair::from_stored(&mut guard.context, &storage_key);
|
|
|
+ guard.storage.storage_key = Some(storage_key);
|
|
|
+ // We take this path if the storage key was generated but not loaded.
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let params = KeyBuilder::for_storage_key(Self::ENCRYPT_SCHEME, self.cookie.as_slice())
|
|
|
.with_parent(Parent::Seed(Hierarchy::Owner))
|
|
|
- .with_allow_dup(false)
|
|
|
- .with_auth(self.cookie.auth())
|
|
|
- .with_restricted(true)
|
|
|
- .with_symmetric(SymmetricDefinitionObject::AES_256_CFB);
|
|
|
+ .with_auth(self.cookie.auth());
|
|
|
let mut guard = self.state.write()?;
|
|
|
let storage_key = params.build(&mut guard.context)?;
|
|
|
guard.storage_key = Some(storage_key.clone());
|
|
|
let tpm_handle = guard.context.persist_key(storage_key.private)?;
|
|
|
guard.storage.storage_key = Some(storage_key.to_stored(tpm_handle));
|
|
|
if let Err(err) = self.save_storage(&mut guard) {
|
|
|
+ guard.storage_key = None;
|
|
|
+ guard.storage.storage_key = None;
|
|
|
guard.context.evict_key(tpm_handle, Some(storage_key.private))?;
|
|
|
return Err(err);
|
|
|
}
|
|
@@ -743,34 +886,52 @@ impl TpmCredStore {
|
|
|
fn gen_node_creds(&self) -> Result<TpmCreds> {
|
|
|
let sign = self.gen_node_sign_key()?;
|
|
|
let enc = self.gen_node_enc_key()?;
|
|
|
- let creds = TpmCreds { sign, enc, state: self.state.clone() };
|
|
|
+ let cred_data = CredData { sign, enc };
|
|
|
+ let creds = TpmCreds::new(cred_data, &self.state);
|
|
|
self.persist(&creds, |storage, handles| storage.node = Some(handles))?;
|
|
|
Ok(creds)
|
|
|
}
|
|
|
|
|
|
- fn gen_root_sign_key(&self, password: &str) -> Result<KeyPair<Sign>> {
|
|
|
- let unique = rand_vec(Self::ENCRYPT_SCHEME.key_len() as usize)?;
|
|
|
+ /// Returns the digest for the policy which allows a key to be duplicated provided that the
|
|
|
+ /// caller supplies the correct password (authValue) for the key being duplicated.
|
|
|
+ fn dup_with_password_policy(&self) -> Result<Digest> {
|
|
|
+ let mut guard = self.state.write()?;
|
|
|
+ let policy_session = guard.context.start_policy_session(IsTrial::True)?;
|
|
|
+ guard.context.execute_with_session(None, |ctx| {
|
|
|
+ ctx.policy_password(policy_session)?;
|
|
|
+ ctx.policy_command_code(policy_session, CommandCode::Duplicate)?;
|
|
|
+ Ok(ctx.policy_get_digest(policy_session)?)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ fn gen_root_sign_key(&self, password: &str, policy: Digest) -> Result<KeyPair<Sign>> {
|
|
|
+ let scheme = Self::SIGN_SCHEME;
|
|
|
+ let unique = rand_vec(scheme.key_len() as usize)?;
|
|
|
let storage_key = self.get_or_init_storage_key()?;
|
|
|
- let params = KeyBuilder::new(Self::SIGN_SCHEME, unique.as_slice())
|
|
|
+ let params = KeyBuilder::new(scheme, unique.as_slice())
|
|
|
.with_parent(Parent::Key(storage_key.private))
|
|
|
.with_allow_dup(true)
|
|
|
- .with_auth(password.as_bytes().try_into()?);
|
|
|
+ .with_auth(password.as_bytes().try_into()?)
|
|
|
+ .with_policy_digest(policy);
|
|
|
self.gen_key(params)
|
|
|
}
|
|
|
|
|
|
- fn gen_root_enc_key(&self, password: &str) -> Result<KeyPair<Encrypt>> {
|
|
|
- let unique = rand_vec(Self::ENCRYPT_SCHEME.key_len() as usize)?;
|
|
|
+ fn gen_root_enc_key(&self, password: &str, policy: Digest) -> Result<KeyPair<Encrypt>> {
|
|
|
+ let scheme = Self::ENCRYPT_SCHEME;
|
|
|
+ let unique = rand_vec(scheme.key_len() as usize)?;
|
|
|
let storage_key = self.get_or_init_storage_key()?;
|
|
|
- let params = KeyBuilder::new(Self::ENCRYPT_SCHEME, unique.as_slice())
|
|
|
+ let params = KeyBuilder::new(scheme, unique.as_slice())
|
|
|
.with_parent(Parent::Key(storage_key.private))
|
|
|
.with_allow_dup(true)
|
|
|
- .with_auth(password.as_bytes().try_into()?);
|
|
|
+ .with_auth(password.as_bytes().try_into()?)
|
|
|
+ .with_policy_digest(policy);
|
|
|
self.gen_key(params)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
impl CredStore for TpmCredStore {
|
|
|
type CredHandle = TpmCreds;
|
|
|
+ type ExportedCreds = ExportedCreds;
|
|
|
|
|
|
fn node_creds(&self) -> Result<TpmCreds> {
|
|
|
{
|
|
@@ -804,9 +965,125 @@ impl CredStore for TpmCredStore {
|
|
|
return Err(Error::custom("root creds have already been generated"))
|
|
|
}
|
|
|
}
|
|
|
- let sign = self.gen_root_sign_key(password)?;
|
|
|
- let enc = self.gen_root_enc_key(password)?;
|
|
|
- let creds = TpmCreds { sign, enc, state: self.state.clone() };
|
|
|
+ let policy = self.dup_with_password_policy()?;
|
|
|
+ let sign = self.gen_root_sign_key(password, policy.clone())?;
|
|
|
+ let enc = self.gen_root_enc_key(password, policy)?;
|
|
|
+ let cred_data = CredData { sign, enc };
|
|
|
+ let creds = TpmCreds::new(cred_data, &self.state);
|
|
|
+ self.persist(&creds, |storage, handles| storage.root = Some(handles))?;
|
|
|
+ Ok(creds)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn storage_key(&self) -> Result<AsymKeyPub<Encrypt>> {
|
|
|
+ let pair = self.get_or_init_storage_key()?;
|
|
|
+ Ok(pair.public)
|
|
|
+ }
|
|
|
+
|
|
|
+ fn export_root_creds(
|
|
|
+ &self, root_creds: &TpmCreds, password: &str, new_parent: &AsymKeyPub<Encrypt>
|
|
|
+ ) -> Result<ExportedCreds> {
|
|
|
+ fn export_key<S: Scheme>(
|
|
|
+ context: &mut Context,
|
|
|
+ key_pair: &KeyPair<S>,
|
|
|
+ new_parent: KeyHandle,
|
|
|
+ key_auth: Auth,
|
|
|
+ policy_session: PolicySession,
|
|
|
+ ) -> Result<ExportedKeyPair<S>> {
|
|
|
+ let result: Result<()> = context.execute_with_session(None, |ctx| {
|
|
|
+ ctx.policy_password(policy_session)?;
|
|
|
+ ctx.policy_command_code(policy_session, CommandCode::Duplicate)?;
|
|
|
+ Ok(())
|
|
|
+ });
|
|
|
+ result?;
|
|
|
+ let (public, ..) = context.read_public(key_pair.private)?;
|
|
|
+ context.execute_with_session(Some(policy_session.into()), |ctx| {
|
|
|
+ let obj_handle = ObjectHandle::from(key_pair.private);
|
|
|
+ ctx.tr_set_auth(obj_handle, key_auth)?;
|
|
|
+ let (_, private, secret) = ctx.duplicate(
|
|
|
+ obj_handle,
|
|
|
+ new_parent.into(),
|
|
|
+ None,
|
|
|
+ SymmetricDefinitionObject::Null,
|
|
|
+ )?;
|
|
|
+ Ok(ExportedKeyPair {
|
|
|
+ scheme: key_pair.public.scheme,
|
|
|
+ public,
|
|
|
+ private,
|
|
|
+ secret,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ let new_parent = new_parent.storage_key_public()?;
|
|
|
+ let mut guard = self.state.write()?;
|
|
|
+ if let Some(storage_key) = guard.storage_key.take() {
|
|
|
+ // Save memory by flushing the storage key from the TPM's RAM.
|
|
|
+ guard.context.flush_context(storage_key.private.into())?;
|
|
|
+ }
|
|
|
+ let new_parent_handle = guard.context.load_external_public(new_parent, Hierarchy::Null)?;
|
|
|
+ let policy_session = guard.context.start_policy_session(IsTrial::False)?;
|
|
|
+ let auth = Auth::try_from(password.as_bytes())?;
|
|
|
+ let sign = export_key(
|
|
|
+ &mut guard.context,
|
|
|
+ &root_creds.sign,
|
|
|
+ new_parent_handle,
|
|
|
+ auth.clone(),
|
|
|
+ policy_session,
|
|
|
+ )?;
|
|
|
+ let enc = export_key(
|
|
|
+ &mut guard.context,
|
|
|
+ &root_creds.enc,
|
|
|
+ new_parent_handle,
|
|
|
+ auth,
|
|
|
+ policy_session,
|
|
|
+ )?;
|
|
|
+ Ok(ExportedCreds { sign, enc })
|
|
|
+ }
|
|
|
+
|
|
|
+ fn import_root_creds(&self, password: &str, exported: ExportedCreds) -> Result<TpmCreds> {
|
|
|
+ fn import_key<S: Scheme>(
|
|
|
+ context: &mut Context,
|
|
|
+ exported: ExportedKeyPair<S>,
|
|
|
+ new_parent: KeyHandle,
|
|
|
+ auth: Auth,
|
|
|
+ ) -> Result<KeyPair<S>> {
|
|
|
+ let private = context.import(
|
|
|
+ new_parent.into(),
|
|
|
+ None,
|
|
|
+ exported.public.clone(),
|
|
|
+ exported.private,
|
|
|
+ exported.secret,
|
|
|
+ SymmetricDefinitionObject::Null,
|
|
|
+ )?;
|
|
|
+ let key_handle = context.load(
|
|
|
+ new_parent,
|
|
|
+ private,
|
|
|
+ exported.public.clone(),
|
|
|
+ )?;
|
|
|
+ context.tr_set_auth(key_handle.into(), auth)?;
|
|
|
+ let public = AsymKeyPub::try_from(exported.public, exported.scheme)?;
|
|
|
+ Ok(KeyPair { public, private: key_handle })
|
|
|
+ }
|
|
|
+
|
|
|
+ let storage_key = self.get_or_init_storage_key()?;
|
|
|
+ let creds = {
|
|
|
+ let mut guard = self.state.write()?;
|
|
|
+ let auth = Auth::try_from(password.as_bytes())?;
|
|
|
+ let sign = import_key(
|
|
|
+ &mut guard.context,
|
|
|
+ exported.sign,
|
|
|
+ storage_key.private,
|
|
|
+ auth.clone()
|
|
|
+ )?;
|
|
|
+ let enc = import_key(
|
|
|
+ &mut guard.context,
|
|
|
+ exported.enc,
|
|
|
+ storage_key.private,
|
|
|
+ auth,
|
|
|
+ )?;
|
|
|
+ let cred_data = CredData { sign, enc, };
|
|
|
+ TpmCreds::new(cred_data, &self.state)
|
|
|
+ };
|
|
|
self.persist(&creds, |storage, handles| storage.root = Some(handles))?;
|
|
|
Ok(creds)
|
|
|
}
|
|
@@ -826,6 +1103,26 @@ impl<S: Scheme> AsymKeyPub<S> {
|
|
|
_ => Err(Error::custom("Unsupported key type returned by TPM")),
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ fn storage_key_public(&self) -> Result<Public> {
|
|
|
+ fn from_rsa<S: Scheme>(
|
|
|
+ scheme: S, rsa: openssl::rsa::Rsa<crypto::Public>
|
|
|
+ ) -> Result<Public> {
|
|
|
+ let exponent = rsa.e().try_into_u32()?;
|
|
|
+ let modulus = rsa.n().to_vec();
|
|
|
+ let builder = KeyBuilder::for_storage_key(scheme, &modulus)
|
|
|
+ .with_rsa_exponent(exponent);
|
|
|
+ builder.rsa_template()
|
|
|
+ }
|
|
|
+ match self.scheme.as_enum() {
|
|
|
+ SchemeKind::Encrypt(inner) => match inner {
|
|
|
+ Encrypt::RsaEsOaep(scheme) => from_rsa(scheme, self.pkey.rsa()?),
|
|
|
+ }
|
|
|
+ SchemeKind::Sign(inner) => match inner {
|
|
|
+ Sign::RsaSsaPss(scheme) => from_rsa(scheme, self.pkey.rsa()?),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
trait NidExt {
|
|
@@ -844,6 +1141,26 @@ trait NidExt {
|
|
|
|
|
|
impl NidExt for Nid {}
|
|
|
|
|
|
+trait BigNumRefExt {
|
|
|
+ fn try_into_u32(self) -> Result<u32>;
|
|
|
+}
|
|
|
+
|
|
|
+impl BigNumRefExt for &BigNumRef {
|
|
|
+ fn try_into_u32(self) -> Result<u32> {
|
|
|
+ let data = self.to_vec();
|
|
|
+ if data.len() > 4 {
|
|
|
+ return Err(Error::custom(format!("data was too long: {}", data.len())));
|
|
|
+ }
|
|
|
+ let mut buf = [0u8; 4];
|
|
|
+ // Note that BigNum data is stored in big endian format, so padding zeros go at the
|
|
|
+ // beginning of the buffer.
|
|
|
+ let subslice = &mut buf[4-data.len()..];
|
|
|
+ subslice.copy_from_slice(data.as_slice());
|
|
|
+ let int = u32::from_be_bytes(buf);
|
|
|
+ Ok(int)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
trait MessageDigestExt {
|
|
|
fn hash_algo(&self) -> Result<HashingAlgorithm>;
|
|
|
fn hash_scheme(&self) -> Result<HashScheme> {
|
|
@@ -909,7 +1226,11 @@ pub(crate) struct TpmCreds {
|
|
|
|
|
|
impl TpmCreds {
|
|
|
fn new(key_handles: CredData, state: &Arc<RwLock<State>>) -> TpmCreds {
|
|
|
- TpmCreds { sign: key_handles.sign, enc: key_handles.enc, state: state.clone() }
|
|
|
+ TpmCreds {
|
|
|
+ sign: key_handles.sign,
|
|
|
+ enc: key_handles.enc,
|
|
|
+ state: state.clone(),
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1163,7 +1484,7 @@ active_pcr_banks = sha256
|
|
|
/// Displays the message associated with a TSS2 return code.
|
|
|
//#[test]
|
|
|
fn print_error_message() {
|
|
|
- const RC: TSS2_RC = 0x00000101;
|
|
|
+ const RC: TSS2_RC = 0x00000982;
|
|
|
let msg = tss2_rc_decode(RC);
|
|
|
println!("{}", msg);
|
|
|
}
|
|
@@ -1415,4 +1736,29 @@ active_pcr_banks = sha256
|
|
|
assert!(encrypt_decrypt_test(&creds).is_err());
|
|
|
Ok(())
|
|
|
}
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn root_key_export_import() -> Result<()> {
|
|
|
+ const PASSWORD: &str = "Frobinate";
|
|
|
+ let (_src_harness, src_store) = test_store()?;
|
|
|
+ let src_root_creds = src_store.gen_root_creds(PASSWORD)?;
|
|
|
+
|
|
|
+ let (_dest_harness, dest_store) = test_store()?;
|
|
|
+ let dest_storage_key = dest_store.storage_key()?;
|
|
|
+
|
|
|
+ let exported = src_store.export_root_creds(&src_root_creds, PASSWORD, &dest_storage_key)?;
|
|
|
+ let vec = to_vec(&exported)?;
|
|
|
+ let mut slice = vec.as_slice();
|
|
|
+ let exported = read_from(&mut slice)?;
|
|
|
+ let dest_root_creds = dest_store.import_root_creds(PASSWORD, exported)?;
|
|
|
+
|
|
|
+ let message = rand_vec(TpmCredStore::ENCRYPT_SCHEME.key_len() as usize / 2)?;
|
|
|
+ let sig = dest_root_creds.sign([message.as_slice()].into_iter())?;
|
|
|
+ src_root_creds.verify([message.as_slice()].into_iter(), sig.as_slice())?;
|
|
|
+
|
|
|
+ let ct = src_root_creds.encrypt(message.as_slice())?;
|
|
|
+ let pt = dest_root_creds.decrypt(ct.as_slice())?;
|
|
|
+ assert_eq!(message.as_slice(), pt.as_slice());
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
}
|