From 81011ec361b1fcabe7b9c4de9c40a543d7049dab Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Mon, 20 Apr 2026 00:43:28 +0900 Subject: [PATCH] core/crypto/noise: Add support for deferred patterns --- core/crypto/noise/api.odin | 126 ++++++++++ core/crypto/noise/doc.odin | 2 +- core/crypto/noise/patterns.odin | 412 ++++++++++++++++++++++++++++++++ core/crypto/noise/protocol.odin | 4 - 4 files changed, 539 insertions(+), 5 deletions(-) diff --git a/core/crypto/noise/api.odin b/core/crypto/noise/api.odin index b16dbab58..30387c2b4 100644 --- a/core/crypto/noise/api.odin +++ b/core/crypto/noise/api.odin @@ -459,3 +459,129 @@ cipherstates_cs :: proc(self: ^Cipher_States, seal_key: bool) -> ^Cipher_State { } unreachable() } + +// split_protocol_string splits a protocol string into individual components. +@(require_results) +split_protocol_string :: proc(protocol_name: string) -> (Handshake_Pattern, ecdh.Curve, aead.Algorithm, hash.Algorithm, Status) { + str := protocol_name + + if len(str) > 255 { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + + s, ok := strings.split_by_byte_iterator(&str, '_') + if !ok || s != "Noise" { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + + if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + + pattern: Handshake_Pattern + switch s { + case "N" : pattern = .N + case "K" : pattern = .K + case "X" : pattern = .X + case "XX": pattern = .XX + case "NK": pattern = .NK + case "NN": pattern = .NN + case "KN": pattern = .KN + case "KK": pattern = .KK + case "NX": pattern = .NX + case "KX": pattern = .KX + case "XN": pattern = .XN + case "IN": pattern = .IN + case "XK": pattern = .XK + case "IK": pattern = .IK + case "IX": pattern = .IX + case "NK1": pattern = .NK1 + case "NX1": pattern = .NX1 + case "X1N": pattern = .X1N + case "X1K": pattern = .X1K + case "XK1": pattern = .XK1 + case "X1K1": pattern = .X1K1 + case "X1X": pattern = .X1X + case "XX1": pattern = .XX1 + case "X1X1": pattern = .X1X1 + case "K1N": pattern = .K1N + case "K1K": pattern = .K1K + case "KK1": pattern = .KK1 + case "K1K1": pattern = .K1K1 + case "K1X": pattern = .K1X + case "KX1": pattern = .KX1 + case "K1X1": pattern = .K1X1 + case "I1N": pattern = .I1N + case "I1K": pattern = .I1K + case "IK1": pattern = .IK1 + case "I1K1": pattern = .I1K1 + case "I1X": pattern = .I1X + case "IX1": pattern = .IX1 + case "I1X1": pattern = .I1X1 + case "Npsk0": pattern = .Npsk0 + case "Kpsk0": pattern = .Kpsk0 + case "Xpsk1": pattern = .Xpsk1 + case "NNpsk0": pattern = .NNpsk0 + case "NNpsk2": pattern = .NNpsk2 + case "NKpsk0": pattern = .NKpsk0 + case "NKpsk2": pattern = .NKpsk2 + case "NXpsk2": pattern = .NXpsk2 + case "XNpsk3": pattern = .XNpsk3 + case "XKpsk3": pattern = .XKpsk3 + case "XXpsk3": pattern = .XXpsk3 + case "KNpsk0": pattern = .KNpsk0 + case "KNpsk2": pattern = .KNpsk2 + case "KKpsk0": pattern = .KKpsk0 + case "KKpsk2": pattern = .KKpsk2 + case "KXpsk2": pattern = .KXpsk2 + case "INpsk1": pattern = .INpsk1 + case "INpsk2": pattern = .INpsk2 + case "IKpsk1": pattern = .IKpsk1 + case "IKpsk2": pattern = .IKpsk2 + case "IXpsk2": pattern = .IXpsk2 + case: pattern = .Invalid + } + + if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + dh: ecdh.Curve + switch s { + case "25519": dh = .X25519 + case "448": dh = .X448 + case: dh = .Invalid + } + + if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + cipher: aead.Algorithm + switch s { + case "AESGCM": cipher = .AES_GCM_256 + case "ChaChaPoly": cipher = .CHACHA20POLY1305 + case: cipher = .Invalid + } + + if s, ok = strings.split_by_byte_iterator(&str, '_'); !ok { + return .Invalid, .Invalid, .Invalid, .Invalid, .Invalid_Protocol_String + } + hash: hash.Algorithm + switch s { + case "SHA512": hash = .SHA512 + case "SHA256": hash = .SHA256 + case "BLAKE2s": hash = .BLAKE2S + case "BLAKE2b": hash = .BLAKE2B + case: hash = .Invalid + } + + status: Status + + if len(str) != 0 { + status = .Invalid_Protocol_String + } + if pattern == .Invalid || dh == .Invalid || cipher == .Invalid || hash == .Invalid { + status = .Invalid_Protocol_String + } + + return pattern, dh, cipher, hash, status +} diff --git a/core/crypto/noise/doc.odin b/core/crypto/noise/doc.odin index 49416c6f0..734a1dc6e 100644 --- a/core/crypto/noise/doc.odin +++ b/core/crypto/noise/doc.odin @@ -1,7 +1,7 @@ /* An implementation of the Noise Protocol Framework (Revision 34). -The `fallback` modifier and deferred/multi-PSK patterns are not supported +The `fallback` modifier and multi-PSK patterns are not supported for the sake of simplicity. See: diff --git a/core/crypto/noise/patterns.odin b/core/crypto/noise/patterns.odin index 73a4221f0..14e5b6498 100644 --- a/core/crypto/noise/patterns.odin +++ b/core/crypto/noise/patterns.odin @@ -51,6 +51,31 @@ Handshake_Pattern :: enum { IK, IX, + // Deferred patterns + NK1, + NX1, + X1N, + X1K, + XK1, + X1K1, + X1X, + XX1, + X1X1, + K1N, + K1K, + KK1, + K1K1, + K1X, + KX1, + K1X1, + I1N, + I1K, + IK1, + I1K1, + I1X, + IX1, + I1X1, + // Recommended PSK patterns Npsk0, Kpsk0, @@ -132,6 +157,7 @@ HANDSHAKE_PATTERNS := [Handshake_Pattern]^Message_Pattern { .N = &PATTERN_N, .K = &PATTERN_K, .X = &PATTERN_X, + .XX = &PATTERN_XX, .NK = &PATTERN_NK, .NN = &PATTERN_NN, @@ -144,6 +170,31 @@ HANDSHAKE_PATTERNS := [Handshake_Pattern]^Message_Pattern { .XK = &PATTERN_XK, .IK = &PATTERN_IK, .IX = &PATTERN_IX, + + .NK1 = &PATTERN_NK1, + .NX1 = &PATTERN_NX1, + .X1N = &PATTERN_X1N, + .X1K = &PATTERN_X1K, + .XK1 = &PATTERN_XK1, + .X1K1 = &PATTERN_X1K1, + .X1X = &PATTERN_X1X, + .XX1 = &PATTERN_XX1, + .X1X1 = &PATTERN_X1X1, + .K1N = &PATTERN_K1N, + .K1K = & PATTERN_K1K, + .KK1 = &PATTERN_KK1, + .K1K1 = &PATTERN_K1K1, + .K1X = &PATTERN_K1X, + .KX1 = &PATTERN_KX1, + .K1X1 = &PATTERN_K1X1, + .I1N = &PATTERN_I1N, + .I1K = &PATTERN_I1K, + .IK1 = &PATTERN_IK1, + .I1K1 = &PATTERN_I1K1, + .I1X = &PATTERN_I1X, + .IX1 = &PATTERN_IX1, + .I1X1 = &PATTERN_I1X1, + .Npsk0 = &PATTERN_Npsk0, .Kpsk0 = &PATTERN_Kpsk0, .Xpsk1 = &PATTERN_Xpsk1, @@ -378,6 +429,367 @@ PATTERN_IX : Message_Pattern = { // ---------------------------------------------------------------------------------------- +// ------------- DEFERRED PATTERNS -------------------------------------------------------- + +// NK1: +// <- s +// ... +// -> e +// <- e, ee, es +@(private,rodata) +PATTERN_NK1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e}, + {.e, .ee, .es}, + }, +} + +// NX1: +// -> e +// <- e, ee, s +// -> es +@(private,rodata) +PATTERN_NX1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s}, + {.es}, + }, +} + +// X1N: +// -> e +// <- e, ee +// -> s +// <- se +@(private,rodata) +PATTERN_X1N : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee}, + {.s}, + {.se}, + }, +} + +// X1K: +// <- s +// ... +// -> e, es +// <- e, ee +// -> s +// <- se +@(private,rodata) +PATTERN_X1K : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es}, + {.e, .ee}, + {.s}, + {.se}, + }, +} + +// XK1: +// <- s +// ... +// -> e +// <- e, ee, es +// -> s, se +@(private,rodata) +PATTERN_XK1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e}, + {.e, .ee, .es}, + {.s, .se}, + }, +} + +// X1K1: +// <- s +// ... +// -> e +// <- e, ee, es +// -> s +// <- se +@(private,rodata) +PATTERN_X1K1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e}, + {.e, .ee, .es}, + {.s}, + {.se}, + }, +} + +// X1X: +// -> e +// <- e, ee, s, es +// -> s +// <- se +@(private,rodata) +PATTERN_X1X : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s, .es}, + {.s}, + {.se}, + }, +} + +// XX1: +// -> e +// <- e, ee, s +// -> es, s, se +@(private,rodata) +PATTERN_XX1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s}, + {.es, .s, .se}, + }, +} + +// X1X1: +// -> e +// <- e, ee, s +// -> es, s +// <- se +@(private,rodata) +PATTERN_X1X1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e}, + {.e, .ee, .s}, + {.es, .s}, + {.se}, + }, +} + +// K1N: +// -> s +// ... +// -> e +// <- e, ee +// -> se +@(private,rodata) +PATTERN_K1N : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e,}, + {.e, .ee}, + {.se}, + }, +} + +// K1K: +// -> s +// <- s +// ... +// -> e, es +// <- e, ee +// -> se +@(private,rodata) +PATTERN_K1K : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e, .es}, + {.e, .ee}, + {.se}, + }, +} + +// KK1: +// -> s +// <- s +// ... +// -> e +// <- e, ee, se, es +@(private,rodata) +PATTERN_KK1 : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e}, + {.e, .ee, .se, .es}, + }, +} + +// K1K1: +// -> s +// <- s +// ... +// -> e +// <- e, ee, es +// -> se +@(private,rodata) +PATTERN_K1K1 : Message_Pattern = { + pre_messages = {.ini_s, .res_s}, + messages = { + {.e}, + {.e, .ee, .es}, + {.se}, + }, +} + +// K1X: +// -> s +// ... +// -> e +// <- e, ee, s, es +// -> se +@(private,rodata) +PATTERN_K1X : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .s, .es}, + {.se}, + }, +} + +// KX1: +// -> s +// ... +// -> e +// <- e, ee, se, s +// -> es +@(private,rodata) +PATTERN_KX1 : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .se, .s}, + {.es}, + }, +} + +// K1X1: +// -> s +// ... +// -> e +// <- e, ee, s +// -> se, es +@(private,rodata) +PATTERN_K1X1 : Message_Pattern = { + pre_messages = {.ini_s}, + messages = { + {.e}, + {.e, .ee, .s}, + {.se, .es}, + }, +} + +// I1N: +// -> e, s +// <- e, ee +// -> se +@(private,rodata) +PATTERN_I1N : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee}, + {.se}, + }, +} + +// I1K: +// <- s +// ... +// -> e, es, s +// <- e, ee +// -> se +@(private,rodata) +PATTERN_I1K : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .es, .s}, + {.e, .ee}, + {.se}, + }, +} + +// IK1: +// <- s +// ... +// -> e, s +// <- e, ee, se, es +@(private,rodata) +PATTERN_IK1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .s}, + {.e, .ee, .se, .es}, + }, +} + +// I1K1: +// <- s +// ... +// -> e, s +// <- e, ee, es +// -> se +@(private,rodata) +PATTERN_I1K1 : Message_Pattern = { + pre_messages = {.res_s}, + messages = { + {.e, .s}, + {.e, .ee, .es}, + {.se}, + }, +} + +// I1X: +// -> e, s +// <- e, ee, s, es +// -> se +@(private,rodata) +PATTERN_I1X : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .s, .es}, + {.se}, + }, +} + +// IX1: +// -> e, s +// <- e, ee, se, s +// -> es +@(private,rodata) +PATTERN_IX1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .se, .s}, + {.es}, + }, +} + +// I1X1: +// -> e, s +// <- e, ee, s +// -> se, es +@(private,rodata) +PATTERN_I1X1 : Message_Pattern = { + pre_messages = nil, + messages = { + {.e, .s}, + {.e, .ee, .s}, + {.se, .es}, + }, +} + +// ---------------------------------------------------------------------------------------- + // ------------- PSK PATTERNS ------------------------------------------------------------- // Npsk0: diff --git a/core/crypto/noise/protocol.odin b/core/crypto/noise/protocol.odin index 1b6f1114e..00d8cbb3d 100644 --- a/core/crypto/noise/protocol.odin +++ b/core/crypto/noise/protocol.odin @@ -817,10 +817,6 @@ handshakestate_write_message :: proc(self: ^Handshake_State, payload, dst: []byt handshakestate_read_message :: proc(self: ^Handshake_State, message, dst: []byte, allocator := context.allocator) -> ([]byte, Status) { ensure(self.status == .Handshake_Pending, "crypto/noise: invalid state for ReadMessage") - if len(message) < MIN_DH_SIZE { - return nil, .Invalid_Handshake_Message - } - protocol := &self.symmetric_state.protocol d_len := dh_len(&self.symmetric_state.protocol)