From ed58d244c10971d0c953e8a7bfa283521041c302 Mon Sep 17 00:00:00 2001 From: Jazz Turner-Baggs <473256+jazzz@users.noreply.github.com> Date: Mon, 16 Feb 2026 13:26:48 -0800 Subject: [PATCH] remove naxoltl --- src/naxolotl.nim | 7 -- src/naxolotl/chacha.nim | 63 ----------- src/naxolotl/curve25519.nim | 123 --------------------- src/naxolotl/errors.nim | 11 -- src/naxolotl/naxolotl.nim | 210 ------------------------------------ src/naxolotl/types.nim | 18 ---- src/naxolotl/utils.nim | 55 ---------- 7 files changed, 487 deletions(-) delete mode 100644 src/naxolotl.nim delete mode 100644 src/naxolotl/chacha.nim delete mode 100644 src/naxolotl/curve25519.nim delete mode 100644 src/naxolotl/errors.nim delete mode 100644 src/naxolotl/naxolotl.nim delete mode 100644 src/naxolotl/types.nim delete mode 100644 src/naxolotl/utils.nim diff --git a/src/naxolotl.nim b/src/naxolotl.nim deleted file mode 100644 index f19bbaf..0000000 --- a/src/naxolotl.nim +++ /dev/null @@ -1,7 +0,0 @@ -import naxolotl/[ - naxolotl, - curve25519, - errors -] - -export naxolotl, curve25519, NaxolotlError \ No newline at end of file diff --git a/src/naxolotl/chacha.nim b/src/naxolotl/chacha.nim deleted file mode 100644 index a20bab9..0000000 --- a/src/naxolotl/chacha.nim +++ /dev/null @@ -1,63 +0,0 @@ -import nim_chacha20_poly1305/[common, chacha20_poly1305, poly1305] -import std/[sysrand] -import results -import strformat - -import types -import errors - - -proc encryptWithChaCha20Poly1305*(msgKey: MessageKey, plaintext: var openArray[byte], associatedData: openArray[byte]) : (Nonce, CipherText) = - - var nonce : Nonce - discard urandom(nonce) - - var tag: Tag - var ciphertext = newSeq[byte](plaintext.len + tag.len) - - var counter : Counter = 0 - - # TODO: check plaintext mutability requirement - chacha20_aead_poly1305_encrypt( - Key(msgKey), - nonce, - counter, - associatedData, - plaintext, - ciphertext.toOpenArray(0, plaintext.high), - tag - ) - - # Combine tag with cipherkey for ease of transport and consistency with other implementations - copyMem(addr ciphertext[plaintext.len], unsafeAddr tag[0], tag.len) - (nonce, ciphertext) - - -proc decryptWithChaCha20Poly1305*(msgKey: MessageKey, nonce: Nonce, ciphertext: var openArray[byte], associatedData: openArray[byte]) : Result[seq[byte], NaxolotlError] = - var tag : Tag - if ciphertext.len <= tag.len: - return err(NaxolotlError(code: errInvalidInput, context: fmt"ciphertext is less than {tag.len} bytes. Expected `ciphertext || tag`" )) - - copyMem(addr tag[0], unsafeAddr ciphertext[^tag.len], tag.len) - - var plaintext = newSeq[byte](ciphertext.len - tag.len) - - var computedTag: Tag - var counter : Counter = 0 - - chacha20_aead_poly1305_decrypt( - Key(msgKey), - nonce, - counter, - associatedData, - plaintext, - ciphertext.toOpenArray(0,ciphertext.high - tag.len), - computedTag - ) - - if not poly1305_verify(tag, computedTag): - return err(NaxolotlError(code: errMessageAuthentication, context: fmt"Got Tag: {tag} expected: {computedTag}")) - - - ok(plaintext) - \ No newline at end of file diff --git a/src/naxolotl/curve25519.nim b/src/naxolotl/curve25519.nim deleted file mode 100644 index 140b928..0000000 --- a/src/naxolotl/curve25519.nim +++ /dev/null @@ -1,123 +0,0 @@ -# See https://github.com/vacp2p/nim-libp2p/blob/master/libp2p/crypto/curve25519.nim - -import bearssl/[ec, rand] -import results -from stew/assign2 import assign -export results - -const Curve25519KeySize* = 32 - -type - Curve25519* = object - Curve25519Key* = array[Curve25519KeySize, byte] - Curve25519Error* = enum - Curver25519GenError - -proc intoCurve25519Key*(s: openArray[byte]): Curve25519Key = - assert s.len == Curve25519KeySize - assign(result, s) - -proc getBytes*(key: Curve25519Key): seq[byte] = - @key - -proc byteswap(buf: var Curve25519Key) {.inline.} = - for i in 0 ..< 16: - let x = buf[i] - buf[i] = buf[31 - i] - buf[31 - i] = x - -proc mul*(_: type[Curve25519], point: var Curve25519Key, multiplier: Curve25519Key) = - let defaultBrEc = ecGetDefault() - - # multiplier needs to be big-endian - var multiplierBs = multiplier - multiplierBs.byteswap() - let res = defaultBrEc.mul( - addr point[0], - Curve25519KeySize, - addr multiplierBs[0], - Curve25519KeySize, - EC_curve25519, - ) - assert res == 1 - -proc mulgen(_: type[Curve25519], dst: var Curve25519Key, point: Curve25519Key) = - let defaultBrEc = ecGetDefault() - - var rpoint = point - rpoint.byteswap() - - let size = - defaultBrEc.mulgen(addr dst[0], addr rpoint[0], Curve25519KeySize, EC_curve25519) - - assert size == Curve25519KeySize - -proc public*(private: Curve25519Key): Curve25519Key = - Curve25519.mulgen(result, private) - -proc random*(_: type[Curve25519Key], rng: var HmacDrbgContext): Curve25519Key = - var res: Curve25519Key - let defaultBrEc = ecGetDefault() - let len = ecKeygen( - PrngClassPointerConst(addr rng.vtable), defaultBrEc, nil, addr res[0], EC_curve25519 - ) - # Per bearssl documentation, the keygen only fails if the curve is - # unrecognised - - doAssert len == Curve25519KeySize, "Could not generate curve" - - res - -const FieldElementSize* = Curve25519KeySize - -type FieldElement* = Curve25519Key - -# Convert bytes to FieldElement -proc bytesToFieldElement*(bytes: openArray[byte]): Result[FieldElement, string] = - if bytes.len != FieldElementSize: - return err("Field element size must be 32 bytes") - ok(intoCurve25519Key(bytes)) - -# Convert FieldElement to bytes -proc fieldElementToBytes*(fe: FieldElement): seq[byte] = - fe.getBytes() - -# Generate a random FieldElement -proc generateRandomFieldElement*(): Result[FieldElement, string] = - let rng = HmacDrbgContext.new() - if rng.isNil: - return err("Failed to creat HmacDrbgContext with system randomness") - ok(Curve25519Key.random(rng[])) - -# Generate a key pair (private key and public key are both FieldElements) -proc generateKeyPair*(): Result[tuple[privateKey, publicKey: FieldElement], string] = - let privateKeyRes = generateRandomFieldElement() - if privateKeyRes.isErr: - return err(privateKeyRes.error) - let privateKey = privateKeyRes.get() - - let publicKey = public(privateKey) - ok((privateKey, publicKey)) - -# # Multiply a given Curve25519 point with a set of scalars -# proc multiplyPointWithScalars*( -# point: FieldElement, scalars: openArray[FieldElement] -# ): FieldElement = -# var res = point -# for scalar in scalars: -# Curve25519.mul(res, scalar) -# res - -# # Multiply the Curve25519 base point with a set of scalars -# proc multiplyBasePointWithScalars*( -# scalars: openArray[FieldElement] -# ): Result[FieldElement, string] = -# if scalars.len <= 0: -# return err("Atleast one scalar must be provided") -# var res: FieldElement = public(scalars[0]) # Use the predefined base point -# for i in 1 ..< scalars.len: -# Curve25519.mul(res, scalars[i]) # Multiply with each scalar -# ok(res) - -# # Compare two FieldElements -# proc compareFieldElements*(a, b: FieldElement): bool = -# a == b diff --git a/src/naxolotl/errors.nim b/src/naxolotl/errors.nim deleted file mode 100644 index 57ac784..0000000 --- a/src/naxolotl/errors.nim +++ /dev/null @@ -1,11 +0,0 @@ - -type - NaxolotlError* = object of CatchableError - code*: ErrorCode - context*: string - - ErrorCode* = enum - errDecryption - errMessageAuthentication - errInvalidInput - errProgram \ No newline at end of file diff --git a/src/naxolotl/naxolotl.nim b/src/naxolotl/naxolotl.nim deleted file mode 100644 index d9058fd..0000000 --- a/src/naxolotl/naxolotl.nim +++ /dev/null @@ -1,210 +0,0 @@ -import curve25519 -import results -import chronicles -import nim_chacha20_poly1305/common -import strformat -import strutils -import sequtils -import tables - -import chacha -import types -import utils -import errors - -const maxSkip = 10 - - -type Doubleratchet* = object - dhSelf: PrivateKey - dhRemote: PublicKey - - rootKey: RootKey - chainKeySend: ChainKey - chainKeyRecv: ChainKey - - msgCountSend: MsgCount - msgCountRecv: MsgCount - prevChainLen: MsgCount - - # TODO: SkippedKeys - skippedMessageKeys: Table[(PublicKey,MsgCount), MessageKey] - -const DomainSepKdfRoot = "DoubleRatchetRootKey" - - -type DrHeader* = object - dhPublic*: PublicKey - msgNumber*: uint32 - prevChainLen*: uint32 - - - -func keyId(dh:PublicKey, recvCount: MsgCount ): KeyId = - (dh, recvCount) - -func hex(a: openArray[byte]) : string = - a.mapIt(&"{it:02X}").join("") - -proc `$`*(x: DrHeader): string = - "DrHeader(pubKey=" & hex(x.dhPublic) & ", msgNum=" & $x.msgNumber & ", msgNum=" & $x.prevChainLen & ")" - - -proc `$`*(key: array[32, byte]): string = - let byteStr = hex(key) - fmt"{byteStr[0..5]}..{byteStr[^6 .. ^1]}" - - -proc generateDhKey() : PrivateKey = - result = generateKeypair().get()[0] - -################################################# -# Kdf -################################################# - - -func kdfRoot(rootKey: RootKey, dhOutput:DhDerivedKey): (RootKey, ChainKey) = - - var salt = rootKey - var ikm = dhOutput - let info = cast[seq[byte]](DomainSepKdfRoot) - - hkdfSplit(salt, ikm, info) - -func kdfChain(chainKey: ChainKey): (MessageKey, ChainKey) = - - let msgKey = hkdfExtract(chainKey, [0x01u8]) - let chainKey = hkdfExtract(chainKey, [0x02u8]) - - return(msgKey, chainKey) - -func dhRatchetSend(self: var Doubleratchet) = - # Perform DH Ratchet step when receiving a new peer key. - let dhOutput : DhDerivedKey = dhExchange(self.dhSelf, self.dhRemote).get() - let (newRootKey, newChainKeySend) = kdfRoot(self.rootKey, dhOutput) - self.rootKey = newRootKey - self.chainKeySend = newChainKeySend - self.msgCountSend = 0 - -proc dhRatchetRecv(self: var Doubleratchet, remotePublickey: PublicKey ) = - self.prevChainLen = self.msgCountSend - self.msgCountSend = 0 - self.msgCountRecv = 0 - - self.dhRemote = remotePublickey - - let dhOutputPre = self.dhSelf.dhExchange(self.dhRemote).get() - let (newRootKey, newChainKeyRecv) = kdfRoot(self.rootKey, dhOutputPre) - self.rootKey = newRootKey - self.chainKeyRecv = newChainKeyRecv - - self.dhSelf = generateDhKey() - - let dhOutputPost = self.dhSelf.dhExchange(self.dhRemote).get() - (self.rootKey, self.chainKeySend) = kdfRoot(self.rootKey, dhOutputPost) - - -proc skipMessageKeys(self: var Doubleratchet, until: MsgCount): Result[(), string] = - - if self.msgCountRecv + maxSkip < until: - return err("Too many skipped messages") - - while self.msgCountRecv < until: - let (msgKey, chainKey) = kdfChain(self.chainKeyRecv) - self.chainKeyRecv = chainKey - - let keyId = keyId(self.dhRemote, self.msgCountRecv) - self.skippedMessageKeys[keyId] = msgKey - inc self.msgCountRecv - - ok(()) - -proc encrypt(self: var Doubleratchet, plaintext: var seq[byte], associatedData: openArray[byte]): (DrHeader, CipherText) = - - let (msgKey, chainKey) = kdfChain(self.chainKeySend) - self.chainKeySend = chainKey - let header = DrHeader( - dhPublic: self.dhSelf.public, #TODO Serialize - msgNumber: self.msgCountSend, - prevChainLen: self.prevChainLen) - - self.msgCountSend = self.msgCountSend + 1 - - - var (nonce, ciphertext) = encryptWithChaCha20Poly1305(msgKey, plaintext, associatedData) - - # TODO: optimize copies - var output : seq[byte] - output.add(nonce) - output.add(ciphertext) - - (header, output) - - -proc decrypt*(self: var Doubleratchet, header: DrHeader, ciphertext: CipherText, associatedData: openArray[byte] ) : Result[seq[byte], NaxolotlError] = - - let peerPublic = header.dhPublic - var msgKey : MessageKey - - # Check Skipped Keys - let keyId = keyId(header.dhPublic, header.msgNumber) - if self.skippedMessageKeys.hasKey(keyId): - debug "detected skipped message", keyId = keyId - msgKey = self.skippedMessageKeys[keyId] - else: - if (peerPublic != self.dhRemote): - let r = self.skipMessageKeys(header.prevChainLen) - if r.isErr: - error "skipMessages", error = r.error() - self.dhRatchetRecv(peerPublic) - let r = self.skipMessageKeys(header.msgNumber) - if r.isErr: - error "skipMessages", error = r.error() - - (msgKey, self.chainKeyRecv) = kdfChain(self.chainKeyRecv) - inc self.msgCountRecv - - var nonce : Nonce - copyMem(addr nonce[0], unsafeAddr ciphertext[0], Nonce.len) - var cipherTag = ciphertext[Nonce.len .. ^1] - - result = decryptWithChaCha20Poly1305(msgKey,nonce, cipherTag, associatedData ) - - if result.isOk: - # TODO: persist chainKey state changes - self.skippedMessageKeys.del(keyId) - - -proc encrypt*(self: var Doubleratchet, plaintext: var seq[byte]) : (DrHeader, CipherText) = - encrypt(self, plaintext,@[]) - - -proc initDoubleratchetSender*(sharedSecret: array[32, byte], dhRemote: PublicKey): Doubleratchet = - - result = Doubleratchet( - dhSelf: generateDhKey(), - dhRemote: dhRemote, - rootKey: RootKey(sharedSecret), - msgCountSend: 0, - msgCountRecv: 0, - prevChainLen: 0, - skippedMessageKeys: initTable[(PublicKey, MsgCount), MessageKey]() - ) - - # Update RK, CKs - result.dhRatchetSend() - -proc initDoubleratchetRecipient*(sharedSecret: array[32, byte], dhSelf: PrivateKey): Doubleratchet = - - result = Doubleratchet( - dhSelf: dhSelf, - #dhRemote: None, - rootKey: RootKey(sharedSecret), - msgCountSend: 0, - msgCountRecv: 0, - prevChainLen: 0, - skippedMessageKeys: initTable[(PublicKey, MsgCount), MessageKey]() - ) - -func dhSelfPublic*(self: Doubleratchet): PublicKey = - self.dhSelf.public \ No newline at end of file diff --git a/src/naxolotl/types.nim b/src/naxolotl/types.nim deleted file mode 100644 index a4504f4..0000000 --- a/src/naxolotl/types.nim +++ /dev/null @@ -1,18 +0,0 @@ -import curve25519 - -type PrivateKey* = Curve25519Key -type PublicKey* = Curve25519Key - -type RootKey* = array[32, byte] -type ChainKey* = array[32, byte] -type MessageKey* = array[32, byte] -type DhDerivedKey* = array[32, byte] - -type GenericArray* = array[32, byte] - -type CipherText* = seq[byte] - -type MsgCount* = uint32 -type KeyId* = (PublicKey, MsgCount) - -const KeyLen* = 32 \ No newline at end of file diff --git a/src/naxolotl/utils.nim b/src/naxolotl/utils.nim deleted file mode 100644 index 4ec9964..0000000 --- a/src/naxolotl/utils.nim +++ /dev/null @@ -1,55 +0,0 @@ -import constantine/hashes -import constantine/kdf/kdf_hkdf -import curve25519 -import results - -import errors -import types - - -func hkdfExtract*(key: openArray[byte], seed: openArray[byte]) : GenericArray = - - assert GenericArray.len == sha256.digestSize() - - var ctx{.noInit.}: HKDF[sha256] - var prk{.noInit.}: array[sha256.digestSize(), byte] - ctx.hkdfExtract(prk, key, seed) - - return prk - - - -func hkdfExtractExpand*(output: var openArray[byte], salt: openArray[byte], ikm: openArray[byte], info: openArray[byte] ) = - var ctx{.noInit.}: HKDF[sha256] - var prk{.noInit.}: array[sha256.digestSize(), byte] - ctx.hkdfExtract(prk, salt, ikm) - ctx.hkdfExpand(output, prk, info, true) - - -func hkdfSplit*(salt: GenericArray, ikm: GenericArray, info: openArray[byte] ) : (RootKey, ChainKey) = - - var output : array[KeyLen*2 , byte] - - hkdfExtractExpand(output, salt, ikm, info) - - var out1 : array[KeyLen, byte] - var out2 : array[KeyLen, byte] - - # Unsafe memcopy - copyMem(addr out1[0], addr output[0], KeyLen) - copyMem(addr out2[0], addr output[32], KeyLen) - - result = (out1,out2) - - - -func dhExchange*(a: PrivateKey, b: PublicKey): Result[DhDerivedKey, NaxolotlError] = - var dhOuput = b - - try: - Curve25519.mul(dhOuput, a) - except CatchableError as e: - return err(NaxolotlError( code: errProgram, context: e.msg)) - ok(DhDerivedKey(dhOuput)) - -