@vex-chat/libvex 9.1.1 → 10.0.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/README.md +1 -1
- package/dist/Client.d.ts +3 -21
- package/dist/Client.d.ts.map +1 -1
- package/dist/Client.js +35 -117
- package/dist/Client.js.map +1 -1
- package/dist/__tests__/harness/memory-storage.d.ts.map +1 -1
- package/dist/__tests__/harness/memory-storage.js +6 -19
- package/dist/__tests__/harness/memory-storage.js.map +1 -1
- package/dist/preset/node.d.ts.map +1 -1
- package/dist/preset/node.js +1 -2
- package/dist/preset/node.js.map +1 -1
- package/dist/preset/test.d.ts.map +1 -1
- package/dist/preset/test.js +1 -2
- package/dist/preset/test.js.map +1 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +5 -15
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/utils/resolveAtRestAesKey.d.ts +1 -3
- package/dist/utils/resolveAtRestAesKey.d.ts.map +1 -1
- package/dist/utils/resolveAtRestAesKey.js +7 -13
- package/dist/utils/resolveAtRestAesKey.js.map +1 -1
- package/dist/utils/verifyKeyBundle.d.ts +1 -2
- package/dist/utils/verifyKeyBundle.d.ts.map +1 -1
- package/dist/utils/verifyKeyBundle.js +7 -10
- package/dist/utils/verifyKeyBundle.js.map +1 -1
- package/package.json +3 -3
- package/src/Client.ts +47 -181
- package/src/__tests__/harness/memory-storage.ts +17 -48
- package/src/__tests__/harness/shared-suite.ts +1 -137
- package/src/__tests__/platform-browser.test.ts +1 -6
- package/src/__tests__/platform-node.test.ts +1 -7
- package/src/__tests__/prekey-reuse.test.ts +8 -77
- package/src/__tests__/verifyKeyBundle.test.ts +15 -43
- package/src/preset/node.ts +1 -5
- package/src/preset/test.ts +1 -5
- package/src/storage/sqlite.ts +4 -20
- package/src/utils/resolveAtRestAesKey.ts +7 -23
- package/src/utils/verifyKeyBundle.ts +2 -16
- package/dist/utils/fipsMailExtra.d.ts +0 -30
- package/dist/utils/fipsMailExtra.d.ts.map +0 -1
- package/dist/utils/fipsMailExtra.js +0 -114
- package/dist/utils/fipsMailExtra.js.map +0 -1
- package/src/utils/fipsMailExtra.ts +0 -164
package/src/Client.ts
CHANGED
|
@@ -52,17 +52,11 @@ import type {
|
|
|
52
52
|
import type { ClientMessage } from "@vex-chat/types";
|
|
53
53
|
|
|
54
54
|
import {
|
|
55
|
-
type CryptoProfile,
|
|
56
|
-
enterCryptoProfileScope,
|
|
57
|
-
getCryptoProfile,
|
|
58
|
-
leaveCryptoProfileScope,
|
|
59
|
-
setCryptoProfile,
|
|
60
55
|
xBoxKeyPairAsync,
|
|
61
56
|
xBoxKeyPairFromSecretAsync,
|
|
62
57
|
xConcat,
|
|
63
58
|
xConstants,
|
|
64
59
|
xDHAsync,
|
|
65
|
-
xEcdhKeyPairFromEcdsaKeyPairAsync,
|
|
66
60
|
xEncode,
|
|
67
61
|
xHMAC,
|
|
68
62
|
xKDF,
|
|
@@ -78,7 +72,6 @@ import {
|
|
|
78
72
|
xSignKeyPair,
|
|
79
73
|
xSignKeyPairAsync,
|
|
80
74
|
xSignKeyPairFromSecret,
|
|
81
|
-
xSignKeyPairFromSecretAsync,
|
|
82
75
|
xSignOpenAsync,
|
|
83
76
|
XUtils,
|
|
84
77
|
} from "@vex-chat/crypto";
|
|
@@ -113,12 +106,6 @@ import {
|
|
|
113
106
|
WebSocketAdapter,
|
|
114
107
|
WebSocketNotOpenError,
|
|
115
108
|
} from "./transport/websocket.js";
|
|
116
|
-
import {
|
|
117
|
-
decodeFipsInitialExtraV1,
|
|
118
|
-
encodeFipsInitialExtraV1,
|
|
119
|
-
fipsP256AdFromIdentityPubs,
|
|
120
|
-
isFipsInitialExtraV1,
|
|
121
|
-
} from "./utils/fipsMailExtra.js";
|
|
122
109
|
import {
|
|
123
110
|
decodeRatchetHeader,
|
|
124
111
|
deriveBootstrapSendChain,
|
|
@@ -465,12 +452,6 @@ export interface Channels {
|
|
|
465
452
|
* ClientOptions are the options you can pass into the client.
|
|
466
453
|
*/
|
|
467
454
|
export interface ClientOptions {
|
|
468
|
-
/**
|
|
469
|
-
* Select crypto profile from `@vex-chat/crypto` (`setCryptoProfile`):
|
|
470
|
-
* `tweetnacl` (Ed25519 / X25519) or `fips` (P-256 + Web Crypto, separate wire
|
|
471
|
-
* layout). Deployments do not interop across profiles; pick one for all peers and server.
|
|
472
|
-
*/
|
|
473
|
-
cryptoProfile?: "fips" | "tweetnacl";
|
|
474
455
|
/** Folder path where the sqlite file is created. */
|
|
475
456
|
dbFolder?: string;
|
|
476
457
|
/**
|
|
@@ -1768,8 +1749,6 @@ export class Client {
|
|
|
1768
1749
|
|
|
1769
1750
|
private autoReconnectEnabled = false;
|
|
1770
1751
|
|
|
1771
|
-
private readonly cryptoProfile: CryptoProfile;
|
|
1772
|
-
|
|
1773
1752
|
private readonly database: Storage;
|
|
1774
1753
|
|
|
1775
1754
|
private readonly dbPath: string;
|
|
@@ -1840,7 +1819,6 @@ export class Client {
|
|
|
1840
1819
|
|
|
1841
1820
|
private constructor(
|
|
1842
1821
|
material: {
|
|
1843
|
-
cryptoProfile: CryptoProfile;
|
|
1844
1822
|
idKeys: KeyPair;
|
|
1845
1823
|
signKeys: KeyPair;
|
|
1846
1824
|
},
|
|
@@ -1851,7 +1829,6 @@ export class Client {
|
|
|
1851
1829
|
this.localMessageRetentionDays = clampLocalMessageRetentionDays(
|
|
1852
1830
|
options?.localMessageRetentionDays,
|
|
1853
1831
|
);
|
|
1854
|
-
this.cryptoProfile = material.cryptoProfile;
|
|
1855
1832
|
this.signKeys = material.signKeys;
|
|
1856
1833
|
this.idKeys = material.idKeys;
|
|
1857
1834
|
|
|
@@ -1916,47 +1893,15 @@ export class Client {
|
|
|
1916
1893
|
options?: ClientOptions,
|
|
1917
1894
|
storage?: Storage,
|
|
1918
1895
|
): Promise<Client> => {
|
|
1919
|
-
const
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
) {
|
|
1926
|
-
throw new Error(
|
|
1927
|
-
'cryptoProfile="fips" requires Web Crypto (globalThis.crypto.subtle).',
|
|
1928
|
-
);
|
|
1896
|
+
const signKeys = privateKey
|
|
1897
|
+
? xSignKeyPairFromSecret(XUtils.decodeHex(privateKey))
|
|
1898
|
+
: xSignKeyPair();
|
|
1899
|
+
const idKeys = XKeyConvert.convertKeyPair(signKeys);
|
|
1900
|
+
if (!idKeys) {
|
|
1901
|
+
throw new Error("Could not convert key to X25519!");
|
|
1929
1902
|
}
|
|
1930
1903
|
|
|
1931
|
-
|
|
1932
|
-
if (privateKey) {
|
|
1933
|
-
const d = XUtils.decodeHex(privateKey);
|
|
1934
|
-
signKeys =
|
|
1935
|
-
profile === "tweetnacl"
|
|
1936
|
-
? xSignKeyPairFromSecret(d)
|
|
1937
|
-
: await xSignKeyPairFromSecretAsync(d);
|
|
1938
|
-
} else {
|
|
1939
|
-
signKeys =
|
|
1940
|
-
profile === "tweetnacl"
|
|
1941
|
-
? xSignKeyPair()
|
|
1942
|
-
: await xSignKeyPairAsync();
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
|
-
const idKeys =
|
|
1946
|
-
profile === "tweetnacl"
|
|
1947
|
-
? (() => {
|
|
1948
|
-
const c = XKeyConvert.convertKeyPair(signKeys);
|
|
1949
|
-
if (!c) {
|
|
1950
|
-
throw new Error("Could not convert key to X25519!");
|
|
1951
|
-
}
|
|
1952
|
-
return c;
|
|
1953
|
-
})()
|
|
1954
|
-
: await xEcdhKeyPairFromEcdsaKeyPairAsync(signKeys);
|
|
1955
|
-
|
|
1956
|
-
const atRestAes = XUtils.deriveLocalAtRestAesKey(
|
|
1957
|
-
idKeys.secretKey,
|
|
1958
|
-
profile,
|
|
1959
|
-
);
|
|
1904
|
+
const atRestAes = XUtils.deriveLocalAtRestAesKey(idKeys.secretKey);
|
|
1960
1905
|
|
|
1961
1906
|
let resolvedStorage = storage;
|
|
1962
1907
|
if (!resolvedStorage) {
|
|
@@ -1992,7 +1937,6 @@ export class Client {
|
|
|
1992
1937
|
|
|
1993
1938
|
const client = new Client(
|
|
1994
1939
|
{
|
|
1995
|
-
cryptoProfile: profile,
|
|
1996
1940
|
idKeys,
|
|
1997
1941
|
signKeys,
|
|
1998
1942
|
},
|
|
@@ -2003,27 +1947,14 @@ export class Client {
|
|
|
2003
1947
|
return client;
|
|
2004
1948
|
};
|
|
2005
1949
|
|
|
2006
|
-
/**
|
|
2007
|
-
* Generates a signing secret key as a hex string (tweetnacl: Ed25519; fips: P-256 pkcs8).
|
|
2008
|
-
* In `fips` mode, use `Client.generateSecretKeyAsync()` instead (Web Crypto is async).
|
|
2009
|
-
*/
|
|
1950
|
+
/** Generates an Ed25519 signing secret key as a hex string. */
|
|
2010
1951
|
public static generateSecretKey(): string {
|
|
2011
|
-
if (getCryptoProfile() === "fips") {
|
|
2012
|
-
throw new Error(
|
|
2013
|
-
'Use await Client.generateSecretKeyAsync() when the active crypto profile is "fips".',
|
|
2014
|
-
);
|
|
2015
|
-
}
|
|
2016
1952
|
return XUtils.encodeHex(xSignKeyPair().secretKey);
|
|
2017
1953
|
}
|
|
2018
1954
|
|
|
2019
|
-
/**
|
|
2020
|
-
* Async key generation — required for `fips` profile; safe for `tweetnacl` as well.
|
|
2021
|
-
*/
|
|
1955
|
+
/** Async Ed25519 key generation. */
|
|
2022
1956
|
public static async generateSecretKeyAsync(): Promise<string> {
|
|
2023
|
-
|
|
2024
|
-
return XUtils.encodeHex((await xSignKeyPairAsync()).secretKey);
|
|
2025
|
-
}
|
|
2026
|
-
return XUtils.encodeHex(xSignKeyPair().secretKey);
|
|
1957
|
+
return XUtils.encodeHex((await xSignKeyPairAsync()).secretKey);
|
|
2027
1958
|
}
|
|
2028
1959
|
|
|
2029
1960
|
/**
|
|
@@ -2054,11 +1985,7 @@ export class Client {
|
|
|
2054
1985
|
): Uint8Array[] {
|
|
2055
1986
|
switch (type) {
|
|
2056
1987
|
case MailType.initial: {
|
|
2057
|
-
|
|
2058
|
-
const [a, b, c, d] = decodeFipsInitialExtraV1(extra);
|
|
2059
|
-
return [a, b, c, d];
|
|
2060
|
-
}
|
|
2061
|
-
/* 32B sign | 32B eph | 32B PK | 68B AD | 6B index (tweetnacl) */
|
|
1988
|
+
/* 32B sign | 32B eph | 32B PK | 68B AD | 6B index */
|
|
2062
1989
|
const signKey = extra.slice(0, 32);
|
|
2063
1990
|
const ephKey = extra.slice(32, 64);
|
|
2064
1991
|
const ad = extra.slice(96, 164);
|
|
@@ -2765,12 +2692,9 @@ export class Client {
|
|
|
2765
2692
|
|
|
2766
2693
|
// returns the file details and the encryption key
|
|
2767
2694
|
private async createFile(file: Uint8Array): Promise<[FileSQL, string]> {
|
|
2768
|
-
return this.
|
|
2695
|
+
return this.runCrypto(async () => {
|
|
2769
2696
|
const nonce = xMakeNonce();
|
|
2770
|
-
const fileKey
|
|
2771
|
-
this.cryptoProfile === "fips"
|
|
2772
|
-
? xRandomBytes(32)
|
|
2773
|
-
: (await xBoxKeyPairAsync()).secretKey;
|
|
2697
|
+
const fileKey = (await xBoxKeyPairAsync()).secretKey;
|
|
2774
2698
|
const box = await xSecretboxAsync(
|
|
2775
2699
|
Uint8Array.from(file),
|
|
2776
2700
|
nonce,
|
|
@@ -2864,13 +2788,9 @@ export class Client {
|
|
|
2864
2788
|
private async createPreKey(
|
|
2865
2789
|
kind: "one-time" | "signed",
|
|
2866
2790
|
): Promise<UnsavedPreKey> {
|
|
2867
|
-
return this.
|
|
2791
|
+
return this.runCrypto(async () => {
|
|
2868
2792
|
const preKeyPair = await xBoxKeyPairAsync();
|
|
2869
|
-
const toSign = xPreKeySignaturePayload(
|
|
2870
|
-
preKeyPair.publicKey,
|
|
2871
|
-
kind,
|
|
2872
|
-
this.cryptoProfile,
|
|
2873
|
-
);
|
|
2793
|
+
const toSign = xPreKeySignaturePayload(preKeyPair.publicKey, kind);
|
|
2874
2794
|
return {
|
|
2875
2795
|
keyPair: preKeyPair,
|
|
2876
2796
|
signature: await xSignAsync(toSign, this.signKeys.secretKey),
|
|
@@ -2902,16 +2822,12 @@ export class Client {
|
|
|
2902
2822
|
*/
|
|
2903
2823
|
allowKeyBundleFailure = false,
|
|
2904
2824
|
): Promise<Message | null> {
|
|
2905
|
-
return this.
|
|
2825
|
+
return this.runCrypto(async () => {
|
|
2906
2826
|
let keyBundle: KeyBundle;
|
|
2907
2827
|
|
|
2908
2828
|
try {
|
|
2909
2829
|
keyBundle = await this.retrieveKeyBundle(device.deviceID);
|
|
2910
|
-
await verifyKeyBundleSignatures(
|
|
2911
|
-
keyBundle,
|
|
2912
|
-
device,
|
|
2913
|
-
this.cryptoProfile,
|
|
2914
|
-
);
|
|
2830
|
+
await verifyKeyBundleSignatures(keyBundle, device);
|
|
2915
2831
|
} catch (e) {
|
|
2916
2832
|
if (allowKeyBundleFailure) {
|
|
2917
2833
|
return null;
|
|
@@ -2937,25 +2853,16 @@ export class Client {
|
|
|
2937
2853
|
const ephemeralKeys = await xBoxKeyPairAsync();
|
|
2938
2854
|
const EK_A = ephemeralKeys.secretKey;
|
|
2939
2855
|
|
|
2940
|
-
const fips = this.cryptoProfile === "fips";
|
|
2941
|
-
// their keys — FIPS: `signKey` in bundle is the peer P-256 ECDH identity (raw, typically 65B).
|
|
2942
2856
|
const SPK_B = new Uint8Array(keyBundle.preKey.publicKey);
|
|
2943
2857
|
const OPK_B = keyBundle.otk
|
|
2944
2858
|
? new Uint8Array(keyBundle.otk.publicKey)
|
|
2945
2859
|
: null;
|
|
2946
|
-
const IK_B =
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
if (!c) {
|
|
2953
|
-
throw new Error(
|
|
2954
|
-
"Could not convert sign key to X25519.",
|
|
2955
|
-
);
|
|
2956
|
-
}
|
|
2957
|
-
return c;
|
|
2958
|
-
})();
|
|
2860
|
+
const IK_B = XKeyConvert.convertPublicKey(
|
|
2861
|
+
new Uint8Array(keyBundle.signKey),
|
|
2862
|
+
);
|
|
2863
|
+
if (!IK_B) {
|
|
2864
|
+
throw new Error("Could not convert sign key to X25519.");
|
|
2865
|
+
}
|
|
2959
2866
|
|
|
2960
2867
|
// diffie hellman functions
|
|
2961
2868
|
const DH1 = await xDHAsync(new Uint8Array(IK_A), SPK_B);
|
|
@@ -2980,15 +2887,10 @@ export class Client {
|
|
|
2980
2887
|
const initialSubkeys = xMessageKeySubkeys(SK);
|
|
2981
2888
|
const PK = (await xBoxKeyPairFromSecretAsync(SK)).publicKey;
|
|
2982
2889
|
|
|
2983
|
-
const AD =
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
)
|
|
2988
|
-
: xConcat(
|
|
2989
|
-
xEncode(xConstants.CURVE, IK_AP),
|
|
2990
|
-
xEncode(xConstants.CURVE, IK_B),
|
|
2991
|
-
);
|
|
2890
|
+
const AD = xConcat(
|
|
2891
|
+
xEncode(xConstants.CURVE, IK_AP),
|
|
2892
|
+
xEncode(xConstants.CURVE, IK_B),
|
|
2893
|
+
);
|
|
2992
2894
|
|
|
2993
2895
|
const nonce = xMakeNonce();
|
|
2994
2896
|
const cipher = await xSecretboxAsync(
|
|
@@ -2997,18 +2899,13 @@ export class Client {
|
|
|
2997
2899
|
initialSubkeys.encryptionKey,
|
|
2998
2900
|
);
|
|
2999
2901
|
|
|
3000
|
-
const
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
ephemeralKeys.publicKey,
|
|
3008
|
-
PK,
|
|
3009
|
-
AD,
|
|
3010
|
-
IDX,
|
|
3011
|
-
);
|
|
2902
|
+
const extra = xConcat(
|
|
2903
|
+
this.signKeys.publicKey,
|
|
2904
|
+
ephemeralKeys.publicKey,
|
|
2905
|
+
PK,
|
|
2906
|
+
AD,
|
|
2907
|
+
IDX,
|
|
2908
|
+
);
|
|
3012
2909
|
|
|
3013
2910
|
const mail: MailWS = {
|
|
3014
2911
|
authorID: this.getUser().userID,
|
|
@@ -4100,11 +3997,10 @@ export class Client {
|
|
|
4100
3997
|
private async isPreKeySignedByCurrentDevice(
|
|
4101
3998
|
preKey: PreKeysCrypto,
|
|
4102
3999
|
): Promise<boolean> {
|
|
4103
|
-
return this.
|
|
4000
|
+
return this.runCrypto(async () => {
|
|
4104
4001
|
const payload = xPreKeySignaturePayload(
|
|
4105
4002
|
preKey.keyPair.publicKey,
|
|
4106
4003
|
"signed",
|
|
4107
|
-
this.cryptoProfile,
|
|
4108
4004
|
);
|
|
4109
4005
|
const opened = await xSignOpenAsync(
|
|
4110
4006
|
preKey.signature,
|
|
@@ -4321,7 +4217,7 @@ export class Client {
|
|
|
4321
4217
|
}
|
|
4322
4218
|
|
|
4323
4219
|
private async populateKeyRing() {
|
|
4324
|
-
await this.
|
|
4220
|
+
await this.runCrypto(async () => {
|
|
4325
4221
|
// we've checked in the constructor that these exist
|
|
4326
4222
|
if (!this.idKeys) {
|
|
4327
4223
|
throw new Error("Identity keys are missing.");
|
|
@@ -4500,7 +4396,7 @@ export class Client {
|
|
|
4500
4396
|
this.reading = true;
|
|
4501
4397
|
|
|
4502
4398
|
try {
|
|
4503
|
-
await this.
|
|
4399
|
+
await this.runCrypto(async () => {
|
|
4504
4400
|
const healSession = () => {
|
|
4505
4401
|
if (this.manuallyClosing || !this.xKeyRing) {
|
|
4506
4402
|
return;
|
|
@@ -4613,19 +4509,7 @@ export class Client {
|
|
|
4613
4509
|
}
|
|
4614
4510
|
|
|
4615
4511
|
// their public keys
|
|
4616
|
-
const
|
|
4617
|
-
new Uint8Array(mail.extra),
|
|
4618
|
-
);
|
|
4619
|
-
const IK_A = fipsRead
|
|
4620
|
-
? signKey
|
|
4621
|
-
: (() => {
|
|
4622
|
-
const c =
|
|
4623
|
-
XKeyConvert.convertPublicKey(signKey);
|
|
4624
|
-
if (!c) {
|
|
4625
|
-
return null;
|
|
4626
|
-
}
|
|
4627
|
-
return c;
|
|
4628
|
-
})();
|
|
4512
|
+
const IK_A = XKeyConvert.convertPublicKey(signKey);
|
|
4629
4513
|
if (!IK_A) {
|
|
4630
4514
|
const failureCount =
|
|
4631
4515
|
this.registerDecryptFailure(mail);
|
|
@@ -4635,7 +4519,6 @@ export class Client {
|
|
|
4635
4519
|
"readMail initial: abort (IK_A null, Ed→X25519?)",
|
|
4636
4520
|
{
|
|
4637
4521
|
attempts: failureCount,
|
|
4638
|
-
fips: String(fipsRead),
|
|
4639
4522
|
mailID: mail.mailID,
|
|
4640
4523
|
thisDevice:
|
|
4641
4524
|
this.getDevice().deviceID,
|
|
@@ -4705,12 +4588,10 @@ export class Client {
|
|
|
4705
4588
|
);
|
|
4706
4589
|
|
|
4707
4590
|
// associated data
|
|
4708
|
-
const AD =
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
xEncode(xConstants.CURVE, IK_BP),
|
|
4713
|
-
);
|
|
4591
|
+
const AD = xConcat(
|
|
4592
|
+
xEncode(xConstants.CURVE, IK_A),
|
|
4593
|
+
xEncode(xConstants.CURVE, IK_BP),
|
|
4594
|
+
);
|
|
4714
4595
|
|
|
4715
4596
|
if (!XUtils.bytesEqual(hmac, header)) {
|
|
4716
4597
|
const failureCount =
|
|
@@ -5311,9 +5192,7 @@ export class Client {
|
|
|
5311
5192
|
throw new Error("Couldn't fetch token.");
|
|
5312
5193
|
}
|
|
5313
5194
|
|
|
5314
|
-
// Stored on Spire for signature verification
|
|
5315
|
-
// P-256 ECDSA SPKI (hex) in FIPS. The server maps this to a raw ECDH
|
|
5316
|
-
// identity in `getKeyBundle` for X3DH; see spire `Database.getKeyBundle`.
|
|
5195
|
+
// Stored on Spire for Ed25519 signature verification.
|
|
5317
5196
|
const signKey = this.getKeys().public;
|
|
5318
5197
|
const signed = XUtils.encodeHex(
|
|
5319
5198
|
await xSignAsync(
|
|
@@ -5508,6 +5387,10 @@ export class Client {
|
|
|
5508
5387
|
return device;
|
|
5509
5388
|
}
|
|
5510
5389
|
|
|
5390
|
+
private async runCrypto<T>(fn: () => Promise<T>): Promise<T> {
|
|
5391
|
+
return fn();
|
|
5392
|
+
}
|
|
5393
|
+
|
|
5511
5394
|
private async runLocalRetentionPurge(): Promise<void> {
|
|
5512
5395
|
if (this.isManualCloseInFlight()) {
|
|
5513
5396
|
return;
|
|
@@ -5521,23 +5404,6 @@ export class Client {
|
|
|
5521
5404
|
}
|
|
5522
5405
|
}
|
|
5523
5406
|
|
|
5524
|
-
/**
|
|
5525
|
-
* `xDHAsync` and other helpers in `@vex-chat/crypto` use the process-wide
|
|
5526
|
-
* active profile. When several {@link Client} instances use different
|
|
5527
|
-
* `cryptoProfile` values, scope the global to this instance for the duration
|
|
5528
|
-
* of that crypto work.
|
|
5529
|
-
*/
|
|
5530
|
-
private async runWithThisCryptoProfile<T>(
|
|
5531
|
-
fn: () => Promise<T>,
|
|
5532
|
-
): Promise<T> {
|
|
5533
|
-
enterCryptoProfileScope(this.cryptoProfile);
|
|
5534
|
-
try {
|
|
5535
|
-
return await fn();
|
|
5536
|
-
} finally {
|
|
5537
|
-
leaveCryptoProfileScope();
|
|
5538
|
-
}
|
|
5539
|
-
}
|
|
5540
|
-
|
|
5541
5407
|
private scheduleMailBatchFlush(): void {
|
|
5542
5408
|
if (this.mailBatchFlushTimer) {
|
|
5543
5409
|
return;
|
|
@@ -14,12 +14,8 @@ import type {
|
|
|
14
14
|
import type { Device, PreKeysSQL, SessionSQL } from "@vex-chat/types";
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
|
-
getCryptoProfile,
|
|
18
17
|
xBoxKeyPairFromSecret,
|
|
19
|
-
xBoxKeyPairFromSecretAsync,
|
|
20
|
-
xSecretbox,
|
|
21
18
|
xSecretboxAsync,
|
|
22
|
-
xSecretboxOpen,
|
|
23
19
|
xSecretboxOpenAsync,
|
|
24
20
|
XUtils,
|
|
25
21
|
} from "@vex-chat/crypto";
|
|
@@ -117,10 +113,7 @@ export class MemoryStorage extends EventEmitter implements Storage {
|
|
|
117
113
|
const sk = XUtils.decodeHex(otk.privateKey);
|
|
118
114
|
return {
|
|
119
115
|
index: otk.index,
|
|
120
|
-
keyPair:
|
|
121
|
-
getCryptoProfile() === "fips"
|
|
122
|
-
? await xBoxKeyPairFromSecretAsync(sk)
|
|
123
|
-
: xBoxKeyPairFromSecret(sk),
|
|
116
|
+
keyPair: xBoxKeyPairFromSecret(sk),
|
|
124
117
|
signature: XUtils.decodeHex(otk.signature),
|
|
125
118
|
};
|
|
126
119
|
}
|
|
@@ -132,10 +125,7 @@ export class MemoryStorage extends EventEmitter implements Storage {
|
|
|
132
125
|
const sk = XUtils.decodeHex(pk.privateKey);
|
|
133
126
|
return {
|
|
134
127
|
index: pk.index,
|
|
135
|
-
keyPair:
|
|
136
|
-
getCryptoProfile() === "fips"
|
|
137
|
-
? await xBoxKeyPairFromSecretAsync(sk)
|
|
138
|
-
: xBoxKeyPairFromSecret(sk),
|
|
128
|
+
keyPair: xBoxKeyPairFromSecret(sk),
|
|
139
129
|
signature: XUtils.decodeHex(pk.signature),
|
|
140
130
|
};
|
|
141
131
|
}
|
|
@@ -223,18 +213,11 @@ export class MemoryStorage extends EventEmitter implements Storage {
|
|
|
223
213
|
return;
|
|
224
214
|
}
|
|
225
215
|
const copy = { ...message };
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
this.atRestAesKey,
|
|
232
|
-
)
|
|
233
|
-
: xSecretbox(
|
|
234
|
-
XUtils.decodeUTF8(message.message),
|
|
235
|
-
XUtils.decodeHex(message.nonce),
|
|
236
|
-
this.atRestAesKey,
|
|
237
|
-
);
|
|
216
|
+
const ct = await xSecretboxAsync(
|
|
217
|
+
XUtils.decodeUTF8(message.message),
|
|
218
|
+
XUtils.decodeHex(message.nonce),
|
|
219
|
+
this.atRestAesKey,
|
|
220
|
+
);
|
|
238
221
|
copy.message = XUtils.encodeHex(ct);
|
|
239
222
|
this.messages.push(copy);
|
|
240
223
|
}
|
|
@@ -302,18 +285,11 @@ export class MemoryStorage extends EventEmitter implements Storage {
|
|
|
302
285
|
next.extra = patch.extra;
|
|
303
286
|
}
|
|
304
287
|
if (patch.message !== undefined) {
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
this.atRestAesKey,
|
|
311
|
-
)
|
|
312
|
-
: xSecretbox(
|
|
313
|
-
XUtils.decodeUTF8(patch.message),
|
|
314
|
-
XUtils.decodeHex(current.nonce),
|
|
315
|
-
this.atRestAesKey,
|
|
316
|
-
);
|
|
288
|
+
const ct = await xSecretboxAsync(
|
|
289
|
+
XUtils.decodeUTF8(patch.message),
|
|
290
|
+
XUtils.decodeHex(current.nonce),
|
|
291
|
+
this.atRestAesKey,
|
|
292
|
+
);
|
|
317
293
|
next.message = XUtils.encodeHex(ct);
|
|
318
294
|
}
|
|
319
295
|
this.messages[idx] = next;
|
|
@@ -323,18 +299,11 @@ export class MemoryStorage extends EventEmitter implements Storage {
|
|
|
323
299
|
private async decryptMessage(msg: Message): Promise<Message> {
|
|
324
300
|
const copy = { ...msg };
|
|
325
301
|
if (copy.decrypted) {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
this.atRestAesKey,
|
|
332
|
-
)
|
|
333
|
-
: xSecretboxOpen(
|
|
334
|
-
XUtils.decodeHex(copy.message),
|
|
335
|
-
XUtils.decodeHex(copy.nonce),
|
|
336
|
-
this.atRestAesKey,
|
|
337
|
-
);
|
|
302
|
+
const dec = await xSecretboxOpenAsync(
|
|
303
|
+
XUtils.decodeHex(copy.message),
|
|
304
|
+
XUtils.decodeHex(copy.nonce),
|
|
305
|
+
this.atRestAesKey,
|
|
306
|
+
);
|
|
338
307
|
if (dec) copy.message = XUtils.encodeUTF8(dec);
|
|
339
308
|
}
|
|
340
309
|
return copy;
|