Skip to content

Commit 451ae4f

Browse files
committed
WIP: Create credentials
Signed-off-by: Arthur Gautier <[email protected]>
1 parent 995ca94 commit 451ae4f

File tree

7 files changed

+706
-2
lines changed

7 files changed

+706
-2
lines changed

Cargo.toml

+8
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@ p192 = { git = "https://github.com/RustCrypto/elliptic-curves.git" }
77
p224 = { git = "https://github.com/RustCrypto/elliptic-curves.git" }
88
sm2 = { git = "https://github.com/RustCrypto/elliptic-curves.git" }
99

10+
# https://github.com/RustCrypto/KDFs/pull/108
11+
kbkdf = { git = "https://github.com/TheBestTvarynka/KDFs.git", branch = "feat/kbkdf" }
12+
concat-kdf = { git = "https://github.com/RustCrypto/KDFs.git" }
13+
14+
cfb-mode = { git = "https://github.com/RustCrypto/block-modes.git" }
15+
16+
# https://github.com/RustCrypto/RSA/pull/467
17+
rsa = { git = "https://github.com/baloo/RSA.git", branch = "baloo/oaep/non-string-label" }

tss-esapi/Cargo.toml

+12-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ regex = "1.3.9"
3535
zeroize = { version = "1.5.7", features = ["zeroize_derive"] }
3636
tss-esapi-sys = { path = "../tss-esapi-sys", version = "0.5.0" }
3737
x509-cert = { version = "0.3.0-pre.0", optional = true }
38+
aes = { version = "0.9.0-pre.2", optional = true }
39+
cfb-mode = { version = "0.9.0-pre", optional = true }
3840
ecdsa = { version = "0.17.0-pre.9", features = ["der", "hazmat", "arithmetic", "verifying"], optional = true }
3941
elliptic-curve = { version = "0.14.0-rc.1", optional = true, features = ["alloc", "pkcs8"] }
42+
hmac = { version = "0.13.0-pre.4", optional = true }
4043
p192 = { version = "0.14.0-pre", optional = true }
4144
p224 = { version = "0.14.0-pre", optional = true }
4245
p256 = { version = "0.14.0-pre.2", optional = true }
@@ -48,16 +51,22 @@ sha2 = { version = "0.11.0-pre.4", optional = true }
4851
sha3 = { version = "0.11.0-pre.4", optional = true }
4952
sm2 = { version = "0.14.0-pre", optional = true }
5053
sm3 = { version = "0.5.0-pre.4", optional = true }
54+
kbkdf = { version = "0.1.0", optional = true }
55+
concat-kdf = { version = "0.2.0-pre", optional = true }
5156
digest = "0.11.0-pre.9"
5257
signature = { version = "2.3.0-pre.4", features = ["std"], optional = true}
5358
cfg-if = "1.0.0"
5459
strum = { version = "0.26.3", optional = true }
5560
strum_macros = { version = "0.26.4", optional = true }
5661
paste = "1.0.14"
5762
getrandom = "0.2.11"
63+
rand = "0.8"
5864

