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