From d350377b14a758eb5539c2d65a698b4271b0aa1f Mon Sep 17 00:00:00 2001 From: James Cape Date: Fri, 2 Sep 2022 10:04:48 -0700 Subject: [PATCH] Messages for noise protocol exchanges with explicit nonces (#2461) * attest::NonceMessage, EnclaveNonceMessage, add handling to attest-ake * Rustfmt fixes --- attest/ake/src/event.rs | 34 +++++++++++++++++++++++++++++++ attest/ake/src/shared.rs | 38 ++++++++++++++++++++++++++++++++++- attest/ake/src/state.rs | 23 +++++++++++++++++++++ attest/api/proto/attest.proto | 17 ++++++++++++++++ attest/api/src/conversions.rs | 30 +++++++++++++++++++++++---- attest/enclave-api/src/lib.rs | 13 ++++++++++++ 6 files changed, 150 insertions(+), 5 deletions(-) diff --git a/attest/ake/src/event.rs b/attest/ake/src/event.rs index 39987b04c8..c38277522b 100644 --- a/attest/ake/src/event.rs +++ b/attest/ake/src/event.rs @@ -351,3 +351,37 @@ impl MealyInput for Ciphertext<'_, '_> {} /// Our outputs may be simple vectors for the proto-inside-grpc use case. impl MealyOutput for Vec {} + +/// A type similar to [`aead::Payload`] used to distinguish writer inputs from +/// outputs when there's an explicit nonce. +pub struct NoncePlaintext<'aad, 'msg> { + pub aad: &'aad [u8], + pub msg: &'msg [u8], + pub nonce: u64, +} + +impl<'aad, 'msg> NoncePlaintext<'aad, 'msg> { + pub fn new(aad: &'aad [u8], msg: &'msg [u8], nonce: u64) -> Self { + Self { aad, msg, nonce } + } +} + +/// Plaintext may be provided to an FST for encryption into a vector +impl MealyInput for NoncePlaintext<'_, '_> {} + +/// A type similar to [`aead::Payload`] used to distinguish reader inputs from +/// outputs when there's an explicit nonce. +pub struct NonceCiphertext<'aad, 'msg> { + pub aad: &'aad [u8], + pub msg: &'msg [u8], + pub nonce: u64, +} + +impl<'aad, 'msg> NonceCiphertext<'aad, 'msg> { + pub fn new(aad: &'aad [u8], msg: &'msg [u8], nonce: u64) -> Self { + Self { aad, msg, nonce } + } +} + +/// Plaintext may be provided to an FST for encryption into a vector +impl MealyInput for NonceCiphertext<'_, '_> {} diff --git a/attest/ake/src/shared.rs b/attest/ake/src/shared.rs index 37676bae6d..2c8e19b36a 100644 --- a/attest/ake/src/shared.rs +++ b/attest/ake/src/shared.rs @@ -3,7 +3,7 @@ //! Common transitions between initiator and responder. use crate::{ - event::{Ciphertext, Plaintext}, + event::{Ciphertext, NonceCiphertext, NoncePlaintext, Plaintext}, mealy::Transition, state::Ready, }; @@ -46,3 +46,39 @@ where Ok((retval, ciphertext)) } } + +/// Ready + NonceCiphertext => Ready + Vec +impl Transition, NonceCiphertext<'_, '_>, Vec> for Ready +where + Cipher: NoiseCipher, +{ + type Error = CipherError; + + fn try_next( + self, + _csprng: &mut R, + input: NonceCiphertext<'_, '_>, + ) -> Result<(Ready, Vec), Self::Error> { + let mut retval = self; + let plaintext = retval.decrypt_with_nonce(input.aad, input.msg, input.nonce)?; + Ok((retval, plaintext)) + } +} + +/// Ready + NoncePlaintext => Ready + Vec +impl Transition, NoncePlaintext<'_, '_>, Vec> for Ready +where + Cipher: NoiseCipher, +{ + type Error = CipherError; + + fn try_next( + self, + _csprng: &mut R, + input: NoncePlaintext<'_, '_>, + ) -> Result<(Ready, Vec), Self::Error> { + let mut retval = self; + let ciphertext = retval.encrypt_with_nonce(input.aad, input.msg, input.nonce)?; + Ok((retval, ciphertext)) + } +} diff --git a/attest/ake/src/state.rs b/attest/ake/src/state.rs index 370afadfde..4d9e7ed28c 100644 --- a/attest/ake/src/state.rs +++ b/attest/ake/src/state.rs @@ -74,6 +74,7 @@ where pub fn binding(&self) -> &[u8] { self.binding.as_ref() } + /// Using the writer cipher, encrypt the given plaintext. pub fn encrypt(&mut self, aad: &[u8], plaintext: &[u8]) -> Result, CipherError> { self.writer.encrypt_with_ad(aad, plaintext) @@ -83,6 +84,28 @@ where pub fn decrypt(&mut self, aad: &[u8], ciphertext: &[u8]) -> Result, CipherError> { self.reader.decrypt_with_ad(aad, ciphertext) } + + /// Using the writer cipher, encrypt the given plaintext for the nonce. + pub fn encrypt_with_nonce( + &mut self, + aad: &[u8], + plaintext: &[u8], + nonce: u64, + ) -> Result, CipherError> { + self.writer.set_nonce(nonce); + self.encrypt(aad, plaintext) + } + + /// Using the reader cipher, decrypt the provided ciphertext for the nonce. + pub fn decrypt_with_nonce( + &mut self, + aad: &[u8], + ciphertext: &[u8], + nonce: u64, + ) -> Result, CipherError> { + self.reader.set_nonce(nonce); + self.decrypt(aad, ciphertext) + } } impl State for Ready where Cipher: NoiseCipher {} diff --git a/attest/api/proto/attest.proto b/attest/api/proto/attest.proto index 799f91c3fb..1461774086 100644 --- a/attest/api/proto/attest.proto +++ b/attest/api/proto/attest.proto @@ -48,3 +48,20 @@ message Message { /// for use in the enclave. bytes data = 3; } + +/// An AEAD message with an explicit nonce. +/// +/// This message is technically compatible with [`Message`], but exists to +// ensure generated code doesn't use Message. +message NonceMessage { + /// A byte array containing plaintext authenticated data. + bytes aad = 1; + /// An byte array containing the channel ID this message is + /// associated with. A zero-length channel ID is not valid. + bytes channel_id = 2; + /// A potentially encrypted bytestream containing opaque data intended + /// for use in the enclave. + bytes data = 3; + /// The explicit nonce. + fixed64 nonce = 4; +} diff --git a/attest/api/src/conversions.rs b/attest/api/src/conversions.rs index 2467b959c1..c3ca7f8f5d 100644 --- a/attest/api/src/conversions.rs +++ b/attest/api/src/conversions.rs @@ -2,11 +2,11 @@ //! Conversions from gRPC message types into consensus_enclave_api types. -use crate::attest::{AuthMessage, Message}; +use crate::attest::{AuthMessage, Message, NonceMessage}; use mc_attest_ake::{AuthRequestOutput, AuthResponseOutput}; use mc_attest_enclave_api::{ - ClientAuthRequest, ClientAuthResponse, EnclaveMessage, PeerAuthRequest, PeerAuthResponse, - Session, + ClientAuthRequest, ClientAuthResponse, EnclaveMessage, EnclaveNonceMessage, PeerAuthRequest, + PeerAuthResponse, Session, }; use mc_crypto_keys::Kex; use mc_crypto_noise::{HandshakePattern, NoiseCipher, NoiseDigest}; @@ -103,8 +103,30 @@ impl From> for Message { fn from(src: EnclaveMessage) -> Message { let mut retval = Message::default(); retval.set_aad(src.aad); - retval.set_channel_id(src.channel_id.clone().into()); + retval.set_channel_id(src.channel_id.into()); retval.set_data(src.data); retval } } + +impl From for EnclaveNonceMessage { + fn from(src: NonceMessage) -> EnclaveNonceMessage { + EnclaveNonceMessage { + aad: src.aad, + channel_id: S::from(&src.channel_id), + data: src.data, + nonce: src.nonce, + } + } +} + +impl From> for NonceMessage { + fn from(src: EnclaveNonceMessage) -> NonceMessage { + let mut retval = NonceMessage::default(); + retval.set_aad(src.aad); + retval.set_channel_id(src.channel_id.into()); + retval.set_data(src.data); + retval.set_nonce(src.nonce); + retval + } +} diff --git a/attest/enclave-api/src/lib.rs b/attest/enclave-api/src/lib.rs index 848fe9d327..fe3397fa3d 100644 --- a/attest/enclave-api/src/lib.rs +++ b/attest/enclave-api/src/lib.rs @@ -95,6 +95,19 @@ pub struct EnclaveMessage { pub data: Vec, } +/// Inbound and outbound messages to/from an enclave with an explicit nonce. +#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] +pub struct EnclaveNonceMessage { + /// Authenticated data, if any. + pub aad: Vec, + /// The channel ID of this message. + pub channel_id: S, + /// The encrypted payload data of this message. + pub data: Vec, + /// The explicit nonce for this message. + pub nonce: u64, +} + /// The response to a request for a new report. The enclave will expect the /// QuoteNonce to be used when the report is quoted, and both the quote and /// report to be returned to the enclave during the verify_quote() phase.