@unknownncat/swt-libsignal 1.0.6 → 1.1.0

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.
Files changed (67) hide show
  1. package/dist/core/index.d.ts +3 -0
  2. package/dist/core/index.js +3 -0
  3. package/dist/crypto.d.ts +1 -1
  4. package/dist/crypto.js +19 -16
  5. package/dist/curve.d.ts +1 -1
  6. package/dist/errors/index.d.ts +2 -0
  7. package/dist/errors/index.js +2 -0
  8. package/dist/errors/protobuf-validation-error.d.ts +4 -0
  9. package/dist/errors/protobuf-validation-error.js +6 -0
  10. package/dist/fingerprint.js +1 -1
  11. package/dist/index.d.ts +21 -19
  12. package/dist/index.js +13 -12
  13. package/dist/job_queue.d.ts +4 -1
  14. package/dist/job_queue.js +32 -9
  15. package/dist/key-helper.d.ts +1 -1
  16. package/dist/key-helper.js +1 -1
  17. package/dist/proto/generated/whisper-text-protocol.d.ts +29 -0
  18. package/dist/proto/generated/whisper-text-protocol.js +132 -0
  19. package/dist/proto/index.d.ts +1 -0
  20. package/dist/proto/index.js +1 -0
  21. package/dist/protobuf.d.ts +1 -1
  22. package/dist/protobuf.js +1 -2
  23. package/dist/session/builder/index.d.ts +2 -2
  24. package/dist/session/builder/index.js +2 -2
  25. package/dist/session/builder/session-builder.d.ts +3 -3
  26. package/dist/session/builder/session-builder.js +26 -23
  27. package/dist/session/builder/types.d.ts +9 -7
  28. package/dist/session/cipher/encoding.d.ts +3 -7
  29. package/dist/session/cipher/encoding.js +35 -119
  30. package/dist/session/cipher/index.d.ts +3 -3
  31. package/dist/session/cipher/index.js +2 -3
  32. package/dist/session/cipher/session-cipher.d.ts +2 -2
  33. package/dist/session/cipher/session-cipher.js +28 -23
  34. package/dist/session/cipher/types.d.ts +18 -10
  35. package/dist/session/index.d.ts +5 -3
  36. package/dist/session/index.js +5 -3
  37. package/dist/session/record/index.d.ts +3 -3
  38. package/dist/session/record/index.js +3 -3
  39. package/dist/session/record/session-entry.d.ts +1 -1
  40. package/dist/session/record/session-entry.js +5 -5
  41. package/dist/session/record/session-record.d.ts +2 -2
  42. package/dist/session/record/session-record.js +4 -4
  43. package/dist/session/record/types.d.ts +2 -2
  44. package/dist/session/storage/adapter.d.ts +34 -0
  45. package/dist/session/storage/adapter.js +111 -0
  46. package/dist/session/storage/in-memory.d.ts +22 -0
  47. package/dist/session/storage/in-memory.js +65 -0
  48. package/dist/session/storage/index.d.ts +19 -0
  49. package/dist/session/storage/index.js +37 -0
  50. package/dist/session/storage/migrations.d.ts +2 -0
  51. package/dist/session/storage/migrations.js +8 -0
  52. package/dist/session/storage/runtime.d.ts +7 -0
  53. package/dist/session/storage/runtime.js +45 -0
  54. package/dist/session/storage/sqlite-async.d.ts +26 -0
  55. package/dist/session/storage/sqlite-async.js +95 -0
  56. package/dist/session/storage/types.d.ts +58 -0
  57. package/dist/session/storage/types.js +1 -0
  58. package/dist/session/utils.js +2 -2
  59. package/dist/transport/index.d.ts +1 -0
  60. package/dist/transport/index.js +1 -0
  61. package/dist/types/index.d.ts +2 -2
  62. package/dist/types/index.js +1 -0
  63. package/dist/utils/index.d.ts +2 -0
  64. package/dist/utils/index.js +2 -0
  65. package/package.json +2 -3
  66. package/dist/generated/WhisperTextProtocol.d.ts +0 -49
  67. package/dist/generated/WhisperTextProtocol.js +0 -199
@@ -0,0 +1,3 @@
1
+ export { SessionCipher } from '../session/cipher/session-cipher';
2
+ export { SessionBuilder } from '../session/builder/session-builder';
3
+ export { SessionRecord, SessionEntry } from '../session/record';
@@ -0,0 +1,3 @@
1
+ export { SessionCipher } from '../session/cipher/session-cipher';
2
+ export { SessionBuilder } from '../session/builder/session-builder';
3
+ export { SessionRecord, SessionEntry } from '../session/record';
package/dist/crypto.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import type { CryptoAPI } from './types/crypto.js';
1
+ import type { CryptoAPI } from './types/crypto';
2
2
  export declare const crypto: CryptoAPI;
package/dist/crypto.js CHANGED
@@ -1,24 +1,27 @@
1
1
  import { createCipheriv, createDecipheriv, createHmac, createHash, randomBytes } from 'node:crypto';
2
2
  const EMPTY_BUFFER = Buffer.alloc(0);
3
- function toBuffer(data) {
3
+ function toBufferView(data) {
4
4
  return Buffer.isBuffer(data)
5
5
  ? data
6
6
  : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
7
7
  }
8
+ function toBufferCopy(data) {
9
+ return Buffer.from(data);
10
+ }
8
11
  function toUint8(data) {
9
12
  return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
10
13
  }
11
14
  function encrypt(key, plaintext, options) {
12
- const k = toBuffer(key);
15
+ const k = toBufferCopy(key);
13
16
  if (k.length !== 32)
14
17
  throw new Error('Key must be 32 bytes');
15
- const iv = options?.iv ? toBuffer(options.iv) : randomBytes(12);
18
+ const iv = options?.iv ? toBufferCopy(options.iv) : randomBytes(12);
16
19
  if (iv.length !== 12)
17
20
  throw new Error('IV must be 12 bytes for AES-GCM');
18
21
  const cipher = createCipheriv('aes-256-gcm', k, iv);
19
22
  if (options?.aad)
20
- cipher.setAAD(toBuffer(options.aad));
21
- const encrypted = Buffer.concat([cipher.update(toBuffer(plaintext)), cipher.final()]);
23
+ cipher.setAAD(toBufferView(options.aad));
24
+ const encrypted = Buffer.concat([cipher.update(toBufferView(plaintext)), cipher.final()]);
22
25
  const tag = cipher.getAuthTag();
23
26
  k.fill(0);
24
27
  return {
@@ -28,13 +31,13 @@ function encrypt(key, plaintext, options) {
28
31
  };
29
32
  }
30
33
  function decrypt(key, data, options) {
31
- const k = toBuffer(key);
32
- const decipher = createDecipheriv('aes-256-gcm', k, toBuffer(data.iv));
34
+ const k = toBufferCopy(key);
35
+ const decipher = createDecipheriv('aes-256-gcm', k, toBufferView(data.iv));
33
36
  if (options?.aad) {
34
- decipher.setAAD(toBuffer(options.aad));
37
+ decipher.setAAD(toBufferView(options.aad));
35
38
  }
36
- decipher.setAuthTag(toBuffer(data.tag));
37
- const cipherBuf = toBuffer(data.ciphertext);
39
+ decipher.setAuthTag(toBufferView(data.tag));
40
+ const cipherBuf = toBufferView(data.ciphertext);
38
41
  const decrypted = decipher.update(cipherBuf);
39
42
  const final = decipher.final();
40
43
  const plaintext = decrypted.length === 0
@@ -69,9 +72,9 @@ function hkdf(ikm, salt, info, options) {
69
72
  }
70
73
  const hashLen = 32;
71
74
  const blocksNeeded = Math.ceil(length / hashLen);
72
- const ikmBuf = toBuffer(ikm);
73
- const saltBuf = toBuffer(salt);
74
- const infoBuf = toBuffer(info);
75
+ const ikmBuf = toBufferCopy(ikm);
76
+ const saltBuf = toBufferCopy(salt);
77
+ const infoBuf = toBufferCopy(info);
75
78
  const prkKey = saltBuf.length ? saltBuf : Buffer.alloc(hashLen, 0);
76
79
  try {
77
80
  const prk = createHmac('sha256', prkKey)
@@ -112,12 +115,12 @@ function hkdf(ikm, salt, info, options) {
112
115
  }
113
116
  function sha512(data) {
114
117
  return toUint8(createHash('sha512')
115
- .update(toBuffer(data))
118
+ .update(toBufferView(data))
116
119
  .digest());
117
120
  }
118
121
  function hmacSha256(key, data) {
119
- return toUint8(createHmac('sha256', toBuffer(key))
120
- .update(toBuffer(data))
122
+ return toUint8(createHmac('sha256', toBufferView(key))
123
+ .update(toBufferView(data))
121
124
  .digest());
122
125
  }
123
126
  export const crypto = {
package/dist/curve.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { DHKeyPair, SignalAsymmetricAPI } from './types/asymmetric.js';
1
+ import type { DHKeyPair, SignalAsymmetricAPI } from './types/asymmetric';
2
2
  declare function generateKeyPair(): Promise<DHKeyPair>;
3
3
  declare function calculateSignature(identityPrivateKey: Uint8Array, data: Uint8Array): Uint8Array<ArrayBufferLike>;
4
4
  export declare const signalCrypto: SignalAsymmetricAPI;
@@ -0,0 +1,2 @@
1
+ export { SignalError, UntrustedIdentityKeyError, SessionError, MessageCounterError, PreKeyError, } from '../signal-errors';
2
+ export { ProtobufValidationError } from './protobuf-validation-error';
@@ -0,0 +1,2 @@
1
+ export { SignalError, UntrustedIdentityKeyError, SessionError, MessageCounterError, PreKeyError, } from '../signal-errors';
2
+ export { ProtobufValidationError } from './protobuf-validation-error';
@@ -0,0 +1,4 @@
1
+ export declare class ProtobufValidationError extends Error {
2
+ readonly name = "ProtobufValidationError";
3
+ constructor(message: string);
4
+ }
@@ -0,0 +1,6 @@
1
+ export class ProtobufValidationError extends Error {
2
+ name = 'ProtobufValidationError';
3
+ constructor(message) {
4
+ super(message);
5
+ }
6
+ }
@@ -1,4 +1,4 @@
1
- import { crypto } from './crypto.js';
1
+ import { crypto } from './crypto';
2
2
  const VERSION = 0;
3
3
  function numberToUint16BE(num) {
4
4
  const out = new Uint8Array(2);
package/dist/index.d.ts CHANGED
@@ -1,19 +1,21 @@
1
- export { ProtocolAddress } from './protocol_address.js';
2
- export { SessionRecord, SessionEntry } from './session/record/index.js';
3
- export type { ChainKey, ChainState, CurrentRatchet, IndexInfo } from './session/record/index.js';
4
- export { SessionCipher } from './session/cipher/index.js';
5
- export type { EncryptResult, DecryptResult, DecryptWithSessionResult, SessionCipherStorage } from './session/cipher/index.js';
6
- export { SessionBuilder } from './session/builder/session-builder.js';
7
- export type { PreKeyBundle, SessionBuilderStorage, KeyPair, IdentityKeyPair } from './session/builder/index.js';
8
- export { crypto } from './crypto.js';
9
- export { signalCrypto, generateKeyPair, calculateSignature } from './curve.js';
10
- export type { CryptoAPI } from './types/crypto.js';
11
- export type { SignalAsymmetricAPI, IdentityKeyPair as AsymIdentityKeyPair, DHKeyPair } from './types/asymmetric.js';
12
- export { generateIdentityKeyPair, generateRegistrationId, generateSignedPreKey, generatePreKey } from './key-helper.js';
13
- export type { SignedPreKey, PreKey } from './key-helper.js';
14
- export { FingerprintGenerator } from './fingerprint.js';
15
- export { SignalError, UntrustedIdentityKeyError, SessionError, MessageCounterError, PreKeyError } from './signal-errors.js';
16
- export * from "./ratchet-types.js";
17
- export type { BaseKeyType as BaseKeyTypeValue } from './ratchet-types.js';
18
- export { PreKeyWhisperMessage, WhisperMessage } from './protobuf.js';
19
- export { enqueue } from './job_queue.js';
1
+ export { ProtocolAddress } from './protocol_address';
2
+ export { SessionRecord, SessionEntry } from './session/record';
3
+ export type { ChainKey, ChainState, CurrentRatchet, IndexInfo } from './session/record';
4
+ export { SessionCipher } from './core';
5
+ export { WhisperMessageEncoder } from './transport';
6
+ export type { EncryptResult, DecryptResult, DecryptWithSessionResult, SessionCipherStorage, } from './types';
7
+ export { SessionBuilder } from './session/builder/session-builder';
8
+ export type { PreKeyBundle, SessionBuilderStorage, KeyPair, IdentityKeyPair, } from './session/builder';
9
+ export { crypto } from './crypto';
10
+ export { signalCrypto, generateKeyPair, calculateSignature } from './curve';
11
+ export type { CryptoAPI } from './types/crypto';
12
+ export type { SignalAsymmetricAPI, IdentityKeyPair as AsymIdentityKeyPair, DHKeyPair } from './types/asymmetric';
13
+ export { generateIdentityKeyPair, generateRegistrationId, generateSignedPreKey, generatePreKey, } from './key-helper';
14
+ export type { SignedPreKey, PreKey } from './key-helper';
15
+ export { FingerprintGenerator } from './fingerprint';
16
+ export { SignalError, UntrustedIdentityKeyError, SessionError, MessageCounterError, PreKeyError, ProtobufValidationError, } from './errors';
17
+ export * from './ratchet-types';
18
+ export type { BaseKeyType as BaseKeyTypeValue } from './ratchet-types';
19
+ export { WhisperMessageCodec, PreKeyWhisperMessageCodec } from './protobuf';
20
+ export type { WhisperMessage, PreKeyWhisperMessage } from './protobuf';
21
+ export { enqueue } from './job_queue';
package/dist/index.js CHANGED
@@ -1,12 +1,13 @@
1
- export { ProtocolAddress } from './protocol_address.js';
2
- export { SessionRecord, SessionEntry } from './session/record/index.js';
3
- export { SessionCipher } from './session/cipher/index.js';
4
- export { SessionBuilder } from './session/builder/session-builder.js';
5
- export { crypto } from './crypto.js';
6
- export { signalCrypto, generateKeyPair, calculateSignature } from './curve.js';
7
- export { generateIdentityKeyPair, generateRegistrationId, generateSignedPreKey, generatePreKey } from './key-helper.js';
8
- export { FingerprintGenerator } from './fingerprint.js';
9
- export { SignalError, UntrustedIdentityKeyError, SessionError, MessageCounterError, PreKeyError } from './signal-errors.js';
10
- export * from "./ratchet-types.js";
11
- export { PreKeyWhisperMessage, WhisperMessage } from './protobuf.js';
12
- export { enqueue } from './job_queue.js';
1
+ export { ProtocolAddress } from './protocol_address';
2
+ export { SessionRecord, SessionEntry } from './session/record';
3
+ export { SessionCipher } from './core';
4
+ export { WhisperMessageEncoder } from './transport';
5
+ export { SessionBuilder } from './session/builder/session-builder';
6
+ export { crypto } from './crypto';
7
+ export { signalCrypto, generateKeyPair, calculateSignature } from './curve';
8
+ export { generateIdentityKeyPair, generateRegistrationId, generateSignedPreKey, generatePreKey, } from './key-helper';
9
+ export { FingerprintGenerator } from './fingerprint';
10
+ export { SignalError, UntrustedIdentityKeyError, SessionError, MessageCounterError, PreKeyError, ProtobufValidationError, } from './errors';
11
+ export * from './ratchet-types';
12
+ export { WhisperMessageCodec, PreKeyWhisperMessageCodec } from './protobuf';
13
+ export { enqueue } from './job_queue';
@@ -1,3 +1,6 @@
1
1
  type Awaitable<T> = () => Promise<T>;
2
- export declare function enqueue<T>(bucket: unknown, awaitable: Awaitable<T>): Promise<T>;
2
+ export interface EnqueueOptions {
3
+ timeoutMs?: number;
4
+ }
5
+ export declare function enqueue<T>(bucket: unknown, awaitable: Awaitable<T>, options?: EnqueueOptions): Promise<T>;
3
6
  export {};
package/dist/job_queue.js CHANGED
@@ -1,5 +1,24 @@
1
1
  const queueBuckets = new Map();
2
2
  const GC_LIMIT = 10_000;
3
+ async function runWithTimeout(job) {
4
+ if (job.timeoutMs === undefined) {
5
+ return job.awaitable();
6
+ }
7
+ let timeoutId;
8
+ try {
9
+ return await Promise.race([
10
+ job.awaitable(),
11
+ new Promise((_, reject) => {
12
+ timeoutId = setTimeout(() => reject(new Error(`queue job timeout after ${job.timeoutMs}ms`)), job.timeoutMs);
13
+ }),
14
+ ]);
15
+ }
16
+ finally {
17
+ if (timeoutId) {
18
+ clearTimeout(timeoutId);
19
+ }
20
+ }
21
+ }
3
22
  async function asyncQueueExecutor(bucket, state) {
4
23
  const queue = state.queue;
5
24
  try {
@@ -10,7 +29,7 @@ async function asyncQueueExecutor(bucket, state) {
10
29
  if (!job)
11
30
  continue;
12
31
  try {
13
- const result = await job.awaitable();
32
+ const result = await runWithTimeout(job);
14
33
  job.resolve(result);
15
34
  }
16
35
  catch (err) {
@@ -29,20 +48,24 @@ async function asyncQueueExecutor(bucket, state) {
29
48
  queueBuckets.delete(bucket);
30
49
  }
31
50
  }
32
- export function enqueue(bucket, awaitable) {
51
+ export function enqueue(bucket, awaitable, options) {
33
52
  let state = queueBuckets.get(bucket);
34
53
  const isInactive = !state;
35
54
  if (!state) {
36
- state = {
37
- queue: [],
38
- offset: 0,
39
- running: false
40
- };
55
+ state = { queue: [], offset: 0, running: false };
41
56
  queueBuckets.set(bucket, state);
42
57
  }
43
- const queue = state.queue;
58
+ const timeoutMs = options?.timeoutMs;
59
+ if (timeoutMs !== undefined && (!Number.isInteger(timeoutMs) || timeoutMs <= 0)) {
60
+ throw new TypeError('timeoutMs must be a positive integer');
61
+ }
44
62
  const jobPromise = new Promise((resolve, reject) => {
45
- queue.push({ awaitable, resolve, reject });
63
+ state.queue.push({
64
+ awaitable: awaitable,
65
+ resolve: resolve,
66
+ reject,
67
+ timeoutMs,
68
+ });
46
69
  });
47
70
  if (isInactive) {
48
71
  state.running = true;
@@ -1,4 +1,4 @@
1
- import type { IdentityKeyPair, DHKeyPair } from './types/asymmetric.js';
1
+ import type { IdentityKeyPair, DHKeyPair } from './types/asymmetric';
2
2
  export interface SignedPreKey {
3
3
  keyId: number;
4
4
  keyPair: DHKeyPair;
@@ -1,5 +1,5 @@
1
1
  import { randomBytes } from 'crypto';
2
- import * as curve from './curve.js';
2
+ import * as curve from './curve';
3
3
  function isNonNegativeInteger(n) {
4
4
  return (typeof n === 'number' &&
5
5
  Number.isInteger(n) &&
@@ -0,0 +1,29 @@
1
+ export interface WhisperMessage {
2
+ readonly ephemeralKey?: Uint8Array;
3
+ readonly counter?: number;
4
+ readonly previousCounter?: number;
5
+ readonly ciphertext?: Uint8Array;
6
+ }
7
+ export interface PreKeyWhisperMessage {
8
+ readonly registrationId?: number;
9
+ readonly preKeyId?: number;
10
+ readonly signedPreKeyId?: number;
11
+ readonly baseKey?: Uint8Array;
12
+ readonly identityKey?: Uint8Array;
13
+ readonly message?: Uint8Array;
14
+ }
15
+ export interface KeyExchangeMessage {
16
+ readonly id?: number;
17
+ readonly baseKey?: Uint8Array;
18
+ readonly ephemeralKey?: Uint8Array;
19
+ readonly identityKey?: Uint8Array;
20
+ readonly baseKeySignature?: Uint8Array;
21
+ }
22
+ export declare const WhisperMessageCodec: {
23
+ readonly encode: (message: WhisperMessage) => Uint8Array;
24
+ readonly decode: (input: Uint8Array) => WhisperMessage;
25
+ };
26
+ export declare const PreKeyWhisperMessageCodec: {
27
+ readonly encode: (message: PreKeyWhisperMessage) => Uint8Array;
28
+ readonly decode: (input: Uint8Array) => PreKeyWhisperMessage;
29
+ };
@@ -0,0 +1,132 @@
1
+ function varintSize(value) {
2
+ let size = 1;
3
+ while (value > 0x7f) {
4
+ size++;
5
+ value >>>= 7;
6
+ }
7
+ return size;
8
+ }
9
+ function writeVarint(out, offset, value) {
10
+ while (value > 0x7f) {
11
+ out[offset++] = (value & 0x7f) | 0x80;
12
+ value >>>= 7;
13
+ }
14
+ out[offset++] = value;
15
+ return offset;
16
+ }
17
+ function readVarint(input, offset) {
18
+ let value = 0;
19
+ let shift = 0;
20
+ let i = offset;
21
+ while (i < input.length) {
22
+ const b = input[i];
23
+ value |= (b & 0x7f) << shift;
24
+ i++;
25
+ if ((b & 0x80) === 0)
26
+ return [value >>> 0, i];
27
+ shift += 7;
28
+ }
29
+ throw new Error('Malformed varint');
30
+ }
31
+ function skipUnknown(type, input, offset) {
32
+ if (type === 0) {
33
+ const [, next] = readVarint(input, offset);
34
+ return next;
35
+ }
36
+ if (type === 2) {
37
+ const [len, next] = readVarint(input, offset);
38
+ return next + len;
39
+ }
40
+ throw new Error(`Unsupported wire type: ${type}`);
41
+ }
42
+ function decodeMessage(input, bytesMap, varintMap) {
43
+ const target = {};
44
+ let offset = 0;
45
+ while (offset < input.length) {
46
+ const [header, afterHeader] = readVarint(input, offset);
47
+ const tag = header >>> 3;
48
+ const wireType = header & 0x7;
49
+ offset = afterHeader;
50
+ const byteField = bytesMap[tag];
51
+ if (wireType === 2 && byteField) {
52
+ const [len, next] = readVarint(input, offset);
53
+ const end = next + len;
54
+ target[byteField] = input.subarray(next, end);
55
+ offset = end;
56
+ continue;
57
+ }
58
+ const varintField = varintMap[tag];
59
+ if (wireType === 0 && varintField) {
60
+ const [value, next] = readVarint(input, offset);
61
+ target[varintField] = value;
62
+ offset = next;
63
+ continue;
64
+ }
65
+ offset = skipUnknown(wireType, input, offset);
66
+ }
67
+ return target;
68
+ }
69
+ function encodeFields(message, bytesFields, varintFields) {
70
+ let size = 0;
71
+ for (const [tag, key] of bytesFields) {
72
+ const value = message[key];
73
+ if (value instanceof Uint8Array) {
74
+ size += varintSize((tag << 3) | 2) + varintSize(value.length) + value.length;
75
+ }
76
+ }
77
+ for (const [tag, key] of varintFields) {
78
+ const value = message[key];
79
+ if (typeof value === 'number') {
80
+ size += varintSize((tag << 3) | 0) + varintSize(value >>> 0);
81
+ }
82
+ }
83
+ const out = new Uint8Array(size);
84
+ let offset = 0;
85
+ for (const [tag, key] of bytesFields) {
86
+ const value = message[key];
87
+ if (!(value instanceof Uint8Array))
88
+ continue;
89
+ offset = writeVarint(out, offset, (tag << 3) | 2);
90
+ offset = writeVarint(out, offset, value.length);
91
+ out.set(value, offset);
92
+ offset += value.length;
93
+ }
94
+ for (const [tag, key] of varintFields) {
95
+ const value = message[key];
96
+ if (typeof value !== 'number')
97
+ continue;
98
+ offset = writeVarint(out, offset, (tag << 3) | 0);
99
+ offset = writeVarint(out, offset, value >>> 0);
100
+ }
101
+ return out;
102
+ }
103
+ export const WhisperMessageCodec = {
104
+ encode(message) {
105
+ return encodeFields(message, [
106
+ [1, 'ephemeralKey'],
107
+ [4, 'ciphertext'],
108
+ ], [
109
+ [2, 'counter'],
110
+ [3, 'previousCounter'],
111
+ ]);
112
+ },
113
+ decode(input) {
114
+ return decodeMessage(input, { 1: 'ephemeralKey', 4: 'ciphertext' }, { 2: 'counter', 3: 'previousCounter' });
115
+ },
116
+ };
117
+ export const PreKeyWhisperMessageCodec = {
118
+ encode(message) {
119
+ return encodeFields(message, [
120
+ [2, 'baseKey'],
121
+ [3, 'identityKey'],
122
+ [4, 'message'],
123
+ ], [
124
+ [1, 'preKeyId'],
125
+ [5, 'registrationId'],
126
+ [6, 'signedPreKeyId'],
127
+ ]);
128
+ },
129
+ decode(input) {
130
+ return decodeMessage(input, { 2: 'baseKey', 3: 'identityKey', 4: 'message' }, { 1: 'preKeyId', 5: 'registrationId', 6: 'signedPreKeyId' });
131
+ },
132
+ };
@@ -0,0 +1 @@
1
+ export { type WhisperMessage, type PreKeyWhisperMessage, type KeyExchangeMessage, WhisperMessageCodec, PreKeyWhisperMessageCodec, } from './generated/whisper-text-protocol';
@@ -0,0 +1 @@
1
+ export { WhisperMessageCodec, PreKeyWhisperMessageCodec, } from './generated/whisper-text-protocol';
@@ -1 +1 @@
1
- export { PreKeyWhisperMessage, WhisperMessage } from "./generated/WhisperTextProtocol.js";
1
+ export { type WhisperMessage, type PreKeyWhisperMessage, type KeyExchangeMessage, WhisperMessageCodec, PreKeyWhisperMessageCodec, } from './proto';
package/dist/protobuf.js CHANGED
@@ -1,2 +1 @@
1
- 'use strict';
2
- export { PreKeyWhisperMessage, WhisperMessage } from "./generated/WhisperTextProtocol.js";
1
+ export { WhisperMessageCodec, PreKeyWhisperMessageCodec, } from './proto';
@@ -1,2 +1,2 @@
1
- export * from './types.js';
2
- export * from './session-builder.js';
1
+ export * from './types';
2
+ export * from './session-builder';
@@ -1,2 +1,2 @@
1
- export * from './types.js';
2
- export * from './session-builder.js';
1
+ export * from './types';
2
+ export * from './session-builder';
@@ -1,6 +1,6 @@
1
- import type { ProtocolAddress } from '../../protocol_address.js';
2
- import { SessionRecord } from '../record/index.js';
3
- import type { PreKeyBundle, PreKeyWhisperMessage, SessionBuilderStorage } from './types.js';
1
+ import type { ProtocolAddress } from '../../protocol_address';
2
+ import { SessionRecord } from '../record/index';
3
+ import type { PreKeyBundle, PreKeyWhisperMessage, SessionBuilderStorage } from './types';
4
4
  export declare class SessionBuilder {
5
5
  private readonly addr;
6
6
  private readonly storage;
@@ -1,9 +1,11 @@
1
- import { BaseKeyType, ChainType } from "../../ratchet-types.js";
2
- import { SessionRecord } from '../record/index.js';
3
- import { crypto } from '../../crypto.js';
4
- import { signalCrypto } from '../../curve.js';
5
- import { UntrustedIdentityKeyError, PreKeyError } from '../../signal-errors.js';
6
- import { enqueue } from '../../job_queue.js';
1
+ import { BaseKeyType, ChainType } from "../../ratchet-types";
2
+ import { SessionRecord } from '../record/index';
3
+ import { crypto } from '../../crypto';
4
+ import { signalCrypto } from '../../curve';
5
+ import { UntrustedIdentityKeyError, PreKeyError } from '../../signal-errors';
6
+ import { enqueue } from '../../job_queue';
7
+ const textEncoder = new TextEncoder();
8
+ const HKDF_INFO_RATCHET = textEncoder.encode('WhisperRatchet');
7
9
  export class SessionBuilder {
8
10
  addr;
9
11
  storage;
@@ -60,7 +62,7 @@ export class SessionBuilder {
60
62
  }
61
63
  record.setSession(session);
62
64
  await this.storage.storeSession(fqAddr, record);
63
- });
65
+ }, { timeoutMs: 30_000 });
64
66
  }
65
67
  async initIncoming(record, message) {
66
68
  const fqAddr = this.addr.toString();
@@ -70,10 +72,12 @@ export class SessionBuilder {
70
72
  if (record.getSession(message.baseKey)) {
71
73
  return undefined;
72
74
  }
73
- const [preKeyPair, signedPreKeyPair] = await Promise.all([
74
- message.preKeyId ? this.storage.loadPreKey(message.preKeyId) : Promise.resolve(undefined),
75
- this.storage.loadSignedPreKey(message.signedPreKeyId)
76
- ]);
75
+ const [preKeyPair, signedPreKeyPair] = this.storage.loadPreKeyPair
76
+ ? await this.storage.loadPreKeyPair(message.preKeyId, message.signedPreKeyId)
77
+ : await Promise.all([
78
+ message.preKeyId ? this.storage.loadPreKey(message.preKeyId) : Promise.resolve(undefined),
79
+ this.storage.loadSignedPreKey(message.signedPreKeyId)
80
+ ]);
77
81
  if (message.preKeyId && !preKeyPair) {
78
82
  throw new PreKeyError('Invalid PreKey ID');
79
83
  }
@@ -115,18 +119,17 @@ export class SessionBuilder {
115
119
  const masterKey = this.deriveSecrets(sharedSecret, new Uint8Array(32), new TextEncoder().encode('WhisperText'), 2);
116
120
  const session = SessionRecord.createEntry();
117
121
  session.registrationId = registrationId;
118
- const ephemeralKP = isInitiator
119
- ? await signalCrypto.generateDHKeyPair().then(kp => ({
120
- pubKey: kp.publicKey,
121
- privKey: kp.privateKey
122
- }))
123
- : {
124
- pubKey: ourSignedKey.pubKey,
125
- privKey: ourSignedKey.privKey
126
- };
122
+ let resolvedEphemeralKP;
123
+ if (isInitiator) {
124
+ const kp = await signalCrypto.generateDHKeyPair();
125
+ resolvedEphemeralKP = { pubKey: kp.publicKey, privKey: kp.privateKey };
126
+ }
127
+ else {
128
+ resolvedEphemeralKP = { pubKey: ourSignedKey.pubKey, privKey: ourSignedKey.privKey };
129
+ }
127
130
  session.currentRatchet = {
128
131
  rootKey: masterKey[0],
129
- ephemeralKeyPair: ephemeralKP,
132
+ ephemeralKeyPair: resolvedEphemeralKP,
130
133
  lastRemoteEphemeralKey: theirSignedPubKey,
131
134
  previousCounter: 0
132
135
  };
@@ -146,9 +149,9 @@ export class SessionBuilder {
146
149
  calculateSendingRatchet(session, remoteKey) {
147
150
  const ratchet = session.currentRatchet;
148
151
  const sharedSecret = signalCrypto.calculateAgreement(remoteKey, ratchet.ephemeralKeyPair.privKey);
149
- const masterKey = this.deriveSecrets(sharedSecret, ratchet.rootKey, new TextEncoder().encode('WhisperRatchet'), 2);
152
+ const masterKey = this.deriveSecrets(sharedSecret, ratchet.rootKey, HKDF_INFO_RATCHET, 2);
150
153
  session.addChain(ratchet.ephemeralKeyPair.pubKey, {
151
- messageKeys: {},
154
+ messageKeys: new Map(),
152
155
  chainKey: {
153
156
  counter: -1,
154
157
  key: masterKey[1]
@@ -1,4 +1,5 @@
1
- import type { SessionRecord } from '../record/index.js';
1
+ import type { SessionRecord } from '../record/index';
2
+ import type { MaybePromise } from '../storage/types';
2
3
  export interface PreKeyWhisperMessage {
3
4
  identityKey: Uint8Array;
4
5
  registrationId: number;
@@ -29,10 +30,11 @@ export interface PreKeyBundle {
29
30
  };
30
31
  }
31
32
  export interface SessionBuilderStorage {
32
- isTrustedIdentity(addressName: string, identityKey: Uint8Array): Promise<boolean>;
33
- loadSession(addressName: string): Promise<SessionRecord | undefined>;
34
- storeSession(addressName: string, record: SessionRecord): Promise<void>;
35
- getOurIdentity(): Promise<IdentityKeyPair>;
36
- loadPreKey(preKeyId: number): Promise<KeyPair | undefined>;
37
- loadSignedPreKey(signedPreKeyId: number): Promise<KeyPair | undefined>;
33
+ isTrustedIdentity(addressName: string, identityKey: Uint8Array): MaybePromise<boolean>;
34
+ loadSession(addressName: string): MaybePromise<SessionRecord | undefined>;
35
+ storeSession(addressName: string, record: SessionRecord): MaybePromise<void>;
36
+ getOurIdentity(): MaybePromise<IdentityKeyPair>;
37
+ loadPreKey(preKeyId: number): MaybePromise<KeyPair | undefined>;
38
+ loadSignedPreKey(signedPreKeyId: number): MaybePromise<KeyPair | undefined>;
39
+ loadPreKeyPair?(preKeyId: number | undefined, signedPreKeyId: number): MaybePromise<readonly [KeyPair | undefined, KeyPair | undefined]>;
38
40
  }
@@ -1,13 +1,9 @@
1
- import type { WhisperMessageProto, PreKeyWhisperMessageProto } from './types.js';
1
+ import type { WhisperMessageProto, PreKeyWhisperMessageProto } from './types';
2
2
  export declare class WhisperMessageEncoder {
3
3
  static encodeWhisperMessage(msg: WhisperMessageProto): Uint8Array;
4
4
  static decodeWhisperMessage(buf: Uint8Array): WhisperMessageProto;
5
5
  static encodePreKeyWhisperMessage(msg: PreKeyWhisperMessageProto): Uint8Array;
6
6
  static decodePreKeyWhisperMessage(buf: Uint8Array): PreKeyWhisperMessageProto;
7
- private static encodeFieldVarint;
8
- private static encodeFieldBytes;
9
- private static encodeVarint;
10
- private static decodeVarint;
11
- private static decodeFieldHeader;
12
- private static concatUint8Arrays;
7
+ private static validateWhisper;
8
+ private static validatePreKey;
13
9
  }