From 8f9d1094d5107530002e1fc05c97d928cef496d8 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 May 2023 19:00:21 +0200 Subject: [PATCH 1/5] Felix's asymmetric sub-protocol crypto --- Cargo.toml | 6 +- src/lib.rs | 10 +- src/session/crypto.rs | 73 ++++++++------- src/session/mod.rs | 208 +++++++++++++++++++++++++----------------- src/tunnel_packet.rs | 51 ++++------- 5 files changed, 195 insertions(+), 153 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b729c8f..dc0dd15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,6 @@ edition = "2021" [dependencies] aes-gcm = { version = "0.10.1", features = ["aes"] } -async-trait = "0.1.68" -ed25519-dalek = "1.0.1" +hkdf = "0.12.3" rand = "0.8.5" -rand_core = { version = "0.6.4", features = ["std"] } -zeroize = { version = "1.6.0", features = ["zeroize_derive"] } +sha2 = "0.10.6" diff --git a/src/lib.rs b/src/lib.rs index dfd65c2..f5bf0e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,18 @@ +use std::error::Error; mod error; mod macro_rules; mod session; mod tunnel_packet; pub use error::{PacketError, SessionError, TunnelError}; -pub use session::Session; +pub use session::{NonceAesGcm, Session, SESSION_ID_LENGTH}; pub use tunnel_packet::{ ConnectionId, InboundTunnelPacket, OutboundTunnelPacket, TunnelPacket, TunnelPacketHeader, }; + +pub trait SubProtocol { + type Error: Error; + /// Send TALKREQ { initiator-secret, protocol-name } + fn initiate_session() -> Result; + fn accept_session() -> Result; +} diff --git a/src/session/crypto.rs b/src/session/crypto.rs index ca29bd7..3101b29 100644 --- a/src/session/crypto.rs +++ b/src/session/crypto.rs @@ -1,49 +1,56 @@ -use crate::tunnel_packet::HEADER_LENGTH; use aes_gcm::{ aead::{generic_array::GenericArray, AeadMut, Payload}, Aes128Gcm, Error as AesGcmError, KeyInit, }; use rand; -/// Length of [`aes_gcm::Aes128Gcm`] key in bytes. -pub const KEY_AES_GCM_128_LENGTH: usize = 32; -/// Length of [`aes_gcm::Aes128Gcm`] nonce in bytes. -pub const NONCE_AES_GCM_128_LENGTH: usize = 12; -/// Length of [`aes_gcm::Tag`], the [`aes_gcm::Aes128Gcm`] authentication data, in bytes. -pub const TAG_AES_GCM_128_LENGTH: usize = 16; - -/// A message nonce used in encryption. -pub type NonceAesGcm128 = [u8; NONCE_AES_GCM_128_LENGTH]; -/// Additional associated data used in authenticating the encryption of tunnel packet. -pub type Aad = [u8; HEADER_LENGTH]; - -/// Cipher used in [`aes_gcm::Aes128Gcm`] encryption. -pub struct Cipher(Aes128Gcm); - -impl Cipher { - pub fn new() -> Self { - let encryption_key: [u8; KEY_AES_GCM_128_LENGTH] = rand::random(); - let cipher = Aes128Gcm::new(GenericArray::from_slice(&encryption_key)); - Cipher(cipher) - } +/// Length of [`Aes128Gcm`] key in bytes. +pub const KEY_AES_GCM_128_LENGTH: usize = 16; +/// Length of [`Aes128Gcm`] nonce in bytes. +pub const NONCE_AES_GCM_LENGTH: usize = 12; +/// Length of the random bytes in a [`NonceAesGcm`]. +pub const NONCE_RANDOM_LENGTH: usize = 8; +/// Length of [`aes_gcm::Tag`], the [`Aes128Gcm`] authentication data, in bytes. +pub const TAG_AES_GCM_ENGTH: usize = 2; + +/// Nonce used by [`Aes128Gcm`]. +pub type NonceAesGcm = [u8; NONCE_AES_GCM_LENGTH]; + +/// Key used for en-/decryption. +pub type Key = [u8; KEY_AES_GCM_128_LENGTH]; - pub fn aes_gcm_128_encrypt( - &mut self, - nonce: &NonceAesGcm128, +pub trait Encrypt { + fn aes_gcm_128_encrypt( + egress_key: &mut Key, + nonce_counter: &mut u32, msg: &[u8], - aad: &Aad, ) -> Result, AesGcmError> { + let aad: &[u8; TAG_AES_GCM_ENGTH] = &rand::random(); let payload = Payload { msg, aad }; - self.0.encrypt(GenericArray::from_slice(nonce), payload) + + let nonce: [u8; NONCE_RANDOM_LENGTH] = rand::random(); + let mut nonce = nonce.to_vec(); + *nonce_counter += 1u32; + nonce.append(&mut nonce_counter.to_be_bytes().to_vec()); + + let mut cipher = Aes128Gcm::new(GenericArray::from_slice(egress_key)); + cipher.encrypt(GenericArray::from_slice(&nonce), payload) } +} - pub fn aes_gcm_128_decrypt( - &mut self, - nonce: &NonceAesGcm128, - msg: &[u8], - aad: &Aad, +pub trait Decrypt { + fn aes_gcm_128_decrypt( + ingress_key: &Key, + nonce: &NonceAesGcm, + cipher_text: &[u8], ) -> Result, AesGcmError> { + let offset_tag = cipher_text.len() - TAG_AES_GCM_ENGTH - 1; + let aad = &cipher_text[offset_tag..]; + let msg = &cipher_text[..offset_tag]; let payload = Payload { msg, aad }; - self.0.decrypt(GenericArray::from_slice(nonce), payload) + + let mut cipher = Aes128Gcm::new(GenericArray::from_slice(ingress_key)); + + cipher.decrypt(GenericArray::from_slice(nonce), payload) } } diff --git a/src/session/mod.rs b/src/session/mod.rs index 094dee0..b721ef3 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -1,96 +1,136 @@ -use super::*; -use crate::{ - tunnel_packet::{CONNECTION_ID_LENGTH, HEADER_LENGTH}, - SessionError, TunnelPacket, TunnelPacketHeader, -}; +use hkdf::{Hkdf, InvalidLength as HkdfError}; use rand; +use sha2::Sha256; +use std::net::SocketAddr; mod crypto; -pub use crypto::{Aad, Cipher, NonceAesGcm128, NONCE_AES_GCM_128_LENGTH, TAG_AES_GCM_128_LENGTH}; - -/// A Session holds the key chain used to encrypt a [`TunnelPacket`], it uses the same encryption -/// algorithm as discv5. -pub struct Session { - /// The key used to encrypt/decrypt messages. Upon starting a session, the key will be passed - /// from one peer to the other in an encrypted discv5 session using the TALKREQ message. - cipher: Cipher, - /// If a new session is being established using discv5 TALKREQ and TALKRESP, the older key - /// is maintained as race conditions in the key sharing can give different views of which - /// keys are canon. The key that worked to decrypt our last message (or are freshly - /// established) exist in `key` and the previous key is optionally stored in `old_key`. We - /// attempt to decrypt messages with `key` before optionally trying `old_key`. - old_key: Option, - /// Number of messages sent. Used to ensure the nonce used in message encryption is always - /// unique. - counter: u32, +pub use crypto::{ + Key, NonceAesGcm, KEY_AES_GCM_128_LENGTH, NONCE_AES_GCM_LENGTH, TAG_AES_GCM_ENGTH, +}; + +pub const SESSION_INFO: &'static [u8] = b"discv5 sub-protocol session"; +pub const SECRET_LENGTH: usize = 16; +pub const KDATA_LENGTH: usize = 48; +pub const SESSION_ID_LENGTH: usize = 8; +type Secret = [u8; SECRET_LENGTH]; +type KData = [u8; KDATA_LENGTH]; +type SessionId = u64; +type Initiator = (SessionId, Key); +type Recipient = (SessionId, Key); + +pub type Session = (EgressSession, IngressSession); + +/// A session to encrypt outgoing data. +pub struct EgressSession { + /// Index of egress session. + egress_id: u64, + /// The key used to encrypt messages. + egress_key: Key, + /// Nonce counter. Incremented before encrypting each message and included at end of nonce in + /// encryption. + nonce_counter: u32, + /// Ip socket to send packet to. + ip: SocketAddr, } -impl Session { - pub fn new(keys: Cipher) -> Self { - Session { - cipher: keys, - old_key: None, - counter: 0, - } - } +/// A session to decrypt incoming data. +pub struct IngressSession { + /// Index of ingress session. + ingress_id: u64, + /// The key used to decrypt messages. + ingress_key: Key, + /// Index of matching egress session to treat sessions as invariant. + egress_id: u64, +} - /// A new session has been established. Update the session keys. - pub fn update(&mut self, new_session: Session) { - // Optimistically assume the new keys are canonical. - self.old_key = Some(std::mem::replace(&mut self.cipher, new_session.cipher)); - } +pub trait EstablishSession { + fn initiate( + peer_secret: Secret, + recipient_socket: SocketAddr, + protocol_name: &[u8], + host_secret: Secret, + ) -> Result { + let ((ingress_id, ingress_key), (egress_id, egress_key)) = + compute_kdata(peer_secret, protocol_name, host_secret)?; - /// Uses the current `Session` to encrypt a message. Encrypt packets with the current session - /// key if we are awaiting - pub fn encrypt_message( - &mut self, - connection_id: ConnectionId, - msg: &[u8], - ) -> Result { - self.counter += 1; - - // If the message nonce length is ever set below 4 bytes this will explode. The packet - // size constants shouldn't be modified. - let random_nonce: [u8; NONCE_AES_GCM_128_LENGTH - 4] = rand::random(); - let mut nonce: NonceAesGcm128 = [0u8; NONCE_AES_GCM_128_LENGTH]; - nonce[..4].copy_from_slice(&self.counter.to_be_bytes()); - nonce[4..].copy_from_slice(&random_nonce); - - // As the connection id-nonce mapping is unique for every packet, the header serves the - // purpose of additional associated data. - let mut aad = [0u8; HEADER_LENGTH]; - aad[..CONNECTION_ID_LENGTH].copy_from_slice(&connection_id); - aad[CONNECTION_ID_LENGTH..].copy_from_slice(&nonce); - let cipher_text = self.cipher.aes_gcm_128_encrypt(&nonce, msg, &aad)?; - - let header = TunnelPacketHeader(connection_id, nonce); - - Ok(TunnelPacket(header, cipher_text)) + let session = new_session( + egress_id, + egress_key, + recipient_socket, + ingress_id, + ingress_key, + ); + + Ok(session) } - /// Decrypts an encrypted message. If a Session is already established, the original decryption - /// keys are tried first, upon failure, the new keys are attempted. If the new keys succeed, - /// the session keys are update. - pub fn decrypt_message( - &mut self, - nonce: NonceAesGcm128, - msg: &[u8], - aad: &Aad, - ) -> Result, SessionError> { - // First try with the canonical keys. - match self.cipher.aes_gcm_128_decrypt(&nonce, msg, aad) { - Ok(decrypted) => Ok(decrypted), - Err(e) => { - // If these keys did not work, try old_keys and - if let Some(mut old_cipher) = self.old_key.take() { - let decrypted_old_keys = old_cipher.aes_gcm_128_decrypt(&nonce, msg, aad)?; - // rotate the keys - self.old_key = Some(std::mem::replace(&mut self.cipher, old_cipher)); - return Ok(decrypted_old_keys); - } - return Err(SessionError::EncryptionError(e)); - } - } + fn accept( + peer_secret: Secret, + initiator_socket: SocketAddr, + protocol_name: &[u8], + ) -> Result<(Session, Secret), HkdfError> { + let secret: [u8; SECRET_LENGTH] = rand::random(); + + let ((egress_id, egress_key), (ingress_id, ingress_key)) = + compute_kdata(peer_secret, protocol_name, secret)?; + + let session = new_session( + egress_id, + egress_key, + initiator_socket, + ingress_id, + ingress_key, + ); + + Ok((session, secret)) } } + +fn compute_kdata( + peer_secret: Secret, + protocol_name: &[u8], + host_secret: Secret, +) -> Result<(Initiator, Recipient), HkdfError> { + let info = [SESSION_INFO, protocol_name].concat(); + let hk = Hkdf::::new(None, &[peer_secret, host_secret].concat()); + let mut kdata = [0u8; KDATA_LENGTH]; + hk.expand(&info, &mut kdata)?; + + let mut initiator_key = [0u8; KEY_AES_GCM_128_LENGTH]; + let mut recipient_key = [0u8; KEY_AES_GCM_128_LENGTH]; + let mut initiator_id = [0u8; SESSION_ID_LENGTH]; + let mut recipient_id = [0u8; SESSION_ID_LENGTH]; + + initiator_key.copy_from_slice(&kdata[..KEY_AES_GCM_128_LENGTH]); + recipient_key.copy_from_slice(&kdata[KEY_AES_GCM_128_LENGTH..KEY_AES_GCM_128_LENGTH * 2]); + initiator_id + .copy_from_slice(&kdata[KEY_AES_GCM_128_LENGTH * 2..KDATA_LENGTH - SESSION_ID_LENGTH]); + recipient_id.copy_from_slice(&kdata[KDATA_LENGTH - SESSION_ID_LENGTH..]); + + let initiator_id = u64::from_be_bytes(initiator_id); + let recipient_id = u64::from_be_bytes(recipient_id); + + Ok(((initiator_id, initiator_key), (recipient_id, recipient_key))) +} + +fn new_session( + egress_id: SessionId, + egress_key: Key, + remote_socket: SocketAddr, + ingress_id: SessionId, + ingress_key: Key, +) -> Session { + let egress = EgressSession { + egress_id, + egress_key, + nonce_counter: 0, + ip: remote_socket, + }; + let ingress = IngressSession { + ingress_id, + ingress_key, + egress_id, + }; + (egress, ingress) +} diff --git a/src/tunnel_packet.rs b/src/tunnel_packet.rs index e7699bc..7741d4e 100644 --- a/src/tunnel_packet.rs +++ b/src/tunnel_packet.rs @@ -1,5 +1,5 @@ use crate::{ - session::{NonceAesGcm128, NONCE_AES_GCM_128_LENGTH, TAG_AES_GCM_128_LENGTH}, + session::{NonceAesGcm, NONCE_AES_GCM_LENGTH, SESSION_ID_LENGTH, TAG_AES_GCM_ENGTH}, PacketError, }; use std::net::SocketAddr; @@ -7,24 +7,18 @@ use std::net::SocketAddr; /// Discv5 max packet size in bytes. pub const MAX_PACKET_SIZE: usize = 1280; /// Tunnel packet min size in bytes. -pub const MIN_PACKET_SIZE: usize = HEADER_LENGTH + MIN_ENCRYPTED_MESSAGE_LENGTH; +pub const MIN_PACKET_SIZE: usize = HEADER_LENGTH + TAG_AES_GCM_ENGTH; /// Length of the [`TunnelPacketHeader`]. -pub const HEADER_LENGTH: usize = CONNECTION_ID_LENGTH + NONCE_AES_GCM_128_LENGTH; -/// Length of connection id. -pub const CONNECTION_ID_LENGTH: usize = 32; -/// Message min length in bytes. -pub const MIN_ENCRYPTED_MESSAGE_LENGTH: usize = MIN_MESSAGE_LENGTH + TAG_AES_GCM_128_LENGTH; -/// Message min length in bytes. -pub const MIN_MESSAGE_LENGTH: usize = 1; +pub const HEADER_LENGTH: usize = SESSION_ID_LENGTH + NONCE_AES_GCM_LENGTH; /// A connection id maps to a set of session keys. -pub type ConnectionId = [u8; CONNECTION_ID_LENGTH]; +pub type ConnectionId = [u8; SESSION_ID_LENGTH]; -/// An src address and tunnel packet is inbound. +/// Src address and inbound tunnel packet. #[derive(Debug, Clone, PartialEq, Eq)] pub struct InboundTunnelPacket(pub SocketAddr, pub TunnelPacket); -/// An dst address and tunnel packet is outbound. +/// Dst address and outbound tunnel packet. #[derive(Debug, Clone, PartialEq, Eq)] pub struct OutboundTunnelPacket(pub SocketAddr, pub TunnelPacket); @@ -36,7 +30,7 @@ pub struct TunnelPacket(pub TunnelPacketHeader, pub Vec); /// A tunnel packet header has the information to decrypt a tunnel packet. The [`ConnectionId`] /// maps to a set of session keys for this tunnel, shared in a discv5 TALKREQ and TALKRESP. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TunnelPacketHeader(pub ConnectionId, pub NonceAesGcm128); +pub struct TunnelPacketHeader(pub ConnectionId, pub NonceAesGcm); impl TunnelPacket { pub fn decode(data: &[u8]) -> Result { @@ -47,32 +41,27 @@ impl TunnelPacket { return Err(PacketError::TooSmall.into()); } - let mut connection_id = [0u8; CONNECTION_ID_LENGTH]; - connection_id.copy_from_slice(&data[..32]); - let mut nonce = [0u8; NONCE_AES_GCM_128_LENGTH]; - nonce.copy_from_slice(&data[32..44]); + let mut session_id = [0u8; SESSION_ID_LENGTH]; + let mut nonce = [0u8; NONCE_AES_GCM_LENGTH]; - let header = TunnelPacketHeader(connection_id, nonce); + session_id.copy_from_slice(&data[..SESSION_ID_LENGTH]); + nonce.copy_from_slice(&data[SESSION_ID_LENGTH..HEADER_LENGTH]); - // Any remaining bytes are message data - let encrypted_data = data[44..].to_vec(); - - if encrypted_data.len() < MIN_ENCRYPTED_MESSAGE_LENGTH { - return Err(PacketError::TooSmall); - } + let header = TunnelPacketHeader(session_id, nonce); - let packet = TunnelPacket(header, encrypted_data); + // Any remaining bytes are message data + let data = data[HEADER_LENGTH..].to_vec(); - Ok(packet) + Ok(TunnelPacket(header, data)) } pub fn encode(self) -> Vec { - let TunnelPacket(header, encrypted_message) = self; - let TunnelPacketHeader(conn_id, nonce) = header; - let mut buf = Vec::with_capacity(HEADER_LENGTH + encrypted_message.len()); - buf.extend_from_slice(&conn_id); + let TunnelPacket(header, data) = self; + let TunnelPacketHeader(session_id, nonce) = header; + let mut buf = Vec::with_capacity(HEADER_LENGTH + data.len()); + buf.extend_from_slice(&session_id); buf.extend_from_slice(&nonce); - buf.extend_from_slice(&encrypted_message); + buf.extend_from_slice(&data); buf } } From 8cd285328920aceaf8806de72633577564ef97ee Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 May 2023 21:04:53 +0200 Subject: [PATCH 2/5] Add authentication of data --- README.md | 3 +-- src/lib.rs | 2 +- src/session/crypto.rs | 51 ++++++++++++++++++++--------------- src/session/mod.rs | 62 ++++++++++++++++++++++++------------------- src/tunnel_packet.rs | 15 +++++------ 5 files changed, 72 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 40ee12f..3c740c2 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ A tunnel is used to send packets of a different protocol than discv5. -A tunnel packet passes through the same socket as a discv5 packet so it can make use of -discv5's punched NAT holes. A tunnel gets a different set of session keys than the discv5 session +A tunnel gets a different set of session keys than the discv5 session used to share those keys, in TALKREQ and TALKRESP. A tunnel session can be used to send an arbitrary number of packets without making the discv5 session less safe because it doesn't use the discv5 session's keys too often. diff --git a/src/lib.rs b/src/lib.rs index f5bf0e8..f413297 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ mod tunnel_packet; pub use error::{PacketError, SessionError, TunnelError}; pub use session::{NonceAesGcm, Session, SESSION_ID_LENGTH}; pub use tunnel_packet::{ - ConnectionId, InboundTunnelPacket, OutboundTunnelPacket, TunnelPacket, TunnelPacketHeader, + InboundTunnelPacket, OutboundTunnelPacket, TunnelPacket, TunnelPacketHeader, }; pub trait SubProtocol { diff --git a/src/session/crypto.rs b/src/session/crypto.rs index 3101b29..779bac8 100644 --- a/src/session/crypto.rs +++ b/src/session/crypto.rs @@ -1,6 +1,6 @@ use aes_gcm::{ - aead::{generic_array::GenericArray, AeadMut, Payload}, - Aes128Gcm, Error as AesGcmError, KeyInit, + aead::{generic_array::GenericArray, AeadMutInPlace, Payload}, + Aes128Gcm, Error as AesGcmError, }; use rand; @@ -16,41 +16,48 @@ pub const TAG_AES_GCM_ENGTH: usize = 2; /// Nonce used by [`Aes128Gcm`]. pub type NonceAesGcm = [u8; NONCE_AES_GCM_LENGTH]; -/// Key used for en-/decryption. -pub type Key = [u8; KEY_AES_GCM_128_LENGTH]; - pub trait Encrypt { fn aes_gcm_128_encrypt( - egress_key: &mut Key, + egress_cipher: &mut Aes128Gcm, nonce_counter: &mut u32, msg: &[u8], + aad: &[u8], // use session-id ) -> Result, AesGcmError> { - let aad: &[u8; TAG_AES_GCM_ENGTH] = &rand::random(); - let payload = Payload { msg, aad }; - - let nonce: [u8; NONCE_RANDOM_LENGTH] = rand::random(); - let mut nonce = nonce.to_vec(); - *nonce_counter += 1u32; + *nonce_counter += 1; + let random_nonce: [u8; NONCE_RANDOM_LENGTH] = rand::random(); + let mut nonce = random_nonce.to_vec(); nonce.append(&mut nonce_counter.to_be_bytes().to_vec()); + let nonce = GenericArray::from_slice(&nonce); + + let mut cipher_text = msg.to_vec(); - let mut cipher = Aes128Gcm::new(GenericArray::from_slice(egress_key)); - cipher.encrypt(GenericArray::from_slice(&nonce), payload) + let tag = egress_cipher.encrypt_in_place_detached(nonce, aad, &mut cipher_text)?; + cipher_text.append(&mut tag.to_vec()); + + Ok(cipher_text) } } pub trait Decrypt { fn aes_gcm_128_decrypt( - ingress_key: &Key, + ingress_cipher: &mut Aes128Gcm, nonce: &NonceAesGcm, - cipher_text: &[u8], + data: &[u8], + aad: &[u8], // use session-id ) -> Result, AesGcmError> { - let offset_tag = cipher_text.len() - TAG_AES_GCM_ENGTH - 1; - let aad = &cipher_text[offset_tag..]; - let msg = &cipher_text[..offset_tag]; - let payload = Payload { msg, aad }; + let offset_tag = data.len() - TAG_AES_GCM_ENGTH - 1; + let msg = &data[..offset_tag]; + let tag = &data[offset_tag..]; + + let mut plain_text = msg.to_vec(); - let mut cipher = Aes128Gcm::new(GenericArray::from_slice(ingress_key)); + ingress_cipher.decrypt_in_place_detached( + GenericArray::from_slice(nonce), + aad, + &mut plain_text, + GenericArray::from_slice(tag), + )?; - cipher.decrypt(GenericArray::from_slice(nonce), payload) + Ok(plain_text) } } diff --git a/src/session/mod.rs b/src/session/mod.rs index b721ef3..24d86e0 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -1,3 +1,4 @@ +use aes_gcm::{aead::generic_array::GenericArray, Aes128Gcm, KeyInit}; use hkdf::{Hkdf, InvalidLength as HkdfError}; use rand; use sha2::Sha256; @@ -5,9 +6,7 @@ use std::net::SocketAddr; mod crypto; -pub use crypto::{ - Key, NonceAesGcm, KEY_AES_GCM_128_LENGTH, NONCE_AES_GCM_LENGTH, TAG_AES_GCM_ENGTH, -}; +pub use crypto::{NonceAesGcm, KEY_AES_GCM_128_LENGTH, NONCE_AES_GCM_LENGTH, TAG_AES_GCM_ENGTH}; pub const SESSION_INFO: &'static [u8] = b"discv5 sub-protocol session"; pub const SECRET_LENGTH: usize = 16; @@ -15,9 +14,9 @@ pub const KDATA_LENGTH: usize = 48; pub const SESSION_ID_LENGTH: usize = 8; type Secret = [u8; SECRET_LENGTH]; type KData = [u8; KDATA_LENGTH]; -type SessionId = u64; -type Initiator = (SessionId, Key); -type Recipient = (SessionId, Key); +pub type SessionId = u64; +type Initiator = (SessionId, Aes128Gcm); +type Recipient = (SessionId, Aes128Gcm); pub type Session = (EgressSession, IngressSession); @@ -25,8 +24,8 @@ pub type Session = (EgressSession, IngressSession); pub struct EgressSession { /// Index of egress session. egress_id: u64, - /// The key used to encrypt messages. - egress_key: Key, + /// The cipher used to encrypt messages. + egress_cipher: Aes128Gcm, /// Nonce counter. Incremented before encrypting each message and included at end of nonce in /// encryption. nonce_counter: u32, @@ -38,21 +37,23 @@ pub struct EgressSession { pub struct IngressSession { /// Index of ingress session. ingress_id: u64, - /// The key used to decrypt messages. - ingress_key: Key, + /// The cipher used to decrypt messages. + ingress_cipher: Aes128Gcm, + /// Nonce counter used to verify integrity of ingress stream. + nonce_counter: u32, /// Index of matching egress session to treat sessions as invariant. egress_id: u64, } pub trait EstablishSession { fn initiate( - peer_secret: Secret, + peer_key: Secret, recipient_socket: SocketAddr, protocol_name: &[u8], - host_secret: Secret, + host_key: Secret, ) -> Result { let ((ingress_id, ingress_key), (egress_id, egress_key)) = - compute_kdata(peer_secret, protocol_name, host_secret)?; + compute_ciphers_and_ids(peer_key, protocol_name, host_key)?; let session = new_session( egress_id, @@ -66,14 +67,14 @@ pub trait EstablishSession { } fn accept( - peer_secret: Secret, + peer_key: Secret, initiator_socket: SocketAddr, protocol_name: &[u8], ) -> Result<(Session, Secret), HkdfError> { - let secret: [u8; SECRET_LENGTH] = rand::random(); + let key: [u8; SECRET_LENGTH] = rand::random(); let ((egress_id, egress_key), (ingress_id, ingress_key)) = - compute_kdata(peer_secret, protocol_name, secret)?; + compute_ciphers_and_ids(peer_key, protocol_name, key)?; let session = new_session( egress_id, @@ -83,11 +84,11 @@ pub trait EstablishSession { ingress_key, ); - Ok((session, secret)) + Ok((session, key)) } } -fn compute_kdata( +fn compute_ciphers_and_ids( peer_secret: Secret, protocol_name: &[u8], host_secret: Secret, @@ -102,34 +103,41 @@ fn compute_kdata( let mut initiator_id = [0u8; SESSION_ID_LENGTH]; let mut recipient_id = [0u8; SESSION_ID_LENGTH]; - initiator_key.copy_from_slice(&kdata[..KEY_AES_GCM_128_LENGTH]); - recipient_key.copy_from_slice(&kdata[KEY_AES_GCM_128_LENGTH..KEY_AES_GCM_128_LENGTH * 2]); initiator_id .copy_from_slice(&kdata[KEY_AES_GCM_128_LENGTH * 2..KDATA_LENGTH - SESSION_ID_LENGTH]); - recipient_id.copy_from_slice(&kdata[KDATA_LENGTH - SESSION_ID_LENGTH..]); - let initiator_id = u64::from_be_bytes(initiator_id); + recipient_id.copy_from_slice(&kdata[KDATA_LENGTH - SESSION_ID_LENGTH..]); let recipient_id = u64::from_be_bytes(recipient_id); - Ok(((initiator_id, initiator_key), (recipient_id, recipient_key))) + let initiator_cipher = + Aes128Gcm::new(GenericArray::from_slice(&kdata[..KEY_AES_GCM_128_LENGTH])); + let recipient_cipher = Aes128Gcm::new(GenericArray::from_slice( + &kdata[KEY_AES_GCM_128_LENGTH..KEY_AES_GCM_128_LENGTH * 2], + )); + + Ok(( + (initiator_id, initiator_cipher), + (recipient_id, recipient_cipher), + )) } fn new_session( egress_id: SessionId, - egress_key: Key, + egress_key: Aes128Gcm, remote_socket: SocketAddr, ingress_id: SessionId, - ingress_key: Key, + ingress_key: Aes128Gcm, ) -> Session { let egress = EgressSession { egress_id, - egress_key, + egress_cipher: egress_key, nonce_counter: 0, ip: remote_socket, }; let ingress = IngressSession { ingress_id, - ingress_key, + ingress_cipher: ingress_key, + nonce_counter: 0, egress_id, }; (egress, ingress) diff --git a/src/tunnel_packet.rs b/src/tunnel_packet.rs index 7741d4e..05b99fe 100644 --- a/src/tunnel_packet.rs +++ b/src/tunnel_packet.rs @@ -1,5 +1,5 @@ use crate::{ - session::{NonceAesGcm, NONCE_AES_GCM_LENGTH, SESSION_ID_LENGTH, TAG_AES_GCM_ENGTH}, + session::{NonceAesGcm, SessionId, NONCE_AES_GCM_LENGTH, SESSION_ID_LENGTH, TAG_AES_GCM_ENGTH}, PacketError, }; use std::net::SocketAddr; @@ -11,9 +11,6 @@ pub const MIN_PACKET_SIZE: usize = HEADER_LENGTH + TAG_AES_GCM_ENGTH; /// Length of the [`TunnelPacketHeader`]. pub const HEADER_LENGTH: usize = SESSION_ID_LENGTH + NONCE_AES_GCM_LENGTH; -/// A connection id maps to a set of session keys. -pub type ConnectionId = [u8; SESSION_ID_LENGTH]; - /// Src address and inbound tunnel packet. #[derive(Debug, Clone, PartialEq, Eq)] pub struct InboundTunnelPacket(pub SocketAddr, pub TunnelPacket); @@ -30,15 +27,15 @@ pub struct TunnelPacket(pub TunnelPacketHeader, pub Vec); /// A tunnel packet header has the information to decrypt a tunnel packet. The [`ConnectionId`] /// maps to a set of session keys for this tunnel, shared in a discv5 TALKREQ and TALKRESP. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TunnelPacketHeader(pub ConnectionId, pub NonceAesGcm); +pub struct TunnelPacketHeader(pub SessionId, pub NonceAesGcm); impl TunnelPacket { pub fn decode(data: &[u8]) -> Result { if data.len() > MAX_PACKET_SIZE { - return Err(PacketError::TooLarge.into()); + return Err(PacketError::TooLarge); } if data.len() < MIN_PACKET_SIZE { - return Err(PacketError::TooSmall.into()); + return Err(PacketError::TooSmall); } let mut session_id = [0u8; SESSION_ID_LENGTH]; @@ -47,7 +44,7 @@ impl TunnelPacket { session_id.copy_from_slice(&data[..SESSION_ID_LENGTH]); nonce.copy_from_slice(&data[SESSION_ID_LENGTH..HEADER_LENGTH]); - let header = TunnelPacketHeader(session_id, nonce); + let header = TunnelPacketHeader(u64::from_be_bytes(session_id), nonce); // Any remaining bytes are message data let data = data[HEADER_LENGTH..].to_vec(); @@ -59,7 +56,7 @@ impl TunnelPacket { let TunnelPacket(header, data) = self; let TunnelPacketHeader(session_id, nonce) = header; let mut buf = Vec::with_capacity(HEADER_LENGTH + data.len()); - buf.extend_from_slice(&session_id); + buf.extend_from_slice(&session_id.to_be_bytes()); buf.extend_from_slice(&nonce); buf.extend_from_slice(&data); buf From b02a1ef6561d4978b0d3a765bd700c07cd7e6d2f Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Sun, 14 May 2023 21:07:11 +0200 Subject: [PATCH 3/5] fixup! Add authentication of data --- src/session/crypto.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/session/crypto.rs b/src/session/crypto.rs index 779bac8..c3a11f2 100644 --- a/src/session/crypto.rs +++ b/src/session/crypto.rs @@ -29,12 +29,12 @@ pub trait Encrypt { nonce.append(&mut nonce_counter.to_be_bytes().to_vec()); let nonce = GenericArray::from_slice(&nonce); - let mut cipher_text = msg.to_vec(); + let mut buf = msg.to_vec(); - let tag = egress_cipher.encrypt_in_place_detached(nonce, aad, &mut cipher_text)?; - cipher_text.append(&mut tag.to_vec()); + let tag = egress_cipher.encrypt_in_place_detached(nonce, aad, &mut buf)?; + buf.append(&mut tag.to_vec()); - Ok(cipher_text) + Ok(buf) } } @@ -49,15 +49,15 @@ pub trait Decrypt { let msg = &data[..offset_tag]; let tag = &data[offset_tag..]; - let mut plain_text = msg.to_vec(); + let mut buf = msg.to_vec(); ingress_cipher.decrypt_in_place_detached( GenericArray::from_slice(nonce), aad, - &mut plain_text, + &mut buf, GenericArray::from_slice(tag), )?; - Ok(plain_text) + Ok(buf) } } From 68a2c6de46b86fb66d34cd1f6f7ea91d38ba4d28 Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 15 May 2023 12:50:58 +0200 Subject: [PATCH 4/5] Clean up crypto --- Cargo.toml | 4 +- README.md | 19 +++--- src/lib.rs | 2 +- src/session/crypto.rs | 86 +++++++++++++++++++++------- src/session/mod.rs | 130 ++++++++++++++++++------------------------ src/tunnel_packet.rs | 15 ++--- 6 files changed, 142 insertions(+), 114 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dc0dd15..670b8a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -aes-gcm = { version = "0.10.1", features = ["aes"] } +aes-gcm = { version = "0.10.1", features = ["aes"], default-features = false} hkdf = "0.12.3" rand = "0.8.5" -sha2 = "0.10.6" +sha2 = { version = "0.10.6", default-features = false } diff --git a/README.md b/README.md index 3c740c2..9f640cd 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ -# tunnel +# Sub-protocol data transmission, an encrypted tunnel through discv5 -A tunnel is used to send packets of a different protocol than discv5. +A sub-protocol session is used to send packets of a different protocol than discv5. -A tunnel gets a different set of session keys than the discv5 session -used to share those keys, in TALKREQ and TALKRESP. A tunnel session can be used to send an -arbitrary number of packets without making the discv5 session less safe because it doesn't use -the discv5 session's keys too often. +A sub-protocol session gets a different set of session keys than the discv5 session. +The trusted discv5 session is used to key-share, specifically the TALKREQ and TALKRESP. +A sub-protocol session can be used to send an arbitrary number of packets without making +the discv5 session less safe because it doesn't use the discv5 session's keys too often. -A tunnel is indexed by the tuple (sr-address, connection-id). Since the connection id is a 32 byte -hash, there is no need to mask the header of a tunnel packet to protect against packet filtering. -For this a tunnel packet is much lighter than a discv5 frame. +A sub-protocol session is indexed by the tuple (egress-id, ingress-id). The sub-protocol +session packet frame is (session-id, nonce, mac), in total 36 bytes. As the session-id is +unique for each session the frame doesn't need to masked. For this a sub-protocol session +frame is much lighter than a discv5 frame. Plugged into discv5 here: https://github.com/emhane/discv5/tree/tunnel-discv5.2 diff --git a/src/lib.rs b/src/lib.rs index f413297..81ec26d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ mod session; mod tunnel_packet; pub use error::{PacketError, SessionError, TunnelError}; -pub use session::{NonceAesGcm, Session, SESSION_ID_LENGTH}; +pub use session::*; pub use tunnel_packet::{ InboundTunnelPacket, OutboundTunnelPacket, TunnelPacket, TunnelPacketHeader, }; diff --git a/src/session/crypto.rs b/src/session/crypto.rs index c3a11f2..923c1e8 100644 --- a/src/session/crypto.rs +++ b/src/session/crypto.rs @@ -1,25 +1,43 @@ -use aes_gcm::{ - aead::{generic_array::GenericArray, AeadMutInPlace, Payload}, - Aes128Gcm, Error as AesGcmError, +use super::NonceCounter; +pub use aes_gcm::{ + aead::generic_array::GenericArray, aes::cipher::Unsigned, Aes128Gcm as Cipher, + Error as AesGcmError, KeyInit, }; +use aes_gcm::{aead::AeadMutInPlace, AeadCore, KeySizeUser, Nonce as NonceAesGcm}; +use hkdf::Hkdf; +pub use hkdf::InvalidLength as HkdfError; use rand; +use sha2::Sha256; -/// Length of [`Aes128Gcm`] key in bytes. -pub const KEY_AES_GCM_128_LENGTH: usize = 16; -/// Length of [`Aes128Gcm`] nonce in bytes. -pub const NONCE_AES_GCM_LENGTH: usize = 12; -/// Length of the random bytes in a [`NonceAesGcm`]. -pub const NONCE_RANDOM_LENGTH: usize = 8; -/// Length of [`aes_gcm::Tag`], the [`Aes128Gcm`] authentication data, in bytes. -pub const TAG_AES_GCM_ENGTH: usize = 2; +/// Length of key in bytes. +pub const KEY_LENGTH: usize = ::KeySize::USIZE; +/// Length of nonce in bytes. +pub const NONCE_LENGTH: usize = ::NonceSize::USIZE; +/// Length of the random bytes in a [`Nonce`]. +pub const NONCE_RANDOM_LENGTH: usize = NONCE_LENGTH - (NonceCounter::BITS / 8) as usize; +/// Length of mac in bytes. +pub const TAG_LENGTH: usize = ::TagSize::USIZE; +/// Length of secret used to compute key data in bytes. +pub const SECRET_LENGTH: usize = 16; +/// Length of key data in bytes. +pub const KDATA_LENGTH: usize = SESSION_ID_LENGTH * 2 + KEY_LENGTH * 2; +/// Length of session id derived from key data. +pub const SESSION_ID_LENGTH: usize = 8; +/// Protocol identifier used to compute key data. +pub const SESSION_INFO: &'static [u8] = b"discv5 sub-protocol session"; -/// Nonce used by [`Aes128Gcm`]. -pub type NonceAesGcm = [u8; NONCE_AES_GCM_LENGTH]; +/// Nonce used by [`Cipher`]. +pub type Nonce = NonceAesGcm<::NonceSize>; +pub type SessionId = u64; +pub type Secret = [u8; SECRET_LENGTH]; + +type Initiator = (SessionId, Cipher); +type Recipient = (SessionId, Cipher); pub trait Encrypt { - fn aes_gcm_128_encrypt( - egress_cipher: &mut Aes128Gcm, - nonce_counter: &mut u32, + fn aes_gcm_encrypt( + egress_cipher: &mut Cipher, + nonce_counter: &mut NonceCounter, msg: &[u8], aad: &[u8], // use session-id ) -> Result, AesGcmError> { @@ -39,13 +57,13 @@ pub trait Encrypt { } pub trait Decrypt { - fn aes_gcm_128_decrypt( - ingress_cipher: &mut Aes128Gcm, - nonce: &NonceAesGcm, + fn aes_gcm_decrypt( + ingress_cipher: &mut Cipher, + nonce: &Nonce, data: &[u8], aad: &[u8], // use session-id ) -> Result, AesGcmError> { - let offset_tag = data.len() - TAG_AES_GCM_ENGTH - 1; + let offset_tag = data.len() - TAG_LENGTH - 1; let msg = &data[..offset_tag]; let tag = &data[offset_tag..]; @@ -61,3 +79,31 @@ pub trait Decrypt { Ok(buf) } } + +pub(crate) fn compute_ciphers_and_ids( + peer_secret: Secret, + protocol_name: &[u8], + host_secret: Secret, +) -> Result<(Initiator, Recipient), HkdfError> { + let info = [SESSION_INFO, protocol_name].concat(); + let hk = Hkdf::::new(None, &[peer_secret, host_secret].concat()); + let mut kdata = [0u8; KDATA_LENGTH]; + hk.expand(&info, &mut kdata)?; + + let initiator_cipher = Cipher::new(GenericArray::from_slice(&kdata[..KEY_LENGTH])); + let recipient_cipher = + Cipher::new(GenericArray::from_slice(&kdata[KEY_LENGTH..KEY_LENGTH * 2])); + + let mut initiator_id = [0u8; SESSION_ID_LENGTH]; + let mut recipient_id = [0u8; SESSION_ID_LENGTH]; + + initiator_id.copy_from_slice(&kdata[KEY_LENGTH * 2..KDATA_LENGTH - SESSION_ID_LENGTH]); + recipient_id.copy_from_slice(&kdata[KDATA_LENGTH - SESSION_ID_LENGTH..]); + let initiator_id = SessionId::from_be_bytes(initiator_id); + let recipient_id = SessionId::from_be_bytes(recipient_id); + + Ok(( + (initiator_id, initiator_cipher), + (recipient_id, recipient_cipher), + )) +} diff --git a/src/session/mod.rs b/src/session/mod.rs index 24d86e0..1d37d17 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -1,143 +1,123 @@ -use aes_gcm::{aead::generic_array::GenericArray, Aes128Gcm, KeyInit}; -use hkdf::{Hkdf, InvalidLength as HkdfError}; -use rand; -use sha2::Sha256; use std::net::SocketAddr; mod crypto; -pub use crypto::{NonceAesGcm, KEY_AES_GCM_128_LENGTH, NONCE_AES_GCM_LENGTH, TAG_AES_GCM_ENGTH}; +pub use crypto::{ + AesGcmError, Cipher, Decrypt, Encrypt, GenericArray, HkdfError, Nonce, Secret, SessionId, + KEY_LENGTH, NONCE_LENGTH, SECRET_LENGTH, SESSION_ID_LENGTH, TAG_LENGTH, +}; -pub const SESSION_INFO: &'static [u8] = b"discv5 sub-protocol session"; -pub const SECRET_LENGTH: usize = 16; -pub const KDATA_LENGTH: usize = 48; -pub const SESSION_ID_LENGTH: usize = 8; -type Secret = [u8; SECRET_LENGTH]; -type KData = [u8; KDATA_LENGTH]; -pub type SessionId = u64; -type Initiator = (SessionId, Aes128Gcm); -type Recipient = (SessionId, Aes128Gcm); - -pub type Session = (EgressSession, IngressSession); +pub type Session = (EgressSession, IngressSession); +pub(crate) type NonceCounter = u32; /// A session to encrypt outgoing data. -pub struct EgressSession { +pub struct EgressSession { /// Index of egress session. - egress_id: u64, + egress_id: SessionId, /// The cipher used to encrypt messages. - egress_cipher: Aes128Gcm, + egress_cipher: T, /// Nonce counter. Incremented before encrypting each message and included at end of nonce in /// encryption. - nonce_counter: u32, + nonce_counter: NonceCounter, /// Ip socket to send packet to. ip: SocketAddr, } +impl EgressSession { + pub fn encrypt(&mut self, msg: &[u8], aad: &[u8]) -> Result, AesGcmError> { + ::aes_gcm_encrypt( + &mut self.egress_cipher, + &mut self.nonce_counter, + msg, + aad, + ) + } +} + +impl Encrypt for EgressSession {} + /// A session to decrypt incoming data. -pub struct IngressSession { +pub struct IngressSession { /// Index of ingress session. - ingress_id: u64, + ingress_id: SessionId, /// The cipher used to decrypt messages. - ingress_cipher: Aes128Gcm, - /// Nonce counter used to verify integrity of ingress stream. - nonce_counter: u32, + ingress_cipher: T, /// Index of matching egress session to treat sessions as invariant. - egress_id: u64, + egress_id: SessionId, } +impl IngressSession { + pub fn decrypt( + &mut self, + nonce: &Nonce, + data: &[u8], + aad: &[u8], + ) -> Result, AesGcmError> { + ::aes_gcm_decrypt(&mut self.ingress_cipher, nonce, data, aad) + } +} + +impl Decrypt for IngressSession {} + pub trait EstablishSession { fn initiate( - peer_key: Secret, + peer_secret: Secret, recipient_socket: SocketAddr, protocol_name: &[u8], - host_key: Secret, + host_secret: Secret, ) -> Result { - let ((ingress_id, ingress_key), (egress_id, egress_key)) = - compute_ciphers_and_ids(peer_key, protocol_name, host_key)?; + let ((ingress_id, ingress_cipher), (egress_id, egress_cipher)) = + crypto::compute_ciphers_and_ids(peer_secret, protocol_name, host_secret)?; let session = new_session( egress_id, - egress_key, + egress_cipher, recipient_socket, ingress_id, - ingress_key, + ingress_cipher, ); Ok(session) } fn accept( - peer_key: Secret, + peer_secret: Secret, initiator_socket: SocketAddr, protocol_name: &[u8], ) -> Result<(Session, Secret), HkdfError> { let key: [u8; SECRET_LENGTH] = rand::random(); - let ((egress_id, egress_key), (ingress_id, ingress_key)) = - compute_ciphers_and_ids(peer_key, protocol_name, key)?; + let ((egress_id, egress_cipher), (ingress_id, ingress_cipher)) = + crypto::compute_ciphers_and_ids(peer_secret, protocol_name, key)?; let session = new_session( egress_id, - egress_key, + egress_cipher, initiator_socket, ingress_id, - ingress_key, + ingress_cipher, ); Ok((session, key)) } } -fn compute_ciphers_and_ids( - peer_secret: Secret, - protocol_name: &[u8], - host_secret: Secret, -) -> Result<(Initiator, Recipient), HkdfError> { - let info = [SESSION_INFO, protocol_name].concat(); - let hk = Hkdf::::new(None, &[peer_secret, host_secret].concat()); - let mut kdata = [0u8; KDATA_LENGTH]; - hk.expand(&info, &mut kdata)?; - - let mut initiator_key = [0u8; KEY_AES_GCM_128_LENGTH]; - let mut recipient_key = [0u8; KEY_AES_GCM_128_LENGTH]; - let mut initiator_id = [0u8; SESSION_ID_LENGTH]; - let mut recipient_id = [0u8; SESSION_ID_LENGTH]; - - initiator_id - .copy_from_slice(&kdata[KEY_AES_GCM_128_LENGTH * 2..KDATA_LENGTH - SESSION_ID_LENGTH]); - let initiator_id = u64::from_be_bytes(initiator_id); - recipient_id.copy_from_slice(&kdata[KDATA_LENGTH - SESSION_ID_LENGTH..]); - let recipient_id = u64::from_be_bytes(recipient_id); - - let initiator_cipher = - Aes128Gcm::new(GenericArray::from_slice(&kdata[..KEY_AES_GCM_128_LENGTH])); - let recipient_cipher = Aes128Gcm::new(GenericArray::from_slice( - &kdata[KEY_AES_GCM_128_LENGTH..KEY_AES_GCM_128_LENGTH * 2], - )); - - Ok(( - (initiator_id, initiator_cipher), - (recipient_id, recipient_cipher), - )) -} - fn new_session( egress_id: SessionId, - egress_key: Aes128Gcm, + egress_cipher: Cipher, remote_socket: SocketAddr, ingress_id: SessionId, - ingress_key: Aes128Gcm, + ingress_cipher: Cipher, ) -> Session { let egress = EgressSession { egress_id, - egress_cipher: egress_key, + egress_cipher, nonce_counter: 0, ip: remote_socket, }; let ingress = IngressSession { ingress_id, - ingress_cipher: ingress_key, - nonce_counter: 0, + ingress_cipher, egress_id, }; (egress, ingress) diff --git a/src/tunnel_packet.rs b/src/tunnel_packet.rs index 05b99fe..ae18860 100644 --- a/src/tunnel_packet.rs +++ b/src/tunnel_packet.rs @@ -1,5 +1,7 @@ use crate::{ - session::{NonceAesGcm, SessionId, NONCE_AES_GCM_LENGTH, SESSION_ID_LENGTH, TAG_AES_GCM_ENGTH}, + session::{ + GenericArray as NewNonce, Nonce, SessionId, NONCE_LENGTH, SESSION_ID_LENGTH, TAG_LENGTH, + }, PacketError, }; use std::net::SocketAddr; @@ -7,9 +9,9 @@ use std::net::SocketAddr; /// Discv5 max packet size in bytes. pub const MAX_PACKET_SIZE: usize = 1280; /// Tunnel packet min size in bytes. -pub const MIN_PACKET_SIZE: usize = HEADER_LENGTH + TAG_AES_GCM_ENGTH; +pub const MIN_PACKET_SIZE: usize = HEADER_LENGTH + TAG_LENGTH; /// Length of the [`TunnelPacketHeader`]. -pub const HEADER_LENGTH: usize = SESSION_ID_LENGTH + NONCE_AES_GCM_LENGTH; +pub const HEADER_LENGTH: usize = SESSION_ID_LENGTH + NONCE_LENGTH; /// Src address and inbound tunnel packet. #[derive(Debug, Clone, PartialEq, Eq)] @@ -27,7 +29,7 @@ pub struct TunnelPacket(pub TunnelPacketHeader, pub Vec); /// A tunnel packet header has the information to decrypt a tunnel packet. The [`ConnectionId`] /// maps to a set of session keys for this tunnel, shared in a discv5 TALKREQ and TALKRESP. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct TunnelPacketHeader(pub SessionId, pub NonceAesGcm); +pub struct TunnelPacketHeader(pub SessionId, pub Nonce); impl TunnelPacket { pub fn decode(data: &[u8]) -> Result { @@ -39,12 +41,11 @@ impl TunnelPacket { } let mut session_id = [0u8; SESSION_ID_LENGTH]; - let mut nonce = [0u8; NONCE_AES_GCM_LENGTH]; session_id.copy_from_slice(&data[..SESSION_ID_LENGTH]); - nonce.copy_from_slice(&data[SESSION_ID_LENGTH..HEADER_LENGTH]); + let nonce = NewNonce::from_slice(&data[SESSION_ID_LENGTH..HEADER_LENGTH]); - let header = TunnelPacketHeader(u64::from_be_bytes(session_id), nonce); + let header = TunnelPacketHeader(u64::from_be_bytes(session_id), *nonce); // Any remaining bytes are message data let data = data[HEADER_LENGTH..].to_vec(); From 7adee6607e2542df36caf76c93bd3f7cac53965d Mon Sep 17 00:00:00 2001 From: Emilia Hane Date: Mon, 15 May 2023 13:04:19 +0200 Subject: [PATCH 5/5] fixup! Clean up crypto --- src/session/crypto.rs | 28 ++++++++++++++-------------- src/session/mod.rs | 22 +++++++++++----------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/session/crypto.rs b/src/session/crypto.rs index 923c1e8..924e1d4 100644 --- a/src/session/crypto.rs +++ b/src/session/crypto.rs @@ -1,6 +1,6 @@ use super::NonceCounter; pub use aes_gcm::{ - aead::generic_array::GenericArray, aes::cipher::Unsigned, Aes128Gcm as Cipher, + aead::generic_array::GenericArray, aes::cipher::Unsigned, Aes128Gcm as AesGcmCipher, Error as AesGcmError, KeyInit, }; use aes_gcm::{aead::AeadMutInPlace, AeadCore, KeySizeUser, Nonce as NonceAesGcm}; @@ -10,13 +10,13 @@ use rand; use sha2::Sha256; /// Length of key in bytes. -pub const KEY_LENGTH: usize = ::KeySize::USIZE; +pub const KEY_LENGTH: usize = ::KeySize::USIZE; /// Length of nonce in bytes. -pub const NONCE_LENGTH: usize = ::NonceSize::USIZE; +pub const NONCE_LENGTH: usize = ::NonceSize::USIZE; /// Length of the random bytes in a [`Nonce`]. pub const NONCE_RANDOM_LENGTH: usize = NONCE_LENGTH - (NonceCounter::BITS / 8) as usize; /// Length of mac in bytes. -pub const TAG_LENGTH: usize = ::TagSize::USIZE; +pub const TAG_LENGTH: usize = ::TagSize::USIZE; /// Length of secret used to compute key data in bytes. pub const SECRET_LENGTH: usize = 16; /// Length of key data in bytes. @@ -26,17 +26,17 @@ pub const SESSION_ID_LENGTH: usize = 8; /// Protocol identifier used to compute key data. pub const SESSION_INFO: &'static [u8] = b"discv5 sub-protocol session"; -/// Nonce used by [`Cipher`]. -pub type Nonce = NonceAesGcm<::NonceSize>; +/// Nonce used by [`AesGcmCipher`]. +pub type Nonce = NonceAesGcm<::NonceSize>; pub type SessionId = u64; pub type Secret = [u8; SECRET_LENGTH]; -type Initiator = (SessionId, Cipher); -type Recipient = (SessionId, Cipher); +type Initiator = (SessionId, AesGcmCipher); +type Recipient = (SessionId, AesGcmCipher); -pub trait Encrypt { +pub trait AesGcmEncrypt { fn aes_gcm_encrypt( - egress_cipher: &mut Cipher, + egress_cipher: &mut AesGcmCipher, nonce_counter: &mut NonceCounter, msg: &[u8], aad: &[u8], // use session-id @@ -56,9 +56,9 @@ pub trait Encrypt { } } -pub trait Decrypt { +pub trait AesGcmDecrypt { fn aes_gcm_decrypt( - ingress_cipher: &mut Cipher, + ingress_cipher: &mut AesGcmCipher, nonce: &Nonce, data: &[u8], aad: &[u8], // use session-id @@ -90,9 +90,9 @@ pub(crate) fn compute_ciphers_and_ids( let mut kdata = [0u8; KDATA_LENGTH]; hk.expand(&info, &mut kdata)?; - let initiator_cipher = Cipher::new(GenericArray::from_slice(&kdata[..KEY_LENGTH])); + let initiator_cipher = AesGcmCipher::new(GenericArray::from_slice(&kdata[..KEY_LENGTH])); let recipient_cipher = - Cipher::new(GenericArray::from_slice(&kdata[KEY_LENGTH..KEY_LENGTH * 2])); + AesGcmCipher::new(GenericArray::from_slice(&kdata[KEY_LENGTH..KEY_LENGTH * 2])); let mut initiator_id = [0u8; SESSION_ID_LENGTH]; let mut recipient_id = [0u8; SESSION_ID_LENGTH]; diff --git a/src/session/mod.rs b/src/session/mod.rs index 1d37d17..83c5253 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -3,11 +3,11 @@ use std::net::SocketAddr; mod crypto; pub use crypto::{ - AesGcmError, Cipher, Decrypt, Encrypt, GenericArray, HkdfError, Nonce, Secret, SessionId, - KEY_LENGTH, NONCE_LENGTH, SECRET_LENGTH, SESSION_ID_LENGTH, TAG_LENGTH, + AesGcmCipher, AesGcmDecrypt, AesGcmEncrypt, AesGcmError, GenericArray, HkdfError, Nonce, + Secret, SessionId, KEY_LENGTH, NONCE_LENGTH, SECRET_LENGTH, SESSION_ID_LENGTH, TAG_LENGTH, }; -pub type Session = (EgressSession, IngressSession); +pub type Session = (EgressSession, IngressSession); pub(crate) type NonceCounter = u32; /// A session to encrypt outgoing data. @@ -23,9 +23,9 @@ pub struct EgressSession { ip: SocketAddr, } -impl EgressSession { +impl EgressSession { pub fn encrypt(&mut self, msg: &[u8], aad: &[u8]) -> Result, AesGcmError> { - ::aes_gcm_encrypt( + ::aes_gcm_encrypt( &mut self.egress_cipher, &mut self.nonce_counter, msg, @@ -34,7 +34,7 @@ impl EgressSession { } } -impl Encrypt for EgressSession {} +impl AesGcmEncrypt for EgressSession {} /// A session to decrypt incoming data. pub struct IngressSession { @@ -46,18 +46,18 @@ pub struct IngressSession { egress_id: SessionId, } -impl IngressSession { +impl IngressSession { pub fn decrypt( &mut self, nonce: &Nonce, data: &[u8], aad: &[u8], ) -> Result, AesGcmError> { - ::aes_gcm_decrypt(&mut self.ingress_cipher, nonce, data, aad) + ::aes_gcm_decrypt(&mut self.ingress_cipher, nonce, data, aad) } } -impl Decrypt for IngressSession {} +impl AesGcmDecrypt for IngressSession {} pub trait EstablishSession { fn initiate( @@ -104,10 +104,10 @@ pub trait EstablishSession { fn new_session( egress_id: SessionId, - egress_cipher: Cipher, + egress_cipher: AesGcmCipher, remote_socket: SocketAddr, ingress_id: SessionId, - ingress_cipher: Cipher, + ingress_cipher: AesGcmCipher, ) -> Session { let egress = EgressSession { egress_id,