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::{Error, Hash, PublicKey};
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 RsaPublicKey {
26    pub n: Tpm2bPublicKeyRsa,
27    pub e: u32,
28    pub key_bits: u16,
29}
30
31impl TryFrom<&TpmtPublic> for RsaPublicKey {
32    type Error = Error;
33
34    fn try_from(public: &TpmtPublic) -> Result<Self, Self::Error> {
35        if public.object_type != TpmAlgId::Rsa {
36            return Err(Error::InvalidRsaParameters);
37        }
38
39        let params = match &public.parameters {
40            TpmuPublicParms::Rsa(params) => Ok(params),
41            _ => Err(Error::InvalidRsaParameters),
42        }?;
43
44        let n = match &public.unique {
45            TpmuPublicId::Rsa(n) => Ok(*n),
46            _ => Err(Error::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 RsaPublicKey {
64    type Error = Error;
65
66    fn try_from(pkey: &PKey<Private>) -> Result<Self, Self::Error> {
67        let rsa = pkey.rsa().map_err(|_| Error::InvalidRsaParameters)?;
68        let n = Tpm2bPublicKeyRsa::try_from(rsa.n().to_vec().as_slice())
69            .map_err(|_| Error::InvalidRsaParameters)?;
70
71        let e_bn = rsa.e();
72        if e_bn.is_negative() || e_bn.num_bits() > 32 {
73            return Err(Error::InvalidRsaParameters);
74        }
75        let e_bytes = e_bn.to_vec();
76        let mut e_buf = [0u8; 4];
77        e_buf[4 - e_bytes.len()..].copy_from_slice(&e_bytes);
78        let e = u32::from_be_bytes(e_buf);
79
80        let key_bits = u16::try_from(rsa.size() * 8).map_err(|_| Error::InvalidRsaParameters)?;
81
82        Ok(Self { n, e, key_bits })
83    }
84}
85
86impl PublicKey for RsaPublicKey {
87    fn from_der(bytes: &[u8]) -> Result<(Self, Vec<u8>), Error> {
88        let pkey = PKey::private_key_from_der(bytes).map_err(|_| Error::OperationFailed)?;
89        let public_key = RsaPublicKey::try_from(&pkey)?;
90        let rsa = pkey.rsa().map_err(|_| Error::InvalidRsaParameters)?;
91        let sensitive = rsa.p().ok_or(Error::OperationFailed)?.to_vec();
92        Ok((public_key, sensitive))
93    }
94
95    fn to_public(&self, hash_alg: TpmAlgId, symmetric: TpmtSymDefObject) -> TpmtPublic {
96        TpmtPublic {
97            object_type: TpmAlgId::Rsa,
98            name_alg: hash_alg,
99            object_attributes: TpmaObject::USER_WITH_AUTH | TpmaObject::DECRYPT,
100            auth_policy: Tpm2bDigest::default(),
101            parameters: TpmuPublicParms::Rsa(TpmsRsaParms {
102                symmetric,
103                scheme: TpmtRsaScheme {
104                    scheme: TpmAlgId::Oaep,
105                    details: TpmuAsymScheme::Hash(TpmsSchemeHash { hash_alg }),
106                },
107                key_bits: self.key_bits,
108                exponent: 0,
109            }),
110            unique: TpmuPublicId::Rsa(self.n),
111        }
112    }
113
114    fn to_seed(
115        &self,
116        name_alg: Hash,
117        _rng: &mut (impl RngCore + CryptoRng),
118    ) -> Result<(Vec<u8>, Tpm2bEncryptedSecret), Error> {
119        let seed_size = name_alg.size();
120        let mut seed = vec![0u8; seed_size];
121        rand_bytes(&mut seed).map_err(|_| Error::OperationFailed)?;
122
123        let encrypted_seed_bytes = self.oaep(name_alg, &seed)?;
124
125        let encrypted_seed = Tpm2bEncryptedSecret::try_from(encrypted_seed_bytes.as_slice())
126            .map_err(|_| Error::OutOfMemory)?;
127
128        Ok((seed, encrypted_seed))
129    }
130}
131
132impl RsaPublicKey {
133    /// Performs RSA-OAEP.
134    ///
135    /// # Errors
136    ///
137    /// Returns [`InvalidHash`](crate::Error::InvalidHash)
138    /// when the hash algorithm is not recognized.
139    /// Returns [`OperationFailed`](crate::Error::OperationFailed) when an
140    /// internal cryptographic operation fails.
141    /// Returns [`OutOfMemory`](crate::Error::OutOfMemory) when an allocation
142    /// fails.
143    fn oaep(&self, name_alg: Hash, seed: &[u8]) -> Result<Vec<u8>, Error> {
144        let md = Into::<MessageDigest>::into(name_alg);
145
146        let oaep_md = Md::from_nid(md.type_()).ok_or(Error::OperationFailed)?;
147
148        let n = BigNum::from_slice(self.n.as_ref()).map_err(|_| Error::OutOfMemory)?;
149        let e = BigNum::from_u32(self.e).map_err(|_| Error::OutOfMemory)?;
150        let rsa = Rsa::from_public_components(n, e).map_err(|_| Error::OperationFailed)?;
151        let pkey = PKey::from_rsa(rsa).map_err(|_| Error::OperationFailed)?;
152
153        let mut ctx = PkeyCtx::new(&pkey).map_err(|_| Error::OutOfMemory)?;
154
155        ctx.encrypt_init().map_err(|_| Error::OperationFailed)?;
156        ctx.set_rsa_padding(Padding::PKCS1_OAEP)
157            .map_err(|_| Error::OperationFailed)?;
158        ctx.set_rsa_oaep_md(oaep_md)
159            .map_err(|_| Error::OperationFailed)?;
160        ctx.set_rsa_mgf1_md(oaep_md)
161            .map_err(|_| Error::OperationFailed)?;
162        ctx.set_rsa_oaep_label(b"DUPLICATE\0")
163            .map_err(|_| Error::OperationFailed)?;
164
165        let mut encrypted_seed = vec![0; pkey.size()];
166        let len = ctx
167            .encrypt(seed, Some(encrypted_seed.as_mut_slice()))
168            .map_err(|_| Error::OperationFailed)?;
169
170        encrypted_seed.truncate(len);
171        Ok(encrypted_seed)
172    }
173}