IT TALKS! (HELLO, OK)

This commit is contained in:
Adam Ierymenko 2022-06-23 16:40:47 -04:00
parent 37ccc44117
commit 7fa60b10a3
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
4 changed files with 83 additions and 61 deletions

View file

@ -118,6 +118,9 @@ impl<const ROUNDS: usize> Salsa<ROUNDS> {
x14 = x14.wrapping_add(j14);
x15 = x15.wrapping_add(j15);
j8 = j8.wrapping_add(1);
j9 = j9.wrapping_add((j8 == 0) as u32);
if plaintext.len() >= 64 {
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))]
{
@ -162,9 +165,6 @@ impl<const ROUNDS: usize> Salsa<ROUNDS> {
ciphertext[60..64].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(60).cast::<[u8; 4]>() }) ^ x15.to_le()).to_ne_bytes());
}
j8 = j8.wrapping_add(1);
j9 = j9.wrapping_add((j8 == 0) as u32);
plaintext = &plaintext[64..];
ciphertext = &mut ciphertext[64..];
} else {

View file

@ -80,61 +80,68 @@ fn salsa_poly_create(secret: &SymmetricSecret, header: &PacketHeader, packet_siz
}
/// Attempt AEAD packet encryption and MAC validation. Returns message ID on success.
fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8], header: &PacketHeader, fragments: &[Option<PooledPacketBuffer>], payload: &mut PacketBuffer) -> Option<MessageId> {
packet_frag0_payload_bytes.get(0).map_or(None, |verb| {
let cipher = header.cipher();
match cipher {
security_constants::CIPHER_NOCRYPT_POLY1305 | security_constants::CIPHER_SALSA2012_POLY1305 => {
if (verb & packet_constants::VERB_MASK) == verbs::VL1_HELLO {
let mut total_packet_len = packet_frag0_payload_bytes.len() + packet_constants::HEADER_SIZE;
for f in fragments.iter() {
total_packet_len += f.as_ref().map_or(0, |f| f.len());
}
let _ = payload.append_bytes(packet_frag0_payload_bytes);
for f in fragments.iter() {
let _ = f.as_ref().map(|f| f.as_bytes_starting_at(packet_constants::HEADER_SIZE).map(|f| payload.append_bytes(f)));
}
let (mut salsa, poly1305_key) = salsa_poly_create(secret, header, total_packet_len);
let mac = zerotier_core_crypto::poly1305::compute(&poly1305_key, &payload.as_bytes());
if mac[0..8].eq(&header.mac) {
if cipher == security_constants::CIPHER_SALSA2012_POLY1305 {
salsa.crypt_in_place(payload.as_bytes_mut());
}
Some(u64::from_ne_bytes(header.id))
} else {
None
fn try_aead_decrypt(secret: &SymmetricSecret, packet_frag0_payload_bytes: &[u8], packet_header: &PacketHeader, fragments: &[Option<PooledPacketBuffer>], payload: &mut PacketBuffer) -> Option<MessageId> {
let cipher = packet_header.cipher();
match cipher {
security_constants::CIPHER_NOCRYPT_POLY1305 | security_constants::CIPHER_SALSA2012_POLY1305 => {
let _ = payload.append_bytes(packet_frag0_payload_bytes);
for f in fragments.iter() {
if let Some(f) = f.as_ref() {
if let Ok(f) = f.as_bytes_starting_at(packet_constants::FRAGMENT_HEADER_SIZE) {
let _ = payload.append_bytes(f);
}
}
}
let (mut salsa, poly1305_key) = salsa_poly_create(secret, packet_header, payload.len() + packet_constants::HEADER_SIZE);
let mac = zerotier_core_crypto::poly1305::compute(&poly1305_key, &payload.as_bytes());
if mac[0..8].eq(&packet_header.mac) {
let message_id = u64::from_ne_bytes(packet_header.id);
if cipher == security_constants::CIPHER_SALSA2012_POLY1305 {
salsa.crypt_in_place(payload.as_bytes_mut());
Some(message_id)
} else if (payload.u8_at(0).unwrap_or(0) & packet_constants::VERB_MASK) == verbs::VL1_HELLO {
Some(message_id)
} else {
// Only HELLO is permitted without payload encryption. Drop other packet types if sent this way.
// SECURITY: fail if there is no encryption and the message is not HELLO. No other types are allowed
// to be sent without full packet encryption.
None
}
} else {
None
}
security_constants::CIPHER_AES_GMAC_SIV => {
let mut aes = secret.aes_gmac_siv.get();
aes.decrypt_init(&header.aes_gmac_siv_tag());
aes.decrypt_set_aad(&header.aad_bytes());
// NOTE: if there are somehow missing fragments this part will silently fail,
// but the packet will fail MAC check in decrypt_finish() so meh.
let _ = payload.append_bytes_get_mut(packet_frag0_payload_bytes.len()).map(|b| aes.decrypt(packet_frag0_payload_bytes, b));
for f in fragments.iter() {
f.as_ref().map(|f| {
f.as_bytes_starting_at(packet_constants::FRAGMENT_HEADER_SIZE).map(|f| {
let _ = payload.append_bytes_get_mut(f.len()).map(|b| aes.decrypt(f, b));
})
});
}
aes.decrypt_finish().map_or(None, |tag| {
// AES-GMAC-SIV encrypts the packet ID too as part of its computation of a single
// opaque 128-bit tag, so to get the original packet ID we have to grab it from the
// decrypted tag.
Some(u64::from_ne_bytes(*byte_array_range::<16, 0, 8>(tag)))
})
}
_ => None,
}
})
security_constants::CIPHER_AES_GMAC_SIV => {
let mut aes = secret.aes_gmac_siv.get();
aes.decrypt_init(&packet_header.aes_gmac_siv_tag());
aes.decrypt_set_aad(&packet_header.aad_bytes());
if let Ok(b) = payload.append_bytes_get_mut(packet_frag0_payload_bytes.len()) {
aes.decrypt(packet_frag0_payload_bytes, b);
}
for f in fragments.iter() {
if let Some(f) = f.as_ref() {
if let Ok(f) = f.as_bytes_starting_at(packet_constants::FRAGMENT_HEADER_SIZE) {
if let Ok(b) = payload.append_bytes_get_mut(f.len()) {
aes.decrypt(f, b);
}
}
}
}
if let Some(tag) = aes.decrypt_finish() {
// AES-GMAC-SIV encrypts the packet ID too as part of its computation of a single
// opaque 128-bit tag, so to get the original packet ID we have to grab it from the
// decrypted tag.
Some(u64::from_ne_bytes(*byte_array_range::<16, 0, 8>(tag)))
} else {
None
}
}
_ => None,
}
}
impl<SI: SystemInterface> Peer<SI> {
@ -287,7 +294,7 @@ impl<SI: SystemInterface> Peer<SI> {
// If we made it here it decrypted and passed authentication.
// ---------------------------------------------------------------
debug_event!(si, "[vl1] #{:0>16x} decrypted and authenticated, verb: {:0>2x}", u64::from_be_bytes(packet_header.id), (verb & packet_constants::VERB_MASK) as u32);
debug_event!(si, "[vl1] #{:0>16x} decrypted and authenticated, verb: {} ({:0>2x})", u64::from_be_bytes(packet_header.id), verbs::name(verb & packet_constants::VERB_MASK), (verb & packet_constants::VERB_MASK) as u32);
if (verb & packet_constants::VERB_FLAG_COMPRESSED) != 0 {
let mut decompressed_payload: [u8; packet_constants::SIZE_MAX] = unsafe { MaybeUninit::uninit().assume_init() };

View file

@ -74,6 +74,21 @@ pub mod verbs {
pub const VL1_ECHO: u8 = 0x08;
pub const VL1_PUSH_DIRECT_PATHS: u8 = 0x10;
pub const VL1_USER_MESSAGE: u8 = 0x14;
pub fn name(verb: u8) -> &'static str {
match verb {
VL1_NOP => "VL1_NOP",
VL1_HELLO => "VL1_HELLO",
VL1_ERROR => "VL1_ERROR",
VL1_OK => "VL1_OK",
VL1_WHOIS => "VL1_WHOIS",
VL1_RENDEZVOUS => "VL1_RENDEZVOUS",
VL1_ECHO => "VL1_ECHO",
VL1_PUSH_DIRECT_PATHS => "VL1_PUSH_DIRECT_PATHS",
VL1_USER_MESSAGE => "VL1_USER_MESSAGE",
_ => "???",
}
}
}
/// Default maximum payload size for UDP transport.
@ -122,7 +137,7 @@ pub mod packet_constants {
pub const MAC_FIELD_INDEX: usize = 19;
/// Mask to select cipher from header flags field.
pub const FLAGS_FIELD_MASK_CIPHER: u8 = 0x30;
pub const FLAGS_FIELD_MASK_CIPHER: u8 = 0x38;
/// Mask to select packet hops from header flags field.
pub const FLAGS_FIELD_MASK_HOPS: u8 = 0x07;
@ -178,13 +193,13 @@ pub mod security_constants {
/// Packet is encrypted and authenticated with Salsa20/12 and Poly1305.
/// Construction is the same as that which is used in the NaCl secret box functions.
pub const CIPHER_SALSA2012_POLY1305: u8 = 0x10;
pub const CIPHER_SALSA2012_POLY1305: u8 = 0x08;
/// Formerly 'NONE' which is deprecated; reserved for future use.
pub const CIPHER_RESERVED: u8 = 0x20;
pub const CIPHER_RESERVED: u8 = 0x10;
/// Packet is encrypted and authenticated with AES-GMAC-SIV (AES-256).
pub const CIPHER_AES_GMAC_SIV: u8 = 0x30;
pub const CIPHER_AES_GMAC_SIV: u8 = 0x18;
/// KBKDF usage label indicating a key used to HMAC packets for extended authentication.
pub const KBKDF_KEY_USAGE_LABEL_PACKET_HMAC: u8 = b'M';

View file

@ -183,10 +183,10 @@ pub async fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity,
}
}
#[cfg(unix)]
pub fn c_strerror() -> String {
unsafe { std::ffi::CStr::from_ptr(libc::strerror(*libc::__error()).cast()).to_string_lossy().to_string() }
}
//#[cfg(unix)]
//pub fn c_strerror() -> String {
// unsafe { std::ffi::CStr::from_ptr(libc::strerror(*libc::__error()).cast()).to_string_lossy().to_string() }
//}
#[cfg(test)]
mod tests {