Skip to content

Commit e21adf0

Browse files
committed
adds a signature::Signer implementation for ecc keys
Signed-off-by: Arthur Gautier <[email protected]>
1 parent 7ab5c37 commit e21adf0

File tree

4 files changed

+360
-4
lines changed

4 files changed

+360
-4
lines changed

tss-esapi/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,15 @@ getrandom = "0.2.11"
5858

5959
[dev-dependencies]
6060
env_logger = "0.11.5"
61-
sha2 = "0.10.1"
6261
serde_json = "^1.0.108"
62+
sha2 = { version = "0.10.8", features = ["oid"] }
6363
tss-esapi = { path = ".", features = [
6464
"integration-tests",
6565
"serde",
6666
"abstraction",
6767
"rustcrypto-full",
6868
] }
69-
69+
x509-cert = { version = "0.2.0", features = ["builder"] }
7070

7171
[build-dependencies]
7272
semver = "1.0.7"

tss-esapi/src/abstraction/transient/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,14 @@ use zeroize::Zeroize;
3535

3636
mod key_attestation;
3737

38+
#[cfg(feature = "rustcrypto")]
39+
mod signer;
40+
3841
pub use key_attestation::MakeCredParams;
3942

43+
#[cfg(feature = "rustcrypto")]
44+
pub use signer::EcSigner;
45+
4046
/// Parameters for the kinds of keys supported by the context
4147
#[derive(Debug, Clone, Copy)]
4248
pub enum KeyParams {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
// Copyright 2024 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//! Module for exposing a [`signature::Signer`] interface for keys
5+
//!
6+
//! This modules presents objects held in a TPM over a [`signature::DigestSigner`] interface.
7+
use super::TransientKeyContext;
8+
use crate::{
9+
abstraction::{
10+
public::AssociatedTpmCurve,
11+
transient::{KeyMaterial, KeyParams},
12+
AssociatedHashingAlgorithm,
13+
},
14+
interface_types::algorithm::EccSchemeAlgorithm,
15+
structures::{Auth, Digest as TpmDigest, EccScheme, Signature as TpmSignature},
16+
Error, WrapperErrorKind,
17+
};
18+
19+
use std::{convert::TryFrom, ops::Add, sync::Mutex};
20+
21+
use digest::{Digest, FixedOutput, Output};
22+
use ecdsa::{
23+
der::{MaxOverhead, MaxSize, Signature as DerSignature},
24+
hazmat::{DigestPrimitive, SignPrimitive},
25+
Signature, SignatureSize, VerifyingKey,
26+
};
27+
use elliptic_curve::{
28+
generic_array::ArrayLength,
29+
ops::Invert,
30+
sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
31+
subtle::CtOption,
32+
AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, PublicKey, Scalar,
33+
};
34+
use signature::{DigestSigner, Error as SigError, KeypairRef, Signer};
35+
use x509_cert::{
36+
der::asn1::AnyRef,
37+
spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
38+
};
39+
40+
/// [`EcSigner`] will sign a payload with an elliptic curve secret key stored on the TPM.
41+
///
42+
/// # Parameters
43+
///
44+
/// Parameter `C` describes the curve that is of use (Nist P-256, Nist P-384, ...)
45+
///
46+
/// ```no_run
47+
/// # use tss_esapi::{
48+
/// # abstraction::transient::{EcSigner, TransientKeyContextBuilder},
49+
/// # TctiNameConf
50+
/// # };
51+
/// use p256::NistP256;
52+
/// use signature::Signer;
53+
/// #
54+
/// # // Create context
55+
/// # let mut context = TransientKeyContextBuilder::new()
56+
/// # .with_tcti(
57+
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
58+
/// # )
59+
/// # .build()
60+
/// # .expect("Failed to create Context");
61+
///
62+
/// let (tpm_km, _tpm_auth) = context
63+
/// .create_key(EcSigner::<NistP256>::key_params_default(), 0)
64+
/// .expect("Failed to create a private keypair");
65+
///
66+
/// let signer = EcSigner::<NistP256>::new(&mut context, tpm_km, None)
67+
/// .expect("Failed to create a signer");
68+
/// let signature: p256::ecdsa::Signature = signer.sign(b"Hello Bob, Alice here.");
69+
/// ```
70+
#[derive(Debug)]
71+
pub struct EcSigner<'ctx, C>
72+
where
73+
C: PrimeCurve + CurveArithmetic,
74+
{
75+
context: Mutex<&'ctx mut TransientKeyContext>,
76+
key_material: KeyMaterial,
77+
key_auth: Option<Auth>,
78+
verifying_key: VerifyingKey<C>,
79+
}
80+
81+
impl<'ctx, C> EcSigner<'ctx, C>
82+
where
83+
C: PrimeCurve + CurveArithmetic,
84+
C: AssociatedTpmCurve,
85+
FieldBytesSize<C>: ModulusSize,
86+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
87+
{
88+
pub fn new(
89+
context: &'ctx mut TransientKeyContext,
90+
key_material: KeyMaterial,
91+
key_auth: Option<Auth>,
92+
) -> Result<Self, Error> {
93+
let context = Mutex::new(context);
94+
95+
let public_key = PublicKey::try_from(key_material.public())?;
96+
let verifying_key = VerifyingKey::from(public_key);
97+
98+
Ok(Self {
99+
context,
100+
key_material,
101+
key_auth,
102+
verifying_key,
103+
})
104+
}
105+
}
106+
107+
impl<C> EcSigner<'_, C>
108+
where
109+
C: PrimeCurve + CurveArithmetic,
110+
C: AssociatedTpmCurve,
111+
{
112+
/// Key parameters for this curve, selected digest is the one selected by DigestPrimitive
113+
pub fn key_params_default() -> KeyParams
114+
where
115+
C: DigestPrimitive,
116+
<C as DigestPrimitive>::Digest: FixedOutput<OutputSize = FieldBytesSize<C>>,
117+
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
118+
{
119+
Self::key_params::<<C as DigestPrimitive>::Digest>()
120+
}
121+
122+
/// Key parameters for this curve
123+
///
124+
/// # Parameters
125+
///
126+
/// The hashing algorithm `D` is the digest that will be used for signatures (SHA-256, SHA3-256, ...).
127+
pub fn key_params<D>() -> KeyParams
128+
where
129+
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
130+
D: AssociatedHashingAlgorithm,
131+
{
132+
KeyParams::Ecc {
133+
curve: C::TPM_CURVE,
134+
scheme: EccScheme::create(EccSchemeAlgorithm::EcDsa, Some(D::TPM_DIGEST), None)
135+
.expect("Failed to create ecc scheme"),
136+
}
137+
}
138+
}
139+
140+
impl<C> AsRef<VerifyingKey<C>> for EcSigner<'_, C>
141+
where
142+
C: PrimeCurve + CurveArithmetic,
143+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
144+
SignatureSize<C>: ArrayLength<u8>,
145+
{
146+
fn as_ref(&self) -> &VerifyingKey<C> {
147+
&self.verifying_key
148+
}
149+
}
150+
151+
impl<C> KeypairRef for EcSigner<'_, C>
152+
where
153+
C: PrimeCurve + CurveArithmetic,
154+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
155+
SignatureSize<C>: ArrayLength<u8>,
156+
{
157+
type VerifyingKey = VerifyingKey<C>;
158+
}
159+
160+
impl<C, D> DigestSigner<D, Signature<C>> for EcSigner<'_, C>
161+
where
162+
C: PrimeCurve + CurveArithmetic,
163+
C: AssociatedTpmCurve,
164+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
165+
D: AssociatedHashingAlgorithm,
166+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
167+
SignatureSize<C>: ArrayLength<u8>,
168+
TpmDigest: From<Output<D>>,
169+
{
170+
fn try_sign_digest(&self, digest: D) -> Result<Signature<C>, SigError> {
171+
let digest = TpmDigest::from(digest.finalize_fixed());
172+
173+
let key_params = Self::key_params::<D>();
174+
let mut context = self.context.lock().expect("Mutex got poisoned");
175+
let signature = context
176+
.sign(
177+
self.key_material.clone(),
178+
key_params,
179+
self.key_auth.clone(),
180+
digest,
181+
)
182+
.map_err(SigError::from_source)?;
183+
184+
let TpmSignature::EcDsa(signature) = signature else {
185+
return Err(SigError::from_source(Error::local_error(
186+
WrapperErrorKind::InvalidParam,
187+
)));
188+
};
189+
190+
let signature = Signature::try_from(signature).map_err(SigError::from_source)?;
191+
192+
Ok(signature)
193+
}
194+
}
195+
196+
impl<C, D> DigestSigner<D, DerSignature<C>> for EcSigner<'_, C>
197+
where
198+
C: PrimeCurve + CurveArithmetic,
199+
C: AssociatedTpmCurve,
200+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
201+
D: AssociatedHashingAlgorithm,
202+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
203+
SignatureSize<C>: ArrayLength<u8>,
204+
TpmDigest: From<Output<D>>,
205+
206+
MaxSize<C>: ArrayLength<u8>,
207+
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
208+
{
209+
fn try_sign_digest(&self, digest: D) -> Result<DerSignature<C>, SigError> {
210+
let signature: Signature<_> = self.try_sign_digest(digest)?;
211+
Ok(signature.to_der())
212+
}
213+
}
214+
215+
impl<C> Signer<Signature<C>> for EcSigner<'_, C>
216+
where
217+
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
218+
C: AssociatedTpmCurve,
219+
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
220+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
221+
SignatureSize<C>: ArrayLength<u8>,
222+
TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
223+
{
224+
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, SigError> {
225+
self.try_sign_digest(C::Digest::new_with_prefix(msg))
226+
}
227+
}
228+
229+
impl<C> Signer<DerSignature<C>> for EcSigner<'_, C>
230+
where
231+
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
232+
C: AssociatedTpmCurve,
233+
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
234+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
235+
SignatureSize<C>: ArrayLength<u8>,
236+
TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
237+
238+
MaxSize<C>: ArrayLength<u8>,
239+
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
240+
{
241+
fn try_sign(&self, msg: &[u8]) -> Result<DerSignature<C>, SigError> {
242+
self.try_sign_digest(C::Digest::new_with_prefix(msg))
243+
}
244+
}
245+
246+
impl<C> SignatureAlgorithmIdentifier for EcSigner<'_, C>
247+
where
248+
C: PrimeCurve + CurveArithmetic,
249+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
250+
SignatureSize<C>: ArrayLength<u8>,
251+
Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
252+
{
253+
type Params = AnyRef<'static>;
254+
255+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
256+
Signature::<C>::ALGORITHM_IDENTIFIER;
257+
}

0 commit comments

Comments
 (0)