Skip to content

Commit 9f760ad

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

File tree

4 files changed

+349
-4
lines changed

4 files changed

+349
-4
lines changed

tss-esapi/Cargo.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ sha3 = { version = "0.10.8", optional = true }
4242
sm2 = { version = "0.13.3", optional = true }
4343
sm3 = { version = "0.4.2", optional = true }
4444
digest = "0.10.7"
45+
signature = { version = "2.2.0", optional = true}
4546
cfg-if = "1.0.0"
4647
strum = { version = "0.25.0", optional = true }
4748
strum_macros = { version = "0.25.0", optional = true }
@@ -50,14 +51,15 @@ getrandom = "0.2.11"
5051

5152
[dev-dependencies]
5253
env_logger = "0.9.0"
53-
sha2 = "0.10.1"
5454
serde_json = "^1.0.108"
55+
sha2 = { version = "0.10.8", features = ["oid"] }
56+
x509-cert = { version = "0.2.0", features = ["builder"] }
5557

5658
[build-dependencies]
5759
semver = "1.0.7"
5860

5961
[features]
6062
default = ["abstraction"]
6163
generate-bindings = ["tss-esapi-sys/generate-bindings"]
62-
abstraction = ["ecdsa", "elliptic-curve", "rsa", "x509-cert", "p192", "p224", "p256", "p384", "p521", "sha1", "sha2", "sha3", "sm2", "sm3"]
64+
abstraction = ["ecdsa", "elliptic-curve", "signature", "rsa", "x509-cert", "p192", "p224", "p256", "p384", "p521", "sha1", "sha2", "sha3", "sm2", "sm3"]
6365
integration-tests = ["strum", "strum_macros"]

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

+2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@ use std::convert::{AsMut, AsRef, TryFrom, TryInto};
3434
use zeroize::Zeroize;
3535

3636
mod key_attestation;
37+
mod signer;
3738

3839
pub use key_attestation::MakeCredParams;
40+
pub use signer::EcSigner;
3941

