@openlv/core 0.0.1-beta.9 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,9 +7,9 @@ export type DecryptionKey = {
7
7
  toString: () => string;
8
8
  decrypt: (message: string) => Promise<string>;
9
9
  };
10
- export declare const createEncryptionKey: (key: Uint8Array) => Promise<EncryptionKey>;
10
+ export declare const createEncryptionKey: (publicKey: Uint8Array) => Promise<EncryptionKey>;
11
11
  export declare const parseEncryptionKey: (serializedKey: string) => Promise<EncryptionKey>;
12
- export declare const createDecryptionKey: (key: Uint8Array) => Promise<DecryptionKey>;
12
+ export declare const createDecryptionKey: (secretKey: Uint8Array) => Promise<DecryptionKey>;
13
13
  export type EncKeypair = {
14
14
  encryptionKey: EncryptionKey;
15
15
  decryptionKey: DecryptionKey;
@@ -1,7 +1,8 @@
1
- import nacl from "tweetnacl";
1
+ import { secretbox } from "@noble/ciphers/salsa.js";
2
+ import { x25519 } from "@noble/curves/ed25519.js";
2
3
  import { fromBase64, toBase64 } from "./base64.js";
3
- const PUBLIC_KEY_BYTE_LENGTH = nacl.box.publicKeyLength;
4
- const NONCE_BYTE_LENGTH = nacl.box.nonceLength;
4
+ const PUBLIC_KEY_BYTES = 32;
5
+ const NONCE_BYTES = 24;
5
6
  const composePayload = (ephemeralPublicKey, nonce, ciphertext) => {
6
7
  const payload = new Uint8Array(ephemeralPublicKey.length + nonce.length + ciphertext.length);
7
8
  payload.set(ephemeralPublicKey, 0);
@@ -10,66 +11,57 @@ const composePayload = (ephemeralPublicKey, nonce, ciphertext) => {
10
11
  return toBase64(payload);
11
12
  };
12
13
  const decomposePayload = (payload) => {
13
- const bytes = new Uint8Array(atob(payload)
14
- .split("")
15
- .map(char => char.codePointAt(0)));
16
- if (bytes.length <= PUBLIC_KEY_BYTE_LENGTH + NONCE_BYTE_LENGTH) {
14
+ const bytes = fromBase64(payload);
15
+ if (bytes.length <= PUBLIC_KEY_BYTES + NONCE_BYTES) {
17
16
  throw new Error("Encrypted payload is malformed.");
18
17
  }
19
- const ephemeralPublicKey = bytes.slice(0, PUBLIC_KEY_BYTE_LENGTH);
20
- const nonce = bytes.slice(PUBLIC_KEY_BYTE_LENGTH, PUBLIC_KEY_BYTE_LENGTH + NONCE_BYTE_LENGTH);
21
- const ciphertext = bytes.slice(PUBLIC_KEY_BYTE_LENGTH + NONCE_BYTE_LENGTH);
18
+ const ephemeralPublicKey = bytes.slice(0, PUBLIC_KEY_BYTES);
19
+ const nonce = bytes.slice(PUBLIC_KEY_BYTES, PUBLIC_KEY_BYTES + NONCE_BYTES);
20
+ const ciphertext = bytes.slice(PUBLIC_KEY_BYTES + NONCE_BYTES);
22
21
  return {
23
22
  ephemeralPublicKey,
24
23
  nonce,
25
24
  ciphertext,
26
25
  };
27
26
  };
28
- export const createEncryptionKey = async (key) => {
29
- const serializedPublicKey = toBase64(key);
27
+ export const createEncryptionKey = async (publicKey) => {
28
+ const serializedPublicKey = toBase64(publicKey);
30
29
  return {
31
30
  toString: () => serializedPublicKey,
32
31
  encrypt: async (message) => {
33
- const messageBytes = new TextEncoder().encode(message);
34
- const ephemeralKeyPair = nacl.box.keyPair();
35
- const nonce = nacl.randomBytes(NONCE_BYTE_LENGTH);
36
- const ciphertext = nacl.box(messageBytes, nonce, key, ephemeralKeyPair.secretKey);
37
- if (!ciphertext) {
38
- throw new Error("Failed to encrypt message.");
39
- }
40
- return composePayload(ephemeralKeyPair.publicKey, nonce, ciphertext);
32
+ const ephemeralSecret = x25519.utils.randomSecretKey();
33
+ const ephemeralPublic = x25519.getPublicKey(ephemeralSecret);
34
+ const nonce = crypto.getRandomValues(new Uint8Array(NONCE_BYTES));
35
+ const { seal } = secretbox(x25519.getSharedSecret(ephemeralSecret, publicKey), nonce);
36
+ const ciphertext = seal(new TextEncoder().encode(message));
37
+ return composePayload(ephemeralPublic, nonce, ciphertext);
41
38
  },
42
39
  };
43
40
  };
44
- export const parseEncryptionKey = async (serializedKey) => {
45
- const decodedPublicKey = fromBase64(serializedKey);
46
- return createEncryptionKey(decodedPublicKey);
47
- };
48
- export const createDecryptionKey = async (key) => {
49
- const serializedSecretKey = toBase64(key);
41
+ export const parseEncryptionKey = async (serializedKey) => createEncryptionKey(fromBase64(serializedKey));
42
+ export const createDecryptionKey = async (secretKey) => {
43
+ const serializedSecretKey = toBase64(secretKey);
50
44
  return {
51
45
  toString: () => serializedSecretKey,
52
46
  decrypt: async (message) => {
53
47
  if (!message) {
54
- return "";
48
+ throw new Error("Cannot decrypt an empty payload");
55
49
  }
56
50
  const { ephemeralPublicKey, nonce, ciphertext } = decomposePayload(message);
57
- const decrypted = nacl.box.open(ciphertext, nonce, ephemeralPublicKey, key);
58
- if (!decrypted) {
59
- throw new Error("Failed to decrypt message.");
60
- }
61
- return new TextDecoder().decode(decrypted);
51
+ const { open } = secretbox(x25519.getSharedSecret(secretKey, ephemeralPublicKey), nonce);
52
+ return new TextDecoder().decode(open(ciphertext));
62
53
  },
63
54
  };
64
55
  };
65
56
  export const generateKeyPair = async () => {
66
- const { publicKey, secretKey } = nacl.box.keyPair();
67
- const encryptionKey = await createEncryptionKey(publicKey);
68
- const decryptionKey = await createDecryptionKey(secretKey);
69
- return { encryptionKey, decryptionKey };
57
+ const secretKey = x25519.utils.randomSecretKey();
58
+ const publicKey = x25519.getPublicKey(secretKey);
59
+ return {
60
+ encryptionKey: await createEncryptionKey(publicKey),
61
+ decryptionKey: await createDecryptionKey(secretKey),
62
+ };
70
63
  };
71
64
  export const initEncryptionKeys = async (initParameters) => {
72
- const keyPair = nacl.box.keyPair();
73
65
  const { encryptionKey, decryptionKey } = await generateKeyPair();
74
66
  if (initParameters
75
67
  && "publicKey" in initParameters
@@ -84,7 +76,7 @@ export const initEncryptionKeys = async (initParameters) => {
84
76
  return {
85
77
  encryptionKey,
86
78
  decryptionKey,
87
- relyingEncryptionKey: await createEncryptionKey(keyPair.publicKey),
79
+ relyingEncryptionKey: encryptionKey,
88
80
  };
89
81
  };
90
82
  //# sourceMappingURL=asymmetric.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"asymmetric.js","sourceRoot":"","sources":["../../src/encryption/asymmetric.ts"],"names":[],"mappings":"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","sourcesContent":["import nacl from \"tweetnacl\";\n\nimport type { SessionLinkParameters } from \"../session.js\";\nimport { fromBase64, toBase64 } from \"./base64.js\";\n\nconst PUBLIC_KEY_BYTE_LENGTH = nacl.box.publicKeyLength;\nconst NONCE_BYTE_LENGTH = nacl.box.nonceLength;\n\nconst composePayload = (\n ephemeralPublicKey: Uint8Array,\n nonce: Uint8Array,\n ciphertext: Uint8Array,\n): string => {\n const payload = new Uint8Array(\n ephemeralPublicKey.length + nonce.length + ciphertext.length,\n );\n\n payload.set(ephemeralPublicKey, 0);\n payload.set(nonce, ephemeralPublicKey.length);\n payload.set(ciphertext, ephemeralPublicKey.length + nonce.length);\n\n return toBase64(payload);\n};\n\nconst decomposePayload = (\n payload: string,\n): {\n ephemeralPublicKey: Uint8Array;\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n} => {\n const bytes = new Uint8Array(\n atob(payload)\n .split(\"\")\n .map(char => char.codePointAt(0)!),\n );\n\n if (bytes.length <= PUBLIC_KEY_BYTE_LENGTH + NONCE_BYTE_LENGTH) {\n throw new Error(\"Encrypted payload is malformed.\");\n }\n\n const ephemeralPublicKey = bytes.slice(0, PUBLIC_KEY_BYTE_LENGTH);\n const nonce = bytes.slice(\n PUBLIC_KEY_BYTE_LENGTH,\n PUBLIC_KEY_BYTE_LENGTH + NONCE_BYTE_LENGTH,\n );\n const ciphertext = bytes.slice(PUBLIC_KEY_BYTE_LENGTH + NONCE_BYTE_LENGTH);\n\n return {\n ephemeralPublicKey,\n nonce,\n ciphertext,\n };\n};\n\nexport type EncryptionKey = {\n toString: () => string;\n encrypt: (message: string) => Promise<string>;\n};\n\nexport type DecryptionKey = {\n toString: () => string;\n decrypt: (message: string) => Promise<string>;\n};\n\nexport const createEncryptionKey = async (\n key: Uint8Array,\n): Promise<EncryptionKey> => {\n const serializedPublicKey = toBase64(key);\n\n return {\n toString: () => serializedPublicKey,\n encrypt: async (message: string) => {\n const messageBytes = new TextEncoder().encode(message);\n const ephemeralKeyPair = nacl.box.keyPair();\n const nonce = nacl.randomBytes(NONCE_BYTE_LENGTH);\n const ciphertext = nacl.box(\n messageBytes,\n nonce,\n key,\n ephemeralKeyPair.secretKey,\n );\n\n if (!ciphertext) {\n throw new Error(\"Failed to encrypt message.\");\n }\n\n return composePayload(ephemeralKeyPair.publicKey, nonce, ciphertext);\n },\n };\n};\n\nexport const parseEncryptionKey = async (\n serializedKey: string,\n): Promise<EncryptionKey> => {\n const decodedPublicKey = fromBase64(serializedKey);\n\n return createEncryptionKey(decodedPublicKey);\n};\n\nexport const createDecryptionKey = async (\n key: Uint8Array,\n): Promise<DecryptionKey> => {\n const serializedSecretKey = toBase64(key);\n\n return {\n toString: () => serializedSecretKey,\n decrypt: async (message: string) => {\n if (!message) {\n return \"\";\n }\n\n const { ephemeralPublicKey, nonce, ciphertext }\n = decomposePayload(message);\n const decrypted = nacl.box.open(\n ciphertext,\n nonce,\n ephemeralPublicKey,\n key,\n );\n\n if (!decrypted) {\n throw new Error(\"Failed to decrypt message.\");\n }\n\n return new TextDecoder().decode(decrypted);\n },\n };\n};\n\nexport type EncKeypair = {\n encryptionKey: EncryptionKey;\n decryptionKey: DecryptionKey;\n};\n\nexport const generateKeyPair = async (): Promise<EncKeypair> => {\n const { publicKey, secretKey } = nacl.box.keyPair();\n const encryptionKey = await createEncryptionKey(publicKey);\n const decryptionKey = await createDecryptionKey(secretKey);\n\n return { encryptionKey, decryptionKey };\n};\n\nexport const initEncryptionKeys = async (\n initParameters?: SessionLinkParameters,\n) => {\n const keyPair = nacl.box.keyPair();\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n\n if (\n initParameters\n && \"publicKey\" in initParameters\n && initParameters.publicKey\n ) {\n const relyingEncryptionKey = await parseEncryptionKey(\n initParameters.publicKey as string,\n );\n\n return {\n encryptionKey,\n decryptionKey,\n relyingEncryptionKey,\n };\n }\n\n return {\n encryptionKey,\n decryptionKey,\n relyingEncryptionKey: await createEncryptionKey(keyPair.publicKey),\n };\n};\n"]}
1
+ {"version":3,"file":"asymmetric.js","sourceRoot":"","sources":["../../src/encryption/asymmetric.ts"],"names":[],"mappings":"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","sourcesContent":["import { secretbox } from \"@noble/ciphers/salsa.js\";\nimport { x25519 } from \"@noble/curves/ed25519.js\";\n\nimport type { SessionLinkParameters } from \"../session.js\";\nimport { fromBase64, toBase64 } from \"./base64.js\";\n\nconst PUBLIC_KEY_BYTES = 32;\nconst NONCE_BYTES = 24;\n\nconst composePayload = (\n ephemeralPublicKey: Uint8Array,\n nonce: Uint8Array,\n ciphertext: Uint8Array,\n): string => {\n const payload = new Uint8Array(\n ephemeralPublicKey.length + nonce.length + ciphertext.length,\n );\n\n payload.set(ephemeralPublicKey, 0);\n payload.set(nonce, ephemeralPublicKey.length);\n payload.set(ciphertext, ephemeralPublicKey.length + nonce.length);\n\n return toBase64(payload);\n};\n\nconst decomposePayload = (\n payload: string,\n): {\n ephemeralPublicKey: Uint8Array;\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n} => {\n const bytes = fromBase64(payload);\n\n if (bytes.length <= PUBLIC_KEY_BYTES + NONCE_BYTES) {\n throw new Error(\"Encrypted payload is malformed.\");\n }\n\n const ephemeralPublicKey = bytes.slice(0, PUBLIC_KEY_BYTES);\n const nonce = bytes.slice(PUBLIC_KEY_BYTES, PUBLIC_KEY_BYTES + NONCE_BYTES);\n const ciphertext = bytes.slice(PUBLIC_KEY_BYTES + NONCE_BYTES);\n\n return {\n ephemeralPublicKey,\n nonce,\n ciphertext,\n };\n};\n\nexport type EncryptionKey = {\n toString: () => string;\n encrypt: (message: string) => Promise<string>;\n};\n\nexport type DecryptionKey = {\n toString: () => string;\n decrypt: (message: string) => Promise<string>;\n};\n\nexport const createEncryptionKey = async (\n publicKey: Uint8Array,\n): Promise<EncryptionKey> => {\n const serializedPublicKey = toBase64(publicKey);\n\n return {\n toString: () => serializedPublicKey,\n encrypt: async (message: string) => {\n const ephemeralSecret = x25519.utils.randomSecretKey();\n const ephemeralPublic = x25519.getPublicKey(ephemeralSecret);\n const nonce = crypto.getRandomValues(new Uint8Array(NONCE_BYTES));\n const { seal } = secretbox(\n x25519.getSharedSecret(ephemeralSecret, publicKey),\n nonce,\n );\n const ciphertext = seal(new TextEncoder().encode(message));\n\n return composePayload(ephemeralPublic, nonce, ciphertext);\n },\n };\n};\n\nexport const parseEncryptionKey = async (\n serializedKey: string,\n): Promise<EncryptionKey> => createEncryptionKey(fromBase64(serializedKey));\n\nexport const createDecryptionKey = async (\n secretKey: Uint8Array,\n): Promise<DecryptionKey> => {\n const serializedSecretKey = toBase64(secretKey);\n\n return {\n toString: () => serializedSecretKey,\n decrypt: async (message: string) => {\n if (!message) {\n throw new Error(\"Cannot decrypt an empty payload\");\n }\n\n const { ephemeralPublicKey, nonce, ciphertext }\n = decomposePayload(message);\n const { open } = secretbox(\n x25519.getSharedSecret(secretKey, ephemeralPublicKey),\n nonce,\n );\n\n return new TextDecoder().decode(open(ciphertext));\n },\n };\n};\n\nexport type EncKeypair = {\n encryptionKey: EncryptionKey;\n decryptionKey: DecryptionKey;\n};\n\nexport const generateKeyPair = async (): Promise<EncKeypair> => {\n const secretKey = x25519.utils.randomSecretKey();\n const publicKey = x25519.getPublicKey(secretKey);\n\n return {\n encryptionKey: await createEncryptionKey(publicKey),\n decryptionKey: await createDecryptionKey(secretKey),\n };\n};\n\nexport const initEncryptionKeys = async (\n initParameters?: SessionLinkParameters,\n) => {\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n\n if (\n initParameters\n && \"publicKey\" in initParameters\n && initParameters.publicKey\n ) {\n const relyingEncryptionKey = await parseEncryptionKey(\n initParameters.publicKey as string,\n );\n\n return {\n encryptionKey,\n decryptionKey,\n relyingEncryptionKey,\n };\n }\n\n return {\n encryptionKey,\n decryptionKey,\n relyingEncryptionKey: encryptionKey,\n };\n};\n"]}
@@ -21,16 +21,13 @@ describe("Asymmetric encryption", () => {
21
21
  const key = encryptionKey.toString();
22
22
  const parsedKey = await parseEncryptionKey(key);
23
23
  const parsedEncrypted = await parsedKey.encrypt(message);
24
- console.log("parsedEncrypted", parsedEncrypted);
25
24
  const decodedParsedEncrypted = await decryptionKey.decrypt(parsedEncrypted);
26
- console.log("decodedParsedEncrypted", decodedParsedEncrypted);
27
25
  expect(decodedParsedEncrypted).toBe(message);
28
26
  });
29
27
  test("should be able to encrypt large blob", async () => {
30
28
  const { encryptionKey, decryptionKey } = await generateKeyPair();
31
29
  const message = Array.from({ length: 1024 * 1024 }).fill("a")
32
30
  .join("");
33
- console.log("message", message.length);
34
31
  const encrypted = await encryptionKey.encrypt(message);
35
32
  const decrypted = await decryptionKey.decrypt(encrypted);
36
33
  expect(decrypted).toBe(message);
@@ -1 +1 @@
1
- {"version":3,"file":"asymmetric.test.js","sourceRoot":"","sources":["../../src/encryption/asymmetric.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,IAAI,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;QAExC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,MAAM,CAAC;QAEvB,gCAAgC;QAChC,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEzD,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,MAAM,CAAC;QAEvB,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,eAAe,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEzD,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,eAAe,CAAC,CAAC;QAChD,MAAM,sBAAsB,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAE5E,OAAO,CAAC,GAAG,CAAC,wBAAwB,EAAE,sBAAsB,CAAC,CAAC;QAE9D,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;aAC1D,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAEvC,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEzD,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, test } from \"vitest\";\n\nimport { generateKeyPair, parseEncryptionKey } from \"./asymmetric.js\";\n\ndescribe(\"Asymmetric encryption\", () => {\n test(\"should generate a key pair\", async () => {\n const keyPair = await generateKeyPair();\n\n expect(keyPair).toBeDefined();\n expect(keyPair.encryptionKey).toBeDefined();\n expect(keyPair.decryptionKey).toBeDefined();\n });\n\n test(\"should encrypt and decrypt a message\", async () => {\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n const message = \"test\";\n\n // regular encryption decryption\n const encrypted = await encryptionKey.encrypt(message);\n const decrypted = await decryptionKey.decrypt(encrypted);\n\n expect(decrypted).toBe(message);\n });\n\n test(\"should encrypt and decrypt a message with a parsed key\", async () => {\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n const message = \"test\";\n\n const key = encryptionKey.toString();\n const parsedKey = await parseEncryptionKey(key);\n const parsedEncrypted = await parsedKey.encrypt(message);\n\n console.log(\"parsedEncrypted\", parsedEncrypted);\n const decodedParsedEncrypted = await decryptionKey.decrypt(parsedEncrypted);\n\n console.log(\"decodedParsedEncrypted\", decodedParsedEncrypted);\n\n expect(decodedParsedEncrypted).toBe(message);\n });\n\n test(\"should be able to encrypt large blob\", async () => {\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n const message = Array.from({ length: 1024 * 1024 }).fill(\"a\")\n .join(\"\");\n\n console.log(\"message\", message.length);\n\n const encrypted = await encryptionKey.encrypt(message);\n const decrypted = await decryptionKey.decrypt(encrypted);\n\n expect(decrypted).toBe(message);\n });\n});\n"]}
1
+ {"version":3,"file":"asymmetric.test.js","sourceRoot":"","sources":["../../src/encryption/asymmetric.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,IAAI,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,OAAO,GAAG,MAAM,eAAe,EAAE,CAAC;QAExC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC9B,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,MAAM,CAAC;QAEvB,gCAAgC;QAChC,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEzD,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,MAAM,CAAC;QAEvB,MAAM,GAAG,GAAG,aAAa,CAAC,QAAQ,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,eAAe,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEzD,MAAM,sBAAsB,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAE5E,MAAM,CAAC,sBAAsB,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,MAAM,eAAe,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;aAC1D,IAAI,CAAC,EAAE,CAAC,CAAC;QAEZ,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAEzD,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, test } from \"vitest\";\n\nimport { generateKeyPair, parseEncryptionKey } from \"./asymmetric.js\";\n\ndescribe(\"Asymmetric encryption\", () => {\n test(\"should generate a key pair\", async () => {\n const keyPair = await generateKeyPair();\n\n expect(keyPair).toBeDefined();\n expect(keyPair.encryptionKey).toBeDefined();\n expect(keyPair.decryptionKey).toBeDefined();\n });\n\n test(\"should encrypt and decrypt a message\", async () => {\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n const message = \"test\";\n\n // regular encryption decryption\n const encrypted = await encryptionKey.encrypt(message);\n const decrypted = await decryptionKey.decrypt(encrypted);\n\n expect(decrypted).toBe(message);\n });\n\n test(\"should encrypt and decrypt a message with a parsed key\", async () => {\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n const message = \"test\";\n\n const key = encryptionKey.toString();\n const parsedKey = await parseEncryptionKey(key);\n const parsedEncrypted = await parsedKey.encrypt(message);\n\n const decodedParsedEncrypted = await decryptionKey.decrypt(parsedEncrypted);\n\n expect(decodedParsedEncrypted).toBe(message);\n });\n\n test(\"should be able to encrypt large blob\", async () => {\n const { encryptionKey, decryptionKey } = await generateKeyPair();\n const message = Array.from({ length: 1024 * 1024 }).fill(\"a\")\n .join(\"\");\n\n const encrypted = await encryptionKey.encrypt(message);\n const decrypted = await decryptionKey.decrypt(encrypted);\n\n expect(decrypted).toBe(message);\n });\n});\n"]}
@@ -1,5 +1,6 @@
1
+ const decodeHex = (value) => new Uint8Array(value.match(/.{2}/g).map(byte => Number.parseInt(byte, 16)));
1
2
  export const deriveSymmetricKey = async (k) => {
2
- const baseKey = new TextEncoder().encode(k);
3
+ const baseKey = decodeHex(k);
3
4
  const sharedSecret = await crypto.subtle.importKey("raw", baseKey, { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
4
5
  return {
5
6
  toString: () => k,
@@ -1 +1 @@
1
- {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/encryption/handshake.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,CAAS,EAAyB,EAAE;IAC3E,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAE5C,MAAM,YAAY,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAChD,KAAK,EACL,OAAO,EACP,EAAE,IAAI,EAAE,SAAS,EAAE,EACnB,KAAK,EACL,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,CAAC;IAEF,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC;QACjB,OAAO,EAAE,KAAK,EAAE,OAAe,EAAE,EAAE;YACjC,MAAM,EAAE,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;YACtD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC3C,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,EACvB,YAAY,EACZ,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAClC,CAAC;YACF,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;YAElE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACjB,QAAQ,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;YAEnD,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,OAAe,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,IAAI,UAAU,CAC7B,IAAI,CAAC,OAAO,CAAC;iBACV,KAAK,CAAC,EAAE,CAAC;iBACT,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CACrC,CAAC;YACF,MAAM,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACrC,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC3C,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,EACvB,YAAY,EACZ,SAAS,CACV,CAAC;YACF,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;YAElC,OAAO,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC;KACF,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,KAAK,IAA2B,EAAE;IACpE,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IAEjC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SACzC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,OAAO,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC,CAAC","sourcesContent":["export type SymmetricKey = {\n // TODO\n toString: () => string;\n encrypt: (message: string) => Promise<string>;\n decrypt: (message: string) => Promise<string>;\n};\n\nexport const deriveSymmetricKey = async (k: string): Promise<SymmetricKey> => {\n const baseKey = new TextEncoder().encode(k);\n\n const sharedSecret = await crypto.subtle.importKey(\n \"raw\",\n baseKey,\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n\n return {\n toString: () => k,\n encrypt: async (message: string) => {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const encrypted = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n sharedSecret,\n new TextEncoder().encode(message),\n );\n const combined = new Uint8Array(iv.length + encrypted.byteLength);\n\n combined.set(iv);\n combined.set(new Uint8Array(encrypted), iv.length);\n\n return btoa(String.fromCharCode.apply(null, Array.from(combined)));\n },\n decrypt: async (message: string) => {\n const combined = new Uint8Array(\n atob(message)\n .split(\"\")\n .map(char => char.codePointAt(0)!),\n );\n const iv = combined.slice(0, 12);\n const encrypted = combined.slice(12);\n const decrypted = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv },\n sharedSecret,\n encrypted,\n );\n const decoder = new TextDecoder();\n\n return decoder.decode(decrypted);\n },\n };\n};\n\n/**\n * Generate a 16-byte random shared key and return as hex\n */\nexport const generateHandshakeKey = async (): Promise<SymmetricKey> => {\n const array = new Uint8Array(16);\n\n crypto.getRandomValues(array);\n\n const keyHex = Array.from(array)\n .map(b => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n return await deriveSymmetricKey(keyHex);\n};\n"]}
1
+ {"version":3,"file":"handshake.js","sourceRoot":"","sources":["../../src/encryption/handshake.ts"],"names":[],"mappings":"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","sourcesContent":["export type SymmetricKey = {\n toString: () => string;\n encrypt: (message: string) => Promise<string>;\n decrypt: (message: string) => Promise<string>;\n};\n\nconst decodeHex = (value: string): Uint8Array<ArrayBuffer> => new Uint8Array(\n value.match(/.{2}/g)!.map(byte => Number.parseInt(byte, 16)),\n);\n\nexport const deriveSymmetricKey = async (k: string): Promise<SymmetricKey> => {\n const baseKey = decodeHex(k);\n\n const sharedSecret = await crypto.subtle.importKey(\n \"raw\",\n baseKey,\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n\n return {\n toString: () => k,\n encrypt: async (message: string) => {\n const iv = crypto.getRandomValues(new Uint8Array(12));\n const encrypted = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv },\n sharedSecret,\n new TextEncoder().encode(message),\n );\n const combined = new Uint8Array(iv.length + encrypted.byteLength);\n\n combined.set(iv);\n combined.set(new Uint8Array(encrypted), iv.length);\n\n return btoa(String.fromCharCode.apply(null, Array.from(combined)));\n },\n decrypt: async (message: string) => {\n const combined = new Uint8Array(\n atob(message)\n .split(\"\")\n .map(char => char.codePointAt(0)!),\n );\n const iv = combined.slice(0, 12);\n const encrypted = combined.slice(12);\n const decrypted = await crypto.subtle.decrypt(\n { name: \"AES-GCM\", iv },\n sharedSecret,\n encrypted,\n );\n const decoder = new TextDecoder();\n\n return decoder.decode(decrypted);\n },\n };\n};\n\n/**\n * Generate a 16-byte random shared key and return as hex\n */\nexport const generateHandshakeKey = async (): Promise<SymmetricKey> => {\n const array = new Uint8Array(16);\n\n crypto.getRandomValues(array);\n\n const keyHex = Array.from(array)\n .map(b => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n\n return await deriveSymmetricKey(keyHex);\n};\n"]}
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "vitest";
2
- import { generateHandshakeKey } from "./handshake.js";
2
+ import { deriveSymmetricKey, generateHandshakeKey } from "./handshake.js";
3
3
  describe("Handshake", async () => {
4
4
  const key = await generateHandshakeKey();
5
5
  test("should generate a 16-byte random shared key", () => {
@@ -12,5 +12,11 @@ describe("Handshake", async () => {
12
12
  const decrypted = await key.decrypt(encrypted);
13
13
  expect(decrypted).toBe(message);
14
14
  });
15
+ test("should derive symmetric key from decoded hex bytes", async () => {
16
+ const key = await deriveSymmetricKey("000102030405060708090a0b0c0d0e0f");
17
+ const encrypted = await key.encrypt("hello");
18
+ const rederived = await deriveSymmetricKey("000102030405060708090a0b0c0d0e0f");
19
+ await expect(rederived.decrypt(encrypted)).resolves.toBe("hello");
20
+ });
15
21
  });
16
22
  //# sourceMappingURL=handshake.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handshake.test.js","sourceRoot":"","sources":["../../src/encryption/handshake.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;IAC/B,MAAM,GAAG,GAAG,MAAM,oBAAoB,EAAE,CAAC;IAEzC,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,OAAO,GAAG,MAAM,CAAC;QACvB,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE/C,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, test } from \"vitest\";\n\nimport { generateHandshakeKey } from \"./handshake.js\";\n\ndescribe(\"Handshake\", async () => {\n const key = await generateHandshakeKey();\n\n test(\"should generate a 16-byte random shared key\", () => {\n expect(key).toBeDefined();\n expect(key.toString()).toHaveLength(32);\n });\n\n test(\"should encrypt and decrypt a message\", async () => {\n const message = \"test\";\n const encrypted = await key.encrypt(message);\n const decrypted = await key.decrypt(encrypted);\n\n expect(decrypted).toBe(message);\n });\n});\n"]}
1
+ {"version":3,"file":"handshake.test.js","sourceRoot":"","sources":["../../src/encryption/handshake.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE1E,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;IAC/B,MAAM,GAAG,GAAG,MAAM,oBAAoB,EAAE,CAAC;IAEzC,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,OAAO,GAAG,MAAM,CAAC;QACvB,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE/C,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,kCAAkC,CAAC,CAAC;QACzE,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,kCAAkC,CAAC,CAAC;QAE/E,MAAM,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, test } from \"vitest\";\n\nimport { deriveSymmetricKey, generateHandshakeKey } from \"./handshake.js\";\n\ndescribe(\"Handshake\", async () => {\n const key = await generateHandshakeKey();\n\n test(\"should generate a 16-byte random shared key\", () => {\n expect(key).toBeDefined();\n expect(key.toString()).toHaveLength(32);\n });\n\n test(\"should encrypt and decrypt a message\", async () => {\n const message = \"test\";\n const encrypted = await key.encrypt(message);\n const decrypted = await key.decrypt(encrypted);\n\n expect(decrypted).toBe(message);\n });\n\n test(\"should derive symmetric key from decoded hex bytes\", async () => {\n const key = await deriveSymmetricKey(\"000102030405060708090a0b0c0d0e0f\");\n const encrypted = await key.encrypt(\"hello\");\n const rederived = await deriveSymmetricKey(\"000102030405060708090a0b0c0d0e0f\");\n\n await expect(rederived.decrypt(encrypted)).resolves.toBe(\"hello\");\n });\n});\n"]}
@@ -26,4 +26,7 @@ export declare class BaseError extends Error {
26
26
  walk(): Error;
27
27
  walk(fn: (err: unknown) => boolean): Error | null;
28
28
  }
29
+ export declare class SignalNoConnectionError extends BaseError {
30
+ constructor();
31
+ }
29
32
  export {};
@@ -1,4 +1,4 @@
1
- import packageJson from "../../package.json" with { type: "json" };
1
+ import packageJson from "../package.json" with { type: "json" };
2
2
  const version = `openlv@${packageJson.version}`;
3
3
  let errorConfig = {
4
4
  // getDocsUrl: ({
@@ -96,4 +96,9 @@ const walk = (err, fn) => {
96
96
  return walk(err.cause, fn);
97
97
  return fn ? null : err;
98
98
  };
99
- //# sourceMappingURL=base.js.map
99
+ export class SignalNoConnectionError extends BaseError {
100
+ constructor() {
101
+ super(["Hello", "World"].join("\n"), { name: "SignalConnectionError" });
102
+ }
103
+ }
104
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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","sourcesContent":["import packageJson from \"../package.json\" with { type: \"json\" };\n\nconst version = `openlv@${packageJson.version}`;\n\ntype ErrorConfig = {\n getDocsUrl?:\n | ((args: BaseErrorParameters) => string | undefined)\n | undefined;\n version?: string | undefined;\n};\n\nlet errorConfig: ErrorConfig = {\n // getDocsUrl: ({\n // docsBaseUrl,\n // docsPath = '',\n // docsSlug,\n // }: BaseErrorParameters) =>\n // docsPath\n // ? `${docsBaseUrl ?? 'https://viem.sh'}${docsPath}${docsSlug ? `#${docsSlug}` : ''\n // }`\n // : undefined,\n version,\n};\n\nexport const setErrorConfig = (config: ErrorConfig) => {\n errorConfig = config;\n};\n\ntype BaseErrorParameters = {\n cause?: BaseError | Error | undefined;\n details?: string | undefined;\n docsBaseUrl?: string | undefined;\n docsPath?: string | undefined;\n docsSlug?: string | undefined;\n metaMessages?: string[] | undefined;\n name?: string | undefined;\n};\n\nexport type BaseErrorType = BaseError & { name: \"BaseError\"; };\nexport class BaseError extends Error {\n details: string;\n docsPath?: string | undefined;\n metaMessages?: string[] | undefined;\n shortMessage: string;\n version: string;\n\n override name = \"BaseError\";\n\n constructor(shortMessage: string, args: BaseErrorParameters = {}) {\n const details = (() => {\n if (args.cause instanceof BaseError) return args.cause.details;\n\n if (args.cause?.message) return args.cause.message;\n\n return args.details!;\n })();\n const docsPath = (() => {\n if (args.cause instanceof BaseError)\n return args.cause.docsPath || args.docsPath;\n\n return args.docsPath;\n })();\n const docsUrl = errorConfig.getDocsUrl?.({ ...args, docsPath });\n\n const message = [\n shortMessage || \"An error occurred.\",\n \"\",\n ...(args.metaMessages ? [...args.metaMessages, \"\"] : []),\n ...(docsUrl ? [`Docs: ${docsUrl}`] : []),\n ...(details ? [`Details: ${details}`] : []),\n ...(errorConfig.version ? [`Version: ${errorConfig.version}`] : []),\n ].join(\"\\n\");\n\n super(message, args.cause ? { cause: args.cause } : undefined);\n\n this.details = details;\n this.docsPath = docsPath;\n this.metaMessages = args.metaMessages;\n this.name = args.name ?? this.name;\n this.shortMessage = shortMessage;\n this.version = version;\n }\n\n walk(): Error;\n walk(fn: (err: unknown) => boolean): Error | null;\n walk(fn?: (err: unknown) => boolean): Error | null {\n return walk(this, fn) as Error | null;\n }\n}\n\nconst walk = (\n err: unknown,\n fn?: ((err: unknown) => boolean) | undefined,\n): unknown => {\n if (fn?.(err)) return err;\n\n if (\n err\n && typeof err === \"object\"\n && \"cause\" in err\n && err.cause !== undefined\n )\n return walk(err.cause, fn);\n\n return fn ? null : err;\n};\n\nexport class SignalNoConnectionError extends BaseError {\n constructor() {\n super([\"Hello\", \"World\"].join(\"\\n\"), { name: \"SignalConnectionError\" });\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  /** biome-ignore-all lint/performance/noReExportAll: package entrypoint */
2
2
  /** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */
3
3
  export * from "./make.js";
4
- export * from "./messages/index.js";
4
+ export * from "./messages.js";
5
5
  export * from "./session.js";
6
- export * from "./settings.js";
7
6
  export * from "./url/index.js";
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  /** biome-ignore-all lint/performance/noReExportAll: package entrypoint */
2
2
  /** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */
3
3
  export * from "./make.js";
4
- export * from "./messages/index.js";
4
+ export * from "./messages.js";
5
5
  export * from "./session.js";
6
- export * from "./settings.js";
7
6
  export * from "./url/index.js";
8
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAEzE,cAAc,WAAW,CAAC;AAC1B,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC","sourcesContent":["/** biome-ignore-all lint/performance/noReExportAll: package entrypoint */\n/** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */\n\nexport * from \"./make.js\";\nexport * from \"./messages/index.js\";\nexport * from \"./session.js\";\nexport * from \"./settings.js\";\nexport * from \"./url/index.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAEzE,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC","sourcesContent":["/** biome-ignore-all lint/performance/noReExportAll: package entrypoint */\n/** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */\n\nexport * from \"./make.js\";\nexport * from \"./messages.js\";\nexport * from \"./session.js\";\nexport * from \"./url/index.js\";\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"","sourcesContent":["export type SignalMessageBase<T extends string, P> = {\n type: T;\n payload: P;\n timestamp: number;\n};\n\n/**\n * Flash message\n * Sent to initiate handshake by non-host\n */\nexport type SignalMessageFlash = SignalMessageBase<\"flash\", object>;\n\nexport type SignalMessagePubkey = SignalMessageBase<\n \"pubkey\",\n {\n publicKey: string;\n dAppInfo?: {\n name: string;\n url: string;\n icon: string;\n };\n }\n>;\n\nexport type SignalMessageAck = SignalMessageBase<\"ack\", undefined>;\n\nexport type SignalMessageData = SignalMessageBase<\"data\", object>;\n\nexport type SignalMessage =\n | SignalMessageFlash\n | SignalMessagePubkey\n | SignalMessageAck\n | SignalMessageData;\n"]}
package/dist/url/index.js CHANGED
@@ -52,7 +52,7 @@ export const decodeConnectionURL = (url) => {
52
52
  }
53
53
  // Validate shared key format (32 hex characters)
54
54
  if (!/^[0-9a-f]{32}$/.test(k)) {
55
- throw new Error(`Invalid shared key format: must be 32 hex characters, received: ${k}`);
55
+ throw new Error("Invalid shared key format: must be 32 lowercase hex characters");
56
56
  }
57
57
  return {
58
58
  version,
@@ -66,7 +66,9 @@ export const decodeConnectionURL = (url) => {
66
66
  }
67
67
  catch (error) {
68
68
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
69
- throw new Error(`Failed to parse URL: ${url}. Original error: ${errorMessage}`);
69
+ // Strip query string so handshake secrets (k) are not exposed in error messages
70
+ const safeUrl = url.includes("?") ? url.slice(0, url.indexOf("?")) + "?[redacted]" : url;
71
+ throw new Error(`Failed to parse URL: ${safeUrl}. Original error: ${errorMessage}`);
70
72
  }
71
73
  };
72
74
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/url/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionHandshakeParameters } from \"../session.js\";\n\nexport const OPENLV_PROTOCOL_VERSION = 1;\n\nexport const encodeConnectionURL = (payload: SessionHandshakeParameters) => {\n const params = new URLSearchParams();\n\n params.set(\"h\", payload.h);\n params.set(\"k\", payload.k);\n\n if (payload.p) {\n params.set(\"p\", payload.p);\n }\n\n if (payload.s) {\n params.set(\"s\", payload.s);\n }\n\n return `openlv://${payload.sessionId}@${OPENLV_PROTOCOL_VERSION}?${params.toString()}`;\n};\n\nexport const decodeConnectionURL = (\n url: string,\n): SessionHandshakeParameters => {\n // Type check and validation\n if (typeof url !== \"string\") {\n throw new TypeError(`Invalid URL type: expected string, got ${typeof url}`);\n }\n\n if (!url || url.trim() === \"\") {\n throw new Error(\"URL cannot be empty\");\n }\n\n if (!url.startsWith(\"openlv://\")) {\n throw new Error(\n `Invalid URL format: must start with 'openlv://', got: ${url}`,\n );\n }\n\n try {\n const urlObj = new URL(url);\n const sessionId = urlObj.username;\n const version = Number(urlObj.hostname || urlObj.pathname.replace(\"/\", \"\"));\n\n if (version !== OPENLV_PROTOCOL_VERSION) {\n throw new Error(\n `Invalid protocol version: expected ${OPENLV_PROTOCOL_VERSION}, got ${version}`,\n );\n }\n\n const h = urlObj.searchParams.get(\"h\") || \"\";\n const k = urlObj.searchParams.get(\"k\") || \"\";\n const s = urlObj.searchParams.get(\"s\") || undefined;\n\n const p\n = (urlObj.searchParams.get(\"p\") as \"mqtt\" | \"waku\" | \"nostr\") || \"mqtt\";\n\n if (!sessionId) {\n throw new Error(\"Session ID is required in URL\");\n }\n\n // Validate session ID format (16 characters, URL-safe alphabet)\n if (!/^[A-Za-z0-9_-]{16}$/.test(sessionId)) {\n throw new Error(\n \"Invalid session ID format: must be 16 URL-safe characters\",\n );\n }\n\n if (!h) {\n throw new Error(\"Public key hash (h parameter) is required in URL\");\n }\n\n // Validate public key hash format (16 hex characters)\n if (!/^[0-9a-f]{16}$/.test(h)) {\n throw new Error(\n \"Invalid public key hash format: must be 16 hex characters\",\n );\n }\n\n if (!k) {\n throw new Error(\"Shared key (k parameter) is required in URL\");\n }\n\n // Validate shared key format (32 hex characters)\n if (!/^[0-9a-f]{32}$/.test(k)) {\n throw new Error(\n `Invalid shared key format: must be 32 hex characters, received: ${k}`,\n );\n }\n\n return {\n version,\n sessionId,\n h,\n k,\n // TODO: figure out why '' empty string and not undefined\n s: s ? decodeURIComponent(s) : \"\",\n p,\n };\n }\n catch (error) {\n const errorMessage\n = error instanceof Error ? error.message : \"Unknown error\";\n\n throw new Error(\n `Failed to parse URL: ${url}. Original error: ${errorMessage}`,\n );\n }\n};\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/url/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionHandshakeParameters } from \"../session.js\";\n\nexport const OPENLV_PROTOCOL_VERSION = 1;\n\nexport const encodeConnectionURL = (payload: SessionHandshakeParameters) => {\n const params = new URLSearchParams();\n\n params.set(\"h\", payload.h);\n params.set(\"k\", payload.k);\n\n if (payload.p) {\n params.set(\"p\", payload.p);\n }\n\n if (payload.s) {\n params.set(\"s\", payload.s);\n }\n\n return `openlv://${payload.sessionId}@${OPENLV_PROTOCOL_VERSION}?${params.toString()}`;\n};\n\nexport const decodeConnectionURL = (\n url: string,\n): SessionHandshakeParameters => {\n // Type check and validation\n if (typeof url !== \"string\") {\n throw new TypeError(`Invalid URL type: expected string, got ${typeof url}`);\n }\n\n if (!url || url.trim() === \"\") {\n throw new Error(\"URL cannot be empty\");\n }\n\n if (!url.startsWith(\"openlv://\")) {\n throw new Error(\n `Invalid URL format: must start with 'openlv://', got: ${url}`,\n );\n }\n\n try {\n const urlObj = new URL(url);\n const sessionId = urlObj.username;\n const version = Number(urlObj.hostname || urlObj.pathname.replace(\"/\", \"\"));\n\n if (version !== OPENLV_PROTOCOL_VERSION) {\n throw new Error(\n `Invalid protocol version: expected ${OPENLV_PROTOCOL_VERSION}, got ${version}`,\n );\n }\n\n const h = urlObj.searchParams.get(\"h\") || \"\";\n const k = urlObj.searchParams.get(\"k\") || \"\";\n const s = urlObj.searchParams.get(\"s\") || undefined;\n\n const p\n = (urlObj.searchParams.get(\"p\") as \"mqtt\" | \"waku\" | \"nostr\") || \"mqtt\";\n\n if (!sessionId) {\n throw new Error(\"Session ID is required in URL\");\n }\n\n // Validate session ID format (16 characters, URL-safe alphabet)\n if (!/^[A-Za-z0-9_-]{16}$/.test(sessionId)) {\n throw new Error(\n \"Invalid session ID format: must be 16 URL-safe characters\",\n );\n }\n\n if (!h) {\n throw new Error(\"Public key hash (h parameter) is required in URL\");\n }\n\n // Validate public key hash format (16 hex characters)\n if (!/^[0-9a-f]{16}$/.test(h)) {\n throw new Error(\n \"Invalid public key hash format: must be 16 hex characters\",\n );\n }\n\n if (!k) {\n throw new Error(\"Shared key (k parameter) is required in URL\");\n }\n\n // Validate shared key format (32 hex characters)\n if (!/^[0-9a-f]{32}$/.test(k)) {\n throw new Error(\"Invalid shared key format: must be 32 lowercase hex characters\");\n }\n\n return {\n version,\n sessionId,\n h,\n k,\n // TODO: figure out why '' empty string and not undefined\n s: s ? decodeURIComponent(s) : \"\",\n p,\n };\n }\n catch (error) {\n const errorMessage\n = error instanceof Error ? error.message : \"Unknown error\";\n\n // Strip query string so handshake secrets (k) are not exposed in error messages\n const safeUrl = url.includes(\"?\") ? url.slice(0, url.indexOf(\"?\")) + \"?[redacted]\" : url;\n\n throw new Error(`Failed to parse URL: ${safeUrl}. Original error: ${errorMessage}`);\n }\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openlv/core",
3
- "version": "0.0.1-beta.9",
3
+ "version": "0.0.2",
4
4
  "keywords": [
5
5
  "open-lavatory"
6
6
  ],
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "https://github.com/v3xlabs/open-lavatory.git",
13
+ "url": "git+https://github.com/v3xlabs/open-lavatory.git",
14
14
  "directory": "packages/core"
15
15
  },
16
16
  "license": "LGPL-3.0-only",
@@ -31,8 +31,8 @@
31
31
  "default": "./dist/encryption/index.js"
32
32
  },
33
33
  "./errors": {
34
- "types": "./dist/errors/index.d.ts",
35
- "default": "./dist/errors/index.js"
34
+ "types": "./dist/errors.d.ts",
35
+ "default": "./dist/errors.js"
36
36
  },
37
37
  "./icons": {
38
38
  "types": "./dist/icons/index.d.ts",
@@ -46,21 +46,16 @@
46
46
  "README.md"
47
47
  ],
48
48
  "dependencies": {
49
- "eventemitter3": "^5.0.1",
50
- "tweetnacl": "^1.0.3"
49
+ "@noble/ciphers": "^1.3.0",
50
+ "@noble/curves": "^1.9.7",
51
+ "eventemitter3": "^5.0.1"
51
52
  },
52
53
  "devDependencies": {
53
54
  "typescript": "^5",
54
55
  "vitest": "^4"
55
56
  },
56
- "peerDependencies": {
57
- "@wagmi/core": "2",
58
- "viem": "2",
59
- "wagmi": "2"
60
- },
61
57
  "publishConfig": {
62
- "access": "public",
63
- "tag": "beta"
58
+ "access": "public"
64
59
  },
65
60
  "scripts": {
66
61
  "build": "tsc",
@@ -1 +0,0 @@
1
- {"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/errors/base.ts"],"names":[],"mappings":"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","sourcesContent":["import packageJson from \"../../package.json\" with { type: \"json\" };\n\nconst version = `openlv@${packageJson.version}`;\n\ntype ErrorConfig = {\n getDocsUrl?:\n | ((args: BaseErrorParameters) => string | undefined)\n | undefined;\n version?: string | undefined;\n};\n\nlet errorConfig: ErrorConfig = {\n // getDocsUrl: ({\n // docsBaseUrl,\n // docsPath = '',\n // docsSlug,\n // }: BaseErrorParameters) =>\n // docsPath\n // ? `${docsBaseUrl ?? 'https://viem.sh'}${docsPath}${docsSlug ? `#${docsSlug}` : ''\n // }`\n // : undefined,\n version,\n};\n\nexport const setErrorConfig = (config: ErrorConfig) => {\n errorConfig = config;\n};\n\ntype BaseErrorParameters = {\n cause?: BaseError | Error | undefined;\n details?: string | undefined;\n docsBaseUrl?: string | undefined;\n docsPath?: string | undefined;\n docsSlug?: string | undefined;\n metaMessages?: string[] | undefined;\n name?: string | undefined;\n};\n\nexport type BaseErrorType = BaseError & { name: \"BaseError\"; };\nexport class BaseError extends Error {\n details: string;\n docsPath?: string | undefined;\n metaMessages?: string[] | undefined;\n shortMessage: string;\n version: string;\n\n override name = \"BaseError\";\n\n constructor(shortMessage: string, args: BaseErrorParameters = {}) {\n const details = (() => {\n if (args.cause instanceof BaseError) return args.cause.details;\n\n if (args.cause?.message) return args.cause.message;\n\n return args.details!;\n })();\n const docsPath = (() => {\n if (args.cause instanceof BaseError)\n return args.cause.docsPath || args.docsPath;\n\n return args.docsPath;\n })();\n const docsUrl = errorConfig.getDocsUrl?.({ ...args, docsPath });\n\n const message = [\n shortMessage || \"An error occurred.\",\n \"\",\n ...(args.metaMessages ? [...args.metaMessages, \"\"] : []),\n ...(docsUrl ? [`Docs: ${docsUrl}`] : []),\n ...(details ? [`Details: ${details}`] : []),\n ...(errorConfig.version ? [`Version: ${errorConfig.version}`] : []),\n ].join(\"\\n\");\n\n super(message, args.cause ? { cause: args.cause } : undefined);\n\n this.details = details;\n this.docsPath = docsPath;\n this.metaMessages = args.metaMessages;\n this.name = args.name ?? this.name;\n this.shortMessage = shortMessage;\n this.version = version;\n }\n\n walk(): Error;\n walk(fn: (err: unknown) => boolean): Error | null;\n walk(fn?: (err: unknown) => boolean): Error | null {\n return walk(this, fn) as Error | null;\n }\n}\n\nconst walk = (\n err: unknown,\n fn?: ((err: unknown) => boolean) | undefined,\n): unknown => {\n if (fn?.(err)) return err;\n\n if (\n err\n && typeof err === \"object\"\n && \"cause\" in err\n && err.cause !== undefined\n )\n return walk(err.cause, fn);\n\n return fn ? null : err;\n};\n"]}
@@ -1,4 +0,0 @@
1
- /** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */
2
- /** biome-ignore-all lint/performance/noReExportAll: package entrypoint */
3
- export * from "./base.js";
4
- export * from "./signal.js";
@@ -1,5 +0,0 @@
1
- /** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */
2
- /** biome-ignore-all lint/performance/noReExportAll: package entrypoint */
3
- export * from "./base.js";
4
- export * from "./signal.js";
5
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/errors/index.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,0EAA0E;AAE1E,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC","sourcesContent":["/** biome-ignore-all lint/performance/noBarrelFile: package entrypoint */\n/** biome-ignore-all lint/performance/noReExportAll: package entrypoint */\n\nexport * from \"./base.js\";\nexport * from \"./signal.js\";\n"]}
@@ -1,4 +0,0 @@
1
- import { BaseError } from "./base.js";
2
- export declare class SignalNoConnectionError extends BaseError {
3
- constructor();
4
- }
@@ -1,7 +0,0 @@
1
- import { BaseError } from "./base.js";
2
- export class SignalNoConnectionError extends BaseError {
3
- constructor() {
4
- super(["Hello", "World"].join("\n"), { name: "SignalConnectionError" });
5
- }
6
- }
7
- //# sourceMappingURL=signal.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/errors/signal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,OAAO,uBAAwB,SAAQ,SAAS;IACpD;QACE,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;IAC1E,CAAC;CACF","sourcesContent":["import { BaseError } from \"./base.js\";\n\nexport class SignalNoConnectionError extends BaseError {\n constructor() {\n super([\"Hello\", \"World\"].join(\"\\n\"), { name: \"SignalConnectionError\" });\n }\n}\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"","sourcesContent":["export type SignalMessageBase<T extends string, P> = {\n type: T;\n payload: P;\n timestamp: number;\n};\n\n/**\n * Flash message\n * Sent to initiate handshake by non-host\n */\nexport type SignalMessageFlash = SignalMessageBase<\"flash\", object>;\n\nexport type SignalMessagePubkey = SignalMessageBase<\n \"pubkey\",\n {\n publicKey: string;\n dAppInfo?: {\n name: string;\n url: string;\n icon: string;\n };\n }\n>;\n\nexport type SignalMessageAck = SignalMessageBase<\"ack\", undefined>;\n\nexport type SignalMessageData = SignalMessageBase<\"data\", object>;\n\nexport type SignalMessage =\n | SignalMessageFlash\n | SignalMessagePubkey\n | SignalMessageAck\n | SignalMessageData;\n"]}
@@ -1,2 +0,0 @@
1
- import type { SessionCreationParameters } from "./session.js";
2
- export type SignalingSettings = SessionCreationParameters;
package/dist/settings.js DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=settings.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"","sourcesContent":["import type { SessionCreationParameters } from \"./session.js\";\n\nexport type SignalingSettings = SessionCreationParameters;\n"]}
File without changes