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