Skip to content

Commit

Permalink
adds a signature::Signer implementation for ecc keys
Browse files Browse the repository at this point in the history
Signed-off-by: Arthur Gautier <[email protected]>
  • Loading branch information
baloo committed Aug 6, 2024
1 parent bbcb4cc commit fe52b1c
Show file tree
Hide file tree
Showing 3 changed files with 238 additions and 1 deletion.
3 changes: 2 additions & 1 deletion tss-esapi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ sha3 = { version = "0.10.8", optional = true }
sm2 = { version = "0.13.3", optional = true }
sm3 = { version = "0.4.2", optional = true }
digest = "0.10.7"
signature = { version = "2.2.0", optional = true }
cfg-if = "1.0.0"
strum = { version = "0.25.0", optional = true }
strum_macros = { version = "0.25.0", optional = true }
Expand All @@ -59,5 +60,5 @@ semver = "1.0.7"
[features]
default = ["abstraction"]
generate-bindings = ["tss-esapi-sys/generate-bindings"]
abstraction = ["ecdsa", "elliptic-curve", "rsa", "x509-cert", "p192", "p224", "p256", "p384", "p521", "sha1", "sha2", "sha3", "sm2", "sm3"]
abstraction = ["ecdsa", "elliptic-curve", "signature", "rsa", "x509-cert", "p192", "p224", "p256", "p384", "p521", "sha1", "sha2", "sha3", "sm2", "sm3"]
integration-tests = ["strum", "strum_macros"]
2 changes: 2 additions & 0 deletions tss-esapi/src/abstraction/transient/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ use std::convert::{AsMut, AsRef, TryFrom, TryInto};
use zeroize::Zeroize;

mod key_attestation;
mod signer;

pub use key_attestation::MakeCredParams;
pub use signer::EcSigner;

/// Parameters for the kinds of keys supported by the context
#[derive(Debug, Clone, Copy)]
Expand Down
234 changes: 234 additions & 0 deletions tss-esapi/src/abstraction/transient/signer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// Copyright 2024 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0

//! Module for exposing a [`signature::Signer`] interface for keys
//!
//! This modules presents objects held in a TPM over a [`signature::DigestSigner`] interface.
use super::TransientKeyContext;
use crate::{
abstraction::{
public::AssociatedTpmCurve,
transient::{KeyMaterial, KeyParams},
AssociatedHashingAlgorithm,
},
interface_types::algorithm::EccSchemeAlgorithm,
structures::{Auth, Digest as TpmDigest, EccScheme, Signature as TpmSignature},
Error,
};

use std::{convert::TryFrom, marker::PhantomData, ops::Add, sync::Mutex};

use digest::{Digest, FixedOutput, Output};
use ecdsa::{
der::{MaxOverhead, MaxSize, Signature as DerSignature},
hazmat::{DigestPrimitive, SignPrimitive},
Signature, SignatureSize, VerifyingKey,
};
use elliptic_curve::{
generic_array::ArrayLength,
ops::Invert,
sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
subtle::CtOption,
AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, PublicKey, Scalar,
};
use signature::{DigestSigner, Error as SigError, KeypairRef, Signer};

/// [`EcSigner`] will sign a payload with an elliptic curve secret key stored on the TPM.
///
/// # Parameters
///
/// parameter `C` describes the curve that is of use (Nist P-256, Nist P-384, ...)
///
/// the hashing algorithm `D` is the digest that will be used for signatures (SHA-256, SHA3-256, ...).
///
/// ```no_run
/// # use tss_esapi::{
/// # abstraction::transient::{EcSigner, TransientKeyContextBuilder},
/// # TctiNameConf
/// # };
/// use p256::NistP256;
/// use signature::Signer;
/// #
/// # // Create context
/// # let mut context = TransientKeyContextBuilder::new()
/// # .with_tcti(
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
/// # )
/// # .build()
/// # .expect("Failed to create Context");
///
/// let (tpm_km, _tpm_auth) = context
/// .create_key(EcSigner::<NistP256>::key_params(), 0)
/// .expect("Failed to create a private keypair");
///
/// let signer = EcSigner::<NistP256>::new(&mut context, tpm_km, None)
/// .expect("Failed to create a signer");
/// let signature: p256::ecdsa::Signature = signer.sign(b"Hello Bob, Alice here.");
/// ```
#[derive(Debug)]
pub struct EcSigner<'ctx, C, D = <C as DigestPrimitive>::Digest>
where
C: PrimeCurve + CurveArithmetic,
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
{
context: Mutex<&'ctx mut TransientKeyContext>,
key_material: KeyMaterial,
key_auth: Option<Auth>,
verifying_key: VerifyingKey<C>,
_digest: PhantomData<D>,
}

