Skip to content

Commit fe52b1c

Browse files
committed
adds a signature::Signer implementation for ecc keys
Signed-off-by: Arthur Gautier <arthur.gautier@arista.com>
1 parent bbcb4cc commit fe52b1c

File tree

3 files changed

+238
-1
lines changed

3 files changed

+238
-1
lines changed

tss-esapi/Cargo.toml

Lines changed: 2 additions & 1 deletion
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

Lines changed: 2 additions & 0 deletions
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)]
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
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, marker::PhantomData, 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(), 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, D = <C as DigestPrimitive>::Digest>
70+
where
71+
C: PrimeCurve + CurveArithmetic,
72+
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
73+
{
74+
context: Mutex<&'ctx mut TransientKeyContext>,
75+
key_material: KeyMaterial,
76+
key_auth: Option<Auth>,
77+
verifying_key: VerifyingKey<C>,
78+
_digest: PhantomData<D>,
79+
}
80+
81+
impl<'ctx, C, D> EcSigner<'ctx, C, D>
82+
where
83+
C: PrimeCurve + CurveArithmetic,
84+
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
85+
C: AssociatedTpmCurve,
86+
FieldBytesSize<C>: ModulusSize,
87+
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
88+
{
89+
pub fn new(
90+
context: &'ctx mut TransientKeyContext,
91+
key_material: KeyMaterial,
92+
key_auth: Option<Auth>,
93+
) -> Result<Self, Error> {
94+
let context = Mutex::new(context);
95+
96+
let public_key = PublicKey::try_from(key_material.public())?;
97+
let verifying_key = VerifyingKey::from(public_key);
98+
99+
Ok(Self {
100+
context,
101+
key_material,
102+
key_auth,
103+
verifying_key,
104+
_digest: PhantomData,
105+
})
106+
}
107+
}
108+
109+
impl<'ctx, C, D> EcSigner<'ctx, C, D>
110+
where
111+
C: PrimeCurve + CurveArithmetic,
112+
C: AssociatedTpmCurve,
113+
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
114+
D: AssociatedHashingAlgorithm,
115+
{
116+
/// Key parameters for this curve
117+
pub fn key_params() -> KeyParams {
118+
KeyParams::Ecc {
119+
curve: C::TPM_CURVE,
120+
scheme: EccScheme::create(EccSchemeAlgorithm::EcDsa, Some(D::TPM_DIGEST), None)
121+
.expect("Failed to create ecc scheme"),
122+
}
123+
}
124+
}
125+
126+
impl<'ctx, C, D> AsRef<VerifyingKey<C>> for EcSigner<'ctx, C, D>
127+
where
128+
C: PrimeCurve + CurveArithmetic,
129+
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
130+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
131+
SignatureSize<C>: ArrayLength<u8>,
132+
{
133+
fn as_ref(&self) -> &VerifyingKey<C> {
134+
&self.verifying_key
135+
}
136+
}
137+
138+
impl<'ctx, C, D> KeypairRef for EcSigner<'ctx, C, D>
139+
where
140+
C: PrimeCurve + CurveArithmetic,
141+
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
142+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
143+
SignatureSize<C>: ArrayLength<u8>,
144+
{
145+
type VerifyingKey = VerifyingKey<C>;
146+
}
147+
148+
impl<'ctx, C, D> DigestSigner<D, Signature<C>> for EcSigner<'ctx, C, D>
149+
where
150+
C: PrimeCurve + CurveArithmetic,
151+
C: AssociatedTpmCurve,
152+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
153+
D: AssociatedHashingAlgorithm,
154+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
155+
SignatureSize<C>: ArrayLength<u8>,
156+
TpmDigest: From<Output<D>>,
157+
{
158+
fn try_sign_digest(&self, digest: D) -> Result<Signature<C>, SigError> {
159+
let digest = TpmDigest::from(digest.finalize_fixed());
160+
161+
let key_params = Self::key_params();
162+
let mut context = self.context.lock().expect("Mutex got poisoned");
163+
let signature = context
164+
.sign(
165+
self.key_material.clone(),
166+
key_params,
167+
self.key_auth.clone(),
168+
digest,
169+
)
170+
.map_err(SigError::from_source)?;
171+
let TpmSignature::EcDsa(signature) = signature else {
172+
todo!();
173+
};
174+
175+
let signature = Signature::try_from(signature).map_err(SigError::from_source)?;
176+
177+
Ok(signature)
178+
}
179+
}
180+
181+
impl<'ctx, C, D> DigestSigner<D, DerSignature<C>> for EcSigner<'ctx, C, D>
182+
where
183+
C: PrimeCurve + CurveArithmetic,
184+
C: AssociatedTpmCurve,
185+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
186+
D: AssociatedHashingAlgorithm,
187+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
188+
SignatureSize<C>: ArrayLength<u8>,
189+
TpmDigest: From<Output<D>>,
190+
191+
MaxSize<C>: ArrayLength<u8>,
192+
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
193+
{
194+
fn try_sign_digest(&self, digest: D) -> Result<DerSignature<C>, SigError> {
195+
let signature: Signature<_> = self.try_sign_digest(digest)?;
196+
Ok(signature.to_der())
197+
}
198+
}
199+
200+
// TODO: implementations of [`signature::Digest`] should be possible using a simple
201+
// `#[derive(Signer)]` on the struct, but due to a lack of support for lifetimes, this is not
202+
// possible yet and will need to be manually implemented
203+
impl<'ctx, C, D> Signer<Signature<C>> for EcSigner<'ctx, C, D>
204+
where
205+
C: PrimeCurve + CurveArithmetic,
206+
C: AssociatedTpmCurve,
207+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
208+
D: AssociatedHashingAlgorithm,
209+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
210+
SignatureSize<C>: ArrayLength<u8>,
211+
TpmDigest: From<Output<D>>,
212+
{
213+
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, SigError> {
214+
self.try_sign_digest(D::new_with_prefix(msg))
215+
}
216+
}
217+
218+
impl<'ctx, C, D> Signer<DerSignature<C>> for EcSigner<'ctx, C, D>
219+
where
220+
C: PrimeCurve + CurveArithmetic,
221+
C: AssociatedTpmCurve,
222+
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
223+
D: AssociatedHashingAlgorithm,
224+
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
225+
SignatureSize<C>: ArrayLength<u8>,
226+
TpmDigest: From<Output<D>>,
227+
228+
MaxSize<C>: ArrayLength<u8>,
229+
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
230+
{
231+
fn try_sign(&self, msg: &[u8]) -> Result<DerSignature<C>, SigError> {
232+
self.try_sign_digest(D::new_with_prefix(msg))
233+
}
234+
}

0 commit comments

Comments
 (0)