blob: ef16210d38685020af358e50819b220601fc9be4 [file] [log] [blame]
// 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<()>;
}