impl<'ctx, C, D> EcSigner<'ctx, C, D>
where
C: PrimeCurve + CurveArithmetic,
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
C: AssociatedTpmCurve,
FieldBytesSize<C>: ModulusSize,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
{
pub fn new(
context: &'ctx mut TransientKeyContext,
key_material: KeyMaterial,
key_auth: Option<Auth>,
) -> Result<Self, Error> {
let context = Mutex::new(context);

let public_key = PublicKey::try_from(key_material.public())?;
let verifying_key = VerifyingKey::from(public_key);

Ok(Self {
context,
key_material,
key_auth,
verifying_key,
_digest: PhantomData,
})
}
}

impl<'ctx, C, D> EcSigner<'ctx, C, D>
where
C: PrimeCurve + CurveArithmetic,
C: AssociatedTpmCurve,
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
D: AssociatedHashingAlgorithm,
{
/// Key parameters for this curve
pub fn key_params() -> KeyParams {
KeyParams::Ecc {
curve: C::TPM_CURVE,
scheme: EccScheme::create(EccSchemeAlgorithm::EcDsa, Some(D::TPM_DIGEST), None)
.expect("Failed to create ecc scheme"),
}
}
}

impl<'ctx, C, D> AsRef<VerifyingKey<C>> for EcSigner<'ctx, C, D>
where
C: PrimeCurve + CurveArithmetic,
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn as_ref(&self) -> &VerifyingKey<C> {
&self.verifying_key
}
}

impl<'ctx, C, D> KeypairRef for EcSigner<'ctx, C, D>
where
C: PrimeCurve + CurveArithmetic,
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type VerifyingKey = VerifyingKey<C>;
}

impl<'ctx, C, D> DigestSigner<D, Signature<C>> for EcSigner<'ctx, C, D>
where
C: PrimeCurve + CurveArithmetic,
C: AssociatedTpmCurve,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
D: AssociatedHashingAlgorithm,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
TpmDigest: From<Output<D>>,
{
fn try_sign_digest(&self, digest: D) -> Result<Signature<C>, SigError> {
let digest = TpmDigest::from(digest.finalize_fixed());

let key_params = Self::key_params();
let mut context = self.context.lock().expect("Mutex got poisoned");
let signature = context
.sign(
self.key_material.clone(),
key_params,
self.key_auth.clone(),
digest,
)
.map_err(SigError::from_source)?;
let TpmSignature::EcDsa(signature) = signature else {
todo!();
};

let signature = Signature::try_from(signature).map_err(SigError::from_source)?;

Ok(signature)
}
}

impl<'ctx, C, D> DigestSigner<D, DerSignature<C>> for EcSigner<'ctx, C, D>
where
C: PrimeCurve + CurveArithmetic,
C: AssociatedTpmCurve,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
D: AssociatedHashingAlgorithm,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
TpmDigest: From<Output<D>>,

MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn try_sign_digest(&self, digest: D) -> Result<DerSignature<C>, SigError> {
let signature: Signature<_> = self.try_sign_digest(digest)?;
Ok(signature.to_der())
}
}

// TODO: implementations of [`signature::Digest`] should be possible using a simple
// `#[derive(Signer)]` on the struct, but due to a lack of support for lifetimes, this is not
// possible yet and will need to be manually implemented
impl<'ctx, C, D> Signer<Signature<C>> for EcSigner<'ctx, C, D>
where
C: PrimeCurve + CurveArithmetic,
C: AssociatedTpmCurve,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
D: AssociatedHashingAlgorithm,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
TpmDigest: From<Output<D>>,
{
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, SigError> {
self.try_sign_digest(D::new_with_prefix(msg))
}
}

impl<'ctx, C, D> Signer<DerSignature<C>> for EcSigner<'ctx, C, D>
where
C: PrimeCurve + CurveArithmetic,
C: AssociatedTpmCurve,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
D: AssociatedHashingAlgorithm,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
TpmDigest: From<Output<D>>,

MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn try_sign(&self, msg: &[u8]) -> Result<DerSignature<C>, SigError> {
self.try_sign_digest(D::new_with_prefix(msg))
}
}

0 comments on commit fe52b1c

Please sign in to comment.