use crate::{
Context, Error, WrapperErrorKind,
abstraction::{
AssociatedHashingAlgorithm,
public::AssociatedTpmCurve,
transient::{KeyMaterial, KeyParams, TransientKeyContext},
},
handles::KeyHandle,
interface_types::algorithm::EccSchemeAlgorithm,
structures::{
Auth, Digest as TpmDigest, EccScheme, Public, Signature as TpmSignature, SignatureScheme,
},
utils::PublicKey as TpmPublicKey,
};
use std::{convert::TryFrom, ops::Add, sync::Mutex};
use digest::{Digest, FixedOutput, Output};
use ecdsa::{
Signature, SignatureSize, VerifyingKey,
der::{MaxOverhead, MaxSize, Signature as DerSignature},
hazmat::{DigestPrimitive, SignPrimitive},
};
use elliptic_curve::{
AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, PublicKey, Scalar,
generic_array::ArrayLength,
ops::Invert,
sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
subtle::CtOption,
};
use log::error;
use signature::{DigestSigner, Error as SigError, KeypairRef, Signer};
use x509_cert::{
der::asn1::AnyRef,
spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
};
pub trait TpmSigner {
fn public(&self) -> crate::Result<TpmPublicKey>;
fn key_params(&self) -> crate::Result<KeyParams>;
fn sign(&self, digest: TpmDigest) -> crate::Result<TpmSignature>;
}
impl TpmSigner for (Mutex<&'_ mut Context>, KeyHandle) {
fn public(&self) -> crate::Result<TpmPublicKey> {
let mut context = self.0.lock().expect("Mutex got poisoned");
let (public, _, _) = context.read_public(self.1)?;
TpmPublicKey::try_from(public)
}
fn key_params(&self) -> crate::Result<KeyParams> {
let mut context = self.0.lock().expect("Mutex got poisoned");
let (public, _, _) = context.read_public(self.1)?;
match public {
Public::Rsa { parameters, .. } => Ok(KeyParams::Rsa {
size: parameters.key_bits(),
scheme: parameters.rsa_scheme(),
pub_exponent: parameters.exponent(),
}),
Public::Ecc { parameters, .. } => Ok(KeyParams::Ecc {
curve: parameters.ecc_curve(),
scheme: parameters.ecc_scheme(),
}),
other => {
error!("Unsupported key parameter used: {other:?}");
Err(Error::local_error(WrapperErrorKind::InvalidParam))
}
}
}
fn sign(&self, digest: TpmDigest) -> crate::Result<TpmSignature> {
let mut context = self.0.lock().expect("Mutex got poisoned");
context.sign(self.1, digest, SignatureScheme::Null, None)
}
}
impl TpmSigner
for (
Mutex<&'_ mut TransientKeyContext>,
KeyMaterial,
KeyParams,
Option<Auth>,
)
{
fn public(&self) -> crate::Result<TpmPublicKey> {
Ok(self.1.public().clone())
}
fn key_params(&self) -> crate::Result<KeyParams> {
Ok(self.2)
}
fn sign(&self, digest: TpmDigest) -> crate::Result<TpmSignature> {
let mut context = self.0.lock().expect("Mutex got poisoned");
context.sign(self.1.clone(), self.2, self.3.clone(), digest)
}
}
#[derive(Debug)]
pub struct EcSigner<C, Ctx>
where
C: PrimeCurve + CurveArithmetic,
{
context: Ctx,
verifying_key: VerifyingKey<C>,
}
impl<C, Ctx> EcSigner<C, Ctx>
where
C: PrimeCurve + CurveArithmetic,
C: AssociatedTpmCurve,
FieldBytesSize<C>: ModulusSize,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
Ctx: TpmSigner,
{
pub fn new(context: Ctx) -> Result<Self, Error> {
match context.key_params()? {
KeyParams::Ecc { curve, .. } if curve == C::TPM_CURVE => {}
other => {
error!(
"Unsupported key parameters: {other:?}, expected Ecc(curve: {:?})",
C::default()
);
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
}
}
let public_key = context.public()?;
let public_key = PublicKey::try_from(&public_key)?;
let verifying_key = VerifyingKey::from(public_key);
Ok(Self {
context,
verifying_key,
})
}
}
impl<C, Ctx> EcSigner<C, Ctx>
where
C: PrimeCurve + CurveArithmetic,
C: AssociatedTpmCurve,
{
pub fn key_params_default() -> KeyParams
where
C: DigestPrimitive,
<C as DigestPrimitive>::Digest: FixedOutput<OutputSize = FieldBytesSize<C>>,
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
{
Self::key_params::<<C as DigestPrimitive>::Digest>()
}
pub fn key_params<D>() -> KeyParams
where
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
D: AssociatedHashingAlgorithm,
{
KeyParams::Ecc {
curve: C::TPM_CURVE,
scheme: EccScheme::create(EccSchemeAlgorithm::EcDsa, Some(D::TPM_DIGEST), None)
.expect("Failed to create ecc scheme"),
}
}
}
impl<C, Ctx> AsRef<VerifyingKey<C>> for EcSigner<C, Ctx>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn as_ref(&self) -> &VerifyingKey<C> {
&self.verifying_key
}
}
impl<C, Ctx> KeypairRef for EcSigner<C, Ctx>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type VerifyingKey = VerifyingKey<C>;
}
impl<C, Ctx, D> DigestSigner<D, Signature<C>> for EcSigner<C, Ctx>
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>>,
Ctx: TpmSigner,
{
fn try_sign_digest(&self, digest: D) -> Result<Signature<C>, SigError> {
let digest = TpmDigest::from(digest.finalize_fixed());
let signature = self.context.sign(digest).map_err(SigError::from_source)?;
let TpmSignature::EcDsa(signature) = signature else {
return Err(SigError::from_source(Error::local_error(
WrapperErrorKind::InvalidParam,
)));
};
let signature = Signature::try_from(&signature).map_err(SigError::from_source)?;
Ok(signature)
}
}
impl<C, Ctx, D> DigestSigner<D, DerSignature<C>> for EcSigner<C, Ctx>
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>,
Ctx: TpmSigner,
{
fn try_sign_digest(&self, digest: D) -> Result<DerSignature<C>, SigError> {
let signature: Signature<_> = self.try_sign_digest(digest)?;
Ok(signature.to_der())
}
}
impl<C, Ctx> Signer<Signature<C>> for EcSigner<C, Ctx>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
C: AssociatedTpmCurve,
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
Ctx: TpmSigner,
{
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, SigError> {
self.try_sign_digest(C::Digest::new_with_prefix(msg))
}
}
impl<C, Ctx> Signer<DerSignature<C>> for EcSigner<C, Ctx>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
C: AssociatedTpmCurve,
<C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
Ctx: TpmSigner,
{
fn try_sign(&self, msg: &[u8]) -> Result<DerSignature<C>, SigError> {
self.try_sign_digest(C::Digest::new_with_prefix(msg))
}
}
impl<C, Ctx> SignatureAlgorithmIdentifier for EcSigner<C, Ctx>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
Signature::<C>::ALGORITHM_IDENTIFIER;
}
#[cfg(feature = "rsa")]
mod rsa {
use super::TpmSigner;
use crate::{
Error, WrapperErrorKind,
abstraction::{AssociatedHashingAlgorithm, signer::KeyParams},
structures::{Digest as TpmDigest, RsaScheme},
};
use std::fmt;
use digest::{Digest, FixedOutput, Output};
use log::error;
use pkcs8::AssociatedOid;
use signature::{DigestSigner, Error as SigError, Keypair, Signer};
use x509_cert::{
der::asn1::AnyRef,
spki::{
self, AlgorithmIdentifier, AlgorithmIdentifierOwned, AssociatedAlgorithmIdentifier,
DynSignatureAlgorithmIdentifier, SignatureAlgorithmIdentifier,
},
};
use ::rsa::{RsaPublicKey, pkcs1v15, pss};
#[derive(Debug)]
pub struct RsaPkcsSigner<Ctx, D>
where
D: Digest,
{
context: Ctx,
verifying_key: pkcs1v15::VerifyingKey<D>,
}
impl<Ctx, D> RsaPkcsSigner<Ctx, D>
where
Ctx: TpmSigner,
D: Digest + AssociatedOid + AssociatedHashingAlgorithm + fmt::Debug,
{
pub fn new(context: Ctx) -> Result<Self, Error> {
match context.key_params()? {
KeyParams::Rsa {
scheme: RsaScheme::RsaSsa(hash),
..
} if hash.hashing_algorithm() == D::TPM_DIGEST => {}
other => {
error!(
"Unsupported key parameters: {other:?}, expected RsaSsa({:?})",
D::new()
);
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
}
}
let public_key = context.public()?;
let public_key = RsaPublicKey::try_from(&public_key)?;
let verifying_key = pkcs1v15::VerifyingKey::new(public_key);
Ok(Self {
context,
verifying_key,
})
}
}
impl<Ctx, D> Keypair for RsaPkcsSigner<Ctx, D>
where
D: Digest,
{
type VerifyingKey = pkcs1v15::VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
self.verifying_key.clone()
}
}
impl<Ctx, D> DigestSigner<D, pkcs1v15::Signature> for RsaPkcsSigner<Ctx, D>
where
D: Digest + FixedOutput,
D: AssociatedHashingAlgorithm,
TpmDigest: From<Output<D>>,
Ctx: TpmSigner,
{
fn try_sign_digest(&self, digest: D) -> Result<pkcs1v15::Signature, SigError> {
let digest = TpmDigest::from(digest.finalize_fixed());
let signature = self.context.sign(digest).map_err(SigError::from_source)?;
let signature =
pkcs1v15::Signature::try_from(&signature).map_err(SigError::from_source)?;
Ok(signature)
}
}
impl<Ctx, D> Signer<pkcs1v15::Signature> for RsaPkcsSigner<Ctx, D>
where
D: Digest + FixedOutput,
D: AssociatedHashingAlgorithm,
TpmDigest: From<Output<D>>,
Ctx: TpmSigner,
{
fn try_sign(&self, msg: &[u8]) -> Result<pkcs1v15::Signature, SigError> {
let mut d = D::new();
Digest::update(&mut d, msg);
self.try_sign_digest(d)
}
}
impl<Ctx, D> SignatureAlgorithmIdentifier for RsaPkcsSigner<Ctx, D>
where
D: Digest + pkcs1v15::RsaSignatureAssociatedOid,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
pkcs1v15::SigningKey::<D>::ALGORITHM_IDENTIFIER;
}
#[derive(Debug)]
pub struct RsaPssSigner<Ctx, D>
where
D: Digest,
{
context: Ctx,
verifying_key: pss::VerifyingKey<D>,
}
impl<Ctx, D> RsaPssSigner<Ctx, D>
where
Ctx: TpmSigner,
D: Digest + AssociatedHashingAlgorithm + fmt::Debug,
{
pub fn new(context: Ctx) -> Result<Self, Error> {
match context.key_params()? {
KeyParams::Rsa {
scheme: RsaScheme::RsaPss(hash),
..
} if hash.hashing_algorithm() == D::TPM_DIGEST => {}
other => {
error!(
"Unsupported key parameters: {other:?}, expected RsaSsa({:?})",
D::new()
);
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
}
}
let public_key = context.public()?;
let public_key = RsaPublicKey::try_from(&public_key)?;
let verifying_key = pss::VerifyingKey::new(public_key);
Ok(Self {
context,
verifying_key,
})
}
}
impl<Ctx, D> Keypair for RsaPssSigner<Ctx, D>
where
D: Digest,
{
type VerifyingKey = pss::VerifyingKey<D>;
fn verifying_key(&self) -> Self::VerifyingKey {
self.verifying_key.clone()
}
}
impl<Ctx, D> DigestSigner<D, pss::Signature> for RsaPssSigner<Ctx, D>
where
D: Digest + FixedOutput,
D: AssociatedHashingAlgorithm,
TpmDigest: From<Output<D>>,
Ctx: TpmSigner,
{
fn try_sign_digest(&self, digest: D) -> Result<pss::Signature, SigError> {
let digest = TpmDigest::from(digest.finalize_fixed());
let signature = self.context.sign(digest).map_err(SigError::from_source)?;
let signature = pss::Signature::try_from(&signature).map_err(SigError::from_source)?;
Ok(signature)
}
}
impl<Ctx, D> Signer<pss::Signature> for RsaPssSigner<Ctx, D>
where
D: Digest + FixedOutput,
D: AssociatedHashingAlgorithm,
TpmDigest: From<Output<D>>,
Ctx: TpmSigner,
{
fn try_sign(&self, msg: &[u8]) -> Result<pss::Signature, SigError> {
let mut d = D::new();
Digest::update(&mut d, msg);
self.try_sign_digest(d)
}
}
impl<Ctx, D> DynSignatureAlgorithmIdentifier for RsaPssSigner<Ctx, D>
where
D: Digest + AssociatedOid,
{
fn signature_algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
pss::get_default_pss_signature_algo_id::<D>()
}
}
}
#[cfg(feature = "rsa")]
pub use self::rsa::{RsaPkcsSigner, RsaPssSigner};