From 16e123f0cfe71a04507f0ab870c839e7b87a5c73 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 16 Mar 2024 13:58:21 +0000 Subject: [PATCH] More implementation --- noise.nimble | 2 +- src/noiseprotocol.nim | 203 +++++++++++++++++++++----------- src/noiseprotocol/constants.nim | 5 +- 3 files changed, 135 insertions(+), 75 deletions(-) diff --git a/noise.nimble b/noise.nimble index 88d6400..8cda807 100644 --- a/noise.nimble +++ b/noise.nimble @@ -3,4 +3,4 @@ description = "Noise-C library wrapper" license = "MIT" requires "nim >= 1.4.8" srcDir = "src" -version = "20230509" +version = "20240316" diff --git a/src/noiseprotocol.nim b/src/noiseprotocol.nim index 7f4e1e8..6c74a71 100644 --- a/src/noiseprotocol.nim +++ b/src/noiseprotocol.nim @@ -5,6 +5,8 @@ {.pragma: noiseHeader, header: "noise/protocol.h".} {.pragma: noise, importc: "noise_$1", noiseHeader.} +{.pragma: handshakestate, importc: "noise_handshakestate_$1", noiseHeader.} +{.pragma: dhstate, importc: "noise_dhstate_$1", noiseHeader.} {.pragma: noiseDecl, importc: "NOISE_$1", noDecl, noiseHeader.} {.pragma: noiseType, importc: "Noise$1", noiseHeader.} @@ -12,8 +14,17 @@ include ./noiseprotocol/constants type NoiseError = object of CatchableError -template checkError(err: ERROR, msg = "") = - if err != ERROR.NONE: raise newException(NoiseError, $err) + +proc checkError(err: ERROR) = + proc strerror(err: ERROR; buf: ptr char; size: csize_t) {.noise.} + if err != ERROR.NONE: + var msg = newString(32) + strerror(err, msg[0].addr, msg.len.csize_t) + for i, c in msg: + if c == '\0': + msg.setLen(i) + break + raise newException(NoiseError, msg) proc init* = ## Initializes the Noise-c library. @@ -21,7 +32,6 @@ proc init* = checkError noise_init() type - HandshakeState* {.noiseType.} = distinct pointer ProtocolId* {.noiseType.} = object prefix_id*: cint # Protocol name prefix pattern_id*: cint # Handshake pattern @@ -29,133 +39,182 @@ type cipher_id*: cint # Cipher algorithm identifier hash_id*: cint # Hash algorithm identifier hybrid_id*: cint # Hybrid forward secrecy algorithm identifier + HandshakeState* {.noiseType.} = distinct pointer + DHState* {.noiseType.} = distinct pointer + CipherState* {.noiseType.} = distinct pointer + +proc isNil*(p: HandshakeState): bool {.borrow.} +proc isNil*(p: DHState): bool {.borrow.} +proc isNil*(p: CipherState): bool {.borrow.} proc newById*(id: ProtocolId; role: ROLE): HandshakeState = - proc handshakestate_new_by_id( - s: ptr HandshakeState; p: ptr ProtocolId; r: ROLE): ERROR {.noise.} - checkError handshakestate_new_by_id(addr result, unsafeAddr id, role) + proc new_by_id( + s: ptr HandshakeState; p: ptr ProtocolId; r: ROLE): ERROR {.handshakestate.} + checkError new_by_id(addr result, addr id, role) proc newByName*(protocolName: string; role: ROLE): HandshakeState = ## Creates a new HandshakeState object by protocol name. - proc handshakestate_new_by_name( - s: ptr HandshakeState; p: cstring; r: ROLE): ERROR {.noise.} - checkError handshakestate_new_by_name(addr result, protocolName, role) + proc new_by_name( + s: ptr HandshakeState; p: cstring; r: ROLE): ERROR {.handshakestate.} + checkError new_by_name(addr result, protocolName, role) proc destroy*(state: HandshakeState) = ## Frees a HandshakeState object after destroying all sensitive material. - proc handshakestate_free(s: HandshakeState): ERROR {.noise.} - discard handshakestate_free(state) + proc free(s: HandshakeState): ERROR {.handshakestate.} + discard free(state) -proc getRole*(state: HandshakeState): Role {. - importc: "noise_handshakestate_get_role".} +proc get_role*(state: HandshakeState): Role {.handshakestate.} ## Gets the role that a HandshakeState object is playing. proc getProtocolId*(state: HandshakeState): ProtocolId = ## Gets the protocol identifier associated with a HandshakeState object. - proc handshakestate_get_protocol_id(s: HandshakeState; p: ptr ProtocolId): ERROR {.noise.} - checkError handshakestate_get_protocol_id(state, addr result) - -type DHState* {.noiseType.} = distinct pointer + proc get_protocol_id(s: HandshakeState; p: ptr ProtocolId): ERROR {.handshakestate.} + checkError get_protocol_id(state, addr result) -proc getLocalPairDh*(state: HandshakeState): DHState {. - noise: "handshakestate_get_local_keypair_dh".} +proc get_local_keypair_dh*(state: HandshakeState): DHState {.handshakestate.} ## Gets the DHState object that contains the local static keypair. -proc getRemotePublicKeyDh*(state: HandshakeState): DHState {. - noise: "handshakestate_get_remote_public_key_dh".} +proc get_remote_public_key_dh*(state: HandshakeState): DHState {.handshakestate.} ## Gets the DHState object that contains the remote static public key. -proc getFixedEphemeralDh*(state: HandshakeState): DHState {. - noise: "handshakestate_get_fixed_ephemeral_dh".} +proc get_fixed_ephemeral_dh*(state: HandshakeState): DHState {.handshakestate.} ## Gets the DHState object that contains the local ephemeral keypair. -proc getFixedHybridDh*(state: HandshakeState): DHState {. - noise: "handshakestate_get_fixed_hybrid_dh".} +proc get_fixed_hybrid_dh*(state: HandshakeState): DHState {.handshakestate.} ## Gets the DHState object that contains the local additional hybrid secrecy keypair. -proc needsPreSharedKey*(state: HandshakeState): bool {. - noise: "handshakestate_needs_pre_shared_key".} +proc set_public_key*(state: DHState; key: openarray[byte]) = + ## Sets the public key in a DHState object. + assert key.len > 0 + proc set_public_key(s: DHState; k: ptr byte; l: csize_t): ERROR {.dhstate.} + checkError set_public_key(state, addr key[0], csize_t len(key)) + +proc get_public_key*(state: DHState): seq[byte] = + ## Gets the public key value from a DHState object. + proc get_public_key_length(s: DHState): csize_t {.dhstate.} + proc get_public_key(s: DHState; k: ptr byte; l: csize_t): ERROR {.dhstate.} + result.setLen(int get_public_key_length(state)) + checkError get_public_key(state, addr result[0], csize_t len(result)) + +proc needs_pre_shared_key*(state: HandshakeState): bool {.handshakestate.} ## Determine if a HandshakeState object requires a pre shared key. -proc hasPreSharedKey*(state: HandshakeState): bool {. - noise: "handshakestate_has_pre_shared_key".} +proc has_pre_shared_key*(state: HandshakeState): bool {.handshakestate.} ## Determine if a HandshakeState object has already been configured with a pre shared key. proc setPreSharedKey*(state: HandshakeState; key: openarray[byte]) = ## Sets the pre shared key for a HandshakeState. - proc handshakestate_set_pre_shared_key(s: HandshakeState; k: ptr byte; l: csize_t): ERROR {.noise.} - checkError handshakestate_set_pre_shared_key(state, unsafeAddr key[0], csize_t len(key)) + proc set_pre_shared_key(s: HandshakeState; k: ptr byte; l: csize_t): ERROR {.handshakestate.} + checkError set_pre_shared_key(state, addr key[0], csize_t len(key)) -proc setPrologue*(state: HandshakeState; prologue: pointer; len: int) = +proc set_prologue*(state: HandshakeState; prologue: pointer; len: int) = ## Sets the prologue for a HandshakeState. proc handshakestate_set_prologue(s: HandshakeState; p: pointer; l: csize_t): ERROR {.noise.} - checkError handshakestate_set_prologue(state, prologue, csize_t len) + checkError handshakestate_set_prologue(state, prologue, len.csize_t) -proc setPrologue*(state: HandshakeState; prologue: openarray[byte]) = +proc set_prologue*(state: HandshakeState; prologue: openarray[byte]) = ## Sets the prologue for a HandshakeState. - setPrologue(state, unsafeAddr prologue[0], len(prologue)) + setPrologue(state, addr prologue[0], len(prologue)) -proc needsLocalKeypair*(state: HandshakeState): bool {. - noise: "handshakestate_needs_local_keypair".} +proc needs_local_keypair*(state: HandshakeState): bool {.handshakestate.} ## Determine if a HandshakeState still needs to be configured with a local keypair. -proc hasLocalKeypair*(state: HandshakeState): bool {. - noise: "handshakestate_has_local_keypair".} +proc has_local_keypair*(state: HandshakeState): bool {.handshakestate.} ## Determine if a HandshakeState has been configured with a local keypair. -proc needRemoteKeypair*(state: HandshakeState): bool {. - noise: "handshakestate_needs_remote_public_key".} +proc needs_remote_public_key*(state: HandshakeState): bool {.handshakestate.} ## Determine if a HandshakeState still needs to be configured ## with a remote public key before the protocol can start. -proc hasRemotePublicKey*(state: HandshakeState): bool {. - noise: "handshakestate_has_remote_public_key".} +proc has_remote_public_key*(state: HandshakeState): bool {.handshakestate.} ## Determine if a HandshakeState has a remote public key. -proc start*(state: HandshakeState): ERROR {. - noise: "handshakestate_start".} +proc start*(state: HandshakeState) = ## Starts the handshake on a HandshakeState object. + proc handshakestate_start(state: HandshakeState): ERROR {.noise.} + checkError handshakestate_start(state) -proc fallback*(state: HandshakeState): ERROR {. - noise: "handshakestate_fallback".} +proc fallback*(state: HandshakeState): ERROR {.handshakestate.} ## Falls back to the "XXfallback" handshake pattern. -proc fallbackTo*(state: HandshakeState; pat: PATTERN): ERROR {. - noise: "handshakestate_fallback_to".} +proc fallback_to*(state: HandshakeState; pat: PATTERN): ERROR {.handshakestate.} ## Falls back to another handshake pattern. -proc getAction*(state: HandshakeState): ACTION {. - noise: "handshakestate_get_action".} +proc get_action*(state: HandshakeState): ACTION {.handshakestate.} ## Gets the next action the application should perform for ## the handshake phase of the protocol. -type Buffer {.noiseType.} = object - data: pointer - size, max_size: csize_t - -proc writeMessage*[T: byte|char](state: HandshakeState; payload: openarray[T]) = +type + Buffer* {.noiseType.} = object + data: ptr uint8 ## Points to the data in the buffer + max_size: csize_t ## Maximum size of the data in the buffer + size: csize_t ## Current size of the data in the buffer + +proc buffer_init(buf: ptr Buffer) {.noise.} +proc buffer_set_input(buf: ptr Buffer; p: pointer; len: csize_t) {.noise.} +proc buffer_set_output(buf: ptr Buffer; p: pointer; len: csize_t) {.noise.} +proc buffer_set_inout(buf: ptr Buffer; p: pointer; len, cap: csize_t) {.noise.} + +template useBuffer(buf: var Buffer; payload: var seq[byte]; body: untyped) = + buf.data = payload[0].addr + buf.max_size = csize_t(capacity(payload)) + buf.size = csize_t(len(payload)) + body + setLen(payload, buf.size) + +{.push checks: off.} +proc toBuffer(data: seq[byte]): Buffer = + result.data = data[0].addr + result.max_size = data.capacity.csize_t + result.size = data.len.csize_t +{.pop.} + +proc write_message*(state: HandshakeState; message: var seq[byte]; payload: seq[byte]) = ## Writes a message payload using a HandshakeState. - proc handshakestate_write_message(s: HandshakeState; b: addr Buffer): ERROR {.noise.} - assert len(payload) > 0 - var buf = Buffer(data: unsafeAddr payload[0], size: len(payload), max_size: len(payload)) - checkError handshakestate_write_message(state, addr buf) - -proc readMessage*(state: HandshakeState): seq[byte] = + proc handshakestate_write_message(s: HandshakeState; a, b: ptr Buffer): ERROR {.noise.} + var + a = message.toBuffer + b = payload.toBuffer + checkError handshakestate_write_message(state, addr a, addr b) + message.setLen(a.size) + +proc read_message*(state: HandshakeState; message: seq[byte]; payload: var seq[byte]) = ## Reads a message payload using a HandshakeState. - proc handshakestate_read_message(s: HandshakeState; b: ptr Buffer): ERROR {.noise.} - setLen(result, MAX_PAYLOAD_LEN) - var buf = Buffer(data: addr result[0], max_size: csize_t len(result)) - checkError handshakestate_read_message(state, addr buf) - setLen(result, buf.size) - -proc split*(state: HandshakeState): tuple[send: HandshakeState, receive: HandshakeState] = + proc handshakestate_read_message(s: HandshakeState; a, b: ptr Buffer): ERROR {.noise.} + var + m = message.toBuffer + p = payload.toBuffer + checkError handshakestate_read_message(state, addr m, addr p) + payload.setLen(p.size) + +proc split*(state: HandshakeState): tuple[send: CipherState, recv: CipherState] = ## Splits the transport encryption CipherState objects out of ## this HandshakeState object. - proc handshakestate_split(st: HandshakeState; s, r: ptr HandshakeState): ERROR {.noise.} + proc handshakestate_split(st: HandshakeState; s, r: ptr CipherState): ERROR {.noise.} checkError handshakestate_split(state, addr result[0], addr result[1]) proc getHandshakeHash*(state: HandshakeState; buf: var seq[byte]) = ## Gets the handshake hash value once the handshake ends. - proc handshakestate_get_handshake_hash(s: HandshakeState; h: ptr byte; l: csize_t): ERROR {.noise.} + proc get_handshakestate_hash(s: HandshakeState; h: ptr byte; l: csize_t): ERROR {.handshakestate.} assert len(buf) > 0 - checkError handshakestate_get_handshake_hash(state, addr buf[0], csize_t len(buf)) + checkError get_handshakestate_hash(state, addr buf[0], csize_t len(buf)) + +proc destroy*(state: var HandshakeState) = + ## Frees a HandshakeState object after destroying all sensitive material. + proc handshakestate_free(s: HandshakeState): ERROR {.noise.} + checkError handshakestate_free(state) + reset(state) + +proc encrypt*(state: CipherState; data: var seq[byte]) = + ## Encrypts a block of data with this CipherState object. + proc cipherstate_encrypt(s: CipherState; b: ptr Buffer): ERROR {.noise.} + var buf = data.toBuffer + checkError cipherstate_encrypt(state, addr buf) + data.setLen(buf.size) + +proc decrypt*(state: CipherState; data: var seq[byte]) = + ## Decrypts a block of data with this CipherState object. + proc cipherstate_decrypt(s: CipherState; b: ptr Buffer): ERROR {.noise.} + var buf = data.toBuffer + checkError cipherstate_decrypt(state, addr buf) + data.setLen(buf.size) diff --git a/src/noiseprotocol/constants.nim b/src/noiseprotocol/constants.nim index ee1aec0..4543dae 100644 --- a/src/noiseprotocol/constants.nim +++ b/src/noiseprotocol/constants.nim @@ -132,10 +132,11 @@ type INVALID_FORMAT = noiseId('E', 16) INVALID_SIGNATURE = noiseId('E', 17) -var - MAX_PAYLOAD_LEN* {.noiseDecl.}: cint +const + MAX_PAYLOAD_LEN* = 65535 ## Maximum length of a packet payload +var MAX_PROTOCOL_NAME* {.noiseDecl.}: cint ## Maximum length of a protocol name string -- 2.45.2