xref: /aosp_15_r20/external/open-dice/dpe-rs/src/crypto.rs (revision 60b67249c2e226f42f35cc6cfe66c6048e0bae6b)
1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 //! Defines the Crypto trait and related types.
16 
17 use crate::byte_array_wrapper;
18 use crate::constants::*;
19 use crate::error::DpeResult;
20 use crate::memory::{Message, SizedMessage};
21 use zeroize::ZeroizeOnDrop;
22 
23 byte_array_wrapper!(MacKey, HASH_SIZE, "MAC key");
24 byte_array_wrapper!(EncryptionKey, ENCRYPTION_KEY_SIZE, "encryption key");
25 byte_array_wrapper!(DhPublicKey, DH_PUBLIC_KEY_SIZE, "DH public key");
26 byte_array_wrapper!(DhPrivateKey, DH_PRIVATE_KEY_SIZE, "DH private key");
27 byte_array_wrapper!(Hash, HASH_SIZE, "hash");
28 byte_array_wrapper!(
29     SigningPublicKey,
30     SIGNING_PUBLIC_KEY_SIZE,
31     "signing public key"
32 );
33 byte_array_wrapper!(
34     SigningPrivateKey,
35     SIGNING_PRIVATE_KEY_SIZE,
36     "signing private key"
37 );
38 byte_array_wrapper!(
39     SealingPublicKey,
40     SEALING_PUBLIC_KEY_SIZE,
41     "sealing public key"
42 );
43 byte_array_wrapper!(
44     SealingPrivateKey,
45     SEALING_PRIVATE_KEY_SIZE,
46     "sealing private key"
47 );
48 
49 /// A session handshake message.
50 pub type HandshakeMessage = SizedMessage<MAX_HANDSHAKE_MESSAGE_SIZE>;
51 /// A session handshake payload.
52 pub type HandshakePayload = SizedMessage<MAX_HANDSHAKE_PAYLOAD_SIZE>;
53 /// A signature.
54 pub type Signature = SizedMessage<MAX_SIGNATURE_SIZE>;
55 
56 /// A trait for committing previously staged changes.
57 pub trait Commit {
58     /// Commits a previously staged changes. When used with session cipher
59     /// state, the staged changes are typically counter increments that result
60     /// from encrypt or decrypt operations.
commit(&mut self)61     fn commit(&mut self);
62 }
63 
64 /// A trait for maintaining a counter.
65 pub trait Counter {
66     /// Returns the current counter value.
n(&self) -> u6467     fn n(&self) -> u64;
68     /// Sets the counter value to `n`.
set_n(&mut self, n: u64)69     fn set_n(&mut self, n: u64);
70 }
71 
72 /// Provides cryptographic operations for encrypted sessions.
73 pub trait SessionCrypto {
74     /// A type to represent session cipher states. These are owned by and opaque
75     /// to the caller in `new_session_handshake` and `derive_session_handshake`.
76     type SessionCipherState: Commit + Counter;
77 
78     /// Performs a session responder handshake for a new session.
79     ///
80     /// # Parameters
81     ///
82     /// * `static_dh_key`: The DPE session identity, which the client is
83     /// expected to already know.
84     /// * `initiator_handshake`: The handshake message received from the client.
85     /// * `payload`: The payload to include in the `responder_handshake`.
86     /// * `responder_handshake`: Receives the handshake message to be sent back
87     /// to the client.
88     /// * `decrypt_cipher_state`: Receives cipher state for decrypting incoming
89     /// session messages. This is intended to be passed to
90     /// [`SessionCrypto::session_decrypt`].
91     /// * `encrypt_cipher_state`: Receives cipher state for encrypting outgoing
92     /// session messages. This is intended to be passed to
93     /// [`SessionCrypto::session_encrypt`].
94     /// * `psk_seed`: Receives a PSK seed that can be used to construct a PSK to
95     /// be used when deriving a session (see
96     /// [`SessionCrypto::derive_session_handshake`]).
97     ///
98     /// # Errors
99     ///
100     /// This method allows implementers to return an error but it is expected to
101     /// be infallible.
102     #[allow(clippy::too_many_arguments)]
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<()>103     fn new_session_handshake(
104         static_dh_key: &DhPrivateKey,
105         initiator_handshake: &HandshakeMessage,
106         payload: &HandshakePayload,
107         responder_handshake: &mut HandshakeMessage,
108         decrypt_cipher_state: &mut Self::SessionCipherState,
109         encrypt_cipher_state: &mut Self::SessionCipherState,
110         psk_seed: &mut Hash,
111     ) -> DpeResult<()>;
112 
113     /// Performs a session responder handshake for a derived session. In
114     /// contrast to a new session handshake, a derived session does not use a
115     /// static key, but a pre-shared key (PSK) derived from an existing session.
116     ///
117     /// # Parameters
118     ///
119     /// * `psk`: A PSK derived from an existing session.
120     /// * `initiator_handshake`: The handshake message received from the client.
121     /// * `payload`: The payload to include in the `responder_handshake`.
122     /// * `responder_handshake`: Receives the handshake message to be sent back
123     /// to the client.
124     /// * `decrypt_cipher_state`: Receives cipher state for decrypting incoming
125     /// session messages. This is intended to be passed to
126     /// [`SessionCrypto::session_decrypt`].
127     /// * `encrypt_cipher_state`: Receives cipher state for encrypting outgoing
128     /// session messages. This is intended to be passed to
129     /// [`SessionCrypto::session_encrypt`].
130     /// * `psk_seed`: Receives a PSK seed that can be used to construct a PSK to
131     /// be used when deriving another session.
132     ///
133     /// # Errors
134     ///
135     /// This method allows implementers to return an error but it is expected to
136     /// be infallible.
137     #[allow(clippy::too_many_arguments)]
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<()>138     fn derive_session_handshake(
139         psk: &Hash,
140         initiator_handshake: &HandshakeMessage,
141         payload: &HandshakePayload,
142         responder_handshake: &mut HandshakeMessage,
143         decrypt_cipher_state: &mut Self::SessionCipherState,
144         encrypt_cipher_state: &mut Self::SessionCipherState,
145         psk_seed: &mut Hash,
146     ) -> DpeResult<()>;
147 
148     /// Derives a PSK from session state: `psk_seed`, `decrypt_cipher_state`,
149     /// and `encrypt_cipher_state`. The returned PSK is appropriate as an
150     /// argument to [`derive_session_handshake`].
151     ///
152     /// # Errors
153     ///
154     /// This method allows implementers to return an error but it is expected to
155     /// be infallible.
156     ///
157     /// [`derive_session_handshake`]: #method.derive_session_handshake
derive_psk_from_session( psk_seed: &Hash, decrypt_cipher_state: &Self::SessionCipherState, encrypt_cipher_state: &Self::SessionCipherState, ) -> DpeResult<Hash>158     fn derive_psk_from_session(
159         psk_seed: &Hash,
160         decrypt_cipher_state: &Self::SessionCipherState,
161         encrypt_cipher_state: &Self::SessionCipherState,
162     ) -> DpeResult<Hash>;
163 
164     /// Encrypts an outgoing session message with the given `cipher_state`. The
165     /// `in_place_buffer` both provides the plaintext message and receives the
166     /// corresponding ciphertext.
167     ///
168     /// # Errors
169     ///
170     /// This method fails with an OutOfMemory error if the encryption overhead
171     /// does not fit in the buffer.
session_encrypt( cipher_state: &mut Self::SessionCipherState, in_place_buffer: &mut Message, ) -> DpeResult<()>172     fn session_encrypt(
173         cipher_state: &mut Self::SessionCipherState,
174         in_place_buffer: &mut Message,
175     ) -> DpeResult<()>;
176 
177     /// Decrypts an incoming session message with the given `cipher_state`. The
178     /// `in_place_buffer` both provides the ciphertext message and receives the
179     /// corresponding plaintext.
180     ///
181     /// # Errors
182     ///
183     /// This method fails with an InvalidArgument error if the ciphertext cannot
184     /// be decrypted (e.g. if tag authentication fails).
session_decrypt( cipher_state: &mut Self::SessionCipherState, in_place_buffer: &mut Message, ) -> DpeResult<()>185     fn session_decrypt(
186         cipher_state: &mut Self::SessionCipherState,
187         in_place_buffer: &mut Message,
188     ) -> DpeResult<()>;
189 }
190 
191 /// Provides cryptographic operations. These operations are specifically for DPE
192 /// concepts, defined by a DPE profile, and to be invoked by a DPE instance.
193 pub trait Crypto {
194     /// An associated [`SessionCrypto`] type.
195     type S: SessionCrypto;
196 
197     /// Returns a hash of `input`.
198     ///
199     /// # Errors
200     ///
201     /// This method is infallible.
hash(input: &[u8]) -> Hash202     fn hash(input: &[u8]) -> Hash;
203 
204     /// Returns a hash over all items in `iter`, in order.
205     ///
206     /// # Errors
207     ///
208     /// This method is infallible.
hash_iter<'a>(iter: impl Iterator<Item = &'a [u8]>) -> Hash209     fn hash_iter<'a>(iter: impl Iterator<Item = &'a [u8]>) -> Hash;
210 
211     /// Runs a key derivation function (KDF) to derive a key the length of the
212     /// `derived_key` buffer. The inputs are interpreted as documented by the
213     /// [HKDF](<https://datatracker.ietf.org/doc/html/rfc5869>) scheme. The
214     /// implementation doesn't need to be HKDF specifically but needs to work
215     /// with HKDF-style inputs.
216     ///
217     /// # Parameters
218     ///
219     /// * `kdf_ikm`: input keying material
220     /// * `kdf_info`: HKDF-style info (optional)
221     /// * `kdf_salt`: HKDF-style salt (optional)
222     /// * `derived_key`: Receives the derived key
223     ///
224     /// # Errors
225     ///
226     /// Fails with an `InternalError` if `derived_key` is too large.
kdf( kdf_ikm: &[u8], kdf_info: &[u8], kdf_salt: &[u8], derived_key: &mut [u8], ) -> DpeResult<()>227     fn kdf(
228         kdf_ikm: &[u8],
229         kdf_info: &[u8],
230         kdf_salt: &[u8],
231         derived_key: &mut [u8],
232     ) -> DpeResult<()>;
233 
234     /// Derives an asymmetric key pair for signing from a given `seed`.
235     ///
236     /// # Errors
237     ///
238     /// This method allows implementers to return an error but it is expected to
239     /// be infallible.
signing_keypair_from_seed( seed: &Hash, ) -> DpeResult<(SigningPublicKey, SigningPrivateKey)>240     fn signing_keypair_from_seed(
241         seed: &Hash,
242     ) -> DpeResult<(SigningPublicKey, SigningPrivateKey)>;
243 
244     /// Derives an asymmetric key pair for sealing from a given `seed`.
245     ///
246     /// # Errors
247     ///
248     /// This method allows implementers to return an error but it is expected to
249     /// be infallible.
sealing_keypair_from_seed( seed: &Hash, ) -> DpeResult<(SealingPublicKey, SealingPrivateKey)>250     fn sealing_keypair_from_seed(
251         seed: &Hash,
252     ) -> DpeResult<(SealingPublicKey, SealingPrivateKey)>;
253 
254     /// Computes a MAC over `data` using the given `key`.
255     ///
256     /// # Errors
257     ///
258     /// This method allows implementers to return an error but it is expected to
259     /// be infallible.
mac(key: &MacKey, data: &[u8]) -> DpeResult<Hash>260     fn mac(key: &MacKey, data: &[u8]) -> DpeResult<Hash>;
261 
262     /// Generates a signature over `tbs` using the given `key`.
263     ///
264     /// # Errors
265     ///
266     /// This method allows implementers to return an error but it is expected to
267     /// be infallible.
sign(key: &SigningPrivateKey, tbs: &[u8]) -> DpeResult<Signature>268     fn sign(key: &SigningPrivateKey, tbs: &[u8]) -> DpeResult<Signature>;
269 
270     /// Encrypts data using the given `key` in a way that it can be decrypted by
271     /// the `unseal` method with the same `key`. The `in_place_buffer` both
272     /// provides the plaintext input and receives the ciphertext output.
273     ///
274     /// # Errors
275     ///
276     /// Fails with OutOfMemory if the ciphertext, including overhead, does not
277     /// fit in the buffer.
seal( key: &EncryptionKey, in_place_buffer: &mut Message, ) -> DpeResult<()>278     fn seal(
279         key: &EncryptionKey,
280         in_place_buffer: &mut Message,
281     ) -> DpeResult<()>;
282 
283     /// Decrypts and authenticates data previously generated by the `seal`
284     /// method using the given 'key'. The `in_place_buffer` both provides the
285     /// ciphertext input and receives the plaintext output.
286     ///
287     /// # Errors
288     ///
289     /// Fails with InvalidArgument if authenticated decryption fails.
unseal( key: &EncryptionKey, in_place_buffer: &mut Message, ) -> DpeResult<()>290     fn unseal(
291         key: &EncryptionKey,
292         in_place_buffer: &mut Message,
293     ) -> DpeResult<()>;
294 
295     /// Encrypts data using an asymmetric scheme and the given `public_key` in
296     /// a way that it can be decrypted by the `unseal_asymmetric` method given
297     /// the corresponding private key. While this method is useful for testing,
298     /// a DPE does not use this during normal operation. The `in_place_buffer`
299     /// both provides the plaintext input and receives the ciphertext output.
300     ///
301     /// # Errors
302     ///
303     /// Fails with OutOfMemory if the ciphertext, including overhead, does not
304     /// fit in the buffer.
seal_asymmetric( public_key: &SealingPublicKey, in_place_buffer: &mut Message, ) -> DpeResult<()>305     fn seal_asymmetric(
306         public_key: &SealingPublicKey,
307         in_place_buffer: &mut Message,
308     ) -> DpeResult<()>;
309 
310     /// Decrypts data using an asymmetric scheme and the give `key`. The
311     /// `in_place_buffer` both provides the ciphertext input and receives the
312     /// plaintext output.
313     ///
314     /// # Errors
315     ///
316     /// Fails with InvalidArgument if the ciphertext cannot be decrypted.
unseal_asymmetric( key: &SealingPrivateKey, in_place_buffer: &mut Message, ) -> DpeResult<()>317     fn unseal_asymmetric(
318         key: &SealingPrivateKey,
319         in_place_buffer: &mut Message,
320     ) -> DpeResult<()>;
321 }
322