@bitboxswiss/bitbox-api 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +334 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +451 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/connect-simulator.d.ts +2 -0
- package/dist/internal/connect-simulator.d.ts.map +1 -0
- package/dist/internal/connect-simulator.js +25 -0
- package/dist/internal/connect-simulator.js.map +1 -0
- package/dist/internal/connect.d.ts +2 -0
- package/dist/internal/connect.d.ts.map +1 -0
- package/dist/internal/connect.js +54 -0
- package/dist/internal/connect.js.map +1 -0
- package/dist/internal/constants.d.ts +2 -0
- package/dist/internal/constants.d.ts.map +1 -0
- package/dist/internal/constants.js +16 -0
- package/dist/internal/constants.js.map +1 -0
- package/dist/internal/device.d.ts +5 -0
- package/dist/internal/device.d.ts.map +1 -0
- package/dist/internal/device.js +40 -0
- package/dist/internal/device.js.map +1 -0
- package/dist/internal/errors.d.ts +2 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/errors.js +220 -0
- package/dist/internal/errors.js.map +1 -0
- package/dist/internal/eth/antiklepto.d.ts +5 -0
- package/dist/internal/eth/antiklepto.d.ts.map +1 -0
- package/dist/internal/eth/antiklepto.js +59 -0
- package/dist/internal/eth/antiklepto.js.map +1 -0
- package/dist/internal/eth/eip712.d.ts +25 -0
- package/dist/internal/eth/eip712.d.ts.map +1 -0
- package/dist/internal/eth/eip712.js +303 -0
- package/dist/internal/eth/eip712.js.map +1 -0
- package/dist/internal/eth/keypath.d.ts +4 -0
- package/dist/internal/eth/keypath.d.ts.map +1 -0
- package/dist/internal/eth/keypath.js +47 -0
- package/dist/internal/eth/keypath.js.map +1 -0
- package/dist/internal/eth/methods.d.ts +10 -0
- package/dist/internal/eth/methods.d.ts.map +1 -0
- package/dist/internal/eth/methods.js +250 -0
- package/dist/internal/eth/methods.js.map +1 -0
- package/dist/internal/eth/query.d.ts +7 -0
- package/dist/internal/eth/query.d.ts.map +1 -0
- package/dist/internal/eth/query.js +43 -0
- package/dist/internal/eth/query.js.map +1 -0
- package/dist/internal/eth/streaming.d.ts +4 -0
- package/dist/internal/eth/streaming.d.ts.map +1 -0
- package/dist/internal/eth/streaming.js +21 -0
- package/dist/internal/eth/streaming.js.map +1 -0
- package/dist/internal/eth/version.d.ts +9 -0
- package/dist/internal/eth/version.d.ts.map +1 -0
- package/dist/internal/eth/version.js +10 -0
- package/dist/internal/eth/version.js.map +1 -0
- package/dist/internal/hww.d.ts +2 -0
- package/dist/internal/hww.d.ts.map +1 -0
- package/dist/internal/hww.js +236 -0
- package/dist/internal/hww.js.map +1 -0
- package/dist/internal/message-queue.d.ts +2 -0
- package/dist/internal/message-queue.d.ts.map +1 -0
- package/dist/internal/message-queue.js +29 -0
- package/dist/internal/message-queue.js.map +1 -0
- package/dist/internal/noise-config.d.ts +2 -0
- package/dist/internal/noise-config.d.ts.map +1 -0
- package/dist/internal/noise-config.js +160 -0
- package/dist/internal/noise-config.js.map +1 -0
- package/dist/internal/noise.d.ts +2 -0
- package/dist/internal/noise.d.ts.map +1 -0
- package/dist/internal/noise.js +270 -0
- package/dist/internal/noise.js.map +1 -0
- package/dist/internal/pairing.d.ts +2 -0
- package/dist/internal/pairing.d.ts.map +1 -0
- package/dist/internal/pairing.js +130 -0
- package/dist/internal/pairing.js.map +1 -0
- package/dist/internal/read-write.d.ts +2 -0
- package/dist/internal/read-write.d.ts.map +1 -0
- package/dist/internal/read-write.js +40 -0
- package/dist/internal/read-write.js.map +1 -0
- package/dist/internal/restore.d.ts +3 -0
- package/dist/internal/restore.d.ts.map +1 -0
- package/dist/internal/restore.js +27 -0
- package/dist/internal/restore.js.map +1 -0
- package/dist/internal/transport-bridge.d.ts +2 -0
- package/dist/internal/transport-bridge.d.ts.map +1 -0
- package/dist/internal/transport-bridge.js +97 -0
- package/dist/internal/transport-bridge.js.map +1 -0
- package/dist/internal/transport-simulator.d.ts +2 -0
- package/dist/internal/transport-simulator.d.ts.map +1 -0
- package/dist/internal/transport-simulator.js +83 -0
- package/dist/internal/transport-simulator.js.map +1 -0
- package/dist/internal/transport-webhid.d.ts +2 -0
- package/dist/internal/transport-webhid.d.ts.map +1 -0
- package/dist/internal/transport-webhid.js +69 -0
- package/dist/internal/transport-webhid.js.map +1 -0
- package/dist/internal/u2f-framing.d.ts +2 -0
- package/dist/internal/u2f-framing.d.ts.map +1 -0
- package/dist/internal/u2f-framing.js +155 -0
- package/dist/internal/u2f-framing.js.map +1 -0
- package/dist/internal/utils.d.ts +2 -0
- package/dist/internal/utils.d.ts.map +1 -0
- package/dist/internal/utils.js +71 -0
- package/dist/internal/utils.js.map +1 -0
- package/dist/proto/gen/antiklepto_pb.d.ts +49 -0
- package/dist/proto/gen/antiklepto_pb.d.ts.map +1 -0
- package/dist/proto/gen/antiklepto_pb.js +22 -0
- package/dist/proto/gen/antiklepto_pb.js.map +1 -0
- package/dist/proto/gen/backup_commands_pb.d.ts +124 -0
- package/dist/proto/gen/backup_commands_pb.d.ts.map +1 -0
- package/dist/proto/gen/backup_commands_pb.js +42 -0
- package/dist/proto/gen/backup_commands_pb.js.map +1 -0
- package/dist/proto/gen/backup_pb.d.ts +150 -0
- package/dist/proto/gen/backup_pb.d.ts.map +1 -0
- package/dist/proto/gen/backup_pb.js +46 -0
- package/dist/proto/gen/backup_pb.js.map +1 -0
- package/dist/proto/gen/bitbox02_system_pb.d.ts +207 -0
- package/dist/proto/gen/bitbox02_system_pb.d.ts.map +1 -0
- package/dist/proto/gen/bitbox02_system_pb.js +80 -0
- package/dist/proto/gen/bitbox02_system_pb.js.map +1 -0
- package/dist/proto/gen/bluetooth_pb.d.ts +135 -0
- package/dist/proto/gen/bluetooth_pb.d.ts.map +1 -0
- package/dist/proto/gen/bluetooth_pb.js +42 -0
- package/dist/proto/gen/bluetooth_pb.js.map +1 -0
- package/dist/proto/gen/btc_pb.d.ts +1166 -0
- package/dist/proto/gen/btc_pb.d.ts.map +1 -0
- package/dist/proto/gen/btc_pb.js +442 -0
- package/dist/proto/gen/btc_pb.js.map +1 -0
- package/dist/proto/gen/cardano_pb.d.ts +504 -0
- package/dist/proto/gen/cardano_pb.d.ts.map +1 -0
- package/dist/proto/gen/cardano_pb.js +142 -0
- package/dist/proto/gen/cardano_pb.js.map +1 -0
- package/dist/proto/gen/common_pb.d.ts +129 -0
- package/dist/proto/gen/common_pb.d.ts.map +1 -0
- package/dist/proto/gen/common_pb.js +42 -0
- package/dist/proto/gen/common_pb.js.map +1 -0
- package/dist/proto/gen/eth_pb.d.ts +662 -0
- package/dist/proto/gen/eth_pb.d.ts.map +1 -0
- package/dist/proto/gen/eth_pb.js +221 -0
- package/dist/proto/gen/eth_pb.js.map +1 -0
- package/dist/proto/gen/hww_pb.d.ts +349 -0
- package/dist/proto/gen/hww_pb.d.ts.map +1 -0
- package/dist/proto/gen/hww_pb.js +38 -0
- package/dist/proto/gen/hww_pb.js.map +1 -0
- package/dist/proto/gen/keystore_pb.d.ts +108 -0
- package/dist/proto/gen/keystore_pb.d.ts.map +1 -0
- package/dist/proto/gen/keystore_pb.js +33 -0
- package/dist/proto/gen/keystore_pb.js.map +1 -0
- package/dist/proto/gen/mnemonic_pb.d.ts +48 -0
- package/dist/proto/gen/mnemonic_pb.d.ts.map +1 -0
- package/dist/proto/gen/mnemonic_pb.js +22 -0
- package/dist/proto/gen/mnemonic_pb.js.map +1 -0
- package/dist/proto/gen/perform_attestation_pb.d.ts +57 -0
- package/dist/proto/gen/perform_attestation_pb.d.ts.map +1 -0
- package/dist/proto/gen/perform_attestation_pb.js +17 -0
- package/dist/proto/gen/perform_attestation_pb.js.map +1 -0
- package/dist/proto/gen/system_pb.d.ts +38 -0
- package/dist/proto/gen/system_pb.d.ts.map +1 -0
- package/dist/proto/gen/system_pb.js +30 -0
- package/dist/proto/gen/system_pb.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { x25519 } from '@noble/curves/ed25519';
|
|
3
|
+
import { chacha20poly1305 } from '@noble/ciphers/chacha';
|
|
4
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
5
|
+
import { hkdf } from '@noble/hashes/hkdf';
|
|
6
|
+
import { concatBytes, utf8ToBytes } from './utils.js';
|
|
7
|
+
const PROTOCOL_NAME = 'Noise_XX_25519_ChaChaPoly_SHA256';
|
|
8
|
+
const PROTOCOL_NAME_BYTES = utf8ToBytes(PROTOCOL_NAME);
|
|
9
|
+
const DHLEN = 32;
|
|
10
|
+
const HASHLEN = 32;
|
|
11
|
+
const TAGLEN = 16;
|
|
12
|
+
const EMPTY = new Uint8Array(0);
|
|
13
|
+
const XX_PATTERNS = [
|
|
14
|
+
['e'],
|
|
15
|
+
['e', 'ee', 's', 'es'],
|
|
16
|
+
['s', 'se'],
|
|
17
|
+
];
|
|
18
|
+
/** @internal */
|
|
19
|
+
export class NoiseProtocolError extends Error {
|
|
20
|
+
code = 'noise';
|
|
21
|
+
constructor(message) {
|
|
22
|
+
super(message);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function noiseError(message) {
|
|
26
|
+
return new NoiseProtocolError(message);
|
|
27
|
+
}
|
|
28
|
+
function keyPairFromPrivateKey(privateKey) {
|
|
29
|
+
try {
|
|
30
|
+
return { privateKey, publicKey: x25519.getPublicKey(privateKey) };
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw noiseError('invalid Noise private key');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function dh(local, remotePub) {
|
|
37
|
+
try {
|
|
38
|
+
return x25519.getSharedSecret(local.privateKey, remotePub);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw noiseError('Noise DH failed');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function nonce96(n) {
|
|
45
|
+
const buf = new Uint8Array(12);
|
|
46
|
+
new DataView(buf.buffer).setBigUint64(4, n, true);
|
|
47
|
+
return buf;
|
|
48
|
+
}
|
|
49
|
+
function noiseHkdf2(ck, ikm) {
|
|
50
|
+
const out = hkdf(sha256, ikm, ck, EMPTY, HASHLEN * 2);
|
|
51
|
+
return [out.slice(0, HASHLEN), out.slice(HASHLEN, HASHLEN * 2)];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Post-handshake AEAD state. Tracks a 64-bit nonce counter per direction;
|
|
55
|
+
* empty `k` means the cipher is uninitialized (early-handshake state, before
|
|
56
|
+
* the first `MixKey`).
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
export class CipherState {
|
|
60
|
+
k;
|
|
61
|
+
n = 0n;
|
|
62
|
+
constructor(k) {
|
|
63
|
+
if (k !== undefined) {
|
|
64
|
+
this.k = k;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
hasKey() {
|
|
68
|
+
return this.k !== undefined;
|
|
69
|
+
}
|
|
70
|
+
encryptWithAd(ad, plaintext) {
|
|
71
|
+
if (this.k === undefined) {
|
|
72
|
+
return plaintext;
|
|
73
|
+
}
|
|
74
|
+
let ct;
|
|
75
|
+
try {
|
|
76
|
+
ct = chacha20poly1305(this.k, nonce96(this.n), ad).encrypt(plaintext);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
throw noiseError('noise encryption failed');
|
|
80
|
+
}
|
|
81
|
+
this.n += 1n;
|
|
82
|
+
return ct;
|
|
83
|
+
}
|
|
84
|
+
decryptWithAd(ad, ciphertext) {
|
|
85
|
+
if (this.k === undefined) {
|
|
86
|
+
return ciphertext;
|
|
87
|
+
}
|
|
88
|
+
let pt;
|
|
89
|
+
try {
|
|
90
|
+
pt = chacha20poly1305(this.k, nonce96(this.n), ad).decrypt(ciphertext);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
throw noiseError('noise decryption failed');
|
|
94
|
+
}
|
|
95
|
+
this.n += 1n;
|
|
96
|
+
return pt;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
class SymmetricState {
|
|
100
|
+
ck;
|
|
101
|
+
h;
|
|
102
|
+
cipher;
|
|
103
|
+
constructor() {
|
|
104
|
+
this.h = new Uint8Array(HASHLEN);
|
|
105
|
+
this.h.set(PROTOCOL_NAME_BYTES);
|
|
106
|
+
this.ck = new Uint8Array(this.h);
|
|
107
|
+
this.cipher = new CipherState();
|
|
108
|
+
}
|
|
109
|
+
hasCipherKey() {
|
|
110
|
+
return this.cipher.hasKey();
|
|
111
|
+
}
|
|
112
|
+
mixHash(data) {
|
|
113
|
+
this.h = sha256(concatBytes(this.h, data));
|
|
114
|
+
}
|
|
115
|
+
mixKey(ikm) {
|
|
116
|
+
const [newCk, newK] = noiseHkdf2(this.ck, ikm);
|
|
117
|
+
this.ck = newCk;
|
|
118
|
+
this.cipher = new CipherState(newK);
|
|
119
|
+
}
|
|
120
|
+
encryptAndHash(plaintext) {
|
|
121
|
+
const ct = this.cipher.encryptWithAd(this.h, plaintext);
|
|
122
|
+
this.mixHash(ct);
|
|
123
|
+
return ct;
|
|
124
|
+
}
|
|
125
|
+
decryptAndHash(ct) {
|
|
126
|
+
const pt = this.cipher.decryptWithAd(this.h, ct);
|
|
127
|
+
this.mixHash(ct);
|
|
128
|
+
return pt;
|
|
129
|
+
}
|
|
130
|
+
split() {
|
|
131
|
+
const [k1, k2] = noiseHkdf2(this.ck, EMPTY);
|
|
132
|
+
return [new CipherState(k1), new CipherState(k2)];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* `Noise_XX_25519_ChaChaPoly_SHA256` handshake state. The class supports both
|
|
137
|
+
* initiator and responder roles so the same code can drive production
|
|
138
|
+
* handshakes (initiator) and unit-test peer simulation (responder).
|
|
139
|
+
* @internal
|
|
140
|
+
*/
|
|
141
|
+
export class NoiseXX {
|
|
142
|
+
initiator;
|
|
143
|
+
s;
|
|
144
|
+
e;
|
|
145
|
+
re;
|
|
146
|
+
rs;
|
|
147
|
+
symmetric = new SymmetricState();
|
|
148
|
+
patternIndex = 0;
|
|
149
|
+
fixedEphemeralPrivateKey;
|
|
150
|
+
constructor(initiator, staticPrivateKey, options) {
|
|
151
|
+
this.initiator = initiator;
|
|
152
|
+
this.s = keyPairFromPrivateKey(staticPrivateKey);
|
|
153
|
+
this.symmetric.mixHash(PROTOCOL_NAME_BYTES);
|
|
154
|
+
if (options?.fixedEphemeralPrivateKey !== undefined) {
|
|
155
|
+
this.fixedEphemeralPrivateKey = options.fixedEphemeralPrivateKey;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/** Compose the next outgoing handshake message; payload is empty. */
|
|
159
|
+
writeMessage() {
|
|
160
|
+
const tokens = XX_PATTERNS[this.patternIndex];
|
|
161
|
+
if (tokens === undefined) {
|
|
162
|
+
throw new Error('handshake already complete');
|
|
163
|
+
}
|
|
164
|
+
const isOurTurn = (this.patternIndex % 2 === 0) === this.initiator;
|
|
165
|
+
if (!isOurTurn) {
|
|
166
|
+
throw new Error('not the local peer turn to write');
|
|
167
|
+
}
|
|
168
|
+
let buf = EMPTY;
|
|
169
|
+
for (const tok of tokens) {
|
|
170
|
+
if (tok === 'e') {
|
|
171
|
+
const sk = this.fixedEphemeralPrivateKey ?? x25519.utils.randomPrivateKey();
|
|
172
|
+
this.e = keyPairFromPrivateKey(sk);
|
|
173
|
+
buf = concatBytes(buf, this.e.publicKey);
|
|
174
|
+
this.symmetric.mixHash(this.e.publicKey);
|
|
175
|
+
}
|
|
176
|
+
else if (tok === 's') {
|
|
177
|
+
buf = concatBytes(buf, this.symmetric.encryptAndHash(this.s.publicKey));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
this.symmetric.mixKey(this.tokenDh(tok));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
buf = concatBytes(buf, this.symmetric.encryptAndHash(EMPTY));
|
|
184
|
+
this.patternIndex += 1;
|
|
185
|
+
return buf;
|
|
186
|
+
}
|
|
187
|
+
/** Process an incoming handshake message; payload is expected empty. */
|
|
188
|
+
readMessage(message) {
|
|
189
|
+
const tokens = XX_PATTERNS[this.patternIndex];
|
|
190
|
+
if (tokens === undefined) {
|
|
191
|
+
throw new Error('handshake already complete');
|
|
192
|
+
}
|
|
193
|
+
const isOurTurn = (this.patternIndex % 2 === 0) === this.initiator;
|
|
194
|
+
if (isOurTurn) {
|
|
195
|
+
throw new Error('not the local peer turn to read');
|
|
196
|
+
}
|
|
197
|
+
let cursor = 0;
|
|
198
|
+
for (const tok of tokens) {
|
|
199
|
+
if (tok === 'e') {
|
|
200
|
+
if (message.length - cursor < DHLEN) {
|
|
201
|
+
throw noiseError('handshake message truncated at remote ephemeral key');
|
|
202
|
+
}
|
|
203
|
+
this.re = message.slice(cursor, cursor + DHLEN);
|
|
204
|
+
cursor += DHLEN;
|
|
205
|
+
this.symmetric.mixHash(this.re);
|
|
206
|
+
}
|
|
207
|
+
else if (tok === 's') {
|
|
208
|
+
const len = this.symmetric.hasCipherKey() ? DHLEN + TAGLEN : DHLEN;
|
|
209
|
+
if (message.length - cursor < len) {
|
|
210
|
+
throw noiseError('handshake message truncated at remote static key');
|
|
211
|
+
}
|
|
212
|
+
const slice = message.slice(cursor, cursor + len);
|
|
213
|
+
cursor += len;
|
|
214
|
+
this.rs = this.symmetric.decryptAndHash(slice);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
this.symmetric.mixKey(this.tokenDh(tok));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
this.symmetric.decryptAndHash(message.slice(cursor));
|
|
221
|
+
this.patternIndex += 1;
|
|
222
|
+
}
|
|
223
|
+
isComplete() {
|
|
224
|
+
return this.patternIndex >= XX_PATTERNS.length;
|
|
225
|
+
}
|
|
226
|
+
finalize() {
|
|
227
|
+
if (!this.isComplete()) {
|
|
228
|
+
throw new Error('handshake not complete');
|
|
229
|
+
}
|
|
230
|
+
if (this.rs === undefined) {
|
|
231
|
+
throw new Error('remote static key was not received');
|
|
232
|
+
}
|
|
233
|
+
const [c1, c2] = this.symmetric.split();
|
|
234
|
+
return {
|
|
235
|
+
hash: this.symmetric.h,
|
|
236
|
+
remoteStaticPubkey: this.rs,
|
|
237
|
+
send: this.initiator ? c1 : c2,
|
|
238
|
+
recv: this.initiator ? c2 : c1,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
tokenDh(token) {
|
|
242
|
+
if (token === 'ee') {
|
|
243
|
+
return dh(this.e, this.re);
|
|
244
|
+
}
|
|
245
|
+
if (token === 'ss') {
|
|
246
|
+
return dh(this.s, this.rs);
|
|
247
|
+
}
|
|
248
|
+
if (token === 'es') {
|
|
249
|
+
return this.initiator ? dh(this.e, this.rs) : dh(this.s, this.re);
|
|
250
|
+
}
|
|
251
|
+
if (token === 'se') {
|
|
252
|
+
return this.initiator ? dh(this.s, this.re) : dh(this.e, this.rs);
|
|
253
|
+
}
|
|
254
|
+
throw new Error(`unsupported handshake token: ${token}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/** Generate a new x25519 static keypair private key (32 bytes). @internal */
|
|
258
|
+
export function generateStaticPrivateKey() {
|
|
259
|
+
return x25519.utils.randomPrivateKey();
|
|
260
|
+
}
|
|
261
|
+
/** Derive the public key for an x25519 private key. @internal */
|
|
262
|
+
export function publicKeyFromPrivateKey(privateKey) {
|
|
263
|
+
try {
|
|
264
|
+
return x25519.getPublicKey(privateKey);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
throw noiseError('invalid Noise private key');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=noise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noise.js","sourceRoot":"","sources":["../../src/internal/noise.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../src/internal/pairing.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { NoiseXX, generateStaticPrivateKey, } from './noise.js';
|
|
3
|
+
import { addDeviceStaticPubkey, containsDeviceStaticPubkey, } from './noise-config.js';
|
|
4
|
+
import { CODE_UNEXPECTED_RESPONSE, makeError } from './errors.js';
|
|
5
|
+
const OP_UNLOCK = 0x75; // 'u'
|
|
6
|
+
const OP_I_CAN_HAS_HANDSHAEK = 0x68; // 'h'
|
|
7
|
+
const OP_HER_COMEZ_TEH_HANDSHAEK = 0x48; // 'H'
|
|
8
|
+
const OP_I_CAN_HAS_PAIRIN_VERIFICASHUN = 0x76; // 'v'
|
|
9
|
+
const OP_NOISE_MSG = 0x6e; // 'n'
|
|
10
|
+
const RESPONSE_SUCCESS = 0x00;
|
|
11
|
+
const DEVICE_PAIRING_REQUIRED = 0x01;
|
|
12
|
+
const EMPTY = new Uint8Array(0);
|
|
13
|
+
/** @internal */
|
|
14
|
+
export class NoiseError extends Error {
|
|
15
|
+
code;
|
|
16
|
+
constructor(code, message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.code = code;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const RFC4648_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
|
22
|
+
function base32Encode(bytes) {
|
|
23
|
+
let bits = 0;
|
|
24
|
+
let value = 0;
|
|
25
|
+
let out = '';
|
|
26
|
+
for (const b of bytes) {
|
|
27
|
+
value = (value << 8) | b;
|
|
28
|
+
bits += 8;
|
|
29
|
+
while (bits >= 5) {
|
|
30
|
+
bits -= 5;
|
|
31
|
+
out += RFC4648_ALPHABET[(value >> bits) & 0x1f];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (bits > 0) {
|
|
35
|
+
out += RFC4648_ALPHABET[(value << (5 - bits)) & 0x1f];
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
/** @internal */
|
|
40
|
+
export function formatPairingCode(handshakeHash) {
|
|
41
|
+
const encoded = base32Encode(handshakeHash);
|
|
42
|
+
return `${encoded.slice(0, 5)} ${encoded.slice(5, 10)}\n${encoded.slice(10, 15)} ${encoded.slice(15, 20)}`;
|
|
43
|
+
}
|
|
44
|
+
async function handshakeQuery(hww, message) {
|
|
45
|
+
const framed = new Uint8Array(message.length + 1);
|
|
46
|
+
framed[0] = OP_HER_COMEZ_TEH_HANDSHAEK;
|
|
47
|
+
framed.set(message, 1);
|
|
48
|
+
const response = await hww.query(framed);
|
|
49
|
+
if (response.length < 1 || response[0] !== RESPONSE_SUCCESS) {
|
|
50
|
+
throw new NoiseError('noise', 'handshake response was not RESPONSE_SUCCESS');
|
|
51
|
+
}
|
|
52
|
+
return response.slice(1);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Drives `OP_UNLOCK`, `OP_I_CAN_HAS_HANDSHAEK`, and the three Noise XX message
|
|
56
|
+
* exchanges. Returns the in-flight pairing state — caller decides whether to
|
|
57
|
+
* surface a pairing code to the user before completing pairing.
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
export async function performHandshake(hww, config) {
|
|
61
|
+
await hww.query(new Uint8Array([OP_UNLOCK]));
|
|
62
|
+
let configData = config.read();
|
|
63
|
+
let appStaticPrivkey;
|
|
64
|
+
if (configData.appStaticPrivkey !== undefined) {
|
|
65
|
+
appStaticPrivkey = configData.appStaticPrivkey;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
appStaticPrivkey = generateStaticPrivateKey();
|
|
69
|
+
configData = {
|
|
70
|
+
appStaticPrivkey,
|
|
71
|
+
deviceStaticPubkeys: configData.deviceStaticPubkeys,
|
|
72
|
+
};
|
|
73
|
+
config.store(configData);
|
|
74
|
+
}
|
|
75
|
+
const handshakeStart = await hww.query(new Uint8Array([OP_I_CAN_HAS_HANDSHAEK]));
|
|
76
|
+
if (handshakeStart.length !== 1 || handshakeStart[0] !== RESPONSE_SUCCESS) {
|
|
77
|
+
throw new NoiseError('noise', 'device did not accept handshake start');
|
|
78
|
+
}
|
|
79
|
+
const xx = new NoiseXX(true, appStaticPrivkey);
|
|
80
|
+
const m1 = xx.writeMessage();
|
|
81
|
+
const m2 = await handshakeQuery(hww, m1);
|
|
82
|
+
xx.readMessage(m2);
|
|
83
|
+
const m3 = xx.writeMessage();
|
|
84
|
+
const finishResponse = await handshakeQuery(hww, m3);
|
|
85
|
+
const finalState = xx.finalize();
|
|
86
|
+
const cached = config.read();
|
|
87
|
+
const appWantsVerification = !containsDeviceStaticPubkey(cached, finalState.remoteStaticPubkey);
|
|
88
|
+
const deviceWantsVerification = finishResponse.length === 1 && finishResponse[0] === DEVICE_PAIRING_REQUIRED;
|
|
89
|
+
const pairingCode = appWantsVerification || deviceWantsVerification
|
|
90
|
+
? formatPairingCode(finalState.hash)
|
|
91
|
+
: undefined;
|
|
92
|
+
return { hww, config, finalState, pairingCode };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Completes pairing: if a pairing code was shown the host issues
|
|
96
|
+
* `OP_I_CAN_HAS_PAIRIN_VERIFICASHUN` and persists the device's static pubkey on
|
|
97
|
+
* confirmation. Returns an encrypted-channel adapter for subsequent queries.
|
|
98
|
+
* @internal
|
|
99
|
+
*/
|
|
100
|
+
export async function completePairing(state) {
|
|
101
|
+
if (state.pairingCode !== undefined) {
|
|
102
|
+
const verify = await state.hww.query(new Uint8Array([OP_I_CAN_HAS_PAIRIN_VERIFICASHUN]));
|
|
103
|
+
if (verify.length !== 1 || verify[0] !== RESPONSE_SUCCESS) {
|
|
104
|
+
throw new NoiseError('pairing-rejected', 'pairing code rejected by user');
|
|
105
|
+
}
|
|
106
|
+
const cached = state.config.read();
|
|
107
|
+
const updated = addDeviceStaticPubkey(cached, state.finalState.remoteStaticPubkey);
|
|
108
|
+
if (updated !== cached) {
|
|
109
|
+
state.config.store(updated);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return makeEncryptedChannel(state.hww, state.finalState.send, state.finalState.recv);
|
|
113
|
+
}
|
|
114
|
+
/** @internal */
|
|
115
|
+
export function makeEncryptedChannel(hww, send, recv) {
|
|
116
|
+
return {
|
|
117
|
+
async query(plaintext) {
|
|
118
|
+
const ciphertext = send.encryptWithAd(EMPTY, plaintext);
|
|
119
|
+
const framed = new Uint8Array(ciphertext.length + 1);
|
|
120
|
+
framed[0] = OP_NOISE_MSG;
|
|
121
|
+
framed.set(ciphertext, 1);
|
|
122
|
+
const response = await hww.query(framed);
|
|
123
|
+
if (response.length < 1 || response[0] !== RESPONSE_SUCCESS) {
|
|
124
|
+
throw makeError(CODE_UNEXPECTED_RESPONSE, 'BitBox returned an unexpected response');
|
|
125
|
+
}
|
|
126
|
+
return recv.decryptWithAd(EMPTY, response.slice(1));
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=pairing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/internal/pairing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-write.d.ts","sourceRoot":"","sources":["../../src/internal/read-write.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Transport-layer errors. The shape `{ code, message }` is compatible with the
|
|
4
|
+
* public `Error` type in `src/index.ts`, so `ensureError` returns these as-is.
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export class TransportError extends Error {
|
|
8
|
+
code;
|
|
9
|
+
constructor(code, message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.name = 'TransportError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** @internal */
|
|
16
|
+
export async function query(rw, msg) {
|
|
17
|
+
rw.write(msg);
|
|
18
|
+
return rw.read();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Wraps a close callback so it fires exactly once. Clearing the reference
|
|
22
|
+
* after invocation prevents retained callbacks after teardown.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export function makeCloseGuard(cb) {
|
|
26
|
+
let fired = false;
|
|
27
|
+
let stored = cb;
|
|
28
|
+
return () => {
|
|
29
|
+
if (fired) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
fired = true;
|
|
33
|
+
const f = stored;
|
|
34
|
+
stored = undefined;
|
|
35
|
+
if (f !== undefined) {
|
|
36
|
+
f();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=read-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-write.js","sourceRoot":"","sources":["../../src/internal/read-write.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAyBtC;;;;GAIG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACvC,IAAI,CAAqB;IAEzB,YAAY,IAAwB,EAAE,OAAe;QACnD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,gBAAgB;AAChB,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,EAAa,EAAE,GAAe;IACxD,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACd,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,EAAe;IAC5C,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,OAAO,GAAG,EAAE;QACV,IAAI,KAAK,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,KAAK,GAAG,IAAI,CAAC;QACb,MAAM,CAAC,GAAG,MAAM,CAAC;QACjB,MAAM,GAAG,SAAS,CAAC;QACnB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,CAAC,EAAE,CAAC;QACN,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.d.ts","sourceRoot":"","sources":["../../src/internal/restore.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIrD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBlF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { create } from '@bufbuild/protobuf';
|
|
3
|
+
import { RequestSchema } from '../proto/gen/hww_pb.js';
|
|
4
|
+
import { RestoreFromMnemonicRequestSchema } from '../proto/gen/mnemonic_pb.js';
|
|
5
|
+
import { query } from './eth/query.js';
|
|
6
|
+
import { CODE_UNEXPECTED_RESPONSE, makeError } from './errors.js';
|
|
7
|
+
const UINT32_MAX = 0xffffffff;
|
|
8
|
+
export async function restoreFromMnemonic(channel) {
|
|
9
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
10
|
+
const timestamp = nowSec >= 0 && nowSec <= UINT32_MAX ? nowSec : 0;
|
|
11
|
+
// getTimezoneOffset returns minutes WEST of UTC; firmware expects seconds EAST of UTC.
|
|
12
|
+
const timezoneOffset = -new Date().getTimezoneOffset() * 60;
|
|
13
|
+
const request = create(RequestSchema, {
|
|
14
|
+
request: {
|
|
15
|
+
case: 'restoreFromMnemonic',
|
|
16
|
+
value: create(RestoreFromMnemonicRequestSchema, {
|
|
17
|
+
timestamp,
|
|
18
|
+
timezoneOffset,
|
|
19
|
+
}),
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const response = await query(channel, request);
|
|
23
|
+
if (response.response.case !== 'success') {
|
|
24
|
+
throw makeError(CODE_UNEXPECTED_RESPONSE, 'unexpected response to restoreFromMnemonic');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=restore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../src/internal/restore.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gCAAgC,EAAE,MAAM,6BAA6B,CAAC;AAC/E,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGlE,MAAM,UAAU,GAAG,UAAU,CAAC;AAE9B,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAyB;IACjE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,MAAM,IAAI,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,uFAAuF;IACvF,MAAM,cAAc,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC;IAE5D,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,EAAE;QACpC,OAAO,EAAE;YACP,IAAI,EAAE,qBAAqB;YAC3B,KAAK,EAAE,MAAM,CAAC,gCAAgC,EAAE;gBAC9C,SAAS;gBACT,cAAc;aACf,CAAC;SACH;KACF,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACzC,MAAM,SAAS,CAAC,wBAAwB,EAAE,4CAA4C,CAAC,CAAC;IAC1F,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-bridge.d.ts","sourceRoot":"","sources":["../../src/internal/transport-bridge.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { BRIDGE_BASE_URL, BRIDGE_WS_BASE_URL } from './constants.js';
|
|
3
|
+
import { MessageQueue } from './message-queue.js';
|
|
4
|
+
import { TransportError, makeCloseGuard } from './read-write.js';
|
|
5
|
+
import { sleep } from './utils.js';
|
|
6
|
+
const DISCOVERY_ATTEMPTS = 10;
|
|
7
|
+
const DISCOVERY_SLEEP_MS = 100;
|
|
8
|
+
// Values of WebSocket.readyState. Mirror the DOM constants so tests don't
|
|
9
|
+
// need to depend on `globalThis.WebSocket`.
|
|
10
|
+
/** @internal */
|
|
11
|
+
export const WS_OPEN = 1;
|
|
12
|
+
function defaultDeps() {
|
|
13
|
+
const f = globalThis.fetch;
|
|
14
|
+
const WS = globalThis.WebSocket;
|
|
15
|
+
if (f === undefined || WS === undefined) {
|
|
16
|
+
throw new TransportError('bridge', 'fetch or WebSocket is not available');
|
|
17
|
+
}
|
|
18
|
+
// Bind to globalThis: in browsers, `fetch` must be invoked with the realm's
|
|
19
|
+
// global as the receiver. Calling it as a method on a captured object (e.g.
|
|
20
|
+
// `deps.fetch(url)`) triggers `TypeError: Illegal invocation` before any
|
|
21
|
+
// network request is sent.
|
|
22
|
+
return { fetch: f.bind(globalThis), WebSocket: WS };
|
|
23
|
+
}
|
|
24
|
+
async function discoverDevicePath(deps) {
|
|
25
|
+
for (let i = 0; i < DISCOVERY_ATTEMPTS; i += 1) {
|
|
26
|
+
let resp;
|
|
27
|
+
try {
|
|
28
|
+
resp = await deps.fetch(`${BRIDGE_BASE_URL}/devices`);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new TransportError('bridge', 'BitBoxBridge not found.');
|
|
32
|
+
}
|
|
33
|
+
if (resp.status === 403) {
|
|
34
|
+
throw new TransportError('bridge', 'Origin not whitelisted.');
|
|
35
|
+
}
|
|
36
|
+
if (!resp.ok) {
|
|
37
|
+
throw new TransportError('bridge', 'Unexpected bridge connection error.');
|
|
38
|
+
}
|
|
39
|
+
const body = (await resp.json());
|
|
40
|
+
const devices = body.devices ?? [];
|
|
41
|
+
if (devices.length === 1) {
|
|
42
|
+
return devices[0].path;
|
|
43
|
+
}
|
|
44
|
+
await sleep(DISCOVERY_SLEEP_MS);
|
|
45
|
+
}
|
|
46
|
+
throw new TransportError('bridge', 'Expected exactly one BitBox02. If one is connected, it might already have an open connection another app. If so, please close the other app first.');
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Open a BitBox02 via the BitBoxBridge service. Discovery polls
|
|
50
|
+
* `http://127.0.0.1:8178/api/v1/devices` for exactly one device, then opens
|
|
51
|
+
* a WebSocket at `ws://127.0.0.1:8178/api/v1/socket/{path}`. The Rust
|
|
52
|
+
* reference uses `localhost` for the HTTP probe; we deviate to `127.0.0.1`
|
|
53
|
+
* because the bridge only binds the IPv4 loopback, and `localhost` resolves
|
|
54
|
+
* to `::1` first on systems where /etc/hosts maps both, breaking the fetch.
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
export async function openBridge(onCloseCb, deps = defaultDeps()) {
|
|
58
|
+
const devicePath = await discoverDevicePath(deps);
|
|
59
|
+
const socket = new deps.WebSocket(BRIDGE_WS_BASE_URL + devicePath);
|
|
60
|
+
socket.binaryType = 'arraybuffer';
|
|
61
|
+
const queue = new MessageQueue();
|
|
62
|
+
const guard = makeCloseGuard(onCloseCb);
|
|
63
|
+
let opened = false;
|
|
64
|
+
socket.onmessage = (ev) => {
|
|
65
|
+
queue.push(new Uint8Array(ev.data));
|
|
66
|
+
};
|
|
67
|
+
await new Promise((resolve, reject) => {
|
|
68
|
+
socket.onopen = () => {
|
|
69
|
+
opened = true;
|
|
70
|
+
resolve();
|
|
71
|
+
};
|
|
72
|
+
socket.onclose = () => {
|
|
73
|
+
guard();
|
|
74
|
+
if (!opened) {
|
|
75
|
+
reject(new TransportError('bridge', 'bridge socket closed before opening'));
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
socket.onerror = () => {
|
|
79
|
+
reject(new TransportError('bridge', 'Your BitBox02 is busy.'));
|
|
80
|
+
};
|
|
81
|
+
});
|
|
82
|
+
return {
|
|
83
|
+
write(bytes) {
|
|
84
|
+
if (socket.readyState !== WS_OPEN) {
|
|
85
|
+
throw new TransportError('write', 'bridge socket is not open');
|
|
86
|
+
}
|
|
87
|
+
socket.send(bytes);
|
|
88
|
+
},
|
|
89
|
+
read() {
|
|
90
|
+
return queue.next();
|
|
91
|
+
},
|
|
92
|
+
close() {
|
|
93
|
+
socket.close();
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=transport-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-bridge.js","sourceRoot":"","sources":["../../src/internal/transport-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-simulator.d.ts","sourceRoot":"","sources":["../../src/internal/transport-simulator.ts"],"names":[],"mappings":""}
|