-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adds a
signature::Signer
implementation for ecc keys
Signed-off-by: Arthur Gautier <[email protected]>
- Loading branch information
Showing
3 changed files
with
238 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} | ||
} |