|  | // Copyright 2024 Google LLC | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); you may not | 
|  | // use this file except in compliance with the License. You may obtain a copy of | 
|  | // the License at | 
|  | // | 
|  | //     https://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
|  | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
|  | // License for the specific language governing permissions and limitations under | 
|  | // the License. | 
|  |  | 
|  | //! Defines the Crypto trait and related types. | 
|  |  | 
|  | use crate::byte_array_wrapper; | 
|  | use crate::constants::*; | 
|  | use crate::error::DpeResult; | 
|  | use crate::memory::{Message, SizedMessage}; | 
|  | use zeroize::ZeroizeOnDrop; | 
|  |  | 
|  | byte_array_wrapper!(MacKey, HASH_SIZE, "MAC key"); | 
|  | byte_array_wrapper!(EncryptionKey, ENCRYPTION_KEY_SIZE, "encryption key"); | 
|  | byte_array_wrapper!(DhPublicKey, DH_PUBLIC_KEY_SIZE, "DH public key"); | 
|  | byte_array_wrapper!(DhPrivateKey, DH_PRIVATE_KEY_SIZE, "DH private key"); | 
|  | byte_array_wrapper!(Hash, HASH_SIZE, "hash"); | 
|  | byte_array_wrapper!( | 
|  | SigningPublicKey, | 
|  | SIGNING_PUBLIC_KEY_SIZE, | 
|  | "signing public key" | 
|  | ); | 
|  | byte_array_wrapper!( | 
|  | SigningPrivateKey, | 
|  | SIGNING_PRIVATE_KEY_SIZE, | 
|  | "signing private key" | 
|  | ); | 
|  | byte_array_wrapper!( | 
|  | SealingPublicKey, | 
|  | SEALING_PUBLIC_KEY_SIZE, | 
|  | "sealing public key" | 
|  | ); | 
|  | byte_array_wrapper!( | 
|  | SealingPrivateKey, | 
|  | SEALING_PRIVATE_KEY_SIZE, | 
|  | "sealing private key" | 
|  | ); | 
|  |  | 
|  | /// A session handshake message. | 
|  | pub type HandshakeMessage = SizedMessage<MAX_HANDSHAKE_MESSAGE_SIZE>; | 
|  | /// A session handshake payload. | 
|  | pub type HandshakePayload = SizedMessage<MAX_HANDSHAKE_PAYLOAD_SIZE>; | 
|  | /// A signature. | 
|  | pub type Signature = SizedMessage<MAX_SIGNATURE_SIZE>; | 
|  |  | 
|  | /// A trait for committing previously staged changes. | 
|  | pub trait Commit { | 
|  | /// Commits a previously staged changes. When used with session cipher | 
|  | /// state, the staged changes are typically counter increments that result | 
|  | /// from encrypt or decrypt operations. | 
|  | fn commit(&mut self); | 
|  | } | 
|  |  | 
|  | /// A trait for maintaining a counter. | 
|  | pub trait Counter { | 
|  | /// Returns the current counter value. | 
|  | fn n(&self) -> u64; | 
|  | /// Sets the counter value to `n`. | 
|  | fn set_n(&mut self, n: u64); | 
|  | } | 
|  |  | 
|  | /// Provides cryptographic operations for encrypted sessions. | 
|  | pub trait SessionCrypto { | 
|  | /// A type to represent session cipher states. These are owned by and opaque | 
|  | /// to the caller in `new_session_handshake` and `derive_session_handshake`. | 
|  | type SessionCipherState: Commit + Counter; | 
|  |  | 
|  | /// Performs a session responder handshake for a new session. | 
|  | /// | 
|  | /// # Parameters | 
|  | /// | 
|  | /// * `static_dh_key`: The DPE session identity, which the client is | 
|  | /// expected to already know. | 
|  | /// * `initiator_handshake`: The handshake message received from the client. | 
|  | /// * `payload`: The payload to include in the `responder_handshake`. | 
|  | /// * `responder_handshake`: Receives the handshake message to be sent back | 
|  | /// to the client. | 
|  | /// * `decrypt_cipher_state`: Receives cipher state for decrypting incoming | 
|  | /// session messages. This is intended to be passed to | 
|  | /// [`SessionCrypto::session_decrypt`]. | 
|  | /// * `encrypt_cipher_state`: Receives cipher state for encrypting outgoing | 
|  | /// session messages. This is intended to be passed to | 
|  | /// [`SessionCrypto::session_encrypt`]. | 
|  | /// * `psk_seed`: Receives a PSK seed that can be used to construct a PSK to | 
|  | /// be used when deriving a session (see | 
|  | /// [`SessionCrypto::derive_session_handshake`]). | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method allows implementers to return an error but it is expected to | 
|  | /// be infallible. | 
|  | #[allow(clippy::too_many_arguments)] | 
|  | fn new_session_handshake( | 
|  | static_dh_key: &DhPrivateKey, | 
|  | initiator_handshake: &HandshakeMessage, | 
|  | payload: &HandshakePayload, | 
|  | responder_handshake: &mut HandshakeMessage, | 
|  | decrypt_cipher_state: &mut Self::SessionCipherState, | 
|  | encrypt_cipher_state: &mut Self::SessionCipherState, | 
|  | psk_seed: &mut Hash, | 
|  | ) -> DpeResult<()>; | 
|  |  | 
|  | /// Performs a session responder handshake for a derived session. In | 
|  | /// contrast to a new session handshake, a derived session does not use a | 
|  | /// static key, but a pre-shared key (PSK) derived from an existing session. | 
|  | /// | 
|  | /// # Parameters | 
|  | /// | 
|  | /// * `psk`: A PSK derived from an existing session. | 
|  | /// * `initiator_handshake`: The handshake message received from the client. | 
|  | /// * `payload`: The payload to include in the `responder_handshake`. | 
|  | /// * `responder_handshake`: Receives the handshake message to be sent back | 
|  | /// to the client. | 
|  | /// * `decrypt_cipher_state`: Receives cipher state for decrypting incoming | 
|  | /// session messages. This is intended to be passed to | 
|  | /// [`SessionCrypto::session_decrypt`]. | 
|  | /// * `encrypt_cipher_state`: Receives cipher state for encrypting outgoing | 
|  | /// session messages. This is intended to be passed to | 
|  | /// [`SessionCrypto::session_encrypt`]. | 
|  | /// * `psk_seed`: Receives a PSK seed that can be used to construct a PSK to | 
|  | /// be used when deriving another session. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method allows implementers to return an error but it is expected to | 
|  | /// be infallible. | 
|  | #[allow(clippy::too_many_arguments)] | 
|  | fn derive_session_handshake( | 
|  | psk: &Hash, | 
|  | initiator_handshake: &HandshakeMessage, | 
|  | payload: &HandshakePayload, | 
|  | responder_handshake: &mut HandshakeMessage, | 
|  | decrypt_cipher_state: &mut Self::SessionCipherState, | 
|  | encrypt_cipher_state: &mut Self::SessionCipherState, | 
|  | psk_seed: &mut Hash, | 
|  | ) -> DpeResult<()>; | 
|  |  | 
|  | /// Derives a PSK from session state: `psk_seed`, `decrypt_cipher_state`, | 
|  | /// and `encrypt_cipher_state`. The returned PSK is appropriate as an | 
|  | /// argument to [`derive_session_handshake`]. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method allows implementers to return an error but it is expected to | 
|  | /// be infallible. | 
|  | /// | 
|  | /// [`derive_session_handshake`]: #method.derive_session_handshake | 
|  | fn derive_psk_from_session( | 
|  | psk_seed: &Hash, | 
|  | decrypt_cipher_state: &Self::SessionCipherState, | 
|  | encrypt_cipher_state: &Self::SessionCipherState, | 
|  | ) -> DpeResult<Hash>; | 
|  |  | 
|  | /// Encrypts an outgoing session message with the given `cipher_state`. The | 
|  | /// `in_place_buffer` both provides the plaintext message and receives the | 
|  | /// corresponding ciphertext. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method fails with an OutOfMemory error if the encryption overhead | 
|  | /// does not fit in the buffer. | 
|  | fn session_encrypt( | 
|  | cipher_state: &mut Self::SessionCipherState, | 
|  | in_place_buffer: &mut Message, | 
|  | ) -> DpeResult<()>; | 
|  |  | 
|  | /// Decrypts an incoming session message with the given `cipher_state`. The | 
|  | /// `in_place_buffer` both provides the ciphertext message and receives the | 
|  | /// corresponding plaintext. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method fails with an InvalidArgument error if the ciphertext cannot | 
|  | /// be decrypted (e.g. if tag authentication fails). | 
|  | fn session_decrypt( | 
|  | cipher_state: &mut Self::SessionCipherState, | 
|  | in_place_buffer: &mut Message, | 
|  | ) -> DpeResult<()>; | 
|  | } | 
|  |  | 
|  | /// Provides cryptographic operations. These operations are specifically for DPE | 
|  | /// concepts, defined by a DPE profile, and to be invoked by a DPE instance. | 
|  | pub trait Crypto { | 
|  | /// An associated [`SessionCrypto`] type. | 
|  | type S: SessionCrypto; | 
|  |  | 
|  | /// Returns a hash of `input`. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method is infallible. | 
|  | fn hash(input: &[u8]) -> Hash; | 
|  |  | 
|  | /// Returns a hash over all items in `iter`, in order. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method is infallible. | 
|  | fn hash_iter<'a>(iter: impl Iterator<Item = &'a [u8]>) -> Hash; | 
|  |  | 
|  | /// Runs a key derivation function (KDF) to derive a key the length of the | 
|  | /// `derived_key` buffer. The inputs are interpreted as documented by the | 
|  | /// [HKDF](<https://datatracker.ietf.org/doc/html/rfc5869>) scheme. The | 
|  | /// implementation doesn't need to be HKDF specifically but needs to work | 
|  | /// with HKDF-style inputs. | 
|  | /// | 
|  | /// # Parameters | 
|  | /// | 
|  | /// * `kdf_ikm`: input keying material | 
|  | /// * `kdf_info`: HKDF-style info (optional) | 
|  | /// * `kdf_salt`: HKDF-style salt (optional) | 
|  | /// * `derived_key`: Receives the derived key | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// Fails with an `InternalError` if `derived_key` is too large. | 
|  | fn kdf( | 
|  | kdf_ikm: &[u8], | 
|  | kdf_info: &[u8], | 
|  | kdf_salt: &[u8], | 
|  | derived_key: &mut [u8], | 
|  | ) -> DpeResult<()>; | 
|  |  | 
|  | /// Derives an asymmetric key pair for signing from a given `seed`. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method allows implementers to return an error but it is expected to | 
|  | /// be infallible. | 
|  | fn signing_keypair_from_seed( | 
|  | seed: &Hash, | 
|  | ) -> DpeResult<(SigningPublicKey, SigningPrivateKey)>; | 
|  |  | 
|  | /// Derives an asymmetric key pair for sealing from a given `seed`. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method allows implementers to return an error but it is expected to | 
|  | /// be infallible. | 
|  | fn sealing_keypair_from_seed( | 
|  | seed: &Hash, | 
|  | ) -> DpeResult<(SealingPublicKey, SealingPrivateKey)>; | 
|  |  | 
|  | /// Computes a MAC over `data` using the given `key`. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method allows implementers to return an error but it is expected to | 
|  | /// be infallible. | 
|  | fn mac(key: &MacKey, data: &[u8]) -> DpeResult<Hash>; | 
|  |  | 
|  | /// Generates a signature over `tbs` using the given `key`. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// This method allows implementers to return an error but it is expected to | 
|  | /// be infallible. | 
|  | fn sign(key: &SigningPrivateKey, tbs: &[u8]) -> DpeResult<Signature>; | 
|  |  | 
|  | /// Encrypts data using the given `key` in a way that it can be decrypted by | 
|  | /// the `unseal` method with the same `key`. The `in_place_buffer` both | 
|  | /// provides the plaintext input and receives the ciphertext output. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// Fails with OutOfMemory if the ciphertext, including overhead, does not | 
|  | /// fit in the buffer. | 
|  | fn seal( | 
|  | key: &EncryptionKey, | 
|  | in_place_buffer: &mut Message, | 
|  | ) -> DpeResult<()>; | 
|  |  | 
|  | /// Decrypts and authenticates data previously generated by the `seal` | 
|  | /// method using the given 'key'. The `in_place_buffer` both provides the | 
|  | /// ciphertext input and receives the plaintext output. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// Fails with InvalidArgument if authenticated decryption fails. | 
|  | fn unseal( | 
|  | key: &EncryptionKey, | 
|  | in_place_buffer: &mut Message, | 
|  | ) -> DpeResult<()>; | 
|  |  | 
|  | /// Encrypts data using an asymmetric scheme and the given `public_key` in | 
|  | /// a way that it can be decrypted by the `unseal_asymmetric` method given | 
|  | /// the corresponding private key. While this method is useful for testing, | 
|  | /// a DPE does not use this during normal operation. The `in_place_buffer` | 
|  | /// both provides the plaintext input and receives the ciphertext output. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// Fails with OutOfMemory if the ciphertext, including overhead, does not | 
|  | /// fit in the buffer. | 
|  | fn seal_asymmetric( | 
|  | public_key: &SealingPublicKey, | 
|  | in_place_buffer: &mut Message, | 
|  | ) -> DpeResult<()>; | 
|  |  | 
|  | /// Decrypts data using an asymmetric scheme and the give `key`. The | 
|  | /// `in_place_buffer` both provides the ciphertext input and receives the | 
|  | /// plaintext output. | 
|  | /// | 
|  | /// # Errors | 
|  | /// | 
|  | /// Fails with InvalidArgument if the ciphertext cannot be decrypted. | 
|  | fn unseal_asymmetric( | 
|  | key: &SealingPrivateKey, | 
|  | in_place_buffer: &mut Message, | 
|  | ) -> DpeResult<()>; | 
|  | } |