5965
[dev-dependencies]
66+
aes = "0.9.0-pre.2"
6067
env_logger = "0.11.5"
68+
hex-literal = "0.4.1"
69+
rsa = { version = "0.10.0-pre.3" }
6170
serde_json = "^1.0.108"
6271
sha2 = { version = "0.11.0-pre.4", features = ["oid"] }
6372
tss-esapi = { path = ".", features = [
@@ -66,6 +75,7 @@ tss-esapi = { path = ".", features = [
6675
"abstraction",
6776
"rustcrypto-full",
6877
] }
78+
p256 = { version = "0.14.0-pre.2", features = ["ecdh"] }
6979
x509-cert = { version = "0.3.0-pre.0", features = ["builder"] }
7080

7181
[build-dependencies]
@@ -77,6 +87,6 @@ generate-bindings = ["tss-esapi-sys/generate-bindings"]
7787
abstraction = ["rustcrypto"]
7888
integration-tests = ["strum", "strum_macros"]
7989

80-
rustcrypto = ["ecdsa", "elliptic-curve", "signature", "x509-cert"]
81-
rustcrypto-full = ["rustcrypto", "p192", "p224", "p256", "p384", "p521", "rsa", "sha1", "sha2", "sha3", "sm2", "sm3"]
90+
rustcrypto = ["cfb-mode", "concat-kdf", "ecdsa", "elliptic-curve", "elliptic-curve/ecdh", "hmac", "kbkdf", "signature", "x509-cert"]
91+
rustcrypto-full = ["rustcrypto", "aes", "p192", "p224", "p256", "p384", "p521", "rsa", "sha1", "sha2", "sha3", "sm2", "sm3"]
8292

tss-esapi/src/utils/credential.rs

+232
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
// Copyright 2019 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use aes::cipher::AsyncStreamCipher;
5+
use cfb_mode::cipher::BlockCipherEncrypt;
6+
use core::{
7+
marker::PhantomData,
8+
ops::{Add, Mul},
9+
};
10+
use digest::{
11+
array::ArraySize,
12+
consts::{B1, U8},
13+
crypto_common::{Iv, KeyIvInit, KeySizeUser},
14+
typenum::{
15+
operator_aliases::{Add1, Sum},
16+
Unsigned,
17+
},
18+
Digest, DynDigest, FixedOutputReset, Key, KeyInit, Mac, OutputSizeUser,
19+
};
20+
use ecdsa::elliptic_curve::{
21+
ecdh::{EphemeralSecret, SharedSecret},
22+
sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
23+
AffinePoint, Curve, CurveArithmetic, FieldBytesSize, PublicKey,
24+
};
25+
use hmac::{EagerHash, Hmac};
26+
use rand::{thread_rng, Rng};
27+
use rsa::{Oaep, RsaPublicKey};
28+
29+
use crate::{
30+
structures::{EncryptedSecret, IdObject, Name},
31+
utils::kdf::{self},
32+
};
33+
34+
// [`TpmHmac`] intends to code for the key expected for hmac
35+
// in the KDFa and KDFe derivations. There are no standard sizes for hmac keys really,
36+
// upstream RustCrypto considers it to be [BlockSize], but TPM specification
37+
// has a different opinion on the matter, and expect the key to the output
38+
// bit size of the hash algorithm used.
39+
//
40+
// See https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=202
41+
// section 24.5 HMAC:
42+
// bits the number of bits in the digest produced by ekNameAlg
43+
//
44+
// [BlockSize]: https://docs.rs/hmac/0.12.1/hmac/struct.HmacCore.html#impl-KeySizeUser-for-HmacCore%3CD%3E
45+
struct TpmHmac<H>(PhantomData<H>);
46+
47+
impl<H> KeySizeUser for TpmHmac<H>
48+
where
49+
H: OutputSizeUser,
50+
{
51+
type KeySize = H::OutputSize;
52+
}
53+
54+
pub fn make_credential_ecc<C, EkHash, EkCipher>(
55+
ek_public: PublicKey<C>,
56+
secret: &[u8],
57+
key_name: Name,
58+
) -> (IdObject, EncryptedSecret)
59+
where
60+
C: Curve + CurveArithmetic,
61+
62+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
63+
FieldBytesSize<C>: ModulusSize,
64+
65+
<FieldBytesSize<C> as Add>::Output: Add<FieldBytesSize<C>>,
66+
Sum<FieldBytesSize<C>, FieldBytesSize<C>>: ArraySize,
67+
Sum<FieldBytesSize<C>, FieldBytesSize<C>>: Add<U8>,
68+
Sum<Sum<FieldBytesSize<C>, FieldBytesSize<C>>, U8>: Add<B1>,
69+
Add1<Sum<Sum<FieldBytesSize<C>, FieldBytesSize<C>>, U8>>: ArraySize,
70+
71+
EkHash: Digest + EagerHash + FixedOutputReset,
72+
<EkHash as OutputSizeUser>::OutputSize: Mul<U8>,
73+
<<EkHash as OutputSizeUser>::OutputSize as Mul<U8>>::Output: Unsigned,
74+
<<EkHash as EagerHash>::Core as OutputSizeUser>::OutputSize: ArraySize + Mul<U8>,
75+
<<<EkHash as EagerHash>::Core as OutputSizeUser>::OutputSize as Mul<U8>>::Output: Unsigned,
76+
77+
EkCipher: KeySizeUser + BlockCipherEncrypt + KeyInit,
78+
<EkCipher as KeySizeUser>::KeySize: Mul<U8>,
79+
<<EkCipher as KeySizeUser>::KeySize as Mul<U8>>::Output: ArraySize,
80+
{
81+
let mut rng = thread_rng();
82+
83+
// See Table 22 - Key Generation for the various labels used here after:
84+
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=183
85+
86+
// C.6.4. ECC Secret Sharing for Credentials
87+
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=311
88+
let local = EphemeralSecret::<C>::random(&mut rng);
89+
90+
let ecdh_secret: SharedSecret<C> = local.diffie_hellman(&ek_public);
91+
let local_public = local.public_key();
92+
drop(local);
93+
94+
let seed = kdf::kdfe::<kdf::Identity, EkHash, C, TpmHmac<EkHash>>(
95+
&ecdh_secret,
96+
&local_public,
97+
&ek_public,
98+
);
99+
drop(ecdh_secret);
100+
101+
// The local ECDH pair is used as "encrypted seed"
102+
let encrypted_seed = {
103+
let mut out = vec![];
104+
out.extend_from_slice(&FieldBytesSize::<C>::U16.to_be_bytes()[..]);
105+
out.extend_from_slice(&local_public.to_encoded_point(false).x().unwrap());
106+
out.extend_from_slice(&FieldBytesSize::<C>::U16.to_be_bytes()[..]);
107+
out.extend_from_slice(&local_public.to_encoded_point(false).y().unwrap());
108+
out
109+
};
110+
let encrypted_secret = EncryptedSecret::from_bytes(&encrypted_seed).unwrap();
111+
112+
let id_object = secret_to_credential::<EkHash, EkCipher>(seed, secret, key_name);
113+
114+
(id_object, encrypted_secret)
115+
}
116+
117+
pub fn make_credential_rsa<EkHash, EkCipher>(
118+
ek_public: &RsaPublicKey,
119+
secret: &[u8],
120+
key_name: Name,
121+
) -> (IdObject, EncryptedSecret)
122+
where
123+
EkHash: Digest + DynDigest + Send + Sync + 'static,
124+
EkHash: EagerHash + FixedOutputReset,
125+
<EkHash as OutputSizeUser>::OutputSize: Mul<U8>,
126+
<<EkHash as OutputSizeUser>::OutputSize as Mul<U8>>::Output: Unsigned,
127+
<<EkHash as EagerHash>::Core as OutputSizeUser>::OutputSize: ArraySize + Mul<U8>,
128+
<<<EkHash as EagerHash>::Core as OutputSizeUser>::OutputSize as Mul<U8>>::Output: Unsigned,
129+
130+
EkCipher: KeySizeUser + BlockCipherEncrypt + KeyInit,
131+
<EkCipher as KeySizeUser>::KeySize: Mul<U8>,
132+
<<EkCipher as KeySizeUser>::KeySize as Mul<U8>>::Output: ArraySize,
133+
{
134+
let mut rng = thread_rng();
135+
136+
// See Table 22 - Key Generation for the various labels used here after:
137+
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=183
138+
139+
// B.10.4 RSA Secret Sharing for Credentials
140+
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=302
141+
let random_seed = {
142+
let mut out = Key::<TpmHmac<EkHash>>::default();
143+
rng.fill(out.as_mut_slice());
144+
out
145+
};
146+
147+
// The random seed is then encrypted with RSA-OAEP
148+
//
149+
// B.4 RSAES_OAEP
150+
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=297
151+
//
152+
// The label is a byte-stream whose last byte must be zero
153+
//
154+
// B.10.4. RSA Secret Sharing for Credentials
155+
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=302
156+
//
157+
// The label is going to be "IDENTITY" for secret sharing.
158+
let encrypted_seed = {
159+
let padding = Oaep::new_with_label::<EkHash, _>(b"IDENTITY\0".to_vec());
160+
let enc_data = ek_public
161+
.encrypt(&mut rng, padding, &random_seed[..])
162+
.expect("failed to encrypt");
163+
enc_data
164+
};
165+
let encrypted_secret = EncryptedSecret::from_bytes(&encrypted_seed).unwrap();
166+
167+
let id_object = secret_to_credential::<EkHash, EkCipher>(random_seed, secret, key_name);
168+
169+
(id_object, encrypted_secret)
170+
}
171+
172+
fn secret_to_credential<EkHash, EkCipher>(
173+
seed: Key<TpmHmac<EkHash>>,
174+
secret: &[u8],
175+
key_name: Name,
176+
) -> IdObject
177+
where
178+
EkHash: Digest + EagerHash + FixedOutputReset,
179+
<EkHash as OutputSizeUser>::OutputSize: Mul<U8>,
180+
<<EkHash as OutputSizeUser>::OutputSize as Mul<U8>>::Output: Unsigned,
181+
<<EkHash as EagerHash>::Core as OutputSizeUser>::OutputSize: ArraySize + Mul<U8>,
182+
<<<EkHash as EagerHash>::Core as OutputSizeUser>::OutputSize as Mul<U8>>::Output: Unsigned,
183+
184+
EkCipher: KeySizeUser + BlockCipherEncrypt + KeyInit,
185+
<EkCipher as KeySizeUser>::KeySize: Mul<U8>,
186+
<<EkCipher as KeySizeUser>::KeySize as Mul<U8>>::Output: ArraySize,
187+
{
188+
// Prepare the sensitive data
189+
// this will be then encrypted using AES-CFB (size of the symmetric key depends on the EK).
190+
let mut sensitive_data = {
191+
let mut out = vec![];
192+
out.extend_from_slice(&u16::try_from(secret.len()).unwrap().to_be_bytes()[..]);
193+
out.extend_from_slice(secret);
194+
out
195+
};
196+
197+
// We'll now encrypt the sensitive data, and hmac the result of the encryption
198+
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=201
199+
// See 24.4 Symmetric Encryption
200+
let sym_key = kdf::kdfa::<EkHash, kdf::Storage, EkCipher>(&seed, key_name.value(), &[]);
201+
202+
// TODO(baloo): once the weak key detection merges
203+
// https://github.com/RustCrypto/traits/pull/1739
204+
// https://github.com/RustCrypto/block-ciphers/pull/465
205+
//
206+
// We should check for weak keys, and re-run all the steps above until we get a non-weak key
207+
// this is to be in compliance with TPM spec section 11.4.10.4:
208+
// https://trustedcomputinggroup.org/wp-content/uploads/TPM-2.0-1.83-Part-1-Architecture.pdf#page=82
209+
210+
let iv: Iv<cfb_mode::Encryptor<EkCipher>> = Default::default();
211+
212+
cfb_mode::Encryptor::<EkCipher>::new(&sym_key.into(), &iv.into()).encrypt(&mut sensitive_data);
213+
214+
// See 24.5 HMAC
215+
let hmac_key = kdf::kdfa::<EkHash, kdf::Integrity, TpmHmac<EkHash>>(&seed, &[], &[]);
216+
let mut hmac = Hmac::<EkHash>::new_from_slice(&hmac_key).unwrap();
217+
Mac::update(&mut hmac, &sensitive_data);
218+
Mac::update(&mut hmac, key_name.value());
219+
let hmac = hmac.finalize();
220+
221+
// We'll now serialize the object and get everything through the door.
222+
let mut out = vec![];
223+
out.extend_from_slice(
224+
&u16::try_from(hmac.into_bytes().len())
225+
.unwrap()
226+
.to_be_bytes()[..],
227+
);
228+
out.extend_from_slice(&hmac.into_bytes());
229+
out.extend_from_slice(&sensitive_data);
230+
231+
IdObject::from_bytes(&out).unwrap()
232+
}

0 commit comments

Comments
 (0)