tpm2_crypto/
lib.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5//! TPM 2.0 cryptographic for TPM 2.0 interactions.
6
7#![deny(clippy::all)]
8#![deny(clippy::pedantic)]
9
10mod ecc;
11mod error;
12mod hash;
13mod rsa;
14mod template;
15
16use openssl::{
17    bn::BigNumContext,
18    ec::{EcGroupRef, EcPointRef, PointConversionForm},
19};
20use rand::{CryptoRng, RngCore};
21use tpm2_protocol::{
22    constant::MAX_DIGEST_SIZE,
23    data::{
24        Tpm2bEccParameter, Tpm2bEncryptedSecret, Tpm2bName, TpmAlgId, TpmaObject, TpmtPublic,
25        TpmtSymDefObject,
26    },
27    TpmMarshal, TpmSized, TpmWriter,
28};
29
30pub use ecc::*;
31pub use error::*;
32pub use hash::*;
33pub use rsa::*;
34pub use template::*;
35
36const UNCOMPRESSED_POINT_TAG: u8 = 0x04;
37
38/// Trait for cryptographic public keys.
39pub trait TpmExternalKey
40where
41    Self: Sized,
42{
43    /// Parses a DER-encoded private key.
44    ///
45    /// Returns the public key structure and the sensitive private component.
46    ///
47    /// # Errors
48    ///
49    /// Returns [`OperationFailed`](crate::Error::OperationFailed)
50    /// when the DER parsing or key extraction fails.
51    /// Returns [`InvalidRsaParameters`](crate::Error::InvalidRsaParameters)
52    /// when the key is not a valid RSA key.
53    /// Returns [`InvalidEccParameters`](crate::Error::InvalidEccParameters)
54    /// when the key is not a valid ECC key.
55    fn from_der(bytes: &[u8]) -> Result<(Self, Vec<u8>), TpmCryptoError>;
56
57    /// Converts the public key to a `TpmtPublic` structure.
58    fn to_public(
59        &self,
60        hash_alg: TpmAlgId,
61        object_attributes: TpmaObject,
62        symmetric: TpmtSymDefObject,
63    ) -> TpmtPublic;
64
65    /// Creates a seed and an encrypted seed (inSymSeed) for `TPM2_Import`.
66    ///
67    /// # Errors
68    ///
69    /// Returns [`OperationFailed`](crate::Error::OperationFailed) if the seed
70    /// generation or encryption fails.
71    fn to_seed(
72        &self,
73        name_alg: TpmHash,
74        rng: &mut (impl RngCore + CryptoRng),
75    ) -> Result<(Vec<u8>, Tpm2bEncryptedSecret), TpmCryptoError>;
76}
77
78pub const KDF_LABEL_DUPLICATE: &str = "DUPLICATE";
79pub const KDF_LABEL_INTEGRITY: &str = "INTEGRITY";
80pub const KDF_LABEL_STORAGE: &str = "STORAGE";
81
82/// Calculates the cryptographics name of a transient or persistent TPM object.
83///
84/// # Errors
85///
86/// Returns [`InvalidHash`](crate::Error::InvalidHash) when the hash algorithm
87/// is not recognized.
88/// Returns [`OperationFailed`](crate::Error::OperationFailed) when an internal
89/// cryptographic operation fails.
90/// Returns [`OutOfMemory`](crate::Error::OutOfMemory) when memory allocation
91/// for temporary data fails.
92pub fn tpm_make_name(public: &TpmtPublic) -> Result<Tpm2bName, TpmCryptoError> {
93    let name_alg = TpmHash::from(public.name_alg);
94    let alg_bytes = (public.name_alg as u16).to_be_bytes();
95
96    let len = public.len();
97    let mut public_bytes = vec![0u8; len];
98    let mut writer = TpmWriter::new(&mut public_bytes);
99    public
100        .marshal(&mut writer)
101        .map_err(TpmCryptoError::Marshal)?;
102
103    let digest = name_alg.digest(&[&public_bytes])?;
104    let digest_len = digest.len();
105
106    if digest_len > MAX_DIGEST_SIZE {
107        return Err(TpmCryptoError::OperationFailed);
108    }
109
110    let mut final_buf = [0u8; MAX_DIGEST_SIZE + 2];
111    final_buf[..2].copy_from_slice(&alg_bytes);
112    final_buf[2..2 + digest_len].copy_from_slice(&digest);
113
114    Tpm2bName::try_from(&final_buf[..2 + digest_len]).map_err(|_| TpmCryptoError::OperationFailed)
115}
116
117/// Converts an OpenSSL `EcPoint` to TPM `(x, y)` coordinate buffers.
118///
119/// This function handles the uncompressed point byte representation.
120///
121/// # Errors
122///
123/// Returns [`OperationFailed`](crate::Error::OperationFailed) if the OpenSSL
124/// operation fails or the point format is invalid.
125/// Returns [`OutOfMemory`](crate::Error::OutOfMemory) if allocation fails.
126fn tpm_make_point(
127    point: &EcPointRef,
128    group: &EcGroupRef,
129    ctx: &mut BigNumContext,
130) -> Result<(Tpm2bEccParameter, Tpm2bEccParameter), TpmCryptoError> {
131    let pub_bytes = point
132        .to_bytes(group, PointConversionForm::UNCOMPRESSED, ctx)
133        .map_err(|_| TpmCryptoError::OperationFailed)?;
134
135    if pub_bytes.is_empty() || pub_bytes[0] != UNCOMPRESSED_POINT_TAG {
136        return Err(TpmCryptoError::InvalidEccParameters);
137    }
138
139    let coord_len = (pub_bytes.len() - 1) / 2;
140    let x = Tpm2bEccParameter::try_from(&pub_bytes[1..=coord_len])
141        .map_err(|_| TpmCryptoError::OperationFailed)?;
142    let y = Tpm2bEccParameter::try_from(&pub_bytes[1 + coord_len..])
143        .map_err(|_| TpmCryptoError::OperationFailed)?;
144
145    Ok((x, y))
146}