Skip to content

Commit edf67ed

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

File tree

4 files changed

+351
-4
lines changed

4 files changed

+351
-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,254 @@
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+
/// ```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<'ctx, C> EcSigner<'ctx, 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<'ctx, C> AsRef<VerifyingKey<C>> for EcSigner<'ctx, 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<'ctx, C> KeypairRef for EcSigner<'ctx, 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<'ctx, C, D> DigestSigner<D, Signature<C>> for EcSigner<'ctx, 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+
let TpmSignature::EcDsa(signature) = signature else {
184+
todo!();
185+
};
186+
187+
let signature = Signature::try_from(signature).map_err(SigError::from_source)?;
188+
189+
Ok(signature)
190+
}
191+
}
192+
193+
impl<'ctx, C, D> DigestSigner<D, DerSignature<C>> for EcSigner<'ctx, C>
194+
where
195+
C: PrimeCurve + CurveArithmetic,
196+
C: AssociatedTpmCurve,
197+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
198+
D: AssociatedHashingAlgorithm,
199+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
200+
SignatureSize<C>: ArrayLength<u8>,
201+
TpmDigest: From<Output<D>>,
202+
203+
MaxSize<C>: ArrayLength<u8>,
204+
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
205+
{
206+
fn try_sign_digest(&self, digest: D) -> Result<DerSignature<C>, SigError> {
207+
let signature: Signature<_> = self.try_sign_digest(digest)?;
208+
Ok(signature.to_der())
209+
}
210+
}
211+
212+
impl<'ctx, C> Signer<Signature<C>> for EcSigner<'ctx, C>
213+
where
214+
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
215+
C: AssociatedTpmCurve,
216+
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
217+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
218+
SignatureSize<C>: ArrayLength<u8>,
219+
TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
220+
{
221+
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, SigError> {
222+
self.try_sign_digest(C::Digest::new_with_prefix(msg))
223+
}
224+
}
225+
226+
impl<'ctx, C> Signer<DerSignature<C>> for EcSigner<'ctx, C>
227+
where
228+
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
229+
C: AssociatedTpmCurve,
230+
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
231+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
232+
SignatureSize<C>: ArrayLength<u8>,
233+
TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
234+
235+
MaxSize<C>: ArrayLength<u8>,
236+
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
237+
{
238+
fn try_sign(&self, msg: &[u8]) -> Result<DerSignature<C>, SigError> {
239+
self.try_sign_digest(C::Digest::new_with_prefix(msg))
240+
}
241+
}
242+
243+
impl<'ctx, C> SignatureAlgorithmIdentifier for EcSigner<'ctx, C>
244+
where
245+
C: PrimeCurve + CurveArithmetic,
246+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
247+
SignatureSize<C>: ArrayLength<u8>,
248+
Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
249+
{
250+
type Params = AnyRef<'static>;
251+
252+
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
253+
Signature::<C>::ALGORITHM_IDENTIFIER;
254+
}

0 commit comments

Comments
 (0)