@drakkar.software/octobot-client 0.4.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 +140 -0
- package/dist/client/adapters/accounts.d.ts +66 -0
- package/dist/client/adapters/accounts.d.ts.map +1 -0
- package/dist/client/adapters/accounts.js +214 -0
- package/dist/client/adapters/accounts.js.map +1 -0
- package/dist/client/adapters/actionHandle.d.ts +29 -0
- package/dist/client/adapters/actionHandle.d.ts.map +1 -0
- package/dist/client/adapters/actionHandle.js +34 -0
- package/dist/client/adapters/actionHandle.js.map +1 -0
- package/dist/client/adapters/automations.d.ts +31 -0
- package/dist/client/adapters/automations.d.ts.map +1 -0
- package/dist/client/adapters/automations.js +79 -0
- package/dist/client/adapters/automations.js.map +1 -0
- package/dist/client/adapters/documents.d.ts +55 -0
- package/dist/client/adapters/documents.d.ts.map +1 -0
- package/dist/client/adapters/documents.js +66 -0
- package/dist/client/adapters/documents.js.map +1 -0
- package/dist/client/adapters/nodeApi.d.ts +25 -0
- package/dist/client/adapters/nodeApi.d.ts.map +1 -0
- package/dist/client/adapters/nodeApi.js +81 -0
- package/dist/client/adapters/nodeApi.js.map +1 -0
- package/dist/client/adapters/settings.d.ts +16 -0
- package/dist/client/adapters/settings.d.ts.map +1 -0
- package/dist/client/adapters/settings.js +43 -0
- package/dist/client/adapters/settings.js.map +1 -0
- package/dist/client/adapters/strategies.d.ts +17 -0
- package/dist/client/adapters/strategies.d.ts.map +1 -0
- package/dist/client/adapters/strategies.js +75 -0
- package/dist/client/adapters/strategies.js.map +1 -0
- package/dist/client/connect/connect.d.ts +40 -0
- package/dist/client/connect/connect.d.ts.map +1 -0
- package/dist/client/connect/connect.js +133 -0
- package/dist/client/connect/connect.js.map +1 -0
- package/dist/client/connect/readOnly.d.ts +97 -0
- package/dist/client/connect/readOnly.d.ts.map +1 -0
- package/dist/client/connect/readOnly.js +167 -0
- package/dist/client/connect/readOnly.js.map +1 -0
- package/dist/client/core/errors.d.ts +80 -0
- package/dist/client/core/errors.d.ts.map +1 -0
- package/dist/client/core/errors.js +154 -0
- package/dist/client/core/errors.js.map +1 -0
- package/dist/client/core/options.d.ts +44 -0
- package/dist/client/core/options.d.ts.map +1 -0
- package/dist/client/core/options.js +2 -0
- package/dist/client/core/options.js.map +1 -0
- package/dist/client/core/session.d.ts +90 -0
- package/dist/client/core/session.d.ts.map +1 -0
- package/dist/client/core/session.js +72 -0
- package/dist/client/core/session.js.map +1 -0
- package/dist/client/core/views.d.ts +40 -0
- package/dist/client/core/views.d.ts.map +1 -0
- package/dist/client/core/views.js +32 -0
- package/dist/client/core/views.js.map +1 -0
- package/dist/client/index.d.ts +16 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +9 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/mirror/collections.d.ts +125 -0
- package/dist/client/mirror/collections.d.ts.map +1 -0
- package/dist/client/mirror/collections.js +118 -0
- package/dist/client/mirror/collections.js.map +1 -0
- package/dist/client/mirror/index.d.ts +4 -0
- package/dist/client/mirror/index.d.ts.map +1 -0
- package/dist/client/mirror/index.js +4 -0
- package/dist/client/mirror/index.js.map +1 -0
- package/dist/client/mirror/writer.d.ts +39 -0
- package/dist/client/mirror/writer.d.ts.map +1 -0
- package/dist/client/mirror/writer.js +50 -0
- package/dist/client/mirror/writer.js.map +1 -0
- package/dist/client/pairing/mirrorGrant.d.ts +91 -0
- package/dist/client/pairing/mirrorGrant.d.ts.map +1 -0
- package/dist/client/pairing/mirrorGrant.js +99 -0
- package/dist/client/pairing/mirrorGrant.js.map +1 -0
- package/dist/client/pairing/mirrorReader.d.ts +99 -0
- package/dist/client/pairing/mirrorReader.d.ts.map +1 -0
- package/dist/client/pairing/mirrorReader.js +117 -0
- package/dist/client/pairing/mirrorReader.js.map +1 -0
- package/dist/client/pairing/pairingGrantExchange.d.ts +135 -0
- package/dist/client/pairing/pairingGrantExchange.d.ts.map +1 -0
- package/dist/client/pairing/pairingGrantExchange.js +186 -0
- package/dist/client/pairing/pairingGrantExchange.js.map +1 -0
- package/dist/client/pairing/pairingRequest.d.ts +82 -0
- package/dist/client/pairing/pairingRequest.d.ts.map +1 -0
- package/dist/client/pairing/pairingRequest.js +120 -0
- package/dist/client/pairing/pairingRequest.js.map +1 -0
- package/dist/client/strategy.d.ts +36 -0
- package/dist/client/strategy.d.ts.map +1 -0
- package/dist/client/strategy.js +26 -0
- package/dist/client/strategy.js.map +1 -0
- package/dist/collections/index.d.ts +3 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/collections/index.js +3 -0
- package/dist/collections/index.js.map +1 -0
- package/dist/collections/nodeCollections.d.ts +57 -0
- package/dist/collections/nodeCollections.d.ts.map +1 -0
- package/dist/collections/nodeCollections.js +40 -0
- package/dist/collections/nodeCollections.js.map +1 -0
- package/dist/collections/paths.d.ts +7 -0
- package/dist/collections/paths.d.ts.map +1 -0
- package/dist/collections/paths.js +13 -0
- package/dist/collections/paths.js.map +1 -0
- package/dist/crypto/collectionKeys.d.ts +28 -0
- package/dist/crypto/collectionKeys.d.ts.map +1 -0
- package/dist/crypto/collectionKeys.js +53 -0
- package/dist/crypto/collectionKeys.js.map +1 -0
- package/dist/crypto/hkdf.d.ts +14 -0
- package/dist/crypto/hkdf.d.ts.map +1 -0
- package/dist/crypto/hkdf.js +19 -0
- package/dist/crypto/hkdf.js.map +1 -0
- package/dist/crypto/index.d.ts +5 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +5 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/secretEncryptor.d.ts +20 -0
- package/dist/crypto/secretEncryptor.d.ts.map +1 -0
- package/dist/crypto/secretEncryptor.js +82 -0
- package/dist/crypto/secretEncryptor.js.map +1 -0
- package/dist/crypto/wireConstants.d.ts +14 -0
- package/dist/crypto/wireConstants.d.ts.map +1 -0
- package/dist/crypto/wireConstants.js +17 -0
- package/dist/crypto/wireConstants.js.map +1 -0
- package/dist/identity/address.d.ts +3 -0
- package/dist/identity/address.d.ts.map +1 -0
- package/dist/identity/address.js +17 -0
- package/dist/identity/address.js.map +1 -0
- package/dist/identity/bip39-wordlist.d.ts +2 -0
- package/dist/identity/bip39-wordlist.d.ts.map +1 -0
- package/dist/identity/bip39-wordlist.js +2051 -0
- package/dist/identity/bip39-wordlist.js.map +1 -0
- package/dist/identity/capProvider.d.ts +45 -0
- package/dist/identity/capProvider.d.ts.map +1 -0
- package/dist/identity/capProvider.js +80 -0
- package/dist/identity/capProvider.js.map +1 -0
- package/dist/identity/derivationSchemes.d.ts +25 -0
- package/dist/identity/derivationSchemes.d.ts.map +1 -0
- package/dist/identity/derivationSchemes.js +34 -0
- package/dist/identity/derivationSchemes.js.map +1 -0
- package/dist/identity/evm.d.ts +8 -0
- package/dist/identity/evm.d.ts.map +1 -0
- package/dist/identity/evm.js +78 -0
- package/dist/identity/evm.js.map +1 -0
- package/dist/identity/index.d.ts +10 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +10 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/keys.d.ts +17 -0
- package/dist/identity/keys.d.ts.map +1 -0
- package/dist/identity/keys.js +36 -0
- package/dist/identity/keys.js.map +1 -0
- package/dist/identity/mnemonic.d.ts +4 -0
- package/dist/identity/mnemonic.d.ts.map +1 -0
- package/dist/identity/mnemonic.js +93 -0
- package/dist/identity/mnemonic.js.map +1 -0
- package/dist/identity/pairing.d.ts +45 -0
- package/dist/identity/pairing.d.ts.map +1 -0
- package/dist/identity/pairing.js +91 -0
- package/dist/identity/pairing.js.map +1 -0
- package/dist/identity/pairingRequest.d.ts +99 -0
- package/dist/identity/pairingRequest.d.ts.map +1 -0
- package/dist/identity/pairingRequest.js +217 -0
- package/dist/identity/pairingRequest.js.map +1 -0
- package/dist/identity/secret.d.ts +32 -0
- package/dist/identity/secret.d.ts.map +1 -0
- package/dist/identity/secret.js +54 -0
- package/dist/identity/secret.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/bytes.d.ts +11 -0
- package/dist/internal/bytes.d.ts.map +1 -0
- package/dist/internal/bytes.js +38 -0
- package/dist/internal/bytes.js.map +1 -0
- package/dist/node-api/dexPair.d.ts +24 -0
- package/dist/node-api/dexPair.d.ts.map +1 -0
- package/dist/node-api/dexPair.js +39 -0
- package/dist/node-api/dexPair.js.map +1 -0
- package/dist/node-api/dsl.d.ts +10 -0
- package/dist/node-api/dsl.d.ts.map +1 -0
- package/dist/node-api/dsl.js +7 -0
- package/dist/node-api/dsl.js.map +1 -0
- package/dist/node-api/exchanges.d.ts +49 -0
- package/dist/node-api/exchanges.d.ts.map +1 -0
- package/dist/node-api/exchanges.js +32 -0
- package/dist/node-api/exchanges.js.map +1 -0
- package/dist/node-api/index.d.ts +12 -0
- package/dist/node-api/index.d.ts.map +1 -0
- package/dist/node-api/index.js +12 -0
- package/dist/node-api/index.js.map +1 -0
- package/dist/node-api/marketMaking.d.ts +39 -0
- package/dist/node-api/marketMaking.d.ts.map +1 -0
- package/dist/node-api/marketMaking.js +44 -0
- package/dist/node-api/marketMaking.js.map +1 -0
- package/dist/node-api/octobots.d.ts +14 -0
- package/dist/node-api/octobots.d.ts.map +1 -0
- package/dist/node-api/octobots.js +16 -0
- package/dist/node-api/octobots.js.map +1 -0
- package/dist/node-api/orderBookPreview.d.ts +11 -0
- package/dist/node-api/orderBookPreview.d.ts.map +1 -0
- package/dist/node-api/orderBookPreview.js +7 -0
- package/dist/node-api/orderBookPreview.js.map +1 -0
- package/dist/node-api/pairing.d.ts +39 -0
- package/dist/node-api/pairing.d.ts.map +1 -0
- package/dist/node-api/pairing.js +61 -0
- package/dist/node-api/pairing.js.map +1 -0
- package/dist/node-api/pairs.d.ts +32 -0
- package/dist/node-api/pairs.d.ts.map +1 -0
- package/dist/node-api/pairs.js +72 -0
- package/dist/node-api/pairs.js.map +1 -0
- package/dist/node-api/requiredFunds.d.ts +11 -0
- package/dist/node-api/requiredFunds.d.ts.map +1 -0
- package/dist/node-api/requiredFunds.js +7 -0
- package/dist/node-api/requiredFunds.js.map +1 -0
- package/dist/node-api/setup.d.ts +25 -0
- package/dist/node-api/setup.d.ts.map +1 -0
- package/dist/node-api/setup.js +13 -0
- package/dist/node-api/setup.js.map +1 -0
- package/dist/node-api/wallet.d.ts +55 -0
- package/dist/node-api/wallet.d.ts.map +1 -0
- package/dist/node-api/wallet.js +87 -0
- package/dist/node-api/wallet.js.map +1 -0
- package/dist/protocol/actions.d.ts +40 -0
- package/dist/protocol/actions.d.ts.map +1 -0
- package/dist/protocol/actions.js +118 -0
- package/dist/protocol/actions.js.map +1 -0
- package/dist/protocol/documents.d.ts +25 -0
- package/dist/protocol/documents.d.ts.map +1 -0
- package/dist/protocol/documents.js +2 -0
- package/dist/protocol/documents.js.map +1 -0
- package/dist/protocol/index.d.ts +10 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +12 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/orchestration/createAutomation.d.ts +61 -0
- package/dist/protocol/orchestration/createAutomation.d.ts.map +1 -0
- package/dist/protocol/orchestration/createAutomation.js +71 -0
- package/dist/protocol/orchestration/createAutomation.js.map +1 -0
- package/dist/protocol/poll.d.ts +4 -0
- package/dist/protocol/poll.d.ts.map +1 -0
- package/dist/protocol/poll.js +4 -0
- package/dist/protocol/poll.js.map +1 -0
- package/dist/protocol/proposal.d.ts +36 -0
- package/dist/protocol/proposal.d.ts.map +1 -0
- package/dist/protocol/proposal.js +39 -0
- package/dist/protocol/proposal.js.map +1 -0
- package/dist/protocol/state.d.ts +87 -0
- package/dist/protocol/state.d.ts.map +1 -0
- package/dist/protocol/state.js +303 -0
- package/dist/protocol/state.js.map +1 -0
- package/dist/protocol/strategy/build.d.ts +44 -0
- package/dist/protocol/strategy/build.d.ts.map +1 -0
- package/dist/protocol/strategy/build.js +50 -0
- package/dist/protocol/strategy/build.js.map +1 -0
- package/dist/protocol/strategy/builders.d.ts +252 -0
- package/dist/protocol/strategy/builders.d.ts.map +1 -0
- package/dist/protocol/strategy/builders.js +382 -0
- package/dist/protocol/strategy/builders.js.map +1 -0
- package/dist/protocol/strategy/index.d.ts +5 -0
- package/dist/protocol/strategy/index.d.ts.map +1 -0
- package/dist/protocol/strategy/index.js +4 -0
- package/dist/protocol/strategy/index.js.map +1 -0
- package/dist/protocol/strategy/kinds.d.ts +2 -0
- package/dist/protocol/strategy/kinds.d.ts.map +1 -0
- package/dist/protocol/strategy/kinds.js +2 -0
- package/dist/protocol/strategy/kinds.js.map +1 -0
- package/dist/protocol/strategy/patch.d.ts +59 -0
- package/dist/protocol/strategy/patch.d.ts.map +1 -0
- package/dist/protocol/strategy/patch.js +253 -0
- package/dist/protocol/strategy/patch.js.map +1 -0
- package/dist/protocol/symbols.d.ts +8 -0
- package/dist/protocol/symbols.d.ts.map +1 -0
- package/dist/protocol/symbols.js +11 -0
- package/dist/protocol/symbols.js.map +1 -0
- package/dist/transport/constants.d.ts +8 -0
- package/dist/transport/constants.d.ts.map +1 -0
- package/dist/transport/constants.js +8 -0
- package/dist/transport/constants.js.map +1 -0
- package/dist/transport/documents.d.ts +27 -0
- package/dist/transport/documents.d.ts.map +1 -0
- package/dist/transport/documents.js +34 -0
- package/dist/transport/documents.js.map +1 -0
- package/dist/transport/index.d.ts +8 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +8 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/probe.d.ts +44 -0
- package/dist/transport/probe.d.ts.map +1 -0
- package/dist/transport/probe.js +121 -0
- package/dist/transport/probe.js.map +1 -0
- package/dist/transport/rendezvous.d.ts +116 -0
- package/dist/transport/rendezvous.d.ts.map +1 -0
- package/dist/transport/rendezvous.js +139 -0
- package/dist/transport/rendezvous.js.map +1 -0
- package/dist/transport/rest.d.ts +35 -0
- package/dist/transport/rest.d.ts.map +1 -0
- package/dist/transport/rest.js +58 -0
- package/dist/transport/rest.js.map +1 -0
- package/dist/transport/syncClient.d.ts +14 -0
- package/dist/transport/syncClient.d.ts.map +1 -0
- package/dist/transport/syncClient.js +30 -0
- package/dist/transport/syncClient.js.map +1 -0
- package/dist/transport/urls.d.ts +35 -0
- package/dist/transport/urls.d.ts.map +1 -0
- package/dist/transport/urls.js +126 -0
- package/dist/transport/urls.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { generateDeviceKeys, mintDeviceCap } from '@drakkar.software/starfish-identities';
|
|
2
|
+
import { deriveRoot } from './capProvider.js';
|
|
3
|
+
import { createKeyCache } from './keys.js';
|
|
4
|
+
import { deriveCollectionKeys } from '../crypto/collectionKeys.js';
|
|
5
|
+
import { NODE_COLLECTIONS } from '../collections/nodeCollections.js';
|
|
6
|
+
/** The two node collections a read-only pairing grants by default: enough to
|
|
7
|
+
* reconstruct `accounts.list()`, `automations.list()`, AND `strategies.list()`
|
|
8
|
+
* (the latter is implemented via a `userData` pull, not the legacy
|
|
9
|
+
* `strategies` collection) — with no access to `settings`, `accountTrading`,
|
|
10
|
+
* or the `strategies` collection itself. */
|
|
11
|
+
const DEFAULT_READ_ONLY_COLLECTIONS = ['userData', 'accounts'];
|
|
12
|
+
function assertKnownCollections(collections) {
|
|
13
|
+
const unknown = collections.filter((c) => !(c in NODE_COLLECTIONS));
|
|
14
|
+
if (unknown.length) {
|
|
15
|
+
throw new Error(`createReadOnlyPairing: unknown collection(s): ${unknown.join(', ')}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/** Mint a read-only pairing payload from the wallet's seed. The caller (e.g.
|
|
19
|
+
* mobile2) renders the returned string as a QR code for another device to
|
|
20
|
+
* scan — this package never renders QR images itself. The payload is fully
|
|
21
|
+
* self-contained (it carries the node's endpoint), so
|
|
22
|
+
* `connectReadOnlyDevice()` needs nothing else to connect. */
|
|
23
|
+
export async function createReadOnlyPairing(seed, derivation, node, opts) {
|
|
24
|
+
const root = await deriveRoot(seed, derivation);
|
|
25
|
+
const device = generateDeviceKeys();
|
|
26
|
+
const collections = opts?.collections ?? DEFAULT_READ_ONLY_COLLECTIONS;
|
|
27
|
+
assertKnownCollections(collections);
|
|
28
|
+
const scope = {
|
|
29
|
+
ops: ['read', 'list'],
|
|
30
|
+
collections,
|
|
31
|
+
};
|
|
32
|
+
const cap = await mintDeviceCap(root.keys.edPriv, root.keys.edPub, { edPubHex: device.edPub, kemPubHex: device.kemPub }, scope, opts?.ttlSec != null ? { ttlSec: opts.ttlSec } : undefined);
|
|
33
|
+
const secret = await createKeyCache().getEncryptionKey(seed, derivation);
|
|
34
|
+
const collectionKeys = deriveCollectionKeys(secret, collections);
|
|
35
|
+
const payload = {
|
|
36
|
+
v: 2,
|
|
37
|
+
kind: 'octobot-read-only-pairing',
|
|
38
|
+
node,
|
|
39
|
+
rootEdPub: root.keys.edPub,
|
|
40
|
+
userId: root.userId,
|
|
41
|
+
device,
|
|
42
|
+
cap,
|
|
43
|
+
scope,
|
|
44
|
+
collectionKeys,
|
|
45
|
+
};
|
|
46
|
+
return { payload: JSON.stringify(payload) };
|
|
47
|
+
}
|
|
48
|
+
/** Parse and structurally validate a scanned/pasted read-only pairing
|
|
49
|
+
* payload. Throws on anything that isn't shaped like one — callers doing QR
|
|
50
|
+
* classification (e.g. `classifyScannedCode`) should catch and fall through
|
|
51
|
+
* to the next candidate parser rather than propagate. */
|
|
52
|
+
export function parseReadOnlyPairing(payload) {
|
|
53
|
+
let parsed;
|
|
54
|
+
try {
|
|
55
|
+
parsed = JSON.parse(payload);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new Error('not valid JSON');
|
|
59
|
+
}
|
|
60
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
61
|
+
throw new Error('not an object');
|
|
62
|
+
const p = parsed;
|
|
63
|
+
// A v1 payload (this package's earlier, retired pairing format) is a
|
|
64
|
+
// RECOGNIZED-but-stale payload, not an unrecognized one — a user holding
|
|
65
|
+
// an old QR should be told to re-pair, not that the code is unreadable.
|
|
66
|
+
if (p.v === 1 && p.kind === 'octobot-read-only-pairing') {
|
|
67
|
+
throw new Error('read-only pairing payload is out of date (v1) — re-pair this device to get a current one');
|
|
68
|
+
}
|
|
69
|
+
if (p.v !== 2 || p.kind !== 'octobot-read-only-pairing')
|
|
70
|
+
throw new Error('not a read-only pairing payload');
|
|
71
|
+
if (typeof p.rootEdPub !== 'string' || typeof p.userId !== 'string') {
|
|
72
|
+
throw new Error('malformed read-only pairing payload');
|
|
73
|
+
}
|
|
74
|
+
const node = p.node;
|
|
75
|
+
if (!node || typeof node.host !== 'string' || typeof node.port !== 'number') {
|
|
76
|
+
throw new Error('malformed read-only pairing payload: node');
|
|
77
|
+
}
|
|
78
|
+
const device = p.device;
|
|
79
|
+
if (!device || typeof device.edPriv !== 'string' || typeof device.kemPriv !== 'string') {
|
|
80
|
+
throw new Error('malformed read-only pairing payload: device');
|
|
81
|
+
}
|
|
82
|
+
const cap = p.cap;
|
|
83
|
+
if (!cap || cap.v !== 1 || typeof cap.sig !== 'string') {
|
|
84
|
+
throw new Error('malformed read-only pairing payload: cap');
|
|
85
|
+
}
|
|
86
|
+
if (typeof p.collectionKeys !== 'object' || p.collectionKeys === null) {
|
|
87
|
+
throw new Error('malformed read-only pairing payload: collectionKeys');
|
|
88
|
+
}
|
|
89
|
+
return p;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=pairing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/identity/pairing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { type GeneratedDeviceKeys } from '@drakkar.software/starfish-identities';
|
|
2
|
+
import type { NodeCollectionKey } from '../collections/nodeCollections.js';
|
|
3
|
+
/** A website's device-code pairing request, published to the `joinsessions`
|
|
4
|
+
* rendezvous collection (`Infra/sync/server/drakkar_sync/apps/dk_spaces/collections.py`,
|
|
5
|
+
* `_pairing/session/{code}`) so the phone app can look it up by the short
|
|
6
|
+
* code the user types in. ONE address serves both phases of the exchange:
|
|
7
|
+
* this "request" doc is later overwritten in place by the phone's sealed
|
|
8
|
+
* "grant" doc (see `client/pairing/pairingGrantExchange.ts`) — the two
|
|
9
|
+
* phases are told apart on the wire by the top-level `kind` field, which
|
|
10
|
+
* the grant phase carries on an UNSEALED outer envelope specifically so a
|
|
11
|
+
* poller can tell "not yet approved" from "approved" without needing to
|
|
12
|
+
* unseal anything. `code` is carried on this payload (not just used as the
|
|
13
|
+
* address) so `popSig` can bind to it — see `popSigningInput` above — and
|
|
14
|
+
* so `parsePairingRequest` can reject a request record that doesn't match
|
|
15
|
+
* the code it was fetched under. */
|
|
16
|
+
export interface PairingRequestPayload {
|
|
17
|
+
v: 1;
|
|
18
|
+
kind: 'octobot-pairing-request';
|
|
19
|
+
code: string;
|
|
20
|
+
/** Who's asking: `'website'` — the original case, a third-party site
|
|
21
|
+
* running this package in a browser — or `'device'` — another OctoBot
|
|
22
|
+
* client (e.g. a second phone) pairing as a read-only viewer of this
|
|
23
|
+
* wallet's cloud mirror. Always present on the wire; there is no
|
|
24
|
+
* absent-means-website fallback (this package predates any published
|
|
25
|
+
* consumer, so there was never a shape to stay compatible with). The
|
|
26
|
+
* approving side (`website-pairing-approve.tsx` in mobile2) branches its
|
|
27
|
+
* copy on this — a `'device'` request has no real "origin" to verify,
|
|
28
|
+
* trust instead comes from the human typing the code themselves. */
|
|
29
|
+
requesterKind: 'website' | 'device';
|
|
30
|
+
devEdPub: string;
|
|
31
|
+
devKemPub: string;
|
|
32
|
+
/** `ed25519Suite.sign(popSigningInput(...), devEdPriv)`, hex. Proves the
|
|
33
|
+
* requester holds `devEdPriv` — not proof of the requester's identity or
|
|
34
|
+
* intent, which is exactly why `origin`/`code` are still what the
|
|
35
|
+
* approving human relies on. */
|
|
36
|
+
popSig: string;
|
|
37
|
+
/** `signKemSig({kemPub: devKemPub, edPriv: devEdPriv})` (`@drakkar.software
|
|
38
|
+
* /starfish-spaces`), hex — a SEPARATE proof-of-possession signature,
|
|
39
|
+
* over just `devKemPub`, matching the exact `{edPub, kemPub, userId,
|
|
40
|
+
* kemSig}` "join request" shape `starfish-spaces`' own
|
|
41
|
+
* `parseJoinRequest`/`inviteToNode` expect. The approving side
|
|
42
|
+
* reconstructs that join request from `devEdPub`/`devKemPub` (already
|
|
43
|
+
* present above) plus this field — see `client/pairing/mirrorGrant.ts` —
|
|
44
|
+
* rather than the website needing to hold a full `starfish-spaces`
|
|
45
|
+
* `Session` just to call `makeJoinRequest()` for an identity it has no
|
|
46
|
+
* wallet to derive. */
|
|
47
|
+
joinRequestKemSig: string;
|
|
48
|
+
/** Attacker-controlled in the sense that anyone can put any string here —
|
|
49
|
+
* the approving side must verify it (e.g. a `.well-known` fetch), not
|
|
50
|
+
* trust it at face value. */
|
|
51
|
+
origin: string;
|
|
52
|
+
label?: string;
|
|
53
|
+
requestedCollections?: NodeCollectionKey[];
|
|
54
|
+
createdAt: string;
|
|
55
|
+
expiresAt: string;
|
|
56
|
+
rendezvous: {
|
|
57
|
+
baseUrl: string;
|
|
58
|
+
namespace: string;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Create a pairing request: fresh ephemeral device keys (never the site's
|
|
62
|
+
* own identity), a proof-of-possession signature, and a short human code.
|
|
63
|
+
* Returns the code separately from `request` too (in addition to it being
|
|
64
|
+
* on the payload) since the caller needs it to display immediately, before
|
|
65
|
+
* `request` is even published. */
|
|
66
|
+
export declare function createPairingRequest(opts: {
|
|
67
|
+
origin: string;
|
|
68
|
+
rendezvous: {
|
|
69
|
+
baseUrl: string;
|
|
70
|
+
namespace: string;
|
|
71
|
+
};
|
|
72
|
+
label?: string;
|
|
73
|
+
requestedCollections?: NodeCollectionKey[];
|
|
74
|
+
ttlSec?: number;
|
|
75
|
+
/** Defaults to `'website'` — the original, still-overwhelmingly-common
|
|
76
|
+
* caller. Only a device-pairing consumer needs to pass `'device'`
|
|
77
|
+
* explicitly. The built payload always carries the field either way. */
|
|
78
|
+
requesterKind?: 'website' | 'device';
|
|
79
|
+
}): Promise<{
|
|
80
|
+
request: PairingRequestPayload;
|
|
81
|
+
device: GeneratedDeviceKeys;
|
|
82
|
+
code: string;
|
|
83
|
+
}>;
|
|
84
|
+
/** Parse and validate a pairing request record fetched by code. `expectedCode`
|
|
85
|
+
* MUST be the code the caller fetched this record under (from the address,
|
|
86
|
+
* not the payload) — rejecting a mismatch stops a request record signed
|
|
87
|
+
* for one code from being accepted after being copied/replayed to a
|
|
88
|
+
* different code's slot. Verifies the proof-of-possession signature and
|
|
89
|
+
* rejects an expired request — both before the approving side does
|
|
90
|
+
* anything else with it. Does NOT verify `origin` resolves to anything
|
|
91
|
+
* real; that needs an actual network check the approving side performs
|
|
92
|
+
* itself (this package stays I/O-free here). It DOES bound and
|
|
93
|
+
* sanity-check `origin`/`label` as strings — a length cap, a URL parse for
|
|
94
|
+
* `origin`, and rejecting control/bidi-override characters in both — since
|
|
95
|
+
* they're the two fields an approving human actually reads and relies on.
|
|
96
|
+
* Full homoglyph/IDNA-confusable host detection is out of scope, the same
|
|
97
|
+
* judgment call already made for origin verification itself. */
|
|
98
|
+
export declare function parsePairingRequest(payload: string, expectedCode: string): PairingRequestPayload;
|
|
99
|
+
//# sourceMappingURL=pairingRequest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairingRequest.d.ts","sourceRoot":"","sources":["../../src/identity/pairingRequest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,mBAAmB,EAAE,MAAM,uCAAuC,CAAA;AAIpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AAiE1E;;;;;;;;;;;;qCAYqC;AACrC,MAAM,WAAW,qBAAqB;IACpC,CAAC,EAAE,CAAC,CAAA;IACJ,IAAI,EAAE,yBAAyB,CAAA;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;;;yEAQqE;IACrE,aAAa,EAAE,SAAS,GAAG,QAAQ,CAAA;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB;;;qCAGiC;IACjC,MAAM,EAAE,MAAM,CAAA;IACd;;;;;;;;;4BASwB;IACxB,iBAAiB,EAAE,MAAM,CAAA;IACzB;;kCAE8B;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC1C,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CACnD;AAED;;;;mCAImC;AACnC,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oBAAoB,CAAC,EAAE,iBAAiB,EAAE,CAAA;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;6EAEyE;IACzE,aAAa,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;CACrC,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,qBAAqB,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA+BzF;AAgCD;;;;;;;;;;;;;iEAaiE;AACjE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,qBAAqB,CA2EhG"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { generateDeviceKeys } from '@drakkar.software/starfish-identities';
|
|
2
|
+
import { ed25519Suite } from '@drakkar.software/starfish-protocol';
|
|
3
|
+
import { signKemSig } from '@drakkar.software/starfish-spaces';
|
|
4
|
+
import { toHex, hexToBytes } from '../internal/bytes.js';
|
|
5
|
+
/** Excludes visually-ambiguous characters (0/O, 1/I/L) — Crockford-style,
|
|
6
|
+
* meant to be read off one screen and typed on another. 31 symbols
|
|
7
|
+
* (23 letters + 8 digits), 8 characters ⇒ 8·log2(31) ≈ 39.63 bits of
|
|
8
|
+
* entropy, well above RFC 8628's device-flow `user_code` minimum, and
|
|
9
|
+
* bounded further by the rendezvous collection's short TTL and per-IP
|
|
10
|
+
* rate limit (not something this package controls). `randomCode()` uses
|
|
11
|
+
* rejection sampling (see `CODE_REJECT_THRESHOLD`) so this figure is the
|
|
12
|
+
* real, uniform entropy — not a biased approximation of it. */
|
|
13
|
+
const CODE_ALPHABET = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
|
|
14
|
+
const CODE_LENGTH = 8;
|
|
15
|
+
// 256 is not a multiple of CODE_ALPHABET.length (31) — a plain `byte %
|
|
16
|
+
// length` would over-represent the first `256 % 31 = 8` symbols (A-H) by
|
|
17
|
+
// 12.5% in every character position. Reject any byte at or above the
|
|
18
|
+
// largest multiple of the alphabet length that still fits in a byte
|
|
19
|
+
// (Math.floor(256 / 31) * 31 = 248) and draw a replacement — the remaining
|
|
20
|
+
// range [0, 248) maps onto the 31 symbols with exactly uniform probability.
|
|
21
|
+
const CODE_REJECT_THRESHOLD = Math.floor(256 / CODE_ALPHABET.length) * CODE_ALPHABET.length;
|
|
22
|
+
const DEFAULT_REQUEST_TTL_SEC = 5 * 60;
|
|
23
|
+
// `expiresAt`/`createdAt` are NOT covered by popSig — anyone with the code
|
|
24
|
+
// can rewrite them, and the ONLY thing that kept the code's live window
|
|
25
|
+
// actually short was the 5-minute default nobody was forced to respect.
|
|
26
|
+
// This is the real enforcement: a request whose declared window exceeds it
|
|
27
|
+
// is rejected outright by parsePairingRequest, and createPairingRequest
|
|
28
|
+
// clamps its own ttlSec to it too so a well-meaning caller can't
|
|
29
|
+
// accidentally request something this package would reject anyway.
|
|
30
|
+
const MAX_REQUEST_TTL_SEC = 60 * 60;
|
|
31
|
+
function randomBytes(n) {
|
|
32
|
+
const b = new Uint8Array(n);
|
|
33
|
+
globalThis.crypto.getRandomValues(b);
|
|
34
|
+
return b;
|
|
35
|
+
}
|
|
36
|
+
function randomCode() {
|
|
37
|
+
const chars = [];
|
|
38
|
+
// Pull a batch at a time (rather than one byte per iteration) so a run of
|
|
39
|
+
// rejected bytes doesn't turn into a syscall-per-byte loop — rejection
|
|
40
|
+
// hits ~3% of bytes, so a fresh CODE_LENGTH-sized batch almost always
|
|
41
|
+
// finishes the code outright, with a further batch only on the rare tail.
|
|
42
|
+
while (chars.length < CODE_LENGTH) {
|
|
43
|
+
const batch = randomBytes(CODE_LENGTH);
|
|
44
|
+
for (const b of batch) {
|
|
45
|
+
if (b >= CODE_REJECT_THRESHOLD)
|
|
46
|
+
continue;
|
|
47
|
+
chars.push(CODE_ALPHABET[b % CODE_ALPHABET.length]);
|
|
48
|
+
if (chars.length === CODE_LENGTH)
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return chars.join('');
|
|
53
|
+
}
|
|
54
|
+
/** What binds `code`/`devEdPub`/`devKemPub` together and proves the
|
|
55
|
+
* requester holds `devEdPriv` — without this, a request record's public
|
|
56
|
+
* keys could be swapped in transit with no way to detect it, and binding
|
|
57
|
+
* `code` specifically prevents a validly-signed request minted for one
|
|
58
|
+
* code from being replayed/republished under a DIFFERENT code (the
|
|
59
|
+
* rendezvous collection is public-write, so anyone who captures a signed
|
|
60
|
+
* request record could otherwise re-publish it verbatim elsewhere). Signed
|
|
61
|
+
* over a canonical JSON of the three fields so a signature can't be
|
|
62
|
+
* replayed across a different (key-pair, code) combination. */
|
|
63
|
+
function popSigningInput(code, devEdPub, devKemPub) {
|
|
64
|
+
return new TextEncoder().encode(JSON.stringify({ code, devEdPub, devKemPub }));
|
|
65
|
+
}
|
|
66
|
+
/** Create a pairing request: fresh ephemeral device keys (never the site's
|
|
67
|
+
* own identity), a proof-of-possession signature, and a short human code.
|
|
68
|
+
* Returns the code separately from `request` too (in addition to it being
|
|
69
|
+
* on the payload) since the caller needs it to display immediately, before
|
|
70
|
+
* `request` is even published. */
|
|
71
|
+
export async function createPairingRequest(opts) {
|
|
72
|
+
const device = generateDeviceKeys();
|
|
73
|
+
const code = randomCode();
|
|
74
|
+
// Clamped, not just defaulted — a caller passing an oversized ttlSec
|
|
75
|
+
// would otherwise create a request parsePairingRequest rejects outright
|
|
76
|
+
// (see MAX_REQUEST_TTL_SEC), silently breaking their own integration
|
|
77
|
+
// instead of getting the shortest safe window this package will accept.
|
|
78
|
+
// Does not clamp a NEGATIVE ttlSec (e.g. for testing an already-expired
|
|
79
|
+
// request) — only the upper bound is enforced here.
|
|
80
|
+
const ttlSec = Math.min(opts.ttlSec ?? DEFAULT_REQUEST_TTL_SEC, MAX_REQUEST_TTL_SEC);
|
|
81
|
+
const now = new Date();
|
|
82
|
+
const popSig = toHex(ed25519Suite.sign(popSigningInput(code, device.edPub, device.kemPub), device.edPriv));
|
|
83
|
+
const joinRequestKemSig = signKemSig({ kemPub: device.kemPub, edPriv: device.edPriv });
|
|
84
|
+
const request = {
|
|
85
|
+
v: 1,
|
|
86
|
+
kind: 'octobot-pairing-request',
|
|
87
|
+
code,
|
|
88
|
+
requesterKind: opts.requesterKind ?? 'website',
|
|
89
|
+
devEdPub: device.edPub,
|
|
90
|
+
devKemPub: device.kemPub,
|
|
91
|
+
popSig,
|
|
92
|
+
joinRequestKemSig,
|
|
93
|
+
origin: opts.origin,
|
|
94
|
+
...(opts.label ? { label: opts.label } : {}),
|
|
95
|
+
...(opts.requestedCollections ? { requestedCollections: opts.requestedCollections } : {}),
|
|
96
|
+
createdAt: now.toISOString(),
|
|
97
|
+
expiresAt: new Date(now.getTime() + ttlSec * 1000).toISOString(),
|
|
98
|
+
rendezvous: opts.rendezvous,
|
|
99
|
+
};
|
|
100
|
+
return { request, device, code };
|
|
101
|
+
}
|
|
102
|
+
// Ed25519/X25519 public keys are 32 raw bytes; an Ed25519 signature is 64 —
|
|
103
|
+
// hex-encoded, that's exactly these lengths. Checked BEFORE hexToBytes is
|
|
104
|
+
// called on any of them: hexToBytes allocates proportionally to input
|
|
105
|
+
// length with no ceiling of its own, so an unbounded hex string here would
|
|
106
|
+
// allocate before ever reaching signature verification.
|
|
107
|
+
const HEX_KEY_LENGTH = 64;
|
|
108
|
+
const HEX_SIG_LENGTH = 128;
|
|
109
|
+
const MAX_ORIGIN_LENGTH = 2048;
|
|
110
|
+
const MAX_LABEL_LENGTH = 200;
|
|
111
|
+
// C0/C1 control characters (including \n, \r — RN <Text> renders an
|
|
112
|
+
// embedded newline as a real line break, letting attacker text masquerade
|
|
113
|
+
// as extra app chrome) plus the Unicode bidi override/isolate controls
|
|
114
|
+
// (U+202A-E, U+2066-9) that can visually reverse or reorder a rendered
|
|
115
|
+
// string — e.g. making a hostile host read as a different one. Written as
|
|
116
|
+
// explicit \u escapes, never literal control/bidi characters in source.
|
|
117
|
+
const UNSAFE_TEXT_PATTERN = /[\u0000-\u001F\u007F-\u009F\u202A-\u202E\u2066-\u2069]/;
|
|
118
|
+
function assertBoundedSafeText(value, field, maxLength) {
|
|
119
|
+
if (value.length > maxLength)
|
|
120
|
+
throw new Error(`pairing request: ${field} exceeds max length`);
|
|
121
|
+
if (UNSAFE_TEXT_PATTERN.test(value)) {
|
|
122
|
+
throw new Error(`pairing request: ${field} contains a disallowed control or bidi-override character`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function assertHexLength(value, field, expectedLength) {
|
|
126
|
+
if (value.length !== expectedLength || !/^[0-9a-fA-F]+$/.test(value)) {
|
|
127
|
+
throw new Error(`pairing request: ${field} is not a valid ${expectedLength}-character hex string`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** Parse and validate a pairing request record fetched by code. `expectedCode`
|
|
131
|
+
* MUST be the code the caller fetched this record under (from the address,
|
|
132
|
+
* not the payload) — rejecting a mismatch stops a request record signed
|
|
133
|
+
* for one code from being accepted after being copied/replayed to a
|
|
134
|
+
* different code's slot. Verifies the proof-of-possession signature and
|
|
135
|
+
* rejects an expired request — both before the approving side does
|
|
136
|
+
* anything else with it. Does NOT verify `origin` resolves to anything
|
|
137
|
+
* real; that needs an actual network check the approving side performs
|
|
138
|
+
* itself (this package stays I/O-free here). It DOES bound and
|
|
139
|
+
* sanity-check `origin`/`label` as strings — a length cap, a URL parse for
|
|
140
|
+
* `origin`, and rejecting control/bidi-override characters in both — since
|
|
141
|
+
* they're the two fields an approving human actually reads and relies on.
|
|
142
|
+
* Full homoglyph/IDNA-confusable host detection is out of scope, the same
|
|
143
|
+
* judgment call already made for origin verification itself. */
|
|
144
|
+
export function parsePairingRequest(payload, expectedCode) {
|
|
145
|
+
let parsed;
|
|
146
|
+
try {
|
|
147
|
+
parsed = JSON.parse(payload);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
throw new Error('not valid JSON');
|
|
151
|
+
}
|
|
152
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
153
|
+
throw new Error('not an object');
|
|
154
|
+
const p = parsed;
|
|
155
|
+
if (p.v !== 1 || p.kind !== 'octobot-pairing-request')
|
|
156
|
+
throw new Error('not a pairing request payload');
|
|
157
|
+
if (typeof p.code !== 'string' || typeof p.devEdPub !== 'string' || typeof p.devKemPub !== 'string'
|
|
158
|
+
|| typeof p.popSig !== 'string' || typeof p.joinRequestKemSig !== 'string' || typeof p.origin !== 'string'
|
|
159
|
+
|| typeof p.createdAt !== 'string' || typeof p.expiresAt !== 'string') {
|
|
160
|
+
throw new Error('malformed pairing request payload');
|
|
161
|
+
}
|
|
162
|
+
if (p.requesterKind !== 'website' && p.requesterKind !== 'device') {
|
|
163
|
+
throw new Error('malformed pairing request payload: requesterKind');
|
|
164
|
+
}
|
|
165
|
+
if (p.code !== expectedCode) {
|
|
166
|
+
throw new Error('pairing request: code does not match the address it was fetched from');
|
|
167
|
+
}
|
|
168
|
+
const rendezvous = p.rendezvous;
|
|
169
|
+
if (!rendezvous || typeof rendezvous.baseUrl !== 'string' || typeof rendezvous.namespace !== 'string') {
|
|
170
|
+
throw new Error('malformed pairing request payload: rendezvous');
|
|
171
|
+
}
|
|
172
|
+
if (p.label !== undefined && typeof p.label !== 'string') {
|
|
173
|
+
throw new Error('malformed pairing request payload: label');
|
|
174
|
+
}
|
|
175
|
+
assertHexLength(p.devEdPub, 'devEdPub', HEX_KEY_LENGTH);
|
|
176
|
+
assertHexLength(p.devKemPub, 'devKemPub', HEX_KEY_LENGTH);
|
|
177
|
+
assertHexLength(p.popSig, 'popSig', HEX_SIG_LENGTH);
|
|
178
|
+
assertHexLength(p.joinRequestKemSig, 'joinRequestKemSig', HEX_SIG_LENGTH);
|
|
179
|
+
assertBoundedSafeText(p.origin, 'origin', MAX_ORIGIN_LENGTH);
|
|
180
|
+
if (p.label !== undefined)
|
|
181
|
+
assertBoundedSafeText(p.label, 'label', MAX_LABEL_LENGTH);
|
|
182
|
+
try {
|
|
183
|
+
new URL(p.origin);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
throw new Error('pairing request: origin is not a valid URL');
|
|
187
|
+
}
|
|
188
|
+
const request = p;
|
|
189
|
+
const verified = ed25519Suite.verify(hexToBytes(request.popSig), popSigningInput(request.code, request.devEdPub, request.devKemPub), request.devEdPub);
|
|
190
|
+
if (!verified)
|
|
191
|
+
throw new Error('pairing request: invalid proof-of-possession signature');
|
|
192
|
+
// Date.parse of a malformed string returns NaN, and every comparison
|
|
193
|
+
// against NaN is false — so a garbage expiresAt would otherwise be
|
|
194
|
+
// silently treated as "not expired" instead of rejected. Fail closed.
|
|
195
|
+
const expiresAtMs = Date.parse(request.expiresAt);
|
|
196
|
+
if (!Number.isFinite(expiresAtMs) || expiresAtMs <= Date.now())
|
|
197
|
+
throw new Error('pairing request: expired');
|
|
198
|
+
// The actual enforcement of "short-lived code" — expiresAt/createdAt are
|
|
199
|
+
// NOT covered by popSig, so a party with the code can rewrite either to
|
|
200
|
+
// anything. An earlier version of this check compared expiresAt against
|
|
201
|
+
// the request's OWN createdAt — trivially bypassable, since an attacker
|
|
202
|
+
// controlling both fields can place them arbitrarily far in the future
|
|
203
|
+
// while keeping their difference inside the cap, making the code "look"
|
|
204
|
+
// freshly issued no matter when it's actually redeemed (e.g.
|
|
205
|
+
// createdAt = now+364d, expiresAt = now+364d+1h — passes a
|
|
206
|
+
// createdAt-relative check, but the code stays "valid" for the next
|
|
207
|
+
// year). Anchoring to THIS CALL's real wall clock instead closes that:
|
|
208
|
+
// a request cannot claim to remain valid more than MAX_REQUEST_TTL_SEC
|
|
209
|
+
// from right now, independent of what it claims createdAt to be.
|
|
210
|
+
// createdAt itself is otherwise purely informational — never used for
|
|
211
|
+
// any security decision here.
|
|
212
|
+
if (expiresAtMs - Date.now() > MAX_REQUEST_TTL_SEC * 1000) {
|
|
213
|
+
throw new Error('pairing request: expiry window exceeds the maximum this package allows');
|
|
214
|
+
}
|
|
215
|
+
return request;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=pairingRequest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairingRequest.js","sourceRoot":"","sources":["../../src/identity/pairingRequest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** What a string of key material turned out to be. */
|
|
2
|
+
export type ClassifiedSecret = {
|
|
3
|
+
kind: 'privateKey';
|
|
4
|
+
value: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'seed';
|
|
7
|
+
value: string;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'unknown';
|
|
10
|
+
};
|
|
11
|
+
/** Classify key material that arrived somewhere already known to hold it — a
|
|
12
|
+
* pairing payload's `password`, a standalone `{"password": "…"}` code, or a
|
|
13
|
+
* phrase typed by hand.
|
|
14
|
+
*
|
|
15
|
+
* The `0x` prefix picks the branch, and the branch still validates: a value
|
|
16
|
+
* that is neither a real key nor a real phrase has to be rejected rather than
|
|
17
|
+
* quietly produce a wallet nobody owns.
|
|
18
|
+
*
|
|
19
|
+
* Note what this deliberately does **not** decide: which derivation SCHEME
|
|
20
|
+
* a `seed` needs, if a caller has registered more than the built-in
|
|
21
|
+
* `'bip44'` (see `derivationSchemes.ts`) — that depends on where the phrase
|
|
22
|
+
* came from, not on its shape. */
|
|
23
|
+
export declare function classifySecret(raw: string): Promise<ClassifiedSecret>;
|
|
24
|
+
/** Classify a bare scanned/typed value, where no field name says which shape to
|
|
25
|
+
* expect, so both are tried. Unlike `classifySecret` an unprefixed 64-hex still
|
|
26
|
+
* reads as a key here — there is no envelope to say otherwise. */
|
|
27
|
+
export declare function classifyBareSecret(raw: string): Promise<ClassifiedSecret>;
|
|
28
|
+
/** The wallet inside a standalone `{"password": "…"}` code — JSON carrying key
|
|
29
|
+
* material directly, with no node attached to fetch it from. Null when the
|
|
30
|
+
* value is not that shape. */
|
|
31
|
+
export declare function readSecretEnvelope(data: string): string | null;
|
|
32
|
+
//# sourceMappingURL=secret.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../../src/identity/secret.ts"],"names":[],"mappings":"AAGA,sDAAsD;AACtD,MAAM,MAAM,gBAAgB,GACxB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAA;AAEvB;;;;;;;;;;;mCAWmC;AACnC,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAW3E;AAED;;mEAEmE;AACnE,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAS/E;AAED;;+BAE+B;AAC/B,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU9D"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { isEvmPrivateKey, normalizeEvmPrivateKey } from './evm.js';
|
|
2
|
+
import { validateSeedPhrase } from './mnemonic.js';
|
|
3
|
+
/** Classify key material that arrived somewhere already known to hold it — a
|
|
4
|
+
* pairing payload's `password`, a standalone `{"password": "…"}` code, or a
|
|
5
|
+
* phrase typed by hand.
|
|
6
|
+
*
|
|
7
|
+
* The `0x` prefix picks the branch, and the branch still validates: a value
|
|
8
|
+
* that is neither a real key nor a real phrase has to be rejected rather than
|
|
9
|
+
* quietly produce a wallet nobody owns.
|
|
10
|
+
*
|
|
11
|
+
* Note what this deliberately does **not** decide: which derivation SCHEME
|
|
12
|
+
* a `seed` needs, if a caller has registered more than the built-in
|
|
13
|
+
* `'bip44'` (see `derivationSchemes.ts`) — that depends on where the phrase
|
|
14
|
+
* came from, not on its shape. */
|
|
15
|
+
export async function classifySecret(raw) {
|
|
16
|
+
const trimmed = raw.trim();
|
|
17
|
+
if (trimmed.toLowerCase().startsWith('0x')) {
|
|
18
|
+
const key = normalizeEvmPrivateKey(trimmed);
|
|
19
|
+
return isEvmPrivateKey(key) ? { kind: 'privateKey', value: key } : { kind: 'unknown' };
|
|
20
|
+
}
|
|
21
|
+
const phrase = trimmed.split(/\s+/).map((w) => w.toLowerCase()).filter(Boolean).join(' ');
|
|
22
|
+
if (phrase && (await validateSeedPhrase(phrase)))
|
|
23
|
+
return { kind: 'seed', value: phrase };
|
|
24
|
+
return { kind: 'unknown' };
|
|
25
|
+
}
|
|
26
|
+
/** Classify a bare scanned/typed value, where no field name says which shape to
|
|
27
|
+
* expect, so both are tried. Unlike `classifySecret` an unprefixed 64-hex still
|
|
28
|
+
* reads as a key here — there is no envelope to say otherwise. */
|
|
29
|
+
export async function classifyBareSecret(raw) {
|
|
30
|
+
const trimmed = raw.trim();
|
|
31
|
+
const key = normalizeEvmPrivateKey(trimmed);
|
|
32
|
+
if (isEvmPrivateKey(key))
|
|
33
|
+
return { kind: 'privateKey', value: key };
|
|
34
|
+
const phrase = trimmed.split(/\s+/).map((w) => w.toLowerCase()).filter(Boolean).join(' ');
|
|
35
|
+
if (phrase && (await validateSeedPhrase(phrase)))
|
|
36
|
+
return { kind: 'seed', value: phrase };
|
|
37
|
+
return { kind: 'unknown' };
|
|
38
|
+
}
|
|
39
|
+
/** The wallet inside a standalone `{"password": "…"}` code — JSON carrying key
|
|
40
|
+
* material directly, with no node attached to fetch it from. Null when the
|
|
41
|
+
* value is not that shape. */
|
|
42
|
+
export function readSecretEnvelope(data) {
|
|
43
|
+
try {
|
|
44
|
+
const parsed = JSON.parse(data.trim());
|
|
45
|
+
if (parsed && typeof parsed === 'object' && typeof parsed.password === 'string') {
|
|
46
|
+
return parsed.password;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// not JSON — not an envelope
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=secret.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret.js","sourceRoot":"","sources":["../../src/identity/secret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAA;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAQlD;;;;;;;;;;;mCAWmC;AACnC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IAE1B,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAA;QAC3C,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;IACxF,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACzF,IAAI,MAAM,IAAI,CAAC,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IACxF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;AAC5B,CAAC;AAED;;mEAEmE;AACnE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAW;IAClD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;IAE1B,MAAM,GAAG,GAAG,sBAAsB,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,eAAe,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,EAAE,CAAA;IAEnE,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACzF,IAAI,MAAM,IAAI,CAAC,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IACxF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;AAC5B,CAAC;AAED;;+BAE+B;AAC/B,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;QACtC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAChF,OAAO,MAAM,CAAC,QAAQ,CAAA;QACxB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { connectOctoBot, type OctoBotClient, type ConnectOptions, type SeedDerivation, type CallOptions, type AccountsApi, type AccountInput, type AutomationsApi, type CreateAutomationInput, type CreateAutomationProgress, type StrategiesApi, type SettingsApi, type NodeApi, type DocumentsApi, type ReadOnlyDocumentsApi, type ActionHandle, type AccountView, type AccountKind, type AutomationView, type Holding, OctoBotError, OctoBotConfigError, OctoBotConnectionError, OctoBotAuthError, OctoBotHttpError, OctoBotConflictError, OctoBotActionError, OctoBotTimeoutError, OctoBotScopeError, isOctoBotError, type OctoBotErrorCode, connectReadOnlyDevice, type ConnectReadOnlyOptions, type ReadOnlyOctoBotClient, type ReadOnlyAccountsApi, type ReadOnlyAutomationsApi, type ReadOnlyStrategiesApi, type ProposedAction, accountViewOf, automationViewOf, startPairingRequest, fetchPairingRequestByCode, type StartPairingRequestOptions, type PairingRequestSession, type PairingRequestLookup, buildJoinRequestJson, mintPairingGrant, revokePairingGrant, NothingToShareError, type MintedPairingGrant, parseMirrorGrantBundle, readMirrorCollections, attemptDirectMirrorWrite, type MirrorGrantBundle, type MirrorGrantNodeRef, type ReadMirrorCollectionsOptions, type AttemptDirectMirrorWriteOptions, publishPairingGrant, clearPairingGrant, fetchPairingGrant, awaitPairingGrant, type UnsealedPairingGrant, } from './client/index.js';
|
|
2
|
+
export { MIRROR_COLLECTIONS, DEFAULT_MIRROR_COLLECTIONS, THIRD_PARTY_ELIGIBLE_MIRROR_COLLECTIONS, MIRROR_SPACE_NAME, isIsolatedMirrorCollection, isKnownMirrorCollection, isPublicMirrorCollection, isThirdPartyEligible, mirrorVisibilityFor, type MirrorCollection, type MirrorCollectionId, type MirrorVisibility, syncCloudMirror, type SyncCloudMirrorOptions, type SyncCloudMirrorResult, } from './client/mirror/index.js';
|
|
3
|
+
export { strategy, type StrategyBuilders } from './client/strategy.js';
|
|
4
|
+
export { createReadOnlyPairing, parseReadOnlyPairing, type ReadOnlyPairingPayload } from './identity/pairing.js';
|
|
5
|
+
export { createPairingRequest, parsePairingRequest, type PairingRequestPayload } from './identity/pairingRequest.js';
|
|
6
|
+
export { createRendezvousClient, pullRendezvousDoc, pushRendezvousDoc, clearRendezvousDoc, joinSessionPath, type RendezvousDoc, } from './transport/rendezvous.js';
|
|
7
|
+
export { encodeActionProposal, decodeActionProposal, type ActionProposal, type ProposedActionEntry } from './protocol/proposal.js';
|
|
8
|
+
export type { Strategy as ProtocolStrategy, Account as ProtocolAccount, AccountState, AccountAuthentication, ExchangeConfig, AutomationState, WorkflowStatus, DetailedAsset, UserAction, UserActionConfiguration, DslKeywordsState, MarketMakingConfiguration, AccountTrading, AccountTradingWithAccountId, DetailedAssetsForTradingType, CreateAutomationConfiguration, } from '@drakkar.software/octobot-protocol';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,cAAc,EACd,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,oBAAoB,EACzB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,OAAO,EACZ,YAAY,EACZ,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,KAAK,gBAAgB,EACrB,qBAAqB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,yBAAyB,EACzB,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACpC,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,uCAAuC,EACvC,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,eAAe,EACf,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,GAC3B,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,QAAQ,EAAE,KAAK,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAChH,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,KAAK,qBAAqB,EAAE,MAAM,8BAA8B,CAAA;AACpH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,KAAK,aAAa,GACnB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAOlI,YAAY,EACV,QAAQ,IAAI,gBAAgB,EAC5B,OAAO,IAAI,eAAe,EAC1B,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,cAAc,EACd,aAAa,EACb,UAAU,EACV,uBAAuB,EACvB,gBAAgB,EAChB,yBAAyB,EACzB,cAAc,EACd,2BAA2B,EAC3B,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,oCAAoC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// @drakkar.software/octobot-client — a TypeScript client for the OctoBot node
|
|
2
|
+
// protocol: wallet identity, accounts, automations, strategies, and user
|
|
3
|
+
// actions over the Starfish sync transport.
|
|
4
|
+
//
|
|
5
|
+
// This is the facade tier. It keeps no document cache and no offline
|
|
6
|
+
// queue — every read is a fresh pull, every write is a fresh push/append.
|
|
7
|
+
// An app that needs persistence, an offline queue, or CRDT merge across
|
|
8
|
+
// devices builds that layer ON TOP of this package (see
|
|
9
|
+
// https://docs.octobot.cloud/client-sdk/advanced-primitives for the subpath
|
|
10
|
+
// exports this is built from).
|
|
11
|
+
export { connectOctoBot, OctoBotError, OctoBotConfigError, OctoBotConnectionError, OctoBotAuthError, OctoBotHttpError, OctoBotConflictError, OctoBotActionError, OctoBotTimeoutError, OctoBotScopeError, isOctoBotError, connectReadOnlyDevice, accountViewOf, automationViewOf, startPairingRequest, fetchPairingRequestByCode, buildJoinRequestJson, mintPairingGrant, revokePairingGrant, NothingToShareError, parseMirrorGrantBundle, readMirrorCollections, attemptDirectMirrorWrite, publishPairingGrant, clearPairingGrant, fetchPairingGrant, awaitPairingGrant, } from './client/index.js';
|
|
12
|
+
export { MIRROR_COLLECTIONS, DEFAULT_MIRROR_COLLECTIONS, THIRD_PARTY_ELIGIBLE_MIRROR_COLLECTIONS, MIRROR_SPACE_NAME, isIsolatedMirrorCollection, isKnownMirrorCollection, isPublicMirrorCollection, isThirdPartyEligible, mirrorVisibilityFor, syncCloudMirror, } from './client/mirror/index.js';
|
|
13
|
+
export { strategy } from './client/strategy.js';
|
|
14
|
+
export { createReadOnlyPairing, parseReadOnlyPairing } from './identity/pairing.js';
|
|
15
|
+
export { createPairingRequest, parsePairingRequest } from './identity/pairingRequest.js';
|
|
16
|
+
export { createRendezvousClient, pullRendezvousDoc, pushRendezvousDoc, clearRendezvousDoc, joinSessionPath, } from './transport/rendezvous.js';
|
|
17
|
+
export { encodeActionProposal, decodeActionProposal } from './protocol/proposal.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,yEAAyE;AACzE,4CAA4C;AAC5C,EAAE;AACF,qEAAqE;AACrE,0EAA0E;AAC1E,wEAAwE;AACxE,wDAAwD;AACxD,4EAA4E;AAC5E,+BAA+B;AAE/B,OAAO,EACL,cAAc,EAoBd,YAAY,EACZ,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EAEd,qBAAqB,EAOrB,aAAa,EACb,gBAAgB,EAChB,mBAAmB,EACnB,yBAAyB,EAIzB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EAEnB,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EAKxB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAElB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,uCAAuC,EACvC,iBAAiB,EACjB,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EAInB,eAAe,GAGhB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAAE,QAAQ,EAAyB,MAAM,sBAAsB,CAAA;AACtE,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAA+B,MAAM,uBAAuB,CAAA;AAChH,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAA8B,MAAM,8BAA8B,CAAA;AACpH,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,GAEhB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAiD,MAAM,wBAAwB,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function toHex(b: Uint8Array): string;
|
|
2
|
+
export declare function hexToBytes(hex: string): Uint8Array;
|
|
3
|
+
export declare function toBase64(bytes: Uint8Array): string;
|
|
4
|
+
export declare function fromBase64(b64: string): Uint8Array<ArrayBuffer>;
|
|
5
|
+
/** URL-safe base64 (`+/` → `-_`, no `=` padding) of raw bytes. Distinct from
|
|
6
|
+
* `starfish-protocol`'s `toBase64Url`, which encodes a UTF-8 *string* — not
|
|
7
|
+
* usable here since not every byte sequence (e.g. random session-id bytes)
|
|
8
|
+
* round-trips through UTF-8. */
|
|
9
|
+
export declare function toBase64Url(bytes: Uint8Array): string;
|
|
10
|
+
export declare function fromBase64Url(b64url: string): Uint8Array<ArrayBuffer>;
|
|
11
|
+
//# sourceMappingURL=bytes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/internal/bytes.ts"],"names":[],"mappings":"AAKA,wBAAgB,KAAK,CAAC,CAAC,EAAE,UAAU,GAAG,MAAM,CAE3C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAKlD;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAElD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAM/D;AAED;;;iCAGiC;AACjC,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAErD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAIrE"}
|