|  | 
|  | 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, WrapperErrorKind, | 
|  | 17 | +}; | 
|  | 18 | + | 
|  | 19 | +use std::{convert::TryFrom, 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 | +use x509_cert::{ | 
|  | 36 | +    der::asn1::AnyRef, | 
|  | 37 | +    spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier}, | 
|  | 38 | +}; | 
|  | 39 | + | 
|  | 40 | +/// [`EcSigner`] will sign a payload with an elliptic curve secret key stored on the TPM. | 
|  | 41 | +/// | 
|  | 42 | +/// # Parameters | 
|  | 43 | +/// | 
|  | 44 | +/// Parameter `C` describes the curve that is of use (Nist P-256, Nist P-384, ...) | 
|  | 45 | +/// | 
|  | 46 | +/// ```no_run | 
|  | 47 | +/// # use tss_esapi::{ | 
|  | 48 | +/// #     abstraction::transient::{EcSigner, TransientKeyContextBuilder}, | 
|  | 49 | +/// #     TctiNameConf | 
|  | 50 | +/// # }; | 
|  | 51 | +/// use p256::NistP256; | 
|  | 52 | +/// use signature::Signer; | 
|  | 53 | +/// # | 
|  | 54 | +/// # // Create context | 
|  | 55 | +/// # let mut context = TransientKeyContextBuilder::new() | 
|  | 56 | +/// #     .with_tcti( | 
|  | 57 | +/// #         TctiNameConf::from_environment_variable().expect("Failed to get TCTI"), | 
|  | 58 | +/// #     ) | 
|  | 59 | +/// #     .build() | 
|  | 60 | +/// #     .expect("Failed to create Context"); | 
|  | 61 | +/// | 
|  | 62 | +/// let (tpm_km, _tpm_auth) = context | 
|  | 63 | +///     .create_key(EcSigner::<NistP256>::key_params_default(), 0) | 
|  | 64 | +///     .expect("Failed to create a private keypair"); | 
|  | 65 | +/// | 
|  | 66 | +/// let signer = EcSigner::<NistP256>::new(&mut context, tpm_km, None) | 
|  | 67 | +///      .expect("Failed to create a signer"); | 
|  | 68 | +/// let signature: p256::ecdsa::Signature = signer.sign(b"Hello Bob, Alice here."); | 
|  | 69 | +/// ``` | 
|  | 70 | +#[derive(Debug)] | 
|  | 71 | +pub struct EcSigner<'ctx, C> | 
|  | 72 | +where | 
|  | 73 | +    C: PrimeCurve + CurveArithmetic, | 
|  | 74 | +{ | 
|  | 75 | +    context: Mutex<&'ctx mut TransientKeyContext>, | 
|  | 76 | +    key_material: KeyMaterial, | 
|  | 77 | +    key_auth: Option<Auth>, | 
|  | 78 | +    verifying_key: VerifyingKey<C>, | 
|  | 79 | +} | 
|  | 80 | + | 
|  | 81 | +impl<'ctx, C> EcSigner<'ctx, C> | 
|  | 82 | +where | 
|  | 83 | +    C: PrimeCurve + CurveArithmetic, | 
|  | 84 | +    C: AssociatedTpmCurve, | 
|  | 85 | +    FieldBytesSize<C>: ModulusSize, | 
|  | 86 | +    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>, | 
|  | 87 | +{ | 
|  | 88 | +    pub fn new( | 
|  | 89 | +        context: &'ctx mut TransientKeyContext, | 
|  | 90 | +        key_material: KeyMaterial, | 
|  | 91 | +        key_auth: Option<Auth>, | 
|  | 92 | +    ) -> Result<Self, Error> { | 
|  | 93 | +        let context = Mutex::new(context); | 
|  | 94 | + | 
|  | 95 | +        let public_key = PublicKey::try_from(key_material.public())?; | 
|  | 96 | +        let verifying_key = VerifyingKey::from(public_key); | 
|  | 97 | + | 
|  | 98 | +        Ok(Self { | 
|  | 99 | +            context, | 
|  | 100 | +            key_material, | 
|  | 101 | +            key_auth, | 
|  | 102 | +            verifying_key, | 
|  | 103 | +        }) | 
|  | 104 | +    } | 
|  | 105 | +} | 
|  | 106 | + | 
|  | 107 | +impl<C> EcSigner<'_, C> | 
|  | 108 | +where | 
|  | 109 | +    C: PrimeCurve + CurveArithmetic, | 
|  | 110 | +    C: AssociatedTpmCurve, | 
|  | 111 | +{ | 
|  | 112 | +    /// Key parameters for this curve, selected digest is the one selected by DigestPrimitive | 
|  | 113 | +    pub fn key_params_default() -> KeyParams | 
|  | 114 | +    where | 
|  | 115 | +        C: DigestPrimitive, | 
|  | 116 | +        <C as DigestPrimitive>::Digest: FixedOutput<OutputSize = FieldBytesSize<C>>, | 
|  | 117 | +        <C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm, | 
|  | 118 | +    { | 
|  | 119 | +        Self::key_params::<<C as DigestPrimitive>::Digest>() | 
|  | 120 | +    } | 
|  | 121 | + | 
|  | 122 | +    /// Key parameters for this curve | 
|  | 123 | +    /// | 
|  | 124 | +    /// # Parameters | 
|  | 125 | +    /// | 
|  | 126 | +    /// The hashing algorithm `D` is the digest that will be used for signatures (SHA-256, SHA3-256, ...). | 
|  | 127 | +    pub fn key_params<D>() -> KeyParams | 
|  | 128 | +    where | 
|  | 129 | +        D: FixedOutput<OutputSize = FieldBytesSize<C>>, | 
|  | 130 | +        D: AssociatedHashingAlgorithm, | 
|  | 131 | +    { | 
|  | 132 | +        KeyParams::Ecc { | 
|  | 133 | +            curve: C::TPM_CURVE, | 
|  | 134 | +            scheme: EccScheme::create(EccSchemeAlgorithm::EcDsa, Some(D::TPM_DIGEST), None) | 
|  | 135 | +                .expect("Failed to create ecc scheme"), | 
|  | 136 | +        } | 
|  | 137 | +    } | 
|  | 138 | +} | 
|  | 139 | + | 
|  | 140 | +impl<C> AsRef<VerifyingKey<C>> for EcSigner<'_, C> | 
|  | 141 | +where | 
|  | 142 | +    C: PrimeCurve + CurveArithmetic, | 
|  | 143 | +    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>, | 
|  | 144 | +    SignatureSize<C>: ArrayLength<u8>, | 
|  | 145 | +{ | 
|  | 146 | +    fn as_ref(&self) -> &VerifyingKey<C> { | 
|  | 147 | +        &self.verifying_key | 
|  | 148 | +    } | 
|  | 149 | +} | 
|  | 150 | + | 
|  | 151 | +impl<C> KeypairRef for EcSigner<'_, C> | 
|  | 152 | +where | 
|  | 153 | +    C: PrimeCurve + CurveArithmetic, | 
|  | 154 | +    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>, | 
|  | 155 | +    SignatureSize<C>: ArrayLength<u8>, | 
|  | 156 | +{ | 
|  | 157 | +    type VerifyingKey = VerifyingKey<C>; | 
|  | 158 | +} | 
|  | 159 | + | 
|  | 160 | +impl<C, D> DigestSigner<D, Signature<C>> for EcSigner<'_, C> | 
|  | 161 | +where | 
|  | 162 | +    C: PrimeCurve + CurveArithmetic, | 
|  | 163 | +    C: AssociatedTpmCurve, | 
|  | 164 | +    D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>, | 
|  | 165 | +    D: AssociatedHashingAlgorithm, | 
|  | 166 | +    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>, | 
|  | 167 | +    SignatureSize<C>: ArrayLength<u8>, | 
|  | 168 | +    TpmDigest: From<Output<D>>, | 
|  | 169 | +{ | 
|  | 170 | +    fn try_sign_digest(&self, digest: D) -> Result<Signature<C>, SigError> { | 
|  | 171 | +        let digest = TpmDigest::from(digest.finalize_fixed()); | 
|  | 172 | + | 
|  | 173 | +        let key_params = Self::key_params::<D>(); | 
|  | 174 | +        let mut context = self.context.lock().expect("Mutex got poisoned"); | 
|  | 175 | +        let signature = context | 
|  | 176 | +            .sign( | 
|  | 177 | +                self.key_material.clone(), | 
|  | 178 | +                key_params, | 
|  | 179 | +                self.key_auth.clone(), | 
|  | 180 | +                digest, | 
|  | 181 | +            ) | 
|  | 182 | +            .map_err(SigError::from_source)?; | 
|  | 183 | + | 
|  | 184 | +        let TpmSignature::EcDsa(signature) = signature else { | 
|  | 185 | +            return Err(SigError::from_source(Error::WrapperError( | 
|  | 186 | +                WrapperErrorKind::InvalidParam, | 
|  | 187 | +            ))); | 
|  | 188 | +        }; | 
|  | 189 | + | 
|  | 190 | +        let signature = Signature::try_from(signature).map_err(SigError::from_source)?; | 
|  | 191 | + | 
|  | 192 | +        Ok(signature) | 
|  | 193 | +    } | 
|  | 194 | +} | 
|  | 195 | + | 
|  | 196 | +impl<C, D> DigestSigner<D, DerSignature<C>> for EcSigner<'_, C> | 
|  | 197 | +where | 
|  | 198 | +    C: PrimeCurve + CurveArithmetic, | 
|  | 199 | +    C: AssociatedTpmCurve, | 
|  | 200 | +    D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>, | 
|  | 201 | +    D: AssociatedHashingAlgorithm, | 
|  | 202 | +    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>, | 
|  | 203 | +    SignatureSize<C>: ArrayLength<u8>, | 
|  | 204 | +    TpmDigest: From<Output<D>>, | 
|  | 205 | + | 
|  | 206 | +    MaxSize<C>: ArrayLength<u8>, | 
|  | 207 | +    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, | 
|  | 208 | +{ | 
|  | 209 | +    fn try_sign_digest(&self, digest: D) -> Result<DerSignature<C>, SigError> { | 
|  | 210 | +        let signature: Signature<_> = self.try_sign_digest(digest)?; | 
|  | 211 | +        Ok(signature.to_der()) | 
|  | 212 | +    } | 
|  | 213 | +} | 
|  | 214 | + | 
|  | 215 | +impl<C> Signer<Signature<C>> for EcSigner<'_, C> | 
|  | 216 | +where | 
|  | 217 | +    C: PrimeCurve + CurveArithmetic + DigestPrimitive, | 
|  | 218 | +    C: AssociatedTpmCurve, | 
|  | 219 | +    <C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm, | 
|  | 220 | +    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>, | 
|  | 221 | +    SignatureSize<C>: ArrayLength<u8>, | 
|  | 222 | +    TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>, | 
|  | 223 | +{ | 
|  | 224 | +    fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>, SigError> { | 
|  | 225 | +        self.try_sign_digest(C::Digest::new_with_prefix(msg)) | 
|  | 226 | +    } | 
|  | 227 | +} | 
|  | 228 | + | 
|  | 229 | +impl<C> Signer<DerSignature<C>> for EcSigner<'_, C> | 
|  | 230 | +where | 
|  | 231 | +    C: PrimeCurve + CurveArithmetic + DigestPrimitive, | 
|  | 232 | +    C: AssociatedTpmCurve, | 
|  | 233 | +    <C as DigestPrimitive>::Digest: AssociatedHashingAlgorithm, | 
|  | 234 | +    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>, | 
|  | 235 | +    SignatureSize<C>: ArrayLength<u8>, | 
|  | 236 | +    TpmDigest: From<Output<<C as DigestPrimitive>::Digest>>, | 
|  | 237 | + | 
|  | 238 | +    MaxSize<C>: ArrayLength<u8>, | 
|  | 239 | +    <FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>, | 
|  | 240 | +{ | 
|  | 241 | +    fn try_sign(&self, msg: &[u8]) -> Result<DerSignature<C>, SigError> { | 
|  | 242 | +        self.try_sign_digest(C::Digest::new_with_prefix(msg)) | 
|  | 243 | +    } | 
|  | 244 | +} | 
|  | 245 | + | 
|  | 246 | +impl<C> SignatureAlgorithmIdentifier for EcSigner<'_, C> | 
|  | 247 | +where | 
|  | 248 | +    C: PrimeCurve + CurveArithmetic, | 
|  | 249 | +    Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>, | 
|  | 250 | +    SignatureSize<C>: ArrayLength<u8>, | 
|  | 251 | +    Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>, | 
|  | 252 | +{ | 
|  | 253 | +    type Params = AnyRef<'static>; | 
|  | 254 | + | 
|  | 255 | +    const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> = | 
|  | 256 | +        Signature::<C>::ALGORITHM_IDENTIFIER; | 
|  | 257 | +} | 
0 commit comments