|
@@ -10,12 +10,12 @@ use std::{
|
|
path::{Path, PathBuf},
|
|
path::{Path, PathBuf},
|
|
fs::{OpenOptions},
|
|
fs::{OpenOptions},
|
|
sync::{Arc, RwLock, RwLockWriteGuard},
|
|
sync::{Arc, RwLock, RwLockWriteGuard},
|
|
- mem::size_of,
|
|
|
|
|
|
+ mem::size_of, ops::Deref,
|
|
};
|
|
};
|
|
use openssl::{
|
|
use openssl::{
|
|
bn::{BigNum, BigNumRef},
|
|
bn::{BigNum, BigNumRef},
|
|
hash::Hasher,
|
|
hash::Hasher,
|
|
- nid::Nid,
|
|
|
|
|
|
+ nid::Nid, pkcs5::pbkdf2_hmac,
|
|
};
|
|
};
|
|
use tss_esapi_sys::{
|
|
use tss_esapi_sys::{
|
|
TSS2_RC,
|
|
TSS2_RC,
|
|
@@ -35,8 +35,8 @@ use tss_esapi::{
|
|
tcti_ldr::{TctiNameConf, TabrmdConfig},
|
|
tcti_ldr::{TctiNameConf, TabrmdConfig},
|
|
interface_types::{
|
|
interface_types::{
|
|
resource_handles::{Hierarchy, Provision},
|
|
resource_handles::{Hierarchy, Provision},
|
|
- algorithm::HashingAlgorithm,
|
|
|
|
- key_bits::RsaKeyBits,
|
|
|
|
|
|
+ algorithm::{HashingAlgorithm},
|
|
|
|
+ key_bits::{RsaKeyBits},
|
|
dynamic_handles::Persistent,
|
|
dynamic_handles::Persistent,
|
|
session_handles::{AuthSession, PolicySession},
|
|
session_handles::{AuthSession, PolicySession},
|
|
},
|
|
},
|
|
@@ -55,7 +55,7 @@ use tss_esapi::{
|
|
SignatureScheme,
|
|
SignatureScheme,
|
|
RsaDecryptionScheme,
|
|
RsaDecryptionScheme,
|
|
Data,
|
|
Data,
|
|
- CapabilityData, EncryptedSecret,
|
|
|
|
|
|
+ CapabilityData, EncryptedSecret, Private,
|
|
},
|
|
},
|
|
attributes::{
|
|
attributes::{
|
|
object::ObjectAttributes, SessionAttributesBuilder,
|
|
object::ObjectAttributes, SessionAttributesBuilder,
|
|
@@ -69,6 +69,7 @@ use tss_esapi::{
|
|
traits::{Marshall, UnMarshall},
|
|
traits::{Marshall, UnMarshall},
|
|
};
|
|
};
|
|
use serde::ser;
|
|
use serde::ser;
|
|
|
|
+use zeroize::Zeroizing;
|
|
|
|
|
|
impl From<tss_esapi::Error> for Error {
|
|
impl From<tss_esapi::Error> for Error {
|
|
fn from(err: tss_esapi::Error) -> Self {
|
|
fn from(err: tss_esapi::Error) -> Self {
|
|
@@ -108,6 +109,25 @@ trait ContextExt {
|
|
fn start_default_auth_session(&mut self) -> Result<AuthSession>;
|
|
fn start_default_auth_session(&mut self) -> Result<AuthSession>;
|
|
|
|
|
|
fn start_policy_session(&mut self, is_trial: IsTrial) -> Result<PolicySession>;
|
|
fn start_policy_session(&mut self, is_trial: IsTrial) -> Result<PolicySession>;
|
|
|
|
+
|
|
|
|
+ fn dup_with_password_policy(&mut self) -> Result<Digest>;
|
|
|
|
+
|
|
|
|
+ fn export_key<S: Scheme>(
|
|
|
|
+ &mut self,
|
|
|
|
+ key_pair: &KeyPair<S>,
|
|
|
|
+ new_parent: KeyHandle,
|
|
|
|
+ key_auth: Auth,
|
|
|
|
+ policy_session: PolicySession,
|
|
|
|
+ encryption_key: &AeadKey,
|
|
|
|
+ ) -> Result<ExportedKeyPair<S>>;
|
|
|
|
+
|
|
|
|
+ fn import_key<S: Scheme>(
|
|
|
|
+ &mut self,
|
|
|
|
+ exported: ExportedKeyPair<S>,
|
|
|
|
+ new_parent: KeyHandle,
|
|
|
|
+ auth: Auth,
|
|
|
|
+ encryption_key: &AeadKey,
|
|
|
|
+ ) -> Result<KeyPair<S>>;
|
|
}
|
|
}
|
|
|
|
|
|
impl ContextExt for Context {
|
|
impl ContextExt for Context {
|
|
@@ -263,6 +283,79 @@ impl ContextExt for Context {
|
|
})?;
|
|
})?;
|
|
Ok(obj_handle.into())
|
|
Ok(obj_handle.into())
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /// 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(&mut self) -> Result<Digest> {
|
|
|
|
+ let policy_session = self.start_policy_session(IsTrial::True)?;
|
|
|
|
+ self.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 export_key<S: Scheme>(
|
|
|
|
+ &mut self,
|
|
|
|
+ key_pair: &KeyPair<S>,
|
|
|
|
+ new_parent: KeyHandle,
|
|
|
|
+ key_auth: Auth,
|
|
|
|
+ policy_session: PolicySession,
|
|
|
|
+ encryption_key: &AeadKey,
|
|
|
|
+ ) -> Result<ExportedKeyPair<S>> {
|
|
|
|
+ let (public, ..) = self.read_public(key_pair.private)?;
|
|
|
|
+ let result: Result<()> = self.execute_with_session(None, |ctx| {
|
|
|
|
+ ctx.policy_password(policy_session)?;
|
|
|
|
+ ctx.policy_command_code(policy_session, CommandCode::Duplicate)?;
|
|
|
|
+ Ok(())
|
|
|
|
+ });
|
|
|
|
+ result?;
|
|
|
|
+ let result: Result<(tss_esapi::structures::Private, EncryptedSecret)> = self
|
|
|
|
+ .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((private, secret))
|
|
|
|
+ });
|
|
|
|
+ let (private, secret) = result?;
|
|
|
|
+ let tagged_ct = encryption_key.encrypt(
|
|
|
|
+ PublicWrapper(public),
|
|
|
|
+ &TpmBlobs { private, secret }
|
|
|
|
+ )?;
|
|
|
|
+ Ok(ExportedKeyPair { scheme: key_pair.public.scheme, tagged_ct })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn import_key<S: Scheme>(
|
|
|
|
+ &mut self,
|
|
|
|
+ exported: ExportedKeyPair<S>,
|
|
|
|
+ new_parent: KeyHandle,
|
|
|
|
+ auth: Auth,
|
|
|
|
+ encryption_key: &AeadKey,
|
|
|
|
+ ) -> Result<KeyPair<S>> {
|
|
|
|
+ let blobs = encryption_key.decrypt(&exported.tagged_ct)?;
|
|
|
|
+ let public = exported.tagged_ct.aad;
|
|
|
|
+ let private = self.import(
|
|
|
|
+ new_parent.into(),
|
|
|
|
+ None,
|
|
|
|
+ public.clone(),
|
|
|
|
+ blobs.private,
|
|
|
|
+ blobs.secret,
|
|
|
|
+ SymmetricDefinitionObject::Null,
|
|
|
|
+ )?;
|
|
|
|
+ let key_handle = self.load(
|
|
|
|
+ new_parent,
|
|
|
|
+ private,
|
|
|
|
+ public.clone(),
|
|
|
|
+ )?;
|
|
|
|
+ self.tr_set_auth(key_handle.into(), auth)?;
|
|
|
|
+ let public = AsymKeyPub::try_from(public.0, exported.scheme)?;
|
|
|
|
+ Ok(KeyPair { public, private: key_handle })
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
enum IsTrial {
|
|
enum IsTrial {
|
|
@@ -323,30 +416,74 @@ impl Cookie {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#[derive(Serialize, Deserialize)]
|
|
|
|
+struct PublicWrapper(
|
|
|
|
+ #[serde(serialize_with = "serialize_marshall")]
|
|
|
|
+ #[serde(deserialize_with = "deserialize_unmarshall")]
|
|
|
|
+ Public
|
|
|
|
+);
|
|
|
|
+
|
|
|
|
+impl Deref for PublicWrapper {
|
|
|
|
+ type Target = Public;
|
|
|
|
+ fn deref(&self) -> &Self::Target {
|
|
|
|
+ &self.0
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn serialize_marshall<S: Serializer, T: Marshall>(
|
|
|
|
+ value: &T, ser: S
|
|
|
|
+) -> std::result::Result<S::Ok, S::Error> {
|
|
|
|
+ let vec = value.marshall().map_err(ser::Error::custom)?;
|
|
|
|
+ vec.as_slice().serialize(ser)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn deserialize_unmarshall<'de, D: Deserializer<'de>, T: UnMarshall>(
|
|
|
|
+ de: D
|
|
|
|
+) -> std::result::Result<T, D::Error> {
|
|
|
|
+ let vec: Vec<u8> = Deserialize::deserialize(de)?;
|
|
|
|
+ T::unmarshall(&vec).map_err(de::Error::custom)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#[derive(Serialize, Deserialize)]
|
|
|
|
+struct TpmBlobs {
|
|
|
|
+ #[serde(serialize_with = "serialize_as_vec")]
|
|
|
|
+ #[serde(deserialize_with = "deserialize_as_vec")]
|
|
|
|
+ private: Private,
|
|
|
|
+ #[serde(serialize_with = "serialize_as_vec")]
|
|
|
|
+ #[serde(deserialize_with = "deserialize_as_vec")]
|
|
|
|
+ secret: EncryptedSecret,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn serialize_as_vec<S: Serializer, T: Deref<Target=Vec<u8>>>(
|
|
|
|
+ value: &T, ser: S
|
|
|
|
+) -> std::result::Result<S::Ok, S::Error> {
|
|
|
|
+ value.deref().serialize(ser)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fn deserialize_as_vec<'de, D: Deserializer<'de>, T: TryFrom<Vec<u8>>>(
|
|
|
|
+ de: D
|
|
|
|
+) -> std::result::Result<T, D::Error> {
|
|
|
|
+ T::try_from(Vec::deserialize(de)?)
|
|
|
|
+ .map_err(|_| de::Error::custom("Unable to convert from vector"))
|
|
|
|
+}
|
|
|
|
+
|
|
pub struct ExportedKeyPair<S: Scheme> {
|
|
pub struct ExportedKeyPair<S: Scheme> {
|
|
scheme: S,
|
|
scheme: S,
|
|
- public: Public,
|
|
|
|
- private: tss_esapi::structures::Private,
|
|
|
|
- secret: EncryptedSecret,
|
|
|
|
|
|
+ tagged_ct: TaggedCiphertext<TpmBlobs, PublicWrapper>,
|
|
}
|
|
}
|
|
|
|
|
|
impl<S: Scheme> ExportedKeyPair<S> {
|
|
impl<S: Scheme> ExportedKeyPair<S> {
|
|
const FIELDS: &'static [&'static str] = &[
|
|
const FIELDS: &'static [&'static str] = &[
|
|
"scheme",
|
|
"scheme",
|
|
- "public",
|
|
|
|
- "private",
|
|
|
|
- "secret",
|
|
|
|
|
|
+ "tagged_ct",
|
|
];
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
impl<S: Scheme> Serialize for ExportedKeyPair<S> {
|
|
impl<S: Scheme> Serialize for ExportedKeyPair<S> {
|
|
fn serialize<T: Serializer>(&self, ser: T) -> std::result::Result<T::Ok, T::Error> {
|
|
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())?;
|
|
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[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.serialize_field(Self::FIELDS[1], &self.tagged_ct)?;
|
|
struct_ser.end()
|
|
struct_ser.end()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -373,19 +510,9 @@ impl<'de, S: Scheme> Deserialize<'de> for ExportedKeyPair<S> {
|
|
|
|
|
|
let scheme: S = seq.next_element()?
|
|
let scheme: S = seq.next_element()?
|
|
.ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[0]))?;
|
|
.ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[0]))?;
|
|
- let public: Box<[u8]> = seq.next_element()?
|
|
|
|
|
|
+ let tagged_ct: TaggedCiphertext<TpmBlobs, PublicWrapper> = seq.next_element()?
|
|
.ok_or_else(|| de::Error::missing_field(ExportedKeyPair::<S>::FIELDS[1]))?;
|
|
.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())?,
|
|
|
|
- })
|
|
|
|
|
|
+ Ok(ExportedKeyPair { scheme, tagged_ct })
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -397,6 +524,7 @@ impl<'de, S: Scheme> Deserialize<'de> for ExportedKeyPair<S> {
|
|
pub struct ExportedCreds {
|
|
pub struct ExportedCreds {
|
|
sign: ExportedKeyPair<Sign>,
|
|
sign: ExportedKeyPair<Sign>,
|
|
enc: ExportedKeyPair<Encrypt>,
|
|
enc: ExportedKeyPair<Encrypt>,
|
|
|
|
+ params: DerivationParams,
|
|
}
|
|
}
|
|
|
|
|
|
/// A public/private key pair in a form which can be serialized and deserialized.
|
|
/// A public/private key pair in a form which can be serialized and deserialized.
|
|
@@ -581,13 +709,6 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- 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 {
|
|
fn with_allow_dup(mut self, allow_dup: bool) -> Self {
|
|
self.allow_dup = allow_dup;
|
|
self.allow_dup = allow_dup;
|
|
self
|
|
self
|
|
@@ -707,6 +828,15 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+impl<'a> KeyBuilder<'a, Encrypt> {
|
|
|
|
+ fn for_storage_key(scheme: Encrypt, unique: &'a [u8]) -> KeyBuilder<'a, Encrypt> {
|
|
|
|
+ KeyBuilder::new(scheme, unique)
|
|
|
|
+ .with_allow_dup(false)
|
|
|
|
+ .with_restricted(true)
|
|
|
|
+ .with_symmetric(SymmetricDefinitionObject::AES_128_CFB)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/// A public/private key pair.
|
|
/// A public/private key pair.
|
|
#[derive(Clone)]
|
|
#[derive(Clone)]
|
|
struct KeyPair<S: Scheme> {
|
|
struct KeyPair<S: Scheme> {
|
|
@@ -892,18 +1022,6 @@ impl TpmCredStore {
|
|
Ok(creds)
|
|
Ok(creds)
|
|
}
|
|
}
|
|
|
|
|
|
- /// 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>> {
|
|
fn gen_root_sign_key(&self, password: &str, policy: Digest) -> Result<KeyPair<Sign>> {
|
|
let scheme = Self::SIGN_SCHEME;
|
|
let scheme = Self::SIGN_SCHEME;
|
|
let unique = rand_vec(scheme.key_len() as usize)?;
|
|
let unique = rand_vec(scheme.key_len() as usize)?;
|
|
@@ -929,6 +1047,46 @@ impl TpmCredStore {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#[derive(Serialize, Deserialize)]
|
|
|
|
+struct DerivationParams {
|
|
|
|
+ iter: usize,
|
|
|
|
+ hash: HashKind,
|
|
|
|
+ kind: AeadKeyKind,
|
|
|
|
+ salt: Vec<u8>,
|
|
|
|
+ iv: Vec<u8>,
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+impl DerivationParams {
|
|
|
|
+ const PBKDF2_ITER: usize = 1000000;
|
|
|
|
+ const PBKDF2_HASH: HashKind = HashKind::Sha2_256;
|
|
|
|
+ const EXPORT_KEY_KIND: AeadKeyKind = AeadKeyKind::AesGcm256;
|
|
|
|
+
|
|
|
|
+ fn new() -> Result<DerivationParams> {
|
|
|
|
+ const_assert!(
|
|
|
|
+ DerivationParams::PBKDF2_HASH.len() == DerivationParams::EXPORT_KEY_KIND.key_len()
|
|
|
|
+ );
|
|
|
|
+ Ok(DerivationParams {
|
|
|
|
+ iter: Self::PBKDF2_ITER,
|
|
|
|
+ hash: Self::PBKDF2_HASH,
|
|
|
|
+ kind: Self::EXPORT_KEY_KIND,
|
|
|
|
+ salt: rand_vec(Self::PBKDF2_HASH.len())?,
|
|
|
|
+ iv: rand_vec(Self::EXPORT_KEY_KIND.iv_len())?,
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn derive_key(&self, password: &str) -> Result<AeadKey> {
|
|
|
|
+ let mut key = Zeroizing::new([0u8; Self::EXPORT_KEY_KIND.key_len()]);
|
|
|
|
+ pbkdf2_hmac(
|
|
|
|
+ password.as_bytes(),
|
|
|
|
+ self.salt.as_slice(),
|
|
|
|
+ self.iter,
|
|
|
|
+ self.hash.into(),
|
|
|
|
+ key.as_mut_slice()
|
|
|
|
+ )?;
|
|
|
|
+ AeadKey::copy_components(self.kind, key.as_slice(), &self.iv)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
impl CredStore for TpmCredStore {
|
|
impl CredStore for TpmCredStore {
|
|
type CredHandle = TpmCreds;
|
|
type CredHandle = TpmCreds;
|
|
type ExportedCreds = ExportedCreds;
|
|
type ExportedCreds = ExportedCreds;
|
|
@@ -965,7 +1123,10 @@ impl CredStore for TpmCredStore {
|
|
return Err(Error::custom("root creds have already been generated"))
|
|
return Err(Error::custom("root creds have already been generated"))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- let policy = self.dup_with_password_policy()?;
|
|
|
|
|
|
+ let policy = {
|
|
|
|
+ let mut guard = self.state.write()?;
|
|
|
|
+ guard.context.dup_with_password_policy()?
|
|
|
|
+ };
|
|
let sign = self.gen_root_sign_key(password, policy.clone())?;
|
|
let sign = self.gen_root_sign_key(password, policy.clone())?;
|
|
let enc = self.gen_root_enc_key(password, policy)?;
|
|
let enc = self.gen_root_enc_key(password, policy)?;
|
|
let cred_data = CredData { sign, enc };
|
|
let cred_data = CredData { sign, enc };
|
|
@@ -982,38 +1143,8 @@ impl CredStore for TpmCredStore {
|
|
fn export_root_creds(
|
|
fn export_root_creds(
|
|
&self, root_creds: &TpmCreds, password: &str, new_parent: &AsymKeyPub<Encrypt>
|
|
&self, root_creds: &TpmCreds, password: &str, new_parent: &AsymKeyPub<Encrypt>
|
|
) -> Result<ExportedCreds> {
|
|
) -> 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 params = DerivationParams::new()?;
|
|
|
|
+ let aead_key = params.derive_key(password)?;
|
|
let new_parent = new_parent.storage_key_public()?;
|
|
let new_parent = new_parent.storage_key_public()?;
|
|
let mut guard = self.state.write()?;
|
|
let mut guard = self.state.write()?;
|
|
if let Some(storage_key) = guard.storage_key.take() {
|
|
if let Some(storage_key) = guard.storage_key.take() {
|
|
@@ -1023,63 +1154,40 @@ impl CredStore for TpmCredStore {
|
|
let new_parent_handle = guard.context.load_external_public(new_parent, Hierarchy::Null)?;
|
|
let new_parent_handle = guard.context.load_external_public(new_parent, Hierarchy::Null)?;
|
|
let policy_session = guard.context.start_policy_session(IsTrial::False)?;
|
|
let policy_session = guard.context.start_policy_session(IsTrial::False)?;
|
|
let auth = Auth::try_from(password.as_bytes())?;
|
|
let auth = Auth::try_from(password.as_bytes())?;
|
|
- let sign = export_key(
|
|
|
|
- &mut guard.context,
|
|
|
|
|
|
+ let sign = guard.context.export_key(
|
|
&root_creds.sign,
|
|
&root_creds.sign,
|
|
new_parent_handle,
|
|
new_parent_handle,
|
|
auth.clone(),
|
|
auth.clone(),
|
|
policy_session,
|
|
policy_session,
|
|
|
|
+ &aead_key,
|
|
)?;
|
|
)?;
|
|
- let enc = export_key(
|
|
|
|
- &mut guard.context,
|
|
|
|
|
|
+ let enc = guard.context.export_key(
|
|
&root_creds.enc,
|
|
&root_creds.enc,
|
|
new_parent_handle,
|
|
new_parent_handle,
|
|
auth,
|
|
auth,
|
|
policy_session,
|
|
policy_session,
|
|
|
|
+ &aead_key,
|
|
)?;
|
|
)?;
|
|
- Ok(ExportedCreds { sign, enc })
|
|
|
|
|
|
+ Ok(ExportedCreds { sign, enc, params })
|
|
}
|
|
}
|
|
|
|
|
|
fn import_root_creds(&self, password: &str, exported: ExportedCreds) -> Result<TpmCreds> {
|
|
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 aead_key = exported.params.derive_key(password)?;
|
|
|
|
+ let auth = Auth::try_from(password.as_bytes())?;
|
|
let storage_key = self.get_or_init_storage_key()?;
|
|
let storage_key = self.get_or_init_storage_key()?;
|
|
let creds = {
|
|
let creds = {
|
|
let mut guard = self.state.write()?;
|
|
let mut guard = self.state.write()?;
|
|
- let auth = Auth::try_from(password.as_bytes())?;
|
|
|
|
- let sign = import_key(
|
|
|
|
- &mut guard.context,
|
|
|
|
|
|
+ let sign = guard.context.import_key(
|
|
exported.sign,
|
|
exported.sign,
|
|
storage_key.private,
|
|
storage_key.private,
|
|
- auth.clone()
|
|
|
|
|
|
+ auth.clone(),
|
|
|
|
+ &aead_key,
|
|
)?;
|
|
)?;
|
|
- let enc = import_key(
|
|
|
|
- &mut guard.context,
|
|
|
|
|
|
+ let enc = guard.context.import_key(
|
|
exported.enc,
|
|
exported.enc,
|
|
storage_key.private,
|
|
storage_key.private,
|
|
auth,
|
|
auth,
|
|
|
|
+ &aead_key,
|
|
)?;
|
|
)?;
|
|
let cred_data = CredData { sign, enc, };
|
|
let cred_data = CredData { sign, enc, };
|
|
TpmCreds::new(cred_data, &self.state)
|
|
TpmCreds::new(cred_data, &self.state)
|
|
@@ -1103,10 +1211,12 @@ impl<S: Scheme> AsymKeyPub<S> {
|
|
_ => Err(Error::custom("Unsupported key type returned by TPM")),
|
|
_ => Err(Error::custom("Unsupported key type returned by TPM")),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+}
|
|
|
|
|
|
|
|
+impl AsymKeyPub<Encrypt> {
|
|
fn storage_key_public(&self) -> Result<Public> {
|
|
fn storage_key_public(&self) -> Result<Public> {
|
|
- fn from_rsa<S: Scheme>(
|
|
|
|
- scheme: S, rsa: openssl::rsa::Rsa<crypto::Public>
|
|
|
|
|
|
+ fn from_rsa(
|
|
|
|
+ scheme: Encrypt, rsa: openssl::rsa::Rsa<crypto::Public>
|
|
) -> Result<Public> {
|
|
) -> Result<Public> {
|
|
let exponent = rsa.e().try_into_u32()?;
|
|
let exponent = rsa.e().try_into_u32()?;
|
|
let modulus = rsa.n().to_vec();
|
|
let modulus = rsa.n().to_vec();
|
|
@@ -1114,14 +1224,9 @@ impl<S: Scheme> AsymKeyPub<S> {
|
|
.with_rsa_exponent(exponent);
|
|
.with_rsa_exponent(exponent);
|
|
builder.rsa_template()
|
|
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()?),
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ match self.scheme {
|
|
|
|
+ Encrypt::RsaEsOaep(_) => from_rsa(self.scheme, self.pkey.rsa()?),
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1482,9 +1587,9 @@ active_pcr_banks = sha256
|
|
}
|
|
}
|
|
|
|
|
|
/// Displays the message associated with a TSS2 return code.
|
|
/// Displays the message associated with a TSS2 return code.
|
|
- //#[test]
|
|
|
|
|
|
+ #[test]
|
|
fn print_error_message() {
|
|
fn print_error_message() {
|
|
- const RC: TSS2_RC = 0x00000982;
|
|
|
|
|
|
+ const RC: TSS2_RC = 2461;
|
|
let msg = tss2_rc_decode(RC);
|
|
let msg = tss2_rc_decode(RC);
|
|
println!("{}", msg);
|
|
println!("{}", msg);
|
|
}
|
|
}
|
|
@@ -1738,27 +1843,115 @@ active_pcr_banks = sha256
|
|
}
|
|
}
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
- fn root_key_export_import() -> Result<()> {
|
|
|
|
|
|
+ fn root_key_export_import() {
|
|
const PASSWORD: &str = "Frobinate";
|
|
const PASSWORD: &str = "Frobinate";
|
|
- let (_src_harness, src_store) = test_store()?;
|
|
|
|
- let src_root_creds = src_store.gen_root_creds(PASSWORD)?;
|
|
|
|
|
|
+ let (_src_harness, src_store) = test_store().unwrap();
|
|
|
|
+ let src_root_creds = src_store.gen_root_creds(PASSWORD).unwrap();
|
|
|
|
|
|
- let (_dest_harness, dest_store) = test_store()?;
|
|
|
|
- let dest_storage_key = dest_store.storage_key()?;
|
|
|
|
|
|
+ let (_dest_harness, dest_store) = test_store().unwrap();
|
|
|
|
+ let dest_storage_key = dest_store.storage_key().unwrap();
|
|
|
|
|
|
- let exported = src_store.export_root_creds(&src_root_creds, PASSWORD, &dest_storage_key)?;
|
|
|
|
- let vec = to_vec(&exported)?;
|
|
|
|
|
|
+ let exported = src_store.export_root_creds(&src_root_creds, PASSWORD, &dest_storage_key)
|
|
|
|
+ .unwrap();
|
|
|
|
+ let vec = to_vec(&exported).unwrap();
|
|
let mut slice = vec.as_slice();
|
|
let mut slice = vec.as_slice();
|
|
- let exported = read_from(&mut slice)?;
|
|
|
|
- let dest_root_creds = dest_store.import_root_creds(PASSWORD, exported)?;
|
|
|
|
|
|
+ let exported = read_from(&mut slice).unwrap();
|
|
|
|
+ let dest_root_creds = dest_store.import_root_creds(PASSWORD, exported).unwrap();
|
|
|
|
|
|
- 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 message = rand_vec(TpmCredStore::ENCRYPT_SCHEME.key_len() as usize / 2).unwrap();
|
|
|
|
+ let sig = dest_root_creds.sign([message.as_slice()].into_iter()).unwrap();
|
|
|
|
+ src_root_creds.verify([message.as_slice()].into_iter(), sig.as_slice()).unwrap();
|
|
|
|
|
|
- let ct = src_root_creds.encrypt(message.as_slice())?;
|
|
|
|
- let pt = dest_root_creds.decrypt(ct.as_slice())?;
|
|
|
|
|
|
+ let ct = src_root_creds.encrypt(message.as_slice()).unwrap();
|
|
|
|
+ let pt = dest_root_creds.decrypt(ct.as_slice()).unwrap();
|
|
assert_eq!(message.as_slice(), pt.as_slice());
|
|
assert_eq!(message.as_slice(), pt.as_slice());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// This test is broken for unknown reasons. It passes if no inner encryption key is supplied.
|
|
|
|
+ /// To work-around this issue I've chosen to wrap the data returned by `duplicate` with a
|
|
|
|
+ /// symmetric key in software.
|
|
|
|
+ //#[test]
|
|
|
|
+ fn key_export_import() -> Result<()> {
|
|
|
|
+ let auth = Auth::try_from(vec![0u8; 32])?;
|
|
|
|
+
|
|
|
|
+ let src_harness = SwtpmHarness::new()?;
|
|
|
|
+ let mut src_ctx = src_harness.context()?;
|
|
|
|
+ {
|
|
|
|
+ let session = src_ctx.start_default_auth_session()?;
|
|
|
|
+ src_ctx.set_sessions((Some(session), None, None));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let src_storage = {
|
|
|
|
+ let unique = [0u8; 0];
|
|
|
|
+ KeyBuilder::for_storage_key(Encrypt::RSA_OAEP_2048_SHA_256, unique.as_slice())
|
|
|
|
+ .with_parent(Parent::Seed(Hierarchy::Owner))
|
|
|
|
+ .with_auth(auth.clone())
|
|
|
|
+ .build(&mut src_ctx)?
|
|
|
|
+ .private
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let dest_harness = SwtpmHarness::new()?;
|
|
|
|
+ let mut dest_ctx = dest_harness.context()?;
|
|
|
|
+ {
|
|
|
|
+ let session = dest_ctx.start_default_auth_session()?;
|
|
|
|
+ dest_ctx.set_sessions((Some(session), None, None));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let dest_storage = {
|
|
|
|
+ let unique = [0u8; 0];
|
|
|
|
+ KeyBuilder::for_storage_key(Encrypt::RSA_OAEP_2048_SHA_256, unique.as_slice())
|
|
|
|
+ .with_parent(Parent::Seed(Hierarchy::Owner))
|
|
|
|
+ .with_auth(auth.clone())
|
|
|
|
+ .build(&mut dest_ctx)?
|
|
|
|
+ .private
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let key_handle = {
|
|
|
|
+ let policy = src_ctx.dup_with_password_policy()?;
|
|
|
|
+ let unique = rand_array::<256>()?;
|
|
|
|
+ KeyBuilder::new(Sign::RSA_PSS_2048_SHA_256, unique.as_slice())
|
|
|
|
+ .with_parent(Parent::Key(src_storage))
|
|
|
|
+ .with_allow_dup(true)
|
|
|
|
+ .with_policy_digest(policy)
|
|
|
|
+ .build(&mut src_ctx)?
|
|
|
|
+ .private
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let new_parent = {
|
|
|
|
+ let (public, ..) = dest_ctx.read_public(dest_storage)?;
|
|
|
|
+ src_ctx.load_external_public(public, Hierarchy::Owner)?
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let (public, ..) = src_ctx.read_public(key_handle)?;
|
|
|
|
+
|
|
|
|
+ let encryption_key = Data::try_from(vec![7u8; 16])?;
|
|
|
|
+
|
|
|
|
+ let (_, private, secret) = {
|
|
|
|
+ let session = src_ctx.start_policy_session(IsTrial::False)?;
|
|
|
|
+ let result: Result<()> = src_ctx.execute_with_session(None, |ctx| {
|
|
|
|
+ ctx.policy_password(session)?;
|
|
|
|
+ ctx.policy_command_code(session, CommandCode::Duplicate)?;
|
|
|
|
+ Ok(())
|
|
|
|
+ });
|
|
|
|
+ result?;
|
|
|
|
+ src_ctx.execute_with_session(Some(session.into()), |ctx| {
|
|
|
|
+ ctx.duplicate(
|
|
|
|
+ key_handle.into(),
|
|
|
|
+ new_parent.into(),
|
|
|
|
+ Some(encryption_key.clone()),
|
|
|
|
+ SymmetricDefinitionObject::AES_128_CFB,
|
|
|
|
+ )
|
|
|
|
+ }).unwrap()
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ dest_ctx.import(
|
|
|
|
+ dest_storage.into(),
|
|
|
|
+ Some(encryption_key),
|
|
|
|
+ public,
|
|
|
|
+ private,
|
|
|
|
+ secret,
|
|
|
|
+ SymmetricDefinitionObject::AES_128_CFB,
|
|
|
|
+ ).unwrap();
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
}
|