Skip to main content

tss_esapi/abstraction/
signer.rs

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.
7use crate::{
8    abstraction::{
9        public::AssociatedTpmCurve,
10        transient::{KeyMaterial, KeyParams, TransientKeyContext},
11        AssociatedHashingAlgorithm,
12    },
13    handles::KeyHandle,
14    interface_types::algorithm::EccSchemeAlgorithm,
15    structures::{
16        Auth, Digest as TpmDigest, EccScheme, Public, Signature as TpmSignature, SignatureScheme,
17    },
18    utils::PublicKey as TpmPublicKey,
19    Context, Error, WrapperErrorKind,
20};
21
22use std::{convert::TryFrom, ops::Add, sync::Mutex};
23
24use digest::{Digest, FixedOutput, Output};
25use ecdsa::{
26    der::{MaxOverhead, MaxSize, Signature as DerSignature},
27    hazmat::{DigestPrimitive, SignPrimitive},
28    Signature, SignatureSize, VerifyingKey,
29};
30use elliptic_curve::{
31    generic_array::ArrayLength,
32    ops::Invert,
33    sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint},
34    subtle::CtOption,
35    AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, PublicKey, Scalar,
36};
37use log::error;
38use signature::{DigestSigner, Error as SigError, KeypairRef, Signer};
39use x509_cert::{
40    der::asn1::AnyRef,
41    spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
42};
43
44pub trait TpmSigner {
45    fn public(&self) -> crate::Result<TpmPublicKey>;
46    fn key_params(&self) -> crate::Result<KeyParams>;
47    fn sign(&self, digest: TpmDigest) -> crate::Result<TpmSignature>;
48}
49
50impl TpmSigner for (Mutex<&'_ mut Context>, KeyHandle) {
51    fn public(&self) -> crate::Result<TpmPublicKey> {
52        let mut context = self.0.lock().expect("Mutex got poisoned");
53        let (public, _, _) = context.read_public(self.1)?;
54
55        TpmPublicKey::try_from(public)
56    }
57
58    fn key_params(&self) -> crate::Result<KeyParams> {
59        let mut context = self.0.lock().expect("Mutex got poisoned");
60        let (public, _, _) = context.read_public(self.1)?;
61
62        match public {
63            Public::Rsa { parameters, .. } => Ok(KeyParams::Rsa {
64                size: parameters.key_bits(),
65                scheme: parameters.rsa_scheme(),
66                pub_exponent: parameters.exponent(),
67            }),
68            Public::Ecc { parameters, .. } => Ok(KeyParams::Ecc {
69                curve: parameters.ecc_curve(),
70                scheme: parameters.ecc_scheme(),
71            }),
72            other => {
73                error!("Unsupported key parameter used: {other:?}");
74                Err(Error::local_error(WrapperErrorKind::InvalidParam))
75            }
76        }
77    }
78
79    fn sign(&self, digest: TpmDigest) -> crate::Result<TpmSignature> {
80        let mut context = self.0.lock().expect("Mutex got poisoned");
81        context.sign(self.1, digest, SignatureScheme::Null, None)
82    }
83}
84
85impl TpmSigner
86    for (
87        Mutex<&'_ mut TransientKeyContext>,
88        KeyMaterial,
89        KeyParams,
90        Option<Auth>,
91    )
92{
93    fn public(&self) -> crate::Result<TpmPublicKey> {
94        Ok(self.1.public().clone())
95    }
96
97    fn key_params(&self) -> crate::Result<KeyParams> {
98        Ok(self.2)
99    }
100
101    fn sign(&self, digest: TpmDigest) -> crate::Result<TpmSignature> {
102        let mut context = self.0.lock().expect("Mutex got poisoned");
103        context.sign(self.1.clone(), self.2, self.3.clone(), digest)
104    }
105}
106
107/// [`EcSigner`] will sign a payload with an elliptic curve secret key stored on the TPM.
108///
109/// # Parameters
110///
111/// Parameter `C` describes the curve that is of use (Nist P-256, Nist P-384, ...)
112///
113/// ```no_run
114/// # use std::sync::Mutex;
115/// # use tss_esapi::{
116/// #     abstraction::{EcSigner, transient::TransientKeyContextBuilder},
117/// #     TctiNameConf
118/// # };
119/// use p256::NistP256;
120/// use signature::Signer;
121/// #
122/// # // Create context
123/// # let mut context = TransientKeyContextBuilder::new()
124/// #     .with_tcti(
125/// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
126/// #     )
127/// #     .build()
128/// #     .expect("Failed to create Context");
129///
130/// let key_params = EcSigner::<NistP256, ()>::key_params_default();
131/// let (tpm_km, _tpm_auth) = context
132///     .create_key(key_params, 0)
133///     .expect("Failed to create a private keypair");
134///
135/// let signer = EcSigner::<NistP256,_>::new((Mutex::new(&mut context), tpm_km, key_params, None))
136///      .expect("Failed to create a signer");
137/// let signature: p256::ecdsa::Signature = signer.sign(b"Hello Bob, Alice here.");
138/// ```
139#[derive(Debug)]
140pub struct EcSigner<C, Ctx>
141where
142    C: PrimeCurve + CurveArithmetic,
143{
144    context: Ctx,
145    verifying_key: VerifyingKey<C>,
146}
147
148impl<C, Ctx> EcSigner<C, Ctx>
149where
150    C: PrimeCurve + CurveArithmetic,
151    C: AssociatedTpmCurve,
152    FieldBytesSize<C>: ModulusSize,
153    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
154
155    Ctx: TpmSigner,
156{
157    pub fn new(context: Ctx) -> Result<Self, Error> {
158        match context.key_params()? {
159            KeyParams::Ecc { curve, .. } if curve == C::TPM_CURVE => {}
160            other => {
161                error!(
162                    "Unsupported key parameters: {other:?}, expected Ecc(curve: {:?})",
163                    C::default()
164                );
165                return Err(Error::local_error(WrapperErrorKind::InvalidParam));
166            }
167        }
168
169        let public_key = context.public()?;
170        let public_key = PublicKey::try_from(&public_key)?;
171        let verifying_key = VerifyingKey::from(public_key);
172
173        Ok(Self {
174            context,
175            verifying_key,
176        })
177    }
178}
179
180impl<C, Ctx> EcSigner<C, Ctx>
181where
182    C: PrimeCurve + CurveArithmetic,
183    C: AssociatedTpmCurve,
184{
185    /// Key parameters for this curve, selected digest is the one selected by DigestPrimitive
186    pub fn key_params_default() -> KeyParams
187    where
188        C: DigestPrimitive,
189        <C as DigestPrimitive>::Digest: FixedOutput<OutputSize = FieldBytesSize<C>>,
190        <C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
191    {
192        Self::key_params::<<C as DigestPrimitive>::Digest>()
193    }
194
195    /// Key parameters for this curve
196    ///
197    /// # Parameters
198    ///
199    /// The hashing algorithm `D` is the digest that will be used for signatures (SHA-256, SHA3-256, ...).
200    pub fn key_params<D>() -> KeyParams
201    where
202        D: FixedOutput<OutputSize = FieldBytesSize<C>>,
203        D: AssociatedHashingAlgorithm,
204    {
205        KeyParams::Ecc {
206            curve: C::TPM_CURVE,
207            scheme: EccScheme::create(EccSchemeAlgorithm::EcDsa, Some(D::TPM_DIGEST), None)
208                .expect("Failed to create ecc scheme"),
209        }
210    }
211}
212
213impl<C, Ctx> AsRef<VerifyingKey<C>> for EcSigner<C, Ctx>
214where
215    C: PrimeCurve + CurveArithmetic,
216    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
217    SignatureSize<C>: ArrayLength<u8>,
218{
219    fn as_ref(&self) -> &VerifyingKey<C> {
220        &self.verifying_key
221    }
222}
223
224impl<C, Ctx> KeypairRef for EcSigner<C, Ctx>
225where
226    C: PrimeCurve + CurveArithmetic,
227    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
228    SignatureSize<C>: ArrayLength<u8>,
229{
230    type VerifyingKey = VerifyingKey<C>;
231}
232
233impl<C, Ctx, D> DigestSigner<D, Signature<C>> for EcSigner<C, Ctx>
234where
235    C: PrimeCurve + CurveArithmetic,
236    C: AssociatedTpmCurve,
237    D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
238    D: AssociatedHashingAlgorithm,
239    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
240    SignatureSize<C>: ArrayLength<u8>,
241    TpmDigest: From<Output<D>>,
242    Ctx: TpmSigner,
243{
244    fn try_sign_digest(&self, digest: D) -> Result<Signature<C>, SigError> {
245        let digest = TpmDigest::from(digest.finalize_fixed());
246
247        //let key_params = Self::key_params::<D>();
248        let signature = self.context.sign(digest).map_err(SigError::from_source)?;
249
250        let TpmSignature::EcDsa(signature) = signature else {
251            return Err(SigError::from_source(Error::local_error(
252                WrapperErrorKind::InvalidParam,
253            )));
254        };
255
256        let signature = Signature::try_from(&signature).map_err(SigError::from_source)?;
257
258        Ok(signature)
259    }
260}
261
262impl<C, Ctx, D> DigestSigner<D, DerSignature<C>> for EcSigner<C, Ctx>
263where
264    C: PrimeCurve + CurveArithmetic,
265    C: AssociatedTpmCurve,
266    D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
267    D: AssociatedHashingAlgorithm,
268    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
269    SignatureSize<C>: ArrayLength<u8>,
270    TpmDigest: From<Output<D>>,
271
272    MaxSize<C>: ArrayLength<u8>,
273    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
274
275    Ctx: TpmSigner,
276{
277    fn try_sign_digest(&self, digest: D) -> Result<DerSignature<C>, SigError> {
278        let signature: Signature<_> = self.try_sign_digest(digest)?;
279        Ok(signature.to_der())
280    }
281}
282
283impl<C, Ctx> Signer<Signature<C>> for EcSigner<C, Ctx>
284where
285    C: PrimeCurve + CurveArithmetic + DigestPrimitive,
286    C: AssociatedTpmCurve,
287    <C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
288    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
289    SignatureSize<C>: ArrayLength<u8>,
290    TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
291
292    Ctx: TpmSigner,
293{
294    fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, SigError> {
295        self.try_sign_digest(C::Digest::new_with_prefix(msg))
296    }
297}
298
299impl<C, Ctx> Signer<DerSignature<C>> for EcSigner<C, Ctx>
300where
301    C: PrimeCurve + CurveArithmetic + DigestPrimitive,
302    C: AssociatedTpmCurve,
303    <C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm,
304    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
305    SignatureSize<C>: ArrayLength<u8>,
306    TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>,
307
308    MaxSize<C>: ArrayLength<u8>,
309    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
310
311    Ctx: TpmSigner,
312{
313    fn try_sign(&self, msg: &[u8]) -> Result<DerSignature<C>, SigError> {
314        self.try_sign_digest(C::Digest::new_with_prefix(msg))
315    }
316}
317
318impl<C, Ctx> SignatureAlgorithmIdentifier for EcSigner<C, Ctx>
319where
320    C: PrimeCurve + CurveArithmetic,
321    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
322    SignatureSize<C>: ArrayLength<u8>,
323    Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
324{
325    type Params = AnyRef<'static>;
326
327    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
328        Signature::<C>::ALGORITHM_IDENTIFIER;
329}
330
331#[cfg(feature = "rsa")]
332mod rsa {
333    use super::TpmSigner;
334
335    use crate::{
336        abstraction::{signer::KeyParams, AssociatedHashingAlgorithm},
337        structures::{Digest as TpmDigest, RsaScheme},
338        Error, WrapperErrorKind,
339    };
340
341    use std::fmt;
342
343    use digest::{Digest, FixedOutput, Output};
344    use log::error;
345    use pkcs8::AssociatedOid;
346    use signature::{DigestSigner, Error as SigError, Keypair, Signer};
347    use x509_cert::{
348        der::asn1::AnyRef,
349        spki::{
350            self, AlgorithmIdentifier, AlgorithmIdentifierOwned, AssociatedAlgorithmIdentifier,
351            DynSignatureAlgorithmIdentifier, SignatureAlgorithmIdentifier,
352        },
353    };
354
355    use ::rsa::{pkcs1v15, pss, RsaPublicKey};
356
357    /// [`RsaPkcsSigner`] will sign a payload with an RSA secret key stored on the TPM.
358    ///
359    /// ```no_run
360    /// # use std::sync::Mutex;
361    /// # use tss_esapi::{
362    /// #     abstraction::{RsaPkcsSigner, transient::{TransientKeyContextBuilder, KeyParams}},
363    /// #     interface_types::{algorithm::{HashingAlgorithm, RsaSchemeAlgorithm}, key_bits::RsaKeyBits},
364    /// #     structures::{RsaExponent, RsaScheme},
365    /// #     TctiNameConf
366    /// # };
367    /// use signature::Signer;
368    /// #
369    /// # // Create context
370    /// # let mut context = TransientKeyContextBuilder::new()
371    /// #     .with_tcti(
372    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
373    /// #     )
374    /// #     .build()
375    /// #     .expect("Failed to create Context");
376    ///
377    /// let key_params = KeyParams::Rsa {
378    ///     size: RsaKeyBits::Rsa1024,
379    ///     scheme: RsaScheme::create(RsaSchemeAlgorithm::RsaSsa, Some(HashingAlgorithm::Sha256))
380    ///         .expect("Failed to create RSA scheme"),
381    ///     pub_exponent: RsaExponent::default(),
382    /// };
383    /// let (tpm_km, _tpm_auth) = context
384    ///     .create_key(key_params, 0)
385    ///     .expect("Failed to create a private keypair");
386    ///
387    /// let signer = RsaPkcsSigner::<_, sha2::Sha256>::new((Mutex::new(&mut context), tpm_km, key_params, None))
388    ///      .expect("Failed to create a signer");
389    /// let signature  = signer.sign(b"Hello Bob, Alice here.");
390    /// ```
391    #[derive(Debug)]
392    pub struct RsaPkcsSigner<Ctx, D>
393    where
394        D: Digest,
395    {
396        context: Ctx,
397        verifying_key: pkcs1v15::VerifyingKey<D>,
398    }
399
400    impl<Ctx, D> RsaPkcsSigner<Ctx, D>
401    where
402        Ctx: TpmSigner,
403        D: Digest + AssociatedOid + AssociatedHashingAlgorithm + fmt::Debug,
404    {
405        pub fn new(context: Ctx) -> Result<Self, Error> {
406            match context.key_params()? {
407                KeyParams::Rsa {
408                    scheme: RsaScheme::RsaSsa(hash),
409                    ..
410                } if hash.hashing_algorithm() == D::TPM_DIGEST => {}
411                other => {
412                    error!(
413                        "Unsupported key parameters: {other:?}, expected RsaSsa({:?})",
414                        D::new()
415                    );
416                    return Err(Error::local_error(WrapperErrorKind::InvalidParam));
417                }
418            }
419
420            let public_key = context.public()?;
421            let public_key = RsaPublicKey::try_from(&public_key)?;
422            let verifying_key = pkcs1v15::VerifyingKey::new(public_key);
423
424            Ok(Self {
425                context,
426                verifying_key,
427            })
428        }
429    }
430
431    impl<Ctx, D> Keypair for RsaPkcsSigner<Ctx, D>
432    where
433        D: Digest,
434    {
435        type VerifyingKey = pkcs1v15::VerifyingKey<D>;
436
437        fn verifying_key(&self) -> Self::VerifyingKey {
438            self.verifying_key.clone()
439        }
440    }
441
442    impl<Ctx, D> DigestSigner<D, pkcs1v15::Signature> for RsaPkcsSigner<Ctx, D>
443    where
444        D: Digest + FixedOutput,
445        D: AssociatedHashingAlgorithm,
446        TpmDigest: From<Output<D>>,
447        Ctx: TpmSigner,
448    {
449        fn try_sign_digest(&self, digest: D) -> Result<pkcs1v15::Signature, SigError> {
450            let digest = TpmDigest::from(digest.finalize_fixed());
451
452            //let key_params = Self::key_params::<D>();
453            let signature = self.context.sign(digest).map_err(SigError::from_source)?;
454
455            let signature =
456                pkcs1v15::Signature::try_from(&signature).map_err(SigError::from_source)?;
457
458            Ok(signature)
459        }
460    }
461
462    impl<Ctx, D> Signer<pkcs1v15::Signature> for RsaPkcsSigner<Ctx, D>
463    where
464        D: Digest + FixedOutput,
465        D: AssociatedHashingAlgorithm,
466        TpmDigest: From<Output<D>>,
467        Ctx: TpmSigner,
468    {
469        fn try_sign(&self, msg: &[u8]) -> Result<pkcs1v15::Signature, SigError> {
470            let mut d = D::new();
471            Digest::update(&mut d, msg);
472
473            self.try_sign_digest(d)
474        }
475    }
476
477    impl<Ctx, D> SignatureAlgorithmIdentifier for RsaPkcsSigner<Ctx, D>
478    where
479        D: Digest + pkcs1v15::RsaSignatureAssociatedOid,
480    {
481        type Params = AnyRef<'static>;
482
483        const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
484            pkcs1v15::SigningKey::<D>::ALGORITHM_IDENTIFIER;
485    }
486
487    /// [`RsaPssSigner`] will sign a payload with an RSA secret key stored on the TPM.
488    ///
489    /// ```no_run
490    /// # use std::sync::Mutex;
491    /// # use tss_esapi::{
492    /// #     abstraction::{RsaPssSigner, transient::{TransientKeyContextBuilder, KeyParams}},
493    /// #     interface_types::{algorithm::{HashingAlgorithm, RsaSchemeAlgorithm}, key_bits::RsaKeyBits},
494    /// #     structures::{RsaExponent, RsaScheme},
495    /// #     TctiNameConf
496    /// # };
497    /// use signature::Signer;
498    /// #
499    /// # // Create context
500    /// # let mut context = TransientKeyContextBuilder::new()
501    /// #     .with_tcti(
502    /// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
503    /// #     )
504    /// #     .build()
505    /// #     .expect("Failed to create Context");
506    ///
507    /// let key_params = KeyParams::Rsa {
508    ///     size: RsaKeyBits::Rsa1024,
509    ///     scheme: RsaScheme::create(RsaSchemeAlgorithm::RsaPss, Some(HashingAlgorithm::Sha256))
510    ///         .expect("Failed to create RSA scheme"),
511    ///     pub_exponent: RsaExponent::default(),
512    /// };
513    /// let (tpm_km, _tpm_auth) = context
514    ///     .create_key(key_params, 0)
515    ///     .expect("Failed to create a private keypair");
516    ///
517    /// let signer = RsaPssSigner::<_, sha2::Sha256>::new((Mutex::new(&mut context), tpm_km, key_params, None))
518    ///      .expect("Failed to create a signer");
519    /// let signature  = signer.sign(b"Hello Bob, Alice here.");
520    /// ```
521    #[derive(Debug)]
522    pub struct RsaPssSigner<Ctx, D>
523    where
524        D: Digest,
525    {
526        context: Ctx,
527        verifying_key: pss::VerifyingKey<D>,
528    }
529
530    impl<Ctx, D> RsaPssSigner<Ctx, D>
531    where
532        Ctx: TpmSigner,
533        D: Digest + AssociatedHashingAlgorithm + fmt::Debug,
534    {
535        pub fn new(context: Ctx) -> Result<Self, Error> {
536            match context.key_params()? {
537                KeyParams::Rsa {
538                    scheme: RsaScheme::RsaPss(hash),
539                    ..
540                } if hash.hashing_algorithm() == D::TPM_DIGEST => {}
541                other => {
542                    error!(
543                        "Unsupported key parameters: {other:?}, expected RsaSsa({:?})",
544                        D::new()
545                    );
546                    return Err(Error::local_error(WrapperErrorKind::InvalidParam));
547                }
548            }
549
550            let public_key = context.public()?;
551            let public_key = RsaPublicKey::try_from(&public_key)?;
552            let verifying_key = pss::VerifyingKey::new(public_key);
553
554            Ok(Self {
555                context,
556                verifying_key,
557            })
558        }
559    }
560
561    impl<Ctx, D> Keypair for RsaPssSigner<Ctx, D>
562    where
563        D: Digest,
564    {
565        type VerifyingKey = pss::VerifyingKey<D>;
566
567        fn verifying_key(&self) -> Self::VerifyingKey {
568            self.verifying_key.clone()
569        }
570    }
571
572    impl<Ctx, D> DigestSigner<D, pss::Signature> for RsaPssSigner<Ctx, D>
573    where
574        D: Digest + FixedOutput,
575        D: AssociatedHashingAlgorithm,
576        TpmDigest: From<Output<D>>,
577        Ctx: TpmSigner,
578    {
579        fn try_sign_digest(&self, digest: D) -> Result<pss::Signature, SigError> {
580            let digest = TpmDigest::from(digest.finalize_fixed());
581
582            let signature = self.context.sign(digest).map_err(SigError::from_source)?;
583
584            let signature = pss::Signature::try_from(&signature).map_err(SigError::from_source)?;
585
586            Ok(signature)
587        }
588    }
589
590    impl<Ctx, D> Signer<pss::Signature> for RsaPssSigner<Ctx, D>
591    where
592        D: Digest + FixedOutput,
593        D: AssociatedHashingAlgorithm,
594        TpmDigest: From<Output<D>>,
595        Ctx: TpmSigner,
596    {
597        fn try_sign(&self, msg: &[u8]) -> Result<pss::Signature, SigError> {
598            let mut d = D::new();
599            Digest::update(&mut d, msg);
600
601            self.try_sign_digest(d)
602        }
603    }
604
605    impl<Ctx, D> DynSignatureAlgorithmIdentifier for RsaPssSigner<Ctx, D>
606    where
607        D: Digest + AssociatedOid,
608    {
609        fn signature_algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
610            pss::get_default_pss_signature_algo_id::<D>()
611        }
612    }
613}
614
615#[cfg(feature = "rsa")]
616pub use self::rsa::{RsaPkcsSigner, RsaPssSigner};