@plane31/blind-relay 0.4.0 → 0.5.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/adapters/index.d.ts +4 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +5 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/crypto/sodium.d.ts.map +1 -1
- package/dist/crypto/sodium.js +3 -33
- package/dist/crypto/sodium.js.map +1 -1
- package/dist/crypto/sodiumLike.d.ts +39 -0
- package/dist/crypto/sodiumLike.d.ts.map +1 -0
- package/dist/crypto/sodiumLike.js +142 -0
- package/dist/crypto/sodiumLike.js.map +1 -0
- package/package.json +1 -1
- package/src/adapters/index.ts +6 -0
- package/src/crypto/sodium.ts +4 -33
- package/src/crypto/sodiumLike.ts +169 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ pnpm add @plane31/blind-relay
|
|
|
13
13
|
- `@plane31/blind-relay`: `open` and the `Client` with `enqueue`, `newest`, `historyPage`, `sync`, the circles API, `subscribe` and `inspect`; the `KindDeclaration` and `RetentionPolicy` a product passes to `open`; `KavachError` and `ErrorCode`; the `RecoveryProvider` contract; `toHex`, `fromHex` and `equalBytes`.
|
|
14
14
|
- `@plane31/blind-relay/adapters`: the `Store`, `Tx`, `SecretStore`, `Transport`, `Directory` and `Relay` contracts an adapter or host implements, with the table and row types; `MemoryStore` and `MemorySecretStore` as the behavioural reference; the signed-document codec a host verifies with; `buildResetKeyList` and `buildResetReceipt` for a host's key tooling.
|
|
15
15
|
- `@plane31/blind-relay/testing`: the fault harness (a fake host, a crashing store, a malicious transport, the crash and lost-response sweeps) and the fixtures a test builds protocol state with.
|
|
16
|
-
- `@plane31/blind-relay/sodium`: the optional libsodium backend for Node
|
|
16
|
+
- `@plane31/blind-relay/sodium`: the optional libsodium backend for Node (wasm). The default backend is portable JavaScript; `sodiumBackend(sodium)` from the adapters entry builds the libsodium backend over any object with libsodium's API, which is how a phone runs the cipher suite natively (`@plane31/blind-relay-expo` wires `react-native-libsodium` through `openKavach`'s `crypto` option).
|
|
17
17
|
|
|
18
18
|
Randomness comes from the Web Crypto global. A runtime without one (Hermes) installs it first; `@plane31/blind-relay-expo` does that from `expo-crypto`.
|
|
19
19
|
|
package/dist/adapters/index.d.ts
CHANGED
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export { Reader, Writer, compareBytes, concat, equalBytes, fromHex, toHex, u16, u32, u64 } from "../bytes.js";
|
|
11
11
|
export { bodyHash, identityId, randomBytes, ready, recordId, signingKeypairFromSeed, streamId, type SigningKeypair } from "../crypto.js";
|
|
12
|
+
export { nobleBackend } from "../crypto/noble.js";
|
|
13
|
+
export { backendName } from "../crypto.js";
|
|
14
|
+
export { buildSodiumBackend, sodiumBackend, type SodiumBackendReport, type SodiumLike } from "../crypto/sodiumLike.js";
|
|
15
|
+
export type { CryptoBackend } from "../crypto/backend.js";
|
|
12
16
|
export { KavachError, isKavachError, type ErrorCode } from "../errors.js";
|
|
13
17
|
export { DocumentType, buildResetKeyList, buildResetReceipt, buildSuccession, expectType, parseDocument, parseVaultHeader, verifyDocument, type AccountDevice, type AccountResetReceipt, type AccountSuccession, type CircleSnapshot, type EncryptedRecord, type EpochManifest, type Parsed, type ResetKeyEntry, type ResetKeyList, type SignedDocument, type VaultHeader } from "../codec.js";
|
|
14
18
|
export { MemorySecretStore, MemoryStore, invokeSync, type ActivationRow, type CertificateRow, type CircleRow, type ClockRow, type EpochRow, type EpochStatus, type HistoryRow, type NewestRow, type MemoryStoreOptions, type MetaRow, type OutboxRow, type OutboxStatus, type EnqueueKeyRow, type HistoryIndexRow, type SuccessionRow, type IdentityRow, type KeyChangeRow, type ReplacementRow, type PreferenceRow, type ChainProgressRow, type KeyRange, inKeyRange, type PendingResetRow, type PinRow, type QuarantineRow, type ReaderEpochRow, type ResetListRow, type ResetReceiptRow, type RevisionRow, type SecretName, type SecretStore, type SelfRow, type Store, type StoreState, type StreamRow, type SubscriptionRow, type TableName, type Tables, type Tx, type UnresolvedRow, type VaultRow } from "../store.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAG9G,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,sBAAsB,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAGzI,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAG1E,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,EAAE,EACP,KAAK,aAAa,EAClB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,UAAU,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,EACL,eAAe,EACf,cAAc,EACd,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/I,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpI,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAG9G,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,sBAAsB,EAAE,QAAQ,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAGzI,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,KAAK,mBAAmB,EAAE,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACvH,YAAY,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAG1E,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,QAAQ,EACb,UAAU,EACV,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,EAAE,EACP,KAAK,aAAa,EAClB,KAAK,QAAQ,EACd,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,UAAU,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,EACL,eAAe,EACf,cAAc,EACd,cAAc,EACd,SAAS,EACV,MAAM,iBAAiB,CAAC;AAGzB,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/I,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpI,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/adapters/index.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
export { Reader, Writer, compareBytes, concat, equalBytes, fromHex, toHex, u16, u32, u64 } from "../bytes.js";
|
|
12
12
|
// Cipher suite: readiness, randomness and the identifiers a host derives --------------------------
|
|
13
13
|
export { bodyHash, identityId, randomBytes, ready, recordId, signingKeypairFromSeed, streamId } from "../crypto.js";
|
|
14
|
+
// Cipher-suite backends: the portable one, and a libsodium one over any object with libsodium's API
|
|
15
|
+
// (a native binding on a phone). `setCryptoBackend` is on the main entry.
|
|
16
|
+
export { nobleBackend } from "../crypto/noble.js";
|
|
17
|
+
export { backendName } from "../crypto.js";
|
|
18
|
+
export { buildSodiumBackend, sodiumBackend } from "../crypto/sodiumLike.js";
|
|
14
19
|
// Errors -------------------------------------------------------------------------------------------
|
|
15
20
|
export { KavachError, isKavachError } from "../errors.js";
|
|
16
21
|
// Signed documents, as a host or transport sees them ----------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,qGAAqG;AACrG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAE9G,oGAAoG;AACpG,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,sBAAsB,EAAE,QAAQ,EAAuB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,qGAAqG;AACrG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAE9G,oGAAoG;AACpG,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,sBAAsB,EAAE,QAAQ,EAAuB,MAAM,cAAc,CAAC;AACzI,oGAAoG;AACpG,0EAA0E;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAA6C,MAAM,yBAAyB,CAAC;AAGvH,qGAAqG;AACrG,OAAO,EAAE,WAAW,EAAE,aAAa,EAAkB,MAAM,cAAc,CAAC;AAE1E,oGAAoG;AACpG,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,cAAc,EAYf,MAAM,aAAa,CAAC;AAErB,qGAAqG;AACrG,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,UAAU,EAsBV,UAAU,EAoBX,MAAM,aAAa,CAAC;AAyBrB,oGAAoG;AACpG,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAuC,MAAM,oBAAoB,CAAC;AACpI,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sodium.d.ts","sourceRoot":"","sources":["../../src/crypto/sodium.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"sodium.d.ts","sourceRoot":"","sources":["../../src/crypto/sodium.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAKlD;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,aAAa,CAAC,CAOhE"}
|
package/dist/crypto/sodium.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* the wasm; use it on Node, where it is faster than the portable backend, and to cross-check it.
|
|
4
4
|
*/
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
|
+
import { sodiumBackend } from "./sodiumLike.js";
|
|
6
7
|
/**
|
|
7
8
|
* Node only. The wrapper is loaded through its `require` condition, the CommonJS build, which is
|
|
8
9
|
* self-contained; its ESM build has shipped broken (0.7.16 imports a sibling file the package does
|
|
@@ -14,38 +15,7 @@ export async function loadSodiumBackend() {
|
|
|
14
15
|
const mod = require("libsodium-wrappers-sumo");
|
|
15
16
|
const sodium = mod.default ?? mod;
|
|
16
17
|
await sodium.ready;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
blake2b: (data, outLen, key) => sodium.crypto_generichash(outLen, data, key ?? null),
|
|
20
|
-
randomBytes: (length) => sodium.randombytes_buf(length),
|
|
21
|
-
signKeypairFromSeed(seed) {
|
|
22
|
-
const kp = sodium.crypto_sign_seed_keypair(seed);
|
|
23
|
-
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
24
|
-
},
|
|
25
|
-
signDetached: (message, secretKey) => sodium.crypto_sign_detached(message, secretKey),
|
|
26
|
-
verifyDetached(signature, message, publicKey) {
|
|
27
|
-
try {
|
|
28
|
-
return sodium.crypto_sign_verify_detached(signature, message, publicKey);
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
isValidSignPoint: (publicKey) => publicKey.length === 32 && sodium.crypto_core_ed25519_is_valid_point(publicKey),
|
|
35
|
-
boxKeypairFromSeed(seed) {
|
|
36
|
-
const kp = sodium.crypto_box_seed_keypair(seed);
|
|
37
|
-
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
38
|
-
},
|
|
39
|
-
boxKeypair() {
|
|
40
|
-
const kp = sodium.crypto_box_keypair();
|
|
41
|
-
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
42
|
-
},
|
|
43
|
-
scalarMult: (secretKey, publicKey) => sodium.crypto_scalarmult(secretKey, publicKey),
|
|
44
|
-
boxEasy: (message, nonce, publicKey, secretKey) => sodium.crypto_box_easy(message, nonce, publicKey, secretKey),
|
|
45
|
-
boxOpenEasy: (box, nonce, publicKey, secretKey) => sodium.crypto_box_open_easy(box, nonce, publicKey, secretKey),
|
|
46
|
-
aeadEncrypt: (plaintext, aad, nonce, key) => sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, aad, null, nonce, key),
|
|
47
|
-
aeadDecrypt: (ciphertextAndTag, aad, nonce, key) => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, ciphertextAndTag, aad, nonce, key),
|
|
48
|
-
memzero: (bytes) => sodium.memzero(bytes)
|
|
49
|
-
};
|
|
18
|
+
// The typed wrapper's overloads (output formats) do not match the structural interface; the calls made are the Uint8Array ones.
|
|
19
|
+
return sodiumBackend(sodium);
|
|
50
20
|
}
|
|
51
21
|
//# sourceMappingURL=sodium.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sodium.js","sourceRoot":"","sources":["../../src/crypto/sodium.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"sodium.js","sourceRoot":"","sources":["../../src/crypto/sodium.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAmB,MAAM,iBAAiB,CAAC;AAIjE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,yBAAyB,CAAkC,CAAC;IAChF,MAAM,MAAM,GAAW,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC;IAC1C,MAAM,MAAM,CAAC,KAAK,CAAC;IACnB,gIAAgI;IAChI,OAAO,aAAa,CAAC,MAA+B,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CryptoBackend } from "./backend.js";
|
|
2
|
+
/** The subset of libsodium's API the backend can use; everything but signing, boxing and hashing may be absent. */
|
|
3
|
+
export interface SodiumLike {
|
|
4
|
+
crypto_generichash(outLen: number, data: Uint8Array, key?: Uint8Array | null): Uint8Array;
|
|
5
|
+
randombytes_buf(length: number): Uint8Array;
|
|
6
|
+
crypto_sign_seed_keypair(seed: Uint8Array): {
|
|
7
|
+
publicKey: Uint8Array;
|
|
8
|
+
privateKey: Uint8Array;
|
|
9
|
+
};
|
|
10
|
+
crypto_sign_detached(message: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
11
|
+
crypto_sign_verify_detached(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean;
|
|
12
|
+
crypto_core_ed25519_is_valid_point?(point: Uint8Array): boolean;
|
|
13
|
+
crypto_box_seed_keypair(seed: Uint8Array): {
|
|
14
|
+
publicKey: Uint8Array;
|
|
15
|
+
privateKey: Uint8Array;
|
|
16
|
+
};
|
|
17
|
+
crypto_box_keypair(): {
|
|
18
|
+
publicKey: Uint8Array;
|
|
19
|
+
privateKey: Uint8Array;
|
|
20
|
+
};
|
|
21
|
+
crypto_scalarmult?(secretKey: Uint8Array, publicKey: Uint8Array): Uint8Array;
|
|
22
|
+
crypto_box_easy(message: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
23
|
+
crypto_box_open_easy(box: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
24
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt?(message: Uint8Array, aad: Uint8Array | null, secretNonce: Uint8Array | null, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
|
25
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt?(secretNonce: Uint8Array | null, ciphertext: Uint8Array, aad: Uint8Array | null, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
|
26
|
+
memzero?(bytes: Uint8Array): void;
|
|
27
|
+
}
|
|
28
|
+
/** Which of the backend's operations run on the binding and which on the portable implementation, after the build-time check. */
|
|
29
|
+
export interface SodiumBackendReport {
|
|
30
|
+
readonly native: readonly string[];
|
|
31
|
+
readonly portable: readonly string[];
|
|
32
|
+
}
|
|
33
|
+
/** Build the backend; see the module comment for how each function is checked. */
|
|
34
|
+
export declare function sodiumBackend(sodium: SodiumLike): CryptoBackend;
|
|
35
|
+
export declare function buildSodiumBackend(sodium: SodiumLike): {
|
|
36
|
+
backend: CryptoBackend;
|
|
37
|
+
report: SodiumBackendReport;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=sodiumLike.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sodiumLike.d.ts","sourceRoot":"","sources":["../../src/crypto/sodiumLike.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAc,aAAa,EAAe,MAAM,cAAc,CAAC;AAG3E,mHAAmH;AACnH,MAAM,WAAW,UAAU;IACzB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,IAAI,GAAG,UAAU,CAAC;IAC1F,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC;IAC5C,wBAAwB,CAAC,IAAI,EAAE,UAAU,GAAG;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,UAAU,EAAE,UAAU,CAAA;KAAE,CAAC;IAC9F,oBAAoB,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IAC7E,2BAA2B,CAAC,SAAS,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC;IACxG,kCAAkC,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC;IAChE,uBAAuB,CAAC,IAAI,EAAE,UAAU,GAAG;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,UAAU,EAAE,UAAU,CAAA;KAAE,CAAC;IAC7F,kBAAkB,IAAI;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,UAAU,EAAE,UAAU,CAAA;KAAE,CAAC;IACxE,iBAAiB,CAAC,CAAC,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IAC7E,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IAClH,oBAAoB,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnH,0CAA0C,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,EAAE,WAAW,EAAE,UAAU,GAAG,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,UAAU,CAAC;IACzK,0CAA0C,CAAC,CAAC,WAAW,EAAE,UAAU,GAAG,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,UAAU,GAAG,UAAU,CAAC;IAC5K,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CACnC;AAED,iIAAiI;AACjI,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAMD,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,aAAa,CAE/D;AAKD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAM9G"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A libsodium backend over any object with libsodium's JavaScript API: `libsodium-wrappers-sumo`
|
|
3
|
+
* on Node (wasm), or a native binding on a phone (`react-native-libsodium` over JSI), where it
|
|
4
|
+
* replaces the portable backend's pure-JavaScript Ed25519 and X25519 with the C implementation.
|
|
5
|
+
*
|
|
6
|
+
* Bindings differ from libsodium's reference API in small ways (react-native-libsodium 1.7 has
|
|
7
|
+
* no `crypto_scalarmult`, no `memzero`, no `crypto_core_ed25519_is_valid_point`, and its AEAD
|
|
8
|
+
* takes only string associated data, while the protocol's is binary). So the backend is built per
|
|
9
|
+
* function: each one the binding provides is checked once against the portable implementation
|
|
10
|
+
* on known inputs, and a function the binding lacks or gets wrong on a type it does not support
|
|
11
|
+
* falls back to the portable one, while a function that answers differently on the same input is
|
|
12
|
+
* refused outright, since silently wrong bytes would be worse than slow ones. The caller awaits
|
|
13
|
+
* `sodium.ready` before building. Both backends pass the same vectors.
|
|
14
|
+
*/
|
|
15
|
+
import { KavachError } from "../errors.js";
|
|
16
|
+
import { nobleBackend } from "./noble.js";
|
|
17
|
+
const same = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
18
|
+
/** The X25519 base point, for deriving a public key from a secret scalar on the portable implementation. */
|
|
19
|
+
const X25519_BASE = Uint8Array.from([9, ...new Array(31).fill(0)]);
|
|
20
|
+
/** Build the backend; see the module comment for how each function is checked. */
|
|
21
|
+
export function sodiumBackend(sodium) {
|
|
22
|
+
return buildSodiumBackend(sodium).backend;
|
|
23
|
+
}
|
|
24
|
+
/** One build per binding object: the checks run once in a JavaScript context, however often the same binding is installed. */
|
|
25
|
+
const built = new WeakMap();
|
|
26
|
+
export function buildSodiumBackend(sodium) {
|
|
27
|
+
const cached = built.get(sodium);
|
|
28
|
+
if (cached)
|
|
29
|
+
return cached;
|
|
30
|
+
const result = checkAndBuild(sodium);
|
|
31
|
+
built.set(sodium, result);
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
function checkAndBuild(sodium) {
|
|
35
|
+
const stage = "sodiumBackend";
|
|
36
|
+
const native = [];
|
|
37
|
+
const portable = [];
|
|
38
|
+
const n = nobleBackend;
|
|
39
|
+
// Known inputs: deterministic, so the check is the same on every device.
|
|
40
|
+
const seed = Uint8Array.from({ length: 32 }, (_, i) => (i * 37 + 11) & 0xff);
|
|
41
|
+
const message = Uint8Array.from({ length: 70 }, (_, i) => (i * 29 + 3) & 0xff);
|
|
42
|
+
const nonce24 = Uint8Array.from({ length: 24 }, (_, i) => (i * 13 + 5) & 0xff);
|
|
43
|
+
const key32 = Uint8Array.from({ length: 32 }, (_, i) => (255 - i * 7) & 0xff);
|
|
44
|
+
const aad = Uint8Array.from([0, 1, 2, 250, 251, 252]);
|
|
45
|
+
/** A required function: present and agreeing with the portable implementation, or the binding is refused. */
|
|
46
|
+
const required = (name, check) => {
|
|
47
|
+
let ok = false;
|
|
48
|
+
try {
|
|
49
|
+
ok = check();
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
ok = false;
|
|
53
|
+
}
|
|
54
|
+
if (!ok)
|
|
55
|
+
throw new KavachError("INVALID_KEY", stage, `${name} is missing or disagrees with the portable implementation; the binding cannot be used`);
|
|
56
|
+
native.push(name);
|
|
57
|
+
};
|
|
58
|
+
/** An optional function: used when present and agreeing on the protocol's input types, portable otherwise. */
|
|
59
|
+
const optional = (name, present, check) => {
|
|
60
|
+
let ok = false;
|
|
61
|
+
if (present) {
|
|
62
|
+
try {
|
|
63
|
+
ok = check();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
ok = false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
(ok ? native : portable).push(name);
|
|
70
|
+
return ok;
|
|
71
|
+
};
|
|
72
|
+
const nSign = n.signKeypairFromSeed(seed);
|
|
73
|
+
required("signKeypairFromSeed", () => {
|
|
74
|
+
const kp = sodium.crypto_sign_seed_keypair(seed);
|
|
75
|
+
return same(kp.publicKey, nSign.publicKey) && same(kp.privateKey, nSign.secretKey);
|
|
76
|
+
});
|
|
77
|
+
required("signDetached", () => same(sodium.crypto_sign_detached(message, nSign.secretKey), n.signDetached(message, nSign.secretKey)));
|
|
78
|
+
required("verifyDetached", () => sodium.crypto_sign_verify_detached(n.signDetached(message, nSign.secretKey), message, nSign.publicKey) === true && sodium.crypto_sign_verify_detached(n.signDetached(message, nSign.secretKey), nonce24, nSign.publicKey) === false);
|
|
79
|
+
required("blake2b", () => same(sodium.crypto_generichash(32, message, null), n.blake2b(message, 32)) && same(sodium.crypto_generichash(64, message, key32), n.blake2b(message, 64, key32)));
|
|
80
|
+
const nBox = n.boxKeypairFromSeed(seed);
|
|
81
|
+
required("boxKeypairFromSeed", () => {
|
|
82
|
+
const kp = sodium.crypto_box_seed_keypair(seed);
|
|
83
|
+
return same(kp.publicKey, nBox.publicKey) && same(kp.privateKey, nBox.secretKey);
|
|
84
|
+
});
|
|
85
|
+
required("boxKeypair", () => {
|
|
86
|
+
// A fresh keypair cannot be compared with a known one; its public key must be the one its secret derives.
|
|
87
|
+
const kp = sodium.crypto_box_keypair();
|
|
88
|
+
return kp.publicKey.length === 32 && kp.privateKey.length === 32 && same(kp.publicKey, n.scalarMult(kp.privateKey, X25519_BASE));
|
|
89
|
+
});
|
|
90
|
+
const other = n.boxKeypairFromSeed(key32);
|
|
91
|
+
required("boxEasy", () => same(sodium.crypto_box_easy(message, nonce24, other.publicKey, nBox.secretKey), n.boxEasy(message, nonce24, other.publicKey, nBox.secretKey)));
|
|
92
|
+
required("boxOpenEasy", () => same(sodium.crypto_box_open_easy(n.boxEasy(message, nonce24, other.publicKey, nBox.secretKey), nonce24, nBox.publicKey, other.secretKey), message));
|
|
93
|
+
required("randomBytes", () => sodium.randombytes_buf(16).length === 16);
|
|
94
|
+
const scalarMultNative = optional("scalarMult", typeof sodium.crypto_scalarmult === "function", () => same(sodium.crypto_scalarmult?.(nBox.secretKey, other.publicKey) ?? new Uint8Array(), n.scalarMult(nBox.secretKey, other.publicKey)));
|
|
95
|
+
const aeadNative = optional("aead", typeof sodium.crypto_aead_xchacha20poly1305_ietf_encrypt === "function" && typeof sodium.crypto_aead_xchacha20poly1305_ietf_decrypt === "function", () => {
|
|
96
|
+
// The protocol's associated data is binary; a binding that takes only strings throws here and falls back.
|
|
97
|
+
const sealed = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt?.(message, aad, null, nonce24, key32) ?? new Uint8Array();
|
|
98
|
+
const opened = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt?.(null, n.aeadEncrypt(message, aad, nonce24, key32), aad, nonce24, key32) ?? new Uint8Array();
|
|
99
|
+
return same(sealed, n.aeadEncrypt(message, aad, nonce24, key32)) && same(opened, message);
|
|
100
|
+
});
|
|
101
|
+
const validPointNative = optional("isValidSignPoint", typeof sodium.crypto_core_ed25519_is_valid_point === "function", () => sodium.crypto_core_ed25519_is_valid_point?.(nSign.publicKey) === true && sodium.crypto_core_ed25519_is_valid_point?.(new Uint8Array(32)) === false);
|
|
102
|
+
const memzeroNative = optional("memzero", typeof sodium.memzero === "function", () => {
|
|
103
|
+
const probe = Uint8Array.from([1, 2, 3]);
|
|
104
|
+
sodium.memzero?.(probe);
|
|
105
|
+
return probe.every((b) => b === 0);
|
|
106
|
+
});
|
|
107
|
+
const backend = {
|
|
108
|
+
name: "sodium",
|
|
109
|
+
blake2b: (data, outLen, key) => sodium.crypto_generichash(outLen, data, key ?? null),
|
|
110
|
+
randomBytes: (length) => sodium.randombytes_buf(length),
|
|
111
|
+
signKeypairFromSeed(s) {
|
|
112
|
+
const kp = sodium.crypto_sign_seed_keypair(s);
|
|
113
|
+
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
114
|
+
},
|
|
115
|
+
signDetached: (m, secretKey) => sodium.crypto_sign_detached(m, secretKey),
|
|
116
|
+
verifyDetached(signature, m, publicKey) {
|
|
117
|
+
try {
|
|
118
|
+
return sodium.crypto_sign_verify_detached(signature, m, publicKey);
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
isValidSignPoint: validPointNative ? (publicKey) => publicKey.length === 32 && sodium.crypto_core_ed25519_is_valid_point?.(publicKey) === true : (publicKey) => n.isValidSignPoint(publicKey),
|
|
125
|
+
boxKeypairFromSeed(s) {
|
|
126
|
+
const kp = sodium.crypto_box_seed_keypair(s);
|
|
127
|
+
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
128
|
+
},
|
|
129
|
+
boxKeypair() {
|
|
130
|
+
const kp = sodium.crypto_box_keypair();
|
|
131
|
+
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
132
|
+
},
|
|
133
|
+
scalarMult: scalarMultNative ? (secretKey, publicKey) => sodium.crypto_scalarmult?.(secretKey, publicKey) ?? n.scalarMult(secretKey, publicKey) : (secretKey, publicKey) => n.scalarMult(secretKey, publicKey),
|
|
134
|
+
boxEasy: (m, nonce, publicKey, secretKey) => sodium.crypto_box_easy(m, nonce, publicKey, secretKey),
|
|
135
|
+
boxOpenEasy: (box, nonce, publicKey, secretKey) => sodium.crypto_box_open_easy(box, nonce, publicKey, secretKey),
|
|
136
|
+
aeadEncrypt: aeadNative ? (plaintext, a, nonce, key) => sodium.crypto_aead_xchacha20poly1305_ietf_encrypt?.(plaintext, a, null, nonce, key) ?? n.aeadEncrypt(plaintext, a, nonce, key) : (plaintext, a, nonce, key) => n.aeadEncrypt(plaintext, a, nonce, key),
|
|
137
|
+
aeadDecrypt: aeadNative ? (c, a, nonce, key) => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt?.(null, c, a, nonce, key) ?? n.aeadDecrypt(c, a, nonce, key) : (c, a, nonce, key) => n.aeadDecrypt(c, a, nonce, key),
|
|
138
|
+
memzero: memzeroNative ? (bytes) => sodium.memzero?.(bytes) : (bytes) => bytes.fill(0)
|
|
139
|
+
};
|
|
140
|
+
return { backend, report: { native, portable } };
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=sodiumLike.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sodiumLike.js","sourceRoot":"","sources":["../../src/crypto/sodiumLike.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/adapters/index.ts
CHANGED
|
@@ -13,6 +13,12 @@ export { Reader, Writer, compareBytes, concat, equalBytes, fromHex, toHex, u16,
|
|
|
13
13
|
|
|
14
14
|
// Cipher suite: readiness, randomness and the identifiers a host derives --------------------------
|
|
15
15
|
export { bodyHash, identityId, randomBytes, ready, recordId, signingKeypairFromSeed, streamId, type SigningKeypair } from "../crypto.js";
|
|
16
|
+
// Cipher-suite backends: the portable one, and a libsodium one over any object with libsodium's API
|
|
17
|
+
// (a native binding on a phone). `setCryptoBackend` is on the main entry.
|
|
18
|
+
export { nobleBackend } from "../crypto/noble.js";
|
|
19
|
+
export { backendName } from "../crypto.js";
|
|
20
|
+
export { buildSodiumBackend, sodiumBackend, type SodiumBackendReport, type SodiumLike } from "../crypto/sodiumLike.js";
|
|
21
|
+
export type { CryptoBackend } from "../crypto/backend.js";
|
|
16
22
|
|
|
17
23
|
// Errors -------------------------------------------------------------------------------------------
|
|
18
24
|
export { KavachError, isKavachError, type ErrorCode } from "../errors.js";
|
package/src/crypto/sodium.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* the wasm; use it on Node, where it is faster than the portable backend, and to cross-check it.
|
|
4
4
|
*/
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
|
-
import type {
|
|
6
|
+
import type { CryptoBackend } from "./backend.js";
|
|
7
|
+
import { sodiumBackend, type SodiumLike } from "./sodiumLike.js";
|
|
7
8
|
|
|
8
9
|
type Sodium = typeof import("libsodium-wrappers-sumo");
|
|
9
10
|
|
|
@@ -18,36 +19,6 @@ export async function loadSodiumBackend(): Promise<CryptoBackend> {
|
|
|
18
19
|
const mod = require("libsodium-wrappers-sumo") as { default?: Sodium } & Sodium;
|
|
19
20
|
const sodium: Sodium = mod.default ?? mod;
|
|
20
21
|
await sodium.ready;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
blake2b: (data, outLen, key) => sodium.crypto_generichash(outLen, data, key ?? null),
|
|
24
|
-
randomBytes: (length) => sodium.randombytes_buf(length),
|
|
25
|
-
signKeypairFromSeed(seed): SignKeypair {
|
|
26
|
-
const kp = sodium.crypto_sign_seed_keypair(seed);
|
|
27
|
-
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
28
|
-
},
|
|
29
|
-
signDetached: (message, secretKey) => sodium.crypto_sign_detached(message, secretKey),
|
|
30
|
-
verifyDetached(signature, message, publicKey) {
|
|
31
|
-
try {
|
|
32
|
-
return sodium.crypto_sign_verify_detached(signature, message, publicKey);
|
|
33
|
-
} catch {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
isValidSignPoint: (publicKey) => publicKey.length === 32 && sodium.crypto_core_ed25519_is_valid_point(publicKey),
|
|
38
|
-
boxKeypairFromSeed(seed): BoxKeypair {
|
|
39
|
-
const kp = sodium.crypto_box_seed_keypair(seed);
|
|
40
|
-
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
41
|
-
},
|
|
42
|
-
boxKeypair(): BoxKeypair {
|
|
43
|
-
const kp = sodium.crypto_box_keypair();
|
|
44
|
-
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
45
|
-
},
|
|
46
|
-
scalarMult: (secretKey, publicKey) => sodium.crypto_scalarmult(secretKey, publicKey),
|
|
47
|
-
boxEasy: (message, nonce, publicKey, secretKey) => sodium.crypto_box_easy(message, nonce, publicKey, secretKey),
|
|
48
|
-
boxOpenEasy: (box, nonce, publicKey, secretKey) => sodium.crypto_box_open_easy(box, nonce, publicKey, secretKey),
|
|
49
|
-
aeadEncrypt: (plaintext, aad, nonce, key) => sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(plaintext, aad, null, nonce, key),
|
|
50
|
-
aeadDecrypt: (ciphertextAndTag, aad, nonce, key) => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, ciphertextAndTag, aad, nonce, key),
|
|
51
|
-
memzero: (bytes) => sodium.memzero(bytes)
|
|
52
|
-
};
|
|
22
|
+
// The typed wrapper's overloads (output formats) do not match the structural interface; the calls made are the Uint8Array ones.
|
|
23
|
+
return sodiumBackend(sodium as unknown as SodiumLike);
|
|
53
24
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A libsodium backend over any object with libsodium's JavaScript API: `libsodium-wrappers-sumo`
|
|
3
|
+
* on Node (wasm), or a native binding on a phone (`react-native-libsodium` over JSI), where it
|
|
4
|
+
* replaces the portable backend's pure-JavaScript Ed25519 and X25519 with the C implementation.
|
|
5
|
+
*
|
|
6
|
+
* Bindings differ from libsodium's reference API in small ways (react-native-libsodium 1.7 has
|
|
7
|
+
* no `crypto_scalarmult`, no `memzero`, no `crypto_core_ed25519_is_valid_point`, and its AEAD
|
|
8
|
+
* takes only string associated data, while the protocol's is binary). So the backend is built per
|
|
9
|
+
* function: each one the binding provides is checked once against the portable implementation
|
|
10
|
+
* on known inputs, and a function the binding lacks or gets wrong on a type it does not support
|
|
11
|
+
* falls back to the portable one, while a function that answers differently on the same input is
|
|
12
|
+
* refused outright, since silently wrong bytes would be worse than slow ones. The caller awaits
|
|
13
|
+
* `sodium.ready` before building. Both backends pass the same vectors.
|
|
14
|
+
*/
|
|
15
|
+
import { KavachError } from "../errors.js";
|
|
16
|
+
import type { BoxKeypair, CryptoBackend, SignKeypair } from "./backend.js";
|
|
17
|
+
import { nobleBackend } from "./noble.js";
|
|
18
|
+
|
|
19
|
+
/** The subset of libsodium's API the backend can use; everything but signing, boxing and hashing may be absent. */
|
|
20
|
+
export interface SodiumLike {
|
|
21
|
+
crypto_generichash(outLen: number, data: Uint8Array, key?: Uint8Array | null): Uint8Array;
|
|
22
|
+
randombytes_buf(length: number): Uint8Array;
|
|
23
|
+
crypto_sign_seed_keypair(seed: Uint8Array): { publicKey: Uint8Array; privateKey: Uint8Array };
|
|
24
|
+
crypto_sign_detached(message: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
25
|
+
crypto_sign_verify_detached(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array): boolean;
|
|
26
|
+
crypto_core_ed25519_is_valid_point?(point: Uint8Array): boolean;
|
|
27
|
+
crypto_box_seed_keypair(seed: Uint8Array): { publicKey: Uint8Array; privateKey: Uint8Array };
|
|
28
|
+
crypto_box_keypair(): { publicKey: Uint8Array; privateKey: Uint8Array };
|
|
29
|
+
crypto_scalarmult?(secretKey: Uint8Array, publicKey: Uint8Array): Uint8Array;
|
|
30
|
+
crypto_box_easy(message: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
31
|
+
crypto_box_open_easy(box: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
32
|
+
crypto_aead_xchacha20poly1305_ietf_encrypt?(message: Uint8Array, aad: Uint8Array | null, secretNonce: Uint8Array | null, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
|
33
|
+
crypto_aead_xchacha20poly1305_ietf_decrypt?(secretNonce: Uint8Array | null, ciphertext: Uint8Array, aad: Uint8Array | null, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
|
34
|
+
memzero?(bytes: Uint8Array): void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Which of the backend's operations run on the binding and which on the portable implementation, after the build-time check. */
|
|
38
|
+
export interface SodiumBackendReport {
|
|
39
|
+
readonly native: readonly string[];
|
|
40
|
+
readonly portable: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const same = (a: Uint8Array, b: Uint8Array): boolean => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
44
|
+
/** The X25519 base point, for deriving a public key from a secret scalar on the portable implementation. */
|
|
45
|
+
const X25519_BASE = Uint8Array.from([9, ...new Array<number>(31).fill(0)]);
|
|
46
|
+
|
|
47
|
+
/** Build the backend; see the module comment for how each function is checked. */
|
|
48
|
+
export function sodiumBackend(sodium: SodiumLike): CryptoBackend {
|
|
49
|
+
return buildSodiumBackend(sodium).backend;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** One build per binding object: the checks run once in a JavaScript context, however often the same binding is installed. */
|
|
53
|
+
const built = new WeakMap<SodiumLike, { backend: CryptoBackend; report: SodiumBackendReport }>();
|
|
54
|
+
|
|
55
|
+
export function buildSodiumBackend(sodium: SodiumLike): { backend: CryptoBackend; report: SodiumBackendReport } {
|
|
56
|
+
const cached = built.get(sodium);
|
|
57
|
+
if (cached) return cached;
|
|
58
|
+
const result = checkAndBuild(sodium);
|
|
59
|
+
built.set(sodium, result);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function checkAndBuild(sodium: SodiumLike): { backend: CryptoBackend; report: SodiumBackendReport } {
|
|
64
|
+
const stage = "sodiumBackend";
|
|
65
|
+
const native: string[] = [];
|
|
66
|
+
const portable: string[] = [];
|
|
67
|
+
const n = nobleBackend;
|
|
68
|
+
// Known inputs: deterministic, so the check is the same on every device.
|
|
69
|
+
const seed = Uint8Array.from({ length: 32 }, (_, i) => (i * 37 + 11) & 0xff);
|
|
70
|
+
const message = Uint8Array.from({ length: 70 }, (_, i) => (i * 29 + 3) & 0xff);
|
|
71
|
+
const nonce24 = Uint8Array.from({ length: 24 }, (_, i) => (i * 13 + 5) & 0xff);
|
|
72
|
+
const key32 = Uint8Array.from({ length: 32 }, (_, i) => (255 - i * 7) & 0xff);
|
|
73
|
+
const aad = Uint8Array.from([0, 1, 2, 250, 251, 252]);
|
|
74
|
+
|
|
75
|
+
/** A required function: present and agreeing with the portable implementation, or the binding is refused. */
|
|
76
|
+
const required = (name: string, check: () => boolean): void => {
|
|
77
|
+
let ok = false;
|
|
78
|
+
try {
|
|
79
|
+
ok = check();
|
|
80
|
+
} catch {
|
|
81
|
+
ok = false;
|
|
82
|
+
}
|
|
83
|
+
if (!ok) throw new KavachError("INVALID_KEY", stage, `${name} is missing or disagrees with the portable implementation; the binding cannot be used`);
|
|
84
|
+
native.push(name);
|
|
85
|
+
};
|
|
86
|
+
/** An optional function: used when present and agreeing on the protocol's input types, portable otherwise. */
|
|
87
|
+
const optional = (name: string, present: boolean, check: () => boolean): boolean => {
|
|
88
|
+
let ok = false;
|
|
89
|
+
if (present) {
|
|
90
|
+
try {
|
|
91
|
+
ok = check();
|
|
92
|
+
} catch {
|
|
93
|
+
ok = false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
(ok ? native : portable).push(name);
|
|
97
|
+
return ok;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const nSign = n.signKeypairFromSeed(seed);
|
|
101
|
+
required("signKeypairFromSeed", () => {
|
|
102
|
+
const kp = sodium.crypto_sign_seed_keypair(seed);
|
|
103
|
+
return same(kp.publicKey, nSign.publicKey) && same(kp.privateKey, nSign.secretKey);
|
|
104
|
+
});
|
|
105
|
+
required("signDetached", () => same(sodium.crypto_sign_detached(message, nSign.secretKey), n.signDetached(message, nSign.secretKey)));
|
|
106
|
+
required("verifyDetached", () => sodium.crypto_sign_verify_detached(n.signDetached(message, nSign.secretKey), message, nSign.publicKey) === true && sodium.crypto_sign_verify_detached(n.signDetached(message, nSign.secretKey), nonce24, nSign.publicKey) === false);
|
|
107
|
+
required("blake2b", () => same(sodium.crypto_generichash(32, message, null), n.blake2b(message, 32)) && same(sodium.crypto_generichash(64, message, key32), n.blake2b(message, 64, key32)));
|
|
108
|
+
const nBox = n.boxKeypairFromSeed(seed);
|
|
109
|
+
required("boxKeypairFromSeed", () => {
|
|
110
|
+
const kp = sodium.crypto_box_seed_keypair(seed);
|
|
111
|
+
return same(kp.publicKey, nBox.publicKey) && same(kp.privateKey, nBox.secretKey);
|
|
112
|
+
});
|
|
113
|
+
required("boxKeypair", () => {
|
|
114
|
+
// A fresh keypair cannot be compared with a known one; its public key must be the one its secret derives.
|
|
115
|
+
const kp = sodium.crypto_box_keypair();
|
|
116
|
+
return kp.publicKey.length === 32 && kp.privateKey.length === 32 && same(kp.publicKey, n.scalarMult(kp.privateKey, X25519_BASE));
|
|
117
|
+
});
|
|
118
|
+
const other = n.boxKeypairFromSeed(key32);
|
|
119
|
+
required("boxEasy", () => same(sodium.crypto_box_easy(message, nonce24, other.publicKey, nBox.secretKey), n.boxEasy(message, nonce24, other.publicKey, nBox.secretKey)));
|
|
120
|
+
required("boxOpenEasy", () => same(sodium.crypto_box_open_easy(n.boxEasy(message, nonce24, other.publicKey, nBox.secretKey), nonce24, nBox.publicKey, other.secretKey), message));
|
|
121
|
+
required("randomBytes", () => sodium.randombytes_buf(16).length === 16);
|
|
122
|
+
const scalarMultNative = optional("scalarMult", typeof sodium.crypto_scalarmult === "function", () => same(sodium.crypto_scalarmult?.(nBox.secretKey, other.publicKey) ?? new Uint8Array(), n.scalarMult(nBox.secretKey, other.publicKey)));
|
|
123
|
+
const aeadNative = optional("aead", typeof sodium.crypto_aead_xchacha20poly1305_ietf_encrypt === "function" && typeof sodium.crypto_aead_xchacha20poly1305_ietf_decrypt === "function", () => {
|
|
124
|
+
// The protocol's associated data is binary; a binding that takes only strings throws here and falls back.
|
|
125
|
+
const sealed = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt?.(message, aad, null, nonce24, key32) ?? new Uint8Array();
|
|
126
|
+
const opened = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt?.(null, n.aeadEncrypt(message, aad, nonce24, key32), aad, nonce24, key32) ?? new Uint8Array();
|
|
127
|
+
return same(sealed, n.aeadEncrypt(message, aad, nonce24, key32)) && same(opened, message);
|
|
128
|
+
});
|
|
129
|
+
const validPointNative = optional("isValidSignPoint", typeof sodium.crypto_core_ed25519_is_valid_point === "function", () => sodium.crypto_core_ed25519_is_valid_point?.(nSign.publicKey) === true && sodium.crypto_core_ed25519_is_valid_point?.(new Uint8Array(32)) === false);
|
|
130
|
+
const memzeroNative = optional("memzero", typeof sodium.memzero === "function", () => {
|
|
131
|
+
const probe = Uint8Array.from([1, 2, 3]);
|
|
132
|
+
sodium.memzero?.(probe);
|
|
133
|
+
return probe.every((b) => b === 0);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const backend: CryptoBackend = {
|
|
137
|
+
name: "sodium",
|
|
138
|
+
blake2b: (data, outLen, key) => sodium.crypto_generichash(outLen, data, key ?? null),
|
|
139
|
+
randomBytes: (length) => sodium.randombytes_buf(length),
|
|
140
|
+
signKeypairFromSeed(s): SignKeypair {
|
|
141
|
+
const kp = sodium.crypto_sign_seed_keypair(s);
|
|
142
|
+
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
143
|
+
},
|
|
144
|
+
signDetached: (m, secretKey) => sodium.crypto_sign_detached(m, secretKey),
|
|
145
|
+
verifyDetached(signature, m, publicKey) {
|
|
146
|
+
try {
|
|
147
|
+
return sodium.crypto_sign_verify_detached(signature, m, publicKey);
|
|
148
|
+
} catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
isValidSignPoint: validPointNative ? (publicKey) => publicKey.length === 32 && sodium.crypto_core_ed25519_is_valid_point?.(publicKey) === true : (publicKey) => n.isValidSignPoint(publicKey),
|
|
153
|
+
boxKeypairFromSeed(s): BoxKeypair {
|
|
154
|
+
const kp = sodium.crypto_box_seed_keypair(s);
|
|
155
|
+
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
156
|
+
},
|
|
157
|
+
boxKeypair(): BoxKeypair {
|
|
158
|
+
const kp = sodium.crypto_box_keypair();
|
|
159
|
+
return { publicKey: kp.publicKey, secretKey: kp.privateKey };
|
|
160
|
+
},
|
|
161
|
+
scalarMult: scalarMultNative ? (secretKey, publicKey) => sodium.crypto_scalarmult?.(secretKey, publicKey) ?? n.scalarMult(secretKey, publicKey) : (secretKey, publicKey) => n.scalarMult(secretKey, publicKey),
|
|
162
|
+
boxEasy: (m, nonce, publicKey, secretKey) => sodium.crypto_box_easy(m, nonce, publicKey, secretKey),
|
|
163
|
+
boxOpenEasy: (box, nonce, publicKey, secretKey) => sodium.crypto_box_open_easy(box, nonce, publicKey, secretKey),
|
|
164
|
+
aeadEncrypt: aeadNative ? (plaintext, a, nonce, key) => sodium.crypto_aead_xchacha20poly1305_ietf_encrypt?.(plaintext, a, null, nonce, key) ?? n.aeadEncrypt(plaintext, a, nonce, key) : (plaintext, a, nonce, key) => n.aeadEncrypt(plaintext, a, nonce, key),
|
|
165
|
+
aeadDecrypt: aeadNative ? (c, a, nonce, key) => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt?.(null, c, a, nonce, key) ?? n.aeadDecrypt(c, a, nonce, key) : (c, a, nonce, key) => n.aeadDecrypt(c, a, nonce, key),
|
|
166
|
+
memzero: memzeroNative ? (bytes) => sodium.memzero?.(bytes) : (bytes) => bytes.fill(0)
|
|
167
|
+
};
|
|
168
|
+
return { backend, report: { native, portable } };
|
|
169
|
+
}
|