tpm2_crypto/
rsa.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4//! TPM 2.0 RSA cryptographic operations.
5
6use crate::{TpmCryptoError, TpmExternalKey, TpmHash};
7use openssl::{
8    bn::BigNum,
9    hash::MessageDigest,
10    md::Md,
11    pkey::{PKey, Private},
12    pkey_ctx::PkeyCtx,
13    rand::rand_bytes,
14    rsa::{Padding, Rsa},
15};
16use rand::{CryptoRng, RngCore};
17use tpm2_protocol::data::{
18    Tpm2bDigest, Tpm2bEncryptedSecret, Tpm2bPublicKeyRsa, TpmAlgId, TpmaObject, TpmsRsaParms,
19    TpmsSchemeHash, TpmtPublic, TpmtRsaScheme, TpmtSymDefObject, TpmuAsymScheme, TpmuPublicId,
20    TpmuPublicParms,
21};
22
23/// RSA public key parameters.
24#[derive(Debug, Clone)]
25pub struct TpmRsaExternalKey {
26    pub n: Tpm2bPublicKeyRsa,
27    pub e: u32,
28    pub key_bits: u16,
29}
30
31impl TryFrom<&TpmtPublic> for TpmRsaExternalKey {
32    type Error = TpmCryptoError;
33
34    fn try_from(public: &TpmtPublic) -> Result<Self, Self::Error> {
35        if public.object_type != TpmAlgId::Rsa {
36            return Err(TpmCryptoError::InvalidRsaParameters);
37        }
38
39        let params = match &public.parameters {
40            TpmuPublicParms::Rsa(params) => Ok(params),
41            _ => Err(TpmCryptoError::InvalidRsaParameters),
42        }?;
43
44        let n = match &public.unique {
45            TpmuPublicId::Rsa(n) => Ok(*n),
46            _ => Err(TpmCryptoError::InvalidRsaParameters),
47        }?;
48
49        let e = if params.exponent == 0 {
50            65537
51        } else {
52            params.exponent
53        };
54
55        Ok(Self {
56            n,
57            e,
58            key_bits: params.key_bits,
59        })
60    }
61}
62
63impl TryFrom<&PKey<Private>> for TpmRsaExternalKey {
64    type Error = TpmCryptoError;
65
66    fn try_from(pkey: &PKey<Private>) -> Result<Self, Self::Error> {
67        let rsa = pkey
68            .rsa()
69            .map_err(|_| TpmCryptoError::InvalidRsaParameters)?;
70        let n = Tpm2bPublicKeyRsa::try_from(rsa.n().to_vec().as_slice())
71            .map_err(|_| TpmCryptoError::InvalidRsaParameters)?;
72
73        let e_bn = rsa.e();
74        if e_bn.is_negative() || e_bn.num_bits() > 32 {
75            return Err(TpmCryptoError::InvalidRsaParameters);
76        }
77        let e_bytes = e_bn.to_vec();
78        let mut e_buf = [0u8; 4];
79        e_buf[4 - e_bytes.len()..].copy_from_slice(&e_bytes);
80        let e = u32::from_be_bytes(e_buf);
81
82        let key_bits =
83            u16::try_from(rsa.size() * 8).map_err(|_| TpmCryptoError::InvalidRsaParameters)?;
84
85        Ok(Self { n, e, key_bits })
86    }
87}
88
89impl TpmExternalKey for TpmRsaExternalKey {
90    fn from_der(bytes: &[u8]) -> Result<(Self, Vec<u8>), TpmCryptoError> {
91        let pkey =
92            PKey::private_key_from_der(bytes).map_err(|_| TpmCryptoError::OperationFailed)?;
93        let public_key = TpmRsaExternalKey::try_from(&pkey)?;
94        let rsa = pkey
95            .rsa()
96            .map_err(|_| TpmCryptoError::InvalidRsaParameters)?;
97        let sensitive = rsa.p().ok_or(TpmCryptoError::OperationFailed)?.to_vec();
98        Ok((public_key, sensitive))
99    }
100
101    fn to_public(
102        &self,
103        hash_alg: TpmAlgId,
104        object_attributes: TpmaObject,
105        symmetric: TpmtSymDefObject,
106    ) -> TpmtPublic {
107        TpmtPublic {
108            object_type: TpmAlgId::Rsa,
109            name_alg: hash_alg,
110            object_attributes,
111            auth_policy: Tpm2bDigest::default(),
112            parameters: TpmuPublicParms::Rsa(TpmsRsaParms {
113                symmetric,
114                scheme: TpmtRsaScheme {
115                    scheme: TpmAlgId::Oaep,
116                    details: TpmuAsymScheme::Hash(TpmsSchemeHash { hash_alg }),
117                },
118                key_bits: self.key_bits,
119                exponent: if self.e == 65537 { 0 } else { self.e },
120            }),
121            unique: TpmuPublicId::Rsa(self.n),
122        }
123    }
124
125    fn to_seed(
126        &self,
127        name_alg: TpmHash,
128        _rng: &mut (impl RngCore + CryptoRng),
129    ) -> Result<(Vec<u8>, Tpm2bEncryptedSecret), TpmCryptoError> {
130        let seed_size = name_alg.size();
131        let mut seed = vec![0u8; seed_size];
132        rand_bytes(&mut seed).map_err(|_| TpmCryptoError::OperationFailed)?;
133
134        let encrypted_seed_bytes = self.oaep(name_alg, &seed)?;
135
136        let encrypted_seed = Tpm2bEncryptedSecret::try_from(encrypted_seed_bytes.as_slice())
137            .map_err(|_| TpmCryptoError::OutOfMemory)?;
138
139        Ok((seed, encrypted_seed))
140    }
141}
142
143impl TpmRsaExternalKey {
144    /// Performs RSA-OAEP.
145    ///
146    /// # Errors
147    ///
148    /// Returns [`InvalidHash`](crate::Error::InvalidHash)
149    /// when the hash algorithm is not recognized.
150    /// Returns [`OperationFailed`](crate::Error::OperationFailed) when an
151    /// internal cryptographic operation fails.
152    /// Returns [`OutOfMemory`](crate::Error::OutOfMemory) when an allocation
153    /// fails.
154    fn oaep(&self, name_alg: TpmHash, seed: &[u8]) -> Result<Vec<u8>, TpmCryptoError> {
155        let md = Into::<MessageDigest>::into(name_alg);
156
157        let oaep_md = Md::from_nid(md.type_()).ok_or(TpmCryptoError::OperationFailed)?;
158
159        let n = BigNum::from_slice(self.n.as_ref()).map_err(|_| TpmCryptoError::OutOfMemory)?;
160        let e = BigNum::from_u32(self.e).map_err(|_| TpmCryptoError::OutOfMemory)?;
161        let rsa = Rsa::from_public_components(n, e).map_err(|_| TpmCryptoError::OperationFailed)?;
162        let pkey = PKey::from_rsa(rsa).map_err(|_| TpmCryptoError::OperationFailed)?;
163
164        let mut ctx = PkeyCtx::new(&pkey).map_err(|_| TpmCryptoError::OutOfMemory)?;
165
166        ctx.encrypt_init()
167            .map_err(|_| TpmCryptoError::OperationFailed)?;
168        ctx.set_rsa_padding(Padding::PKCS1_OAEP)
169            .map_err(|_| TpmCryptoError::OperationFailed)?;
170        ctx.set_rsa_oaep_md(oaep_md)
171            .map_err(|_| TpmCryptoError::OperationFailed)?;
172        ctx.set_rsa_mgf1_md(oaep_md)
173            .map_err(|_| TpmCryptoError::OperationFailed)?;
174        ctx.set_rsa_oaep_label(b"DUPLICATE\0")
175            .map_err(|_| TpmCryptoError::OperationFailed)?;
176
177        let mut encrypted_seed = vec![0; pkey.size()];
178        let len = ctx
179            .encrypt(seed, Some(encrypted_seed.as_mut_slice()))
180            .map_err(|_| TpmCryptoError::OperationFailed)?;
181
182        encrypted_seed.truncate(len);
183        Ok(encrypted_seed)
184    }
185}