4042
/// Parameters for the kinds of keys supported by the context
4143
#[derive(Debug, Clone, Copy)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
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,
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+
/// the hashing algorithm `D` is the digest that will be used for signatures (SHA-256, SHA3-256, ...).
47+
///
48+
/// ```no_run
49+
/// # use tss_esapi::{
50+
/// # abstraction::transient::{EcSigner, TransientKeyContextBuilder},
51+
/// # TctiNameConf
52+
/// # };
53+
/// use p256::NistP256;
54+
/// use signature::Signer;
55+
/// #
56+
/// # // Create context
57+
/// # let mut context = TransientKeyContextBuilder::new()
58+
/// # .with_tcti(
59+
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
60+
/// # )
61+
/// # .build()
62+
/// # .expect("Failed to create Context");
63+
///
64+
/// let (tpm_km, _tpm_auth) = context
65+
/// .create_key(EcSigner::<NistP256>::key_params_default(), 0)
66+
/// .expect("Failed to create a private keypair");
67+
///
68+
/// let signer = EcSigner::<NistP256>::new(&mut context, tpm_km, None)
69+
/// .expect("Failed to create a signer");
70+
/// let signature: p256::ecdsa::Signature = signer.sign(b"Hello Bob, Alice here.");
71+
/// ```
72+
#[derive(Debug)]
73+
pub struct EcSigner<'ctx, C>
74+
where
75+
C: PrimeCurve + CurveArithmetic,
76+
{
77+
context: Mutex<&'ctx mut TransientKeyContext>,
78+
key_material: KeyMaterial,
79+
key_auth: Option<Auth>,
80+
verifying_key: VerifyingKey<C>,
81+
}
82+
83+
impl<'ctx, C> EcSigner<'ctx, C>
84+
where
85+
C: PrimeCurve + CurveArithmetic,
86+
C: AssociatedTpmCurve,
87+
FieldBytesSize<C>: ModulusSize,
88+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
89+
{
90+
pub fn new(
91+
context: &'ctx mut TransientKeyContext,
92+
key_material: KeyMaterial,
93+
key_auth: Option<Auth>,
94+
) -> Result<Self, Error> {
95+
let context = Mutex::new(context);
96+
97+
let public_key = PublicKey::try_from(key_material.public())?;
98+
let verifying_key = VerifyingKey::from(public_key);
99+
100+
Ok(Self {
101+
context,
102+
key_material,
103+
key_auth,
104+
verifying_key,
105+
})
106+
}
107+
}
108+
109+
impl<'ctx, C> EcSigner<'ctx, C>
110+
where
111+
C: PrimeCurve + CurveArithmetic,
112+
C: AssociatedTpmCurve,
113+
{
114+
/// Key parameters for this curve, selected digest is the one selected by DigestPrimitive
115+
pub fn key_params_default() -> KeyParams
116+
where
117+
C: DigestPrimitive,
118+
<C as DigestPrimitive>::Digest: FixedOutput<OutputSize = FieldBytesSize<C>>,
119+
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
120+
{
121+
Self::key_params::<<C as DigestPrimitive>::Digest>()
122+
}
123+
124+
/// Key parameters for this curve
125+
pub fn key_params<D>() -> KeyParams
126+
where
127+
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
128+
D: AssociatedHashingAlgorithm,
129+
{
130+
KeyParams::Ecc {
131+
curve: C::TPM_CURVE,
132+
scheme: EccScheme::create(EccSchemeAlgorithm::EcDsa, Some(D::TPM_DIGEST), None)
133+
.expect("Failed to create ecc scheme"),
134+
}
135+
}
136+
}
137+
138+
impl<'ctx, C> AsRef<VerifyingKey<C>> for EcSigner<'ctx, C>
139+
where
140+
C: PrimeCurve + CurveArithmetic,
141+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
142+
SignatureSize<C>: ArrayLength<u8>,
143+
{
144+
fn as_ref(&self) -> &VerifyingKey<C> {
145+
&self.verifying_key
146+
}
147+
}
148+
149+
impl<'ctx, C> KeypairRef for EcSigner<'ctx, C>
150+
where
151+
C: PrimeCurve + CurveArithmetic,
152+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
153+
SignatureSize<C>: ArrayLength<u8>,
154+
{
155+
type VerifyingKey = VerifyingKey<C>;
156+
}
157+
158+
impl<'ctx, C, D> DigestSigner<D, Signature<C>> for EcSigner<'ctx, C>
159+
where
160+
C: PrimeCurve + CurveArithmetic,
161+
C: AssociatedTpmCurve,
162+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
163+
D: AssociatedHashingAlgorithm,
164+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
165+
SignatureSize<C>: ArrayLength<u8>,
166+
TpmDigest: From<Output<D>>,
167+
{
168+
fn try_sign_digest(&self, digest: D) -> Result<Signature<C>, SigError> {
169+
let digest = TpmDigest::from(digest.finalize_fixed());
170+
171+
let key_params = Self::key_params::<D>();
172+
let mut context = self.context.lock().expect("Mutex got poisoned");
173+
let signature = context
174+
.sign(
175+
self.key_material.clone(),
176+
key_params,
177+
self.key_auth.clone(),
178+
digest,
179+
)
180+
.map_err(SigError::from_source)?;
181+
let TpmSignature::EcDsa(signature) = signature else {
182+
todo!();
183+
};
184+
185+
let signature = Signature::try_from(signature).map_err(SigError::from_source)?;
186+
187+
Ok(signature)
188+
}
189+
}
190+
191+
impl<'ctx, C, D> DigestSigner<D, DerSignature<C>> for EcSigner<'ctx, C>
192+
where
193+
C: PrimeCurve + CurveArithmetic,
194+
C: AssociatedTpmCurve,
195+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
196+
D: AssociatedHashingAlgorithm,
197+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
198+
SignatureSize<C>: ArrayLength<u8>,
199+
TpmDigest: From<Output<D>>,
200+
201+
MaxSize<C>: ArrayLength<u8>,
202+
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
203+
{
204+
fn try_sign_digest(&self, digest: D) -> Result<DerSignature<C>, SigError> {
205+
let signature: Signature<_> = self.try_sign_digest(digest)?;
206+
Ok(signature.to_der())
207+
}
208+
}
209+
210+
impl<'ctx, C> Signer<Signature<C>> for EcSigner<'ctx, C>
211+
where
212+
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
213+
C: AssociatedTpmCurve,
214+
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
215+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
216+
SignatureSize<C>: ArrayLength<u8>,
217+
TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
218+
{
219+
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, SigError> {
220+
self.try_sign_digest(C::Digest::new_with_prefix(msg))
221+
}
222+
}
223+
224+
impl<'ctx, C> Signer<DerSignature<C>> for EcSigner<'ctx, C>
225+
where
226+
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
227+
C: AssociatedTpmCurve,
228+
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
229+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
230+
SignatureSize<C>: ArrayLength<u8>,
231+
TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
232+
233+
MaxSize<C>: ArrayLength<u8>,
234+
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
235+
{
236+
fn try_sign(&self, msg: &[u8]) -> Result<DerSignature<C>, SigError> {
237+
self.try_sign_digest(C::Digest::new_with_prefix(msg))
238+
}
239+
}
240+
241+
impl<'ctx, C> SignatureAlgorithmIdentifier for EcSigner<'ctx, C>
242+
where
243+
C: PrimeCurve + CurveArithmetic,
244+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
245+
SignatureSize<C>: ArrayLength<u8>,
246+
Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
247+
{
248+
type Params = AnyRef<'static>;
249+
250+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
251+
Signature::<C>::ALGORITHM_IDENTIFIER;
252+
}

0 commit comments

Comments
 (0)