Skip to content

Commit 7bcbb0c

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

File tree

3 files changed

+239
-1
lines changed

3 files changed

+239
-1
lines changed

tss-esapi/Cargo.toml

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

0 commit comments

Comments
 (0)