@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.
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +3 -0
- package/dist/crypto.d.ts +1 -1
- package/dist/crypto.js +19 -16
- package/dist/curve.d.ts +1 -1
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +2 -0
- package/dist/errors/protobuf-validation-error.d.ts +4 -0
- package/dist/errors/protobuf-validation-error.js +6 -0
- package/dist/fingerprint.js +1 -1
- package/dist/index.d.ts +21 -19
- package/dist/index.js +13 -12
- package/dist/job_queue.d.ts +4 -1
- package/dist/job_queue.js +32 -9
- package/dist/key-helper.d.ts +1 -1
- package/dist/key-helper.js +1 -1
- package/dist/proto/generated/whisper-text-protocol.d.ts +29 -0
- package/dist/proto/generated/whisper-text-protocol.js +132 -0
- package/dist/proto/index.d.ts +1 -0
- package/dist/proto/index.js +1 -0
- package/dist/protobuf.d.ts +1 -1
- package/dist/protobuf.js +1 -2
- package/dist/session/builder/index.d.ts +2 -2
- package/dist/session/builder/index.js +2 -2
- package/dist/session/builder/session-builder.d.ts +3 -3
- package/dist/session/builder/session-builder.js +26 -23
- package/dist/session/builder/types.d.ts +9 -7
- package/dist/session/cipher/encoding.d.ts +3 -7
- package/dist/session/cipher/encoding.js +35 -119
- package/dist/session/cipher/index.d.ts +3 -3
- package/dist/session/cipher/index.js +2 -3
- package/dist/session/cipher/session-cipher.d.ts +2 -2
- package/dist/session/cipher/session-cipher.js +28 -23
- package/dist/session/cipher/types.d.ts +18 -10
- package/dist/session/index.d.ts +5 -3
- package/dist/session/index.js +5 -3
- package/dist/session/record/index.d.ts +3 -3
- package/dist/session/record/index.js +3 -3
- package/dist/session/record/session-entry.d.ts +1 -1
- package/dist/session/record/session-entry.js +5 -5
- package/dist/session/record/session-record.d.ts +2 -2
- package/dist/session/record/session-record.js +4 -4
- package/dist/session/record/types.d.ts +2 -2
- package/dist/session/storage/adapter.d.ts +34 -0
- package/dist/session/storage/adapter.js +111 -0
- package/dist/session/storage/in-memory.d.ts +22 -0
- package/dist/session/storage/in-memory.js +65 -0
- package/dist/session/storage/index.d.ts +19 -0
- package/dist/session/storage/index.js +37 -0
- package/dist/session/storage/migrations.d.ts +2 -0
- package/dist/session/storage/migrations.js +8 -0
- package/dist/session/storage/runtime.d.ts +7 -0
- package/dist/session/storage/runtime.js +45 -0
- package/dist/session/storage/sqlite-async.d.ts +26 -0
- package/dist/session/storage/sqlite-async.js +95 -0
- package/dist/session/storage/types.d.ts +58 -0
- package/dist/session/storage/types.js +1 -0
- package/dist/session/utils.js +2 -2
- package/dist/transport/index.d.ts +1 -0
- package/dist/transport/index.js +1 -0
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +2 -0
- package/package.json +2 -3
- package/dist/generated/WhisperTextProtocol.d.ts +0 -49
- package/dist/generated/WhisperTextProtocol.js +0 -199
package/dist/crypto.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { CryptoAPI } from './types/crypto
|
|
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
|
|
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 =
|
|
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 ?
|
|
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(
|
|
21
|
-
const encrypted = Buffer.concat([cipher.update(
|
|
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 =
|
|
32
|
-
const decipher = createDecipheriv('aes-256-gcm', k,
|
|
34
|
+
const k = toBufferCopy(key);
|
|
35
|
+
const decipher = createDecipheriv('aes-256-gcm', k, toBufferView(data.iv));
|
|
33
36
|
if (options?.aad) {
|
|
34
|
-
decipher.setAAD(
|
|
37
|
+
decipher.setAAD(toBufferView(options.aad));
|
|
35
38
|
}
|
|
36
|
-
decipher.setAuthTag(
|
|
37
|
-
const cipherBuf =
|
|
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 =
|
|
73
|
-
const saltBuf =
|
|
74
|
-
const infoBuf =
|
|
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(
|
|
118
|
+
.update(toBufferView(data))
|
|
116
119
|
.digest());
|
|
117
120
|
}
|
|
118
121
|
function hmacSha256(key, data) {
|
|
119
|
-
return toUint8(createHmac('sha256',
|
|
120
|
-
.update(
|
|
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
|
|
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;
|
package/dist/fingerprint.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
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 './
|
|
5
|
-
export
|
|
6
|
-
export {
|
|
7
|
-
export
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export
|
|
11
|
-
export type {
|
|
12
|
-
export {
|
|
13
|
-
export
|
|
14
|
-
export {
|
|
15
|
-
export {
|
|
16
|
-
export
|
|
17
|
-
export
|
|
18
|
-
export {
|
|
19
|
-
export {
|
|
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
|
|
2
|
-
export { SessionRecord, SessionEntry } from './session/record
|
|
3
|
-
export { SessionCipher } from './
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export
|
|
11
|
-
export
|
|
12
|
-
export {
|
|
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';
|
package/dist/job_queue.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
type Awaitable<T> = () => Promise<T>;
|
|
2
|
-
export
|
|
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
|
|
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
|
|
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({
|
|
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;
|
package/dist/key-helper.d.ts
CHANGED
package/dist/key-helper.js
CHANGED
|
@@ -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';
|
package/dist/protobuf.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { PreKeyWhisperMessage,
|
|
1
|
+
export { type WhisperMessage, type PreKeyWhisperMessage, type KeyExchangeMessage, WhisperMessageCodec, PreKeyWhisperMessageCodec, } from './proto';
|
package/dist/protobuf.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { PreKeyWhisperMessage, WhisperMessage } from "./generated/WhisperTextProtocol.js";
|
|
1
|
+
export { WhisperMessageCodec, PreKeyWhisperMessageCodec, } from './proto';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './types
|
|
2
|
-
export * from './session-builder
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './session-builder';
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './types
|
|
2
|
-
export * from './session-builder
|
|
1
|
+
export * from './types';
|
|
2
|
+
export * from './session-builder';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ProtocolAddress } from '../../protocol_address
|
|
2
|
-
import { SessionRecord } from '../record/index
|
|
3
|
-
import type { PreKeyBundle, PreKeyWhisperMessage, SessionBuilderStorage } from './types
|
|
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
|
|
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
|
|
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] =
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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:
|
|
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,
|
|
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
|
|
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):
|
|
33
|
-
loadSession(addressName: string):
|
|
34
|
-
storeSession(addressName: string, record: SessionRecord):
|
|
35
|
-
getOurIdentity():
|
|
36
|
-
loadPreKey(preKeyId: number):
|
|
37
|
-
loadSignedPreKey(signedPreKeyId: number):
|
|
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
|
|
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
|
|
8
|
-
private static
|
|
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
|
}
|