diff --git a/examples/bot_echo.nim b/examples/bot_echo.nim index fd2a5d2..809797d 100644 --- a/examples/bot_echo.nim +++ b/examples/bot_echo.nim @@ -8,8 +8,7 @@ import content_types proc main() {.async.} = let waku = initWakuClient(DefaultConfig()) - let ident = createIdentity("EchoBot") - var chatClient = newClient(waku, ident) + var chatClient = newClient(waku).get() chatClient.onNewMessage(proc(convo: Conversation, msg: ReceivedMessage) {.async.} = info "New Message: ", convoId = convo.id(), msg= msg diff --git a/examples/pingpong.nim b/examples/pingpong.nim index 5dbe00d..e6ec95e 100644 --- a/examples/pingpong.nim +++ b/examples/pingpong.nim @@ -26,12 +26,9 @@ proc main() {.async.} = var waku_saro = initWakuClient(DefaultConfig()) var waku_raya = initWakuClient(DefaultConfig()) - let sKey = loadPrivateKeyFromBytes(@[45u8, 216, 160, 24, 19, 207, 193, 214, 98, 92, 153, 145, 222, 247, 101, 99, 96, 131, 149, 185, 33, 187, 229, 251, 100, 158, 20, 131, 111, 97, 181, 210]).get() - let rKey = loadPrivateKeyFromBytes(@[43u8, 12, 160, 51, 212, 90, 199, 160, 154, 164, 129, 229, 147, 69, 151, 17, 239, 51, 190, 33, 86, 164, 50, 105, 39, 250, 182, 116, 138, 132, 114, 234]).get() - # Create Clients - var saro = newClient(waku_saro, Identity(name: "saro", privateKey: sKey)) - var raya = newClient(waku_raya, Identity(name: "raya", privateKey: rKey)) + var saro = newClient(waku_saro).get() + var raya = newClient(waku_raya).get() # Wire Saro Callbacks saro.onNewMessage(proc(convo: Conversation, msg: ReceivedMessage) {.async, closure.} = diff --git a/library/api/client_api.nim b/library/api/client_api.nim index 5a29d12..3f0f6fc 100644 --- a/library/api/client_api.nim +++ b/library/api/client_api.nim @@ -8,7 +8,6 @@ import ffi import src/chat import src/chat/delivery/waku_client -import src/chat/identity import library/utils logScope: @@ -52,14 +51,11 @@ proc createChatClient( for peer in config["staticPeers"]: wakuCfg.staticPeers.add(peer.getStr()) - # Create identity - let identity = createIdentity(name) - # Create Waku client let wakuClient = initWakuClient(wakuCfg) # Create Chat client - let client = newClient(wakuClient, identity) + let client = ?newClient(wakuClient, installation_name = name) # Register event handlers client.onNewMessage(chatCallbacks.onNewMessage) diff --git a/library/liblogoschat.nim b/library/liblogoschat.nim index 87f8de0..d59b73a 100644 --- a/library/liblogoschat.nim +++ b/library/liblogoschat.nim @@ -10,7 +10,6 @@ import stew/byteutils import src/chat/client, - src/chat/identity, src/chat/delivery/waku_client, library/declare_lib, library/utils diff --git a/src/chat.nim b/src/chat.nim index 8cabde4..3bd3472 100644 --- a/src/chat.nim +++ b/src/chat.nim @@ -1,12 +1,10 @@ import chat/[ client, delivery/waku_client, - identity, types ] -export client, identity, waku_client -export identity.`$` +export client, waku_client #export specific frames need by applications export MessageId diff --git a/src/chat/client.nim b/src/chat/client.nim index 3717311..5f86422 100644 --- a/src/chat/client.nim +++ b/src/chat/client.nim @@ -14,7 +14,6 @@ import # Foreign import #local delivery/waku_client, errors, - identity, types, utils @@ -53,16 +52,9 @@ type DeliveryAckCallback* = proc(conversation: Conversation, msgId: MessageId): Future[void] {.async.} - -type KeyEntry* = object - keyType: string - privateKey: PrivateKey - timestamp: int64 - type ChatClient* = ref object libchatCtx: LibChat ds*: WakuClient - id: string inboundQueue: QueueRef isRunning: bool @@ -74,34 +66,38 @@ type ChatClient* = ref object # Constructors ################################################# -proc newClient*(ds: WakuClient, ident: Identity): ChatClient {.raises: [IOError, ValueError].} = - ## Creates new instance of a `ChatClient` with a given `WakuConfig` - ## TODO: (P1) Currently the passed in Identity is not used. Libchat Generates one for every invocation. +proc newClient*(ds: WakuClient, ephemeral: bool = true, installation_name: string = "default"): Result[ChatClient, ErrorType] = + ## Creates new instance of a `ChatClient` with a given `WakuConfig`. + ## A new installation is created if no saved installation with `installation_name` is found + + if not ephemeral: + return err("persistence is not currently supported") + try: var q = QueueRef(queue: newAsyncQueue[ChatPayload](10)) var c = ChatClient( - libchatCtx: newConversationsContext(), + libchatCtx: newConversationsContext(installation_name), ds: ds, - id: ident.getName(), inboundQueue: q, isRunning: false, newMessageCallbacks: @[], newConvoCallbacks: @[]) - notice "Client started", client = c.id + notice "Client started" - result = c + result = ok(c) except Exception as e: error "newCLient", err = e.msg + result = err(e.msg) ################################################# # Parameter Access ################################################# proc getId*(client: ChatClient): string = - result = client.id + result = client.libchatCtx.getInstallationName() proc listConversations*(client: ChatClient): seq[Conversation] = diff --git a/src/chat/crypto/curve25519.nim b/src/chat/crypto/curve25519.nim deleted file mode 100644 index a197778..0000000 --- a/src/chat/crypto/curve25519.nim +++ /dev/null @@ -1,61 +0,0 @@ -# Reference: https://github.com/vacp2p/mix/blob/main/src/curve25519.nim - - -import results -import bearssl/rand -import libp2p/crypto/curve25519 - -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/chat/crypto/ecdh.nim b/src/chat/crypto/ecdh.nim deleted file mode 100644 index 8237fbe..0000000 --- a/src/chat/crypto/ecdh.nim +++ /dev/null @@ -1,58 +0,0 @@ -import results -import libp2p/crypto/curve25519 -import bearssl/rand - -import ../utils - -type PrivateKey* = object - bytes: Curve25519Key - -type PublicKey* = distinct Curve25519Key # TODO: define outside of ECDH - - -proc bytes*(key: PublicKey): array[Curve25519KeySize, byte] = - cast[array[Curve25519KeySize, byte]](key) - -proc get_addr*(pubkey: PublicKey): string = - # TODO: Needs Spec - result = hash_func(pubkey.bytes().bytesToHex()) - - -proc bytes*(key: PrivateKey): Curve25519Key = - return key.bytes - - - -proc createRandomKey*(): Result[PrivateKey, string] = - let rng = HmacDrbgContext.new() - if rng.isNil: - return err("Failed to create HmacDrbgContext with system randomness") - ok(PrivateKey(bytes: Curve25519Key.random(rng[]))) - -proc loadPrivateKeyFromBytes*(bytes: openArray[byte]): Result[PrivateKey, string] = - if bytes.len != Curve25519KeySize: - return err("Private key size must be 32 bytes") - ok(PrivateKey(bytes: intoCurve25519Key(bytes))) - -proc loadPublicKeyFromBytes*(bytes: openArray[byte]): Result[PublicKey, string] = - if bytes.len != Curve25519KeySize: - return err("Public key size must be 32 bytes") - ok(PublicKey(intoCurve25519Key(bytes))) - - -proc getPublicKey*(privateKey: PrivateKey): PublicKey = - PublicKey( public(privateKey.bytes)) - - -proc Dh*(privateKey: PrivateKey, publicKey: PublicKey): Result[seq[ - byte], string] = - - var outputKey = publicKey.bytes - try: - Curve25519.mul(outputKey, privateKey.bytes) - except CatchableError as e: - return err("Failed to compute shared secret: " & e.msg) - - return ok(outputKey.getBytes()) - - diff --git a/src/chat/identity.nim b/src/chat/identity.nim deleted file mode 100644 index 794fcc1..0000000 --- a/src/chat/identity.nim +++ /dev/null @@ -1,44 +0,0 @@ - -import crypto/ecdh -import results -import strformat -import utils - -export PublicKey, PrivateKey, loadPrivateKeyFromBytes, loadPublicKeyFromBytes - - -type - Identity* = object - name*: string - privateKey*: PrivateKey # TODO: protect key exposure - - -################################################# -# Constructors -################################################# - -proc createIdentity*(name: string): Identity = - let privKey = createRandomKey().get() - result = Identity(name: name, privateKey: privKey) - - -################################################# -# Parameter Access -################################################# - -proc getPubkey*(self: Identity): PublicKey = - result = self.privateKey.getPublicKey() - -proc getAddr*(self: Identity): string = - result = get_addr(self.getPubKey()) - -proc getName*(self: Identity): string = - result = self.name - -proc toHex(key: PublicKey): string = - bytesToHex(key.bytes()) - -proc `$`*(key: PublicKey): string = - let byteStr = toHex(key) - fmt"{byteStr[0..3]}..{byteStr[^4 .. ^1]}" - diff --git a/src/chat/types.nim b/src/chat/types.nim index d5989fd..37ae838 100644 --- a/src/chat/types.nim +++ b/src/chat/types.nim @@ -1,2 +1,4 @@ type MessageId* = string type Content* = seq[byte] +type ErrorType* = string +type PublicKey* = array[32, byte] diff --git a/tests/all_tests.nim b/tests/all_tests.nim index c75d9cc..ac25c69 100644 --- a/tests/all_tests.nim +++ b/tests/all_tests.nim @@ -1,3 +1 @@ # import individual test suites - -import ./test_curve25519 diff --git a/tests/smoke_test.nim b/tests/smoke_test.nim index 288df6a..a838712 100644 --- a/tests/smoke_test.nim +++ b/tests/smoke_test.nim @@ -1,13 +1,13 @@ # Smoke test: validates that the binary links all dependencies at runtime. # No networking, no start(), no message exchange — just instantiation. +import results import ../src/chat proc main() = try: let waku = initWakuClient(DefaultConfig()) - let ident = createIdentity("SmokeTest") - var client = newClient(waku, ident) + var client = newClient(waku).get() if client.isNil: raise newException(CatchableError, "newClient returned nil") let id = client.getId() diff --git a/tests/test_curve25519.nim b/tests/test_curve25519.nim deleted file mode 100644 index 1b5cb55..0000000 --- a/tests/test_curve25519.nim +++ /dev/null @@ -1,65 +0,0 @@ -import results -import unittest - -import ../src/chat/crypto/ecdh # TODO use config.nims -import ../src/chat/utils - -# Key share test from RFC-7748: -const ks7748_a_priv = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a" -const ks7748_a_pub = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a" # Public key point (x co-ord) - -const ks7748_b_priv = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb" -const ks7748_b_pub = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f" # Public key point (x co-ord)s - -const ks7748_shared_key = "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742" - -import parseutils - -proc hexToArray*[N: static[int]](hexStr: string): array[N, byte] = - ## Converts hex string to fixed-size byte array - if hexStr.len != N * 2: - raise newException(ValueError, - "Hex string length (" & $hexStr.len & ") doesn't match array size (" & $( - N*2) & ")") - - for i in 0..