smoldot 1.0.11 → 1.0.13

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.
@@ -120,12 +120,11 @@ export interface ConnectionConfig {
120
120
  type: 'single-stream';
121
121
  handshake: 'multistream-select-noise-yamux';
122
122
  initialWritableBytes: number;
123
- writeClosable: boolean;
124
123
  } | {
125
124
  type: 'multi-stream';
126
125
  handshake: 'webrtc';
127
- localTlsCertificateMultihash: Uint8Array;
128
- remoteTlsCertificateMultihash: Uint8Array;
126
+ localTlsCertificateSha256: Uint8Array;
127
+ remoteTlsCertificateSha256: Uint8Array;
129
128
  }) => void;
130
129
  /**
131
130
  * Callback called when the connection transitions to the `Reset` state.
@@ -284,8 +284,11 @@ export function start(options, wasmModule, platformBindings) {
284
284
  if (jsonRpcMaxSubscriptions > 0xffffffff) {
285
285
  jsonRpcMaxSubscriptions = 0xffffffff;
286
286
  }
287
+ // Sanitize `databaseContent`.
288
+ if (options.databaseContent !== undefined && typeof options.databaseContent !== 'string')
289
+ throw new AddChainError("`databaseContent` is not a string");
287
290
  const promise = new Promise((resolve) => state.addChainResults.push(resolve));
288
- state.instance.instance.addChain(options.chainSpec, typeof options.databaseContent === 'string' ? options.databaseContent : "", potentialRelayChainsIds, !!options.disableJsonRpc, jsonRpcMaxPendingRequests, jsonRpcMaxSubscriptions);
291
+ state.instance.instance.addChain(options.chainSpec, options.databaseContent || "", potentialRelayChainsIds, !!options.disableJsonRpc, jsonRpcMaxPendingRequests, jsonRpcMaxSubscriptions);
289
292
  const outcome = yield promise;
290
293
  if (!outcome.success)
291
294
  throw new AddChainError(outcome.error);
@@ -84,10 +84,10 @@ export type ParsedMultiaddr = {
84
84
  url: string;
85
85
  } | {
86
86
  ty: "webrtc";
87
- targetPort: string;
87
+ targetPort: number;
88
88
  ipVersion: string;
89
89
  targetIp: string;
90
- remoteCertMultibase: string;
90
+ remoteTlsCertificateSha256: Uint8Array;
91
91
  };
92
92
  export interface Instance {
93
93
  request: (request: string, chainId: number) => number;
@@ -107,12 +107,11 @@ export interface Instance {
107
107
  type: 'single-stream';
108
108
  handshake: 'multistream-select-noise-yamux';
109
109
  initialWritableBytes: number;
110
- writeClosable: boolean;
111
110
  } | {
112
111
  type: 'multi-stream';
113
112
  handshake: 'webrtc';
114
- localTlsCertificateMultihash: Uint8Array;
115
- remoteTlsCertificateMultihash: Uint8Array;
113
+ localTlsCertificateSha256: Uint8Array;
114
+ remoteTlsCertificateSha256: Uint8Array;
116
115
  }) => void;
117
116
  connectionReset: (connectionId: number, message: string) => void;
118
117
  streamWritableBytes: (connectionId: number, numExtra: number, streamId?: number) => void;
@@ -133,10 +132,3 @@ export interface Instance {
133
132
  * isn't sanitized. In other words, you know what you're doing.
134
133
  */
135
134
  export declare function startLocalInstance(config: Config, wasmModule: WebAssembly.Module, eventCallback: (event: Event) => void): Promise<Instance>;
136
- export declare function parseMultiaddr(address: string, forbidTcp: boolean, forbidWs: boolean, forbidNonLocalWs: boolean, forbidWss: boolean, forbidWebRtc: boolean): {
137
- success: true;
138
- address: ParsedMultiaddr;
139
- } | {
140
- success: false;
141
- error: string;
142
- };
@@ -124,26 +124,90 @@ export function startLocalInstance(config, wasmModule, eventCallback) {
124
124
  }, ms);
125
125
  }
126
126
  },
127
+ // Must indicate whether the given connection type is supported.
128
+ connection_type_supported: (ty) => {
129
+ // TODO: consider extracting config options so user can't change the fields dynamically
130
+ switch (ty) {
131
+ case 0:
132
+ case 1:
133
+ case 2: {
134
+ return config.forbidTcp ? 0 : 1;
135
+ }
136
+ case 4:
137
+ case 5:
138
+ case 6: {
139
+ return config.forbidNonLocalWs ? 0 : 1;
140
+ }
141
+ case 7: {
142
+ return config.forbidWs ? 0 : 1;
143
+ }
144
+ case 14: {
145
+ return config.forbidWss ? 0 : 1;
146
+ }
147
+ case 16:
148
+ case 17: {
149
+ return config.forbidWebRtc ? 0 : 1;
150
+ }
151
+ default:
152
+ // Indicates a bug somewhere.
153
+ throw new Error("Invalid connection type passed to `connection_type_supported`");
154
+ }
155
+ },
127
156
  // Must create a new connection object. This implementation stores the created object in
128
157
  // `connections`.
129
- connection_new: (connectionId, addrPtr, addrLen, errorBufferIndexPtr) => {
158
+ connection_new: (connectionId, addrPtr, addrLen) => {
130
159
  const instance = state.instance;
160
+ const mem = new Uint8Array(instance.exports.memory.buffer);
131
161
  addrPtr >>>= 0;
132
162
  addrLen >>>= 0;
133
- errorBufferIndexPtr >>>= 0;
134
- const address = buffer.utf8BytesToString(new Uint8Array(instance.exports.memory.buffer), addrPtr, addrLen);
135
- // TODO: consider extracting config options so user can't change the fields dynamically
136
- const result = parseMultiaddr(address, config.forbidTcp, config.forbidWs, config.forbidNonLocalWs, config.forbidWss, config.forbidWebRtc);
137
- if (result.success) {
138
- eventCallback({ ty: "new-connection", connectionId, address: result.address });
139
- return 0;
140
- }
141
- else {
142
- const mem = new Uint8Array(instance.exports.memory.buffer);
143
- state.bufferIndices[0] = new TextEncoder().encode(result.error);
144
- buffer.writeUInt32LE(mem, errorBufferIndexPtr, 0);
145
- return 1;
163
+ let address;
164
+ switch (buffer.readUInt8(mem, addrPtr)) {
165
+ case 0:
166
+ case 1:
167
+ case 2: {
168
+ const port = buffer.readUInt16BE(mem, addrPtr + 1);
169
+ const hostname = buffer.utf8BytesToString(mem, addrPtr + 3, addrLen - 3);
170
+ address = { ty: "tcp", port, hostname };
171
+ break;
172
+ }
173
+ case 4:
174
+ case 6: {
175
+ const port = buffer.readUInt16BE(mem, addrPtr + 1);
176
+ const hostname = buffer.utf8BytesToString(mem, addrPtr + 3, addrLen - 3);
177
+ address = { ty: "websocket", url: "ws://" + hostname + ":" + port };
178
+ break;
179
+ }
180
+ case 5: {
181
+ const port = buffer.readUInt16BE(mem, addrPtr + 1);
182
+ const hostname = buffer.utf8BytesToString(mem, addrPtr + 3, addrLen - 3);
183
+ address = { ty: "websocket", url: "ws://[" + hostname + "]:" + port };
184
+ break;
185
+ }
186
+ case 14: {
187
+ const port = buffer.readUInt16BE(mem, addrPtr + 1);
188
+ const hostname = buffer.utf8BytesToString(mem, addrPtr + 3, addrLen - 3);
189
+ address = { ty: "websocket", url: "wss://" + hostname + ":" + port };
190
+ break;
191
+ }
192
+ case 16: {
193
+ const targetPort = buffer.readUInt16BE(mem, addrPtr + 1);
194
+ const remoteTlsCertificateSha256 = mem.slice(addrPtr + 3, addrPtr + 35);
195
+ const targetIp = buffer.utf8BytesToString(mem, addrPtr + 35, addrLen - 3);
196
+ address = { ty: "webrtc", ipVersion: '4', remoteTlsCertificateSha256, targetIp, targetPort };
197
+ break;
198
+ }
199
+ case 17: {
200
+ const targetPort = buffer.readUInt16BE(mem, addrPtr + 1);
201
+ const remoteTlsCertificateSha256 = mem.slice(addrPtr + 3, addrPtr + 35);
202
+ const targetIp = buffer.utf8BytesToString(mem, addrPtr + 35, addrLen - 3);
203
+ address = { ty: "webrtc", ipVersion: '6', remoteTlsCertificateSha256, targetIp, targetPort };
204
+ break;
205
+ }
206
+ default:
207
+ // Indicates a bug somewhere.
208
+ throw new Error("Invalid encoded address passed to `connection_new`");
146
209
  }
210
+ eventCallback({ ty: "new-connection", connectionId, address });
147
211
  },
148
212
  // Must close and destroy the connection object.
149
213
  reset_connection: (connectionId) => {
@@ -473,14 +537,14 @@ export function startLocalInstance(config, wasmModule, eventCallback) {
473
537
  return;
474
538
  switch (info.type) {
475
539
  case 'single-stream': {
476
- state.instance.exports.connection_open_single_stream(connectionId, 0, info.initialWritableBytes, info.writeClosable ? 1 : 0);
540
+ state.instance.exports.connection_open_single_stream(connectionId, info.initialWritableBytes);
477
541
  break;
478
542
  }
479
543
  case 'multi-stream': {
480
- const handshakeTy = new Uint8Array(1 + info.localTlsCertificateMultihash.length + info.remoteTlsCertificateMultihash.length);
544
+ const handshakeTy = new Uint8Array(1 + info.localTlsCertificateSha256.length + info.remoteTlsCertificateSha256.length);
481
545
  buffer.writeUInt8(handshakeTy, 0, 0);
482
- handshakeTy.set(info.localTlsCertificateMultihash, 1);
483
- handshakeTy.set(info.remoteTlsCertificateMultihash, 1 + info.localTlsCertificateMultihash.length);
546
+ handshakeTy.set(info.localTlsCertificateSha256, 1);
547
+ handshakeTy.set(info.remoteTlsCertificateSha256, 1 + info.localTlsCertificateSha256.length);
484
548
  state.bufferIndices[0] = handshakeTy;
485
549
  state.instance.exports.connection_open_multi_stream(connectionId, 0);
486
550
  delete state.bufferIndices[0];
@@ -520,48 +584,3 @@ export function startLocalInstance(config, wasmModule, eventCallback) {
520
584
  };
521
585
  });
522
586
  }
523
- // TODO: consider moving this function somewhere else or something
524
- export function parseMultiaddr(address, forbidTcp, forbidWs, forbidNonLocalWs, forbidWss, forbidWebRtc) {
525
- const tcpParsed = address.match(/^\/(ip4|ip6|dns4|dns6|dns)\/(.*?)\/tcp\/(.*?)$/);
526
- // TODO: remove support for `/wss` in a long time (https://github.com/paritytech/smoldot/issues/1940)
527
- const wsParsed = address.match(/^\/(ip4|ip6|dns4|dns6|dns)\/(.*?)\/tcp\/(.*?)\/(ws|wss|tls\/ws)$/);
528
- const webRTCParsed = address.match(/^\/(ip4|ip6)\/(.*?)\/udp\/(.*?)\/webrtc-direct\/certhash\/(.*?)$/);
529
- if (wsParsed != null) {
530
- const proto = (wsParsed[4] == 'ws') ? 'ws' : 'wss';
531
- if (proto == 'ws' && forbidWs) {
532
- return { success: false, error: 'WebSocket connections not available' };
533
- }
534
- if (proto == 'wss' && forbidWss) {
535
- return { success: false, error: 'WebSocket secure connections not available' };
536
- }
537
- if (proto == 'ws' && wsParsed[2] != 'localhost' && wsParsed[2] != '127.0.0.1' && forbidNonLocalWs) {
538
- return { success: false, error: 'Non-local WebSocket connections not available' };
539
- }
540
- const url = (wsParsed[1] == 'ip6') ?
541
- (proto + "://[" + wsParsed[2] + "]:" + wsParsed[3]) :
542
- (proto + "://" + wsParsed[2] + ":" + wsParsed[3]);
543
- return { success: true, address: { ty: "websocket", url } };
544
- }
545
- else if (tcpParsed != null) {
546
- if (forbidTcp) {
547
- return { success: false, error: 'TCP connections not available' };
548
- }
549
- return { success: true, address: { ty: "tcp", hostname: tcpParsed[2], port: parseInt(tcpParsed[3]) } };
550
- }
551
- else if (webRTCParsed != null) {
552
- const targetPort = webRTCParsed[3];
553
- if (forbidWebRtc) {
554
- return { success: false, error: 'WebRTC connections not available' };
555
- }
556
- if (targetPort === '0') {
557
- return { success: false, error: 'Invalid WebRTC target port' };
558
- }
559
- const ipVersion = webRTCParsed[1] == 'ip4' ? '4' : '6';
560
- const targetIp = webRTCParsed[2];
561
- const remoteCertMultibase = webRTCParsed[4];
562
- return { success: true, address: { ty: "webrtc", targetPort, ipVersion, targetIp, remoteCertMultibase } };
563
- }
564
- else {
565
- return { success: false, error: 'Unrecognized multiaddr format' };
566
- }
567
- }
@@ -11,7 +11,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  });
12
12
  };
13
13
  import { start as innerStart } from './internals/client.js';
14
- import { multibaseBase64Decode } from './internals/base64.js';
15
14
  export { AddChainError, AlreadyDestroyedError, CrashError, JsonRpcDisabledError, MalformedJsonRpcError, QueueFullError } from './public-types.js';
16
15
  /**
17
16
  * Initializes a new client. This is a pre-requisite to connecting to a blockchain.
@@ -97,7 +96,7 @@ function connect(config) {
97
96
  connection.onopen = () => {
98
97
  config.onOpen({
99
98
  type: 'single-stream', handshake: 'multistream-select-noise-yamux',
100
- initialWritableBytes: 1024 * 1024, writeClosable: false,
99
+ initialWritableBytes: 1024 * 1024
101
100
  });
102
101
  };
103
102
  connection.onclose = (event) => {
@@ -149,13 +148,7 @@ function connect(config) {
149
148
  };
150
149
  }
151
150
  else if (config.address.ty === "webrtc") {
152
- const { targetPort, ipVersion, targetIp, remoteCertMultibase } = config.address;
153
- // The payload of `/certhash` is the hash of the self-generated certificate that the
154
- // server presents.
155
- // This function throws an exception if the certhash isn't correct. For this reason, this call
156
- // is performed as part of the parsing of the multiaddr.
157
- const remoteCertMultihash = multibaseBase64Decode(remoteCertMultibase);
158
- const remoteCertSha256Hash = multihashToSha256(remoteCertMultihash);
151
+ const { targetPort, ipVersion, targetIp, remoteTlsCertificateSha256 } = config.address;
159
152
  // TODO: detect localhost for Firefox? https://bugzilla.mozilla.org/show_bug.cgi?id=1659672
160
153
  // Note that `pc` can be the connection, but also null or undefined.
161
154
  // `undefined` means "certificate generation in progress", while `null` means "opening must
@@ -169,10 +162,10 @@ function connect(config) {
169
162
  // to smoldot. This data channel is stored in this variable. Once it is reported to smoldot,
170
163
  // it is inserted in `dataChannels`.
171
164
  let handshakeDataChannel;
172
- // Multihash-encoded DTLS certificate of the local node. Unknown as long as it hasn't been
165
+ // SHA256 hash of the DTLS certificate of the local node. Unknown as long as it hasn't been
173
166
  // generated.
174
167
  // TODO: could be merged with `pc` in one variable, and maybe even the other fields as well
175
- let localTlsCertificateMultihash;
168
+ let localTlsCertificateSha256;
176
169
  // Kills all the JavaScript objects (the connection and all its substreams), ensuring that no
177
170
  // callback will be called again. Doesn't report anything to smoldot, as this should be done
178
171
  // by the caller.
@@ -222,8 +215,8 @@ function connect(config) {
222
215
  handshake: 'webrtc',
223
216
  // `addChannel` can never be called before the local certificate is generated, so this
224
217
  // value is always defined.
225
- localTlsCertificateMultihash: localTlsCertificateMultihash,
226
- remoteTlsCertificateMultihash: remoteCertMultihash
218
+ localTlsCertificateSha256: localTlsCertificateSha256,
219
+ remoteTlsCertificateSha256,
227
220
  });
228
221
  }
229
222
  else {
@@ -328,9 +321,8 @@ function connect(config) {
328
321
  config.onConnectionReset('Failed to obtain the browser certificate fingerprint');
329
322
  return;
330
323
  }
331
- localTlsCertificateMultihash = new Uint8Array(34);
332
- localTlsCertificateMultihash.set([0x12, 32], 0);
333
- localTlsCertificateMultihash.set(localTlsCertificateHex.split(':').map((s) => parseInt(s, 16)), 2);
324
+ localTlsCertificateSha256 = new Uint8Array(32);
325
+ localTlsCertificateSha256.set(localTlsCertificateHex.split(':').map((s) => parseInt(s, 16)), 0);
334
326
  // `onconnectionstatechange` is used to detect when the connection has closed or has failed
335
327
  // to open.
336
328
  // Note that smoldot will think that the connection is open even when it is still opening.
@@ -366,7 +358,7 @@ function connect(config) {
366
358
  sdpOffer = sdpOffer.replace(/^a=ice-pwd.*$/m, 'a=ice-pwd:' + ufragPwd);
367
359
  yield pc.setLocalDescription({ type: 'offer', sdp: sdpOffer });
368
360
  // Transform certificate hash into fingerprint (upper-hex; each byte separated by ":").
369
- const fingerprint = Array.from(remoteCertSha256Hash).map((n) => ("0" + n.toString(16)).slice(-2).toUpperCase()).join(':');
361
+ const fingerprint = Array.from(remoteTlsCertificateSha256).map((n) => ("0" + n.toString(16)).slice(-2).toUpperCase()).join(':');
370
362
  // Note that the trailing line feed is important, as otherwise Chrome
371
363
  // fails to parse the payload.
372
364
  const remoteSdp =
@@ -393,7 +385,7 @@ function connect(config) {
393
385
  // The `<fmt>` component must always be `webrtc-datachannel` for WebRTC.
394
386
  // The rest of the SDP payload adds attributes to this specific media stream.
395
387
  // RFCs: 8839, 8866, 8841
396
- "m=application " + targetPort + " " + "UDP/DTLS/SCTP webrtc-datachannel" + "\n" +
388
+ "m=application " + String(targetPort) + " " + "UDP/DTLS/SCTP webrtc-datachannel" + "\n" +
397
389
  // Indicates the IP address of the remote.
398
390
  // Note that "IN" means "Internet" (and not "input").
399
391
  "c=IN IP" + ipVersion + " " + targetIp + "\n" +
@@ -424,7 +416,7 @@ function connect(config) {
424
416
  "a=max-message-size:16384" + "\n" +
425
417
  // A transport address for a candidate that can be used for connectivity
426
418
  // checks (RFC8839).
427
- "a=candidate:1 1 UDP 1 " + targetIp + " " + targetPort + " typ host" + "\n";
419
+ "a=candidate:1 1 UDP 1 " + targetIp + " " + String(targetPort) + " typ host" + "\n";
428
420
  yield pc.setRemoteDescription({ type: "answer", sdp: remoteSdp });
429
421
  });
430
422
  pc.ondatachannel = ({ channel }) => {
@@ -496,12 +488,3 @@ function connect(config) {
496
488
  throw new Error();
497
489
  }
498
490
  }
499
- /// Parses a multihash-multibase-encoded string into a SHA256 hash.
500
- ///
501
- /// Throws an exception if the multihash algorithm isn't SHA256.
502
- const multihashToSha256 = (certMultihash) => {
503
- if (certMultihash.length != 34 || certMultihash[0] != 0x12 || certMultihash[1] != 32) {
504
- throw new Error('Certificate multihash is not SHA-256');
505
- }
506
- return new Uint8Array(certMultihash.slice(2));
507
- };
@@ -70,7 +70,7 @@ function connect(config) {
70
70
  config.onWritableBytes(wasSent);
71
71
  };
72
72
  socket.onopen = () => {
73
- config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: false });
73
+ config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024 });
74
74
  };
75
75
  socket.onclose = (event) => {
76
76
  const message = "Error code " + event.code + (!!event.reason ? (": " + event.reason) : "");
@@ -125,7 +125,7 @@ function connect(config) {
125
125
  if (socket.destroyed)
126
126
  return established;
127
127
  established === null || established === void 0 ? void 0 : established.setNoDelay();
128
- config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: true });
128
+ config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024 });
129
129
  // Spawns an asynchronous task that continuously reads from the socket.
130
130
  // Every time data is read, the task re-executes itself in order to continue reading.
131
131
  // The task ends automatically if an EOF or error is detected, which should also happen
@@ -66,7 +66,7 @@ function connect(config) {
66
66
  config.onWritableBytes(wasSent);
67
67
  };
68
68
  socket.onopen = () => {
69
- config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024, writeClosable: false });
69
+ config.onOpen({ type: 'single-stream', handshake: 'multistream-select-noise-yamux', initialWritableBytes: 1024 * 1024 });
70
70
  };
71
71
  socket.onclose = (event) => {
72
72
  const message = "Error code " + event.code + (!!event.reason ? (": " + event.reason) : "");
@@ -121,7 +121,7 @@ function connect(config) {
121
121
  return;
122
122
  config.onOpen({
123
123
  type: 'single-stream', handshake: 'multistream-select-noise-yamux',
124
- initialWritableBytes: socket.writableHighWaterMark, writeClosable: true
124
+ initialWritableBytes: socket.writableHighWaterMark
125
125
  });
126
126
  });
127
127
  socket.on('close', (hasError) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoldot",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "Light client that connects to Polkadot and Substrate-based blockchains",
5
5
  "contributors": [
6
6
  "Parity Technologies <admin@parity.io>",
@@ -1,22 +0,0 @@
1
- /**
2
- * Decodes a multibase-encoded string.
3
- *
4
- * Throws an exception if the encoding isn't base64 or one of its variants.
5
- */
6
- export declare function multibaseBase64Decode(input: string): Uint8Array;
7
- /**
8
- * Decodes a base64-encoded string into bytes using the original alphabet from RFC4648.
9
- *
10
- * See <https://datatracker.ietf.org/doc/html/rfc4648#section-4>.
11
- */
12
- export declare function classicDecode(input: string): Uint8Array;
13
- /**
14
- * Decodes a base64-encoded string into bytes using the URL-safe alphabet.
15
- *
16
- * See <https://datatracker.ietf.org/doc/html/rfc4648#section-5>.
17
- */
18
- export declare function urlSafeDecode(input: string): Uint8Array;
19
- /**
20
- * Decodes a base64-encoded string into bytes using the given alphabet.
21
- */
22
- export declare function base64Decode(input: string, alphabet: Map<string, number>): Uint8Array;
@@ -1,104 +0,0 @@
1
- "use strict";
2
- // Smoldot
3
- // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
4
- // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.base64Decode = exports.urlSafeDecode = exports.classicDecode = exports.multibaseBase64Decode = void 0;
7
- // This program is free software: you can redistribute it and/or modify
8
- // it under the terms of the GNU General Public License as published by
9
- // the Free Software Foundation, either version 3 of the License, or
10
- // (at your option) any later version.
11
- // This program is distributed in the hope that it will be useful,
12
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
13
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
- // GNU General Public License for more details.
15
- // You should have received a copy of the GNU General Public License
16
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
17
- let rfc4648Alphabet = new Map();
18
- const rfc4648AlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
19
- for (let i = 0; i < rfc4648AlphabetAsStr.length; ++i) {
20
- rfc4648Alphabet.set(rfc4648AlphabetAsStr[i], i);
21
- }
22
- let urlSafeAlphabet = new Map();
23
- const urlSafeAlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
24
- for (let i = 0; i < urlSafeAlphabetAsStr.length; ++i) {
25
- urlSafeAlphabet.set(urlSafeAlphabetAsStr[i], i);
26
- }
27
- /**
28
- * Decodes a multibase-encoded string.
29
- *
30
- * Throws an exception if the encoding isn't base64 or one of its variants.
31
- */
32
- function multibaseBase64Decode(input) {
33
- if (input.length === 0)
34
- throw new Error("Invalid multibase");
35
- switch (input[0]) {
36
- case 'm':
37
- case 'M':
38
- return classicDecode(input.slice(1));
39
- case 'u':
40
- case 'U':
41
- return urlSafeDecode(input.slice(1));
42
- default:
43
- throw new Error('Unknown multibase prefix: ' + input[0]);
44
- }
45
- }
46
- exports.multibaseBase64Decode = multibaseBase64Decode;
47
- /**
48
- * Decodes a base64-encoded string into bytes using the original alphabet from RFC4648.
49
- *
50
- * See <https://datatracker.ietf.org/doc/html/rfc4648#section-4>.
51
- */
52
- function classicDecode(input) {
53
- return base64Decode(input, rfc4648Alphabet);
54
- }
55
- exports.classicDecode = classicDecode;
56
- /**
57
- * Decodes a base64-encoded string into bytes using the URL-safe alphabet.
58
- *
59
- * See <https://datatracker.ietf.org/doc/html/rfc4648#section-5>.
60
- */
61
- function urlSafeDecode(input) {
62
- return base64Decode(input, urlSafeAlphabet);
63
- }
64
- exports.urlSafeDecode = urlSafeDecode;
65
- /**
66
- * Decodes a base64-encoded string into bytes using the given alphabet.
67
- */
68
- function base64Decode(input, alphabet) {
69
- // Remove the padding bytes at the end of the string. We don't check whether the padding is
70
- // accurate.
71
- while (input.length !== 0 && input[input.length - 1] === '=')
72
- input = input.slice(0, -1);
73
- // Contains the output data.
74
- const out = new Uint8Array(Math.floor(input.length * 6 / 8));
75
- // Position within `out` of the next byte to write.
76
- let outPos = 0;
77
- // The bits decoded from the input are added to the right of this value.
78
- let currentByte = 0;
79
- // The left-most `validBitsInCurrentByte` bits of `currentByte` must be written out.
80
- let validBitsInCurrentByte = 0;
81
- for (let i = 0; i < input.length; ++i) {
82
- const inputChr = input[i];
83
- const bitsToAppend = alphabet.get(inputChr);
84
- if (bitsToAppend === undefined)
85
- throw new Error('Invalid base64 character: ' + inputChr);
86
- console.assert(bitsToAppend < (1 << 6));
87
- currentByte = (currentByte << 6) | bitsToAppend;
88
- validBitsInCurrentByte += 6;
89
- if (validBitsInCurrentByte >= 8) {
90
- let outByte = currentByte >> (validBitsInCurrentByte - 8);
91
- out[outPos] = outByte;
92
- outPos += 1;
93
- validBitsInCurrentByte -= 8;
94
- }
95
- console.assert(validBitsInCurrentByte < 8);
96
- currentByte &= 0xff;
97
- }
98
- if ((currentByte & ((1 << validBitsInCurrentByte) - 1)) !== 0)
99
- throw new Error("Unexpected EOF");
100
- if (validBitsInCurrentByte >= 6)
101
- throw new Error("Unexpected EOF");
102
- return out;
103
- }
104
- exports.base64Decode = base64Decode;
@@ -1,22 +0,0 @@
1
- /**
2
- * Decodes a multibase-encoded string.
3
- *
4
- * Throws an exception if the encoding isn't base64 or one of its variants.
5
- */
6
- export declare function multibaseBase64Decode(input: string): Uint8Array;
7
- /**
8
- * Decodes a base64-encoded string into bytes using the original alphabet from RFC4648.
9
- *
10
- * See <https://datatracker.ietf.org/doc/html/rfc4648#section-4>.
11
- */
12
- export declare function classicDecode(input: string): Uint8Array;
13
- /**
14
- * Decodes a base64-encoded string into bytes using the URL-safe alphabet.
15
- *
16
- * See <https://datatracker.ietf.org/doc/html/rfc4648#section-5>.
17
- */
18
- export declare function urlSafeDecode(input: string): Uint8Array;
19
- /**
20
- * Decodes a base64-encoded string into bytes using the given alphabet.
21
- */
22
- export declare function base64Decode(input: string, alphabet: Map<string, number>): Uint8Array;
@@ -1,97 +0,0 @@
1
- // Smoldot
2
- // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
3
- // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
4
- // This program is free software: you can redistribute it and/or modify
5
- // it under the terms of the GNU General Public License as published by
6
- // the Free Software Foundation, either version 3 of the License, or
7
- // (at your option) any later version.
8
- // This program is distributed in the hope that it will be useful,
9
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
10
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
- // GNU General Public License for more details.
12
- // You should have received a copy of the GNU General Public License
13
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
14
- let rfc4648Alphabet = new Map();
15
- const rfc4648AlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
16
- for (let i = 0; i < rfc4648AlphabetAsStr.length; ++i) {
17
- rfc4648Alphabet.set(rfc4648AlphabetAsStr[i], i);
18
- }
19
- let urlSafeAlphabet = new Map();
20
- const urlSafeAlphabetAsStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
21
- for (let i = 0; i < urlSafeAlphabetAsStr.length; ++i) {
22
- urlSafeAlphabet.set(urlSafeAlphabetAsStr[i], i);
23
- }
24
- /**
25
- * Decodes a multibase-encoded string.
26
- *
27
- * Throws an exception if the encoding isn't base64 or one of its variants.
28
- */
29
- export function multibaseBase64Decode(input) {
30
- if (input.length === 0)
31
- throw new Error("Invalid multibase");
32
- switch (input[0]) {
33
- case 'm':
34
- case 'M':
35
- return classicDecode(input.slice(1));
36
- case 'u':
37
- case 'U':
38
- return urlSafeDecode(input.slice(1));
39
- default:
40
- throw new Error('Unknown multibase prefix: ' + input[0]);
41
- }
42
- }
43
- /**
44
- * Decodes a base64-encoded string into bytes using the original alphabet from RFC4648.
45
- *
46
- * See <https://datatracker.ietf.org/doc/html/rfc4648#section-4>.
47
- */
48
- export function classicDecode(input) {
49
- return base64Decode(input, rfc4648Alphabet);
50
- }
51
- /**
52
- * Decodes a base64-encoded string into bytes using the URL-safe alphabet.
53
- *
54
- * See <https://datatracker.ietf.org/doc/html/rfc4648#section-5>.
55
- */
56
- export function urlSafeDecode(input) {
57
- return base64Decode(input, urlSafeAlphabet);
58
- }
59
- /**
60
- * Decodes a base64-encoded string into bytes using the given alphabet.
61
- */
62
- export function base64Decode(input, alphabet) {
63
- // Remove the padding bytes at the end of the string. We don't check whether the padding is
64
- // accurate.
65
- while (input.length !== 0 && input[input.length - 1] === '=')
66
- input = input.slice(0, -1);
67
- // Contains the output data.
68
- const out = new Uint8Array(Math.floor(input.length * 6 / 8));
69
- // Position within `out` of the next byte to write.
70
- let outPos = 0;
71
- // The bits decoded from the input are added to the right of this value.
72
- let currentByte = 0;
73
- // The left-most `validBitsInCurrentByte` bits of `currentByte` must be written out.
74
- let validBitsInCurrentByte = 0;
75
- for (let i = 0; i < input.length; ++i) {
76
- const inputChr = input[i];
77
- const bitsToAppend = alphabet.get(inputChr);
78
- if (bitsToAppend === undefined)
79
- throw new Error('Invalid base64 character: ' + inputChr);
80
- console.assert(bitsToAppend < (1 << 6));
81
- currentByte = (currentByte << 6) | bitsToAppend;
82
- validBitsInCurrentByte += 6;
83
- if (validBitsInCurrentByte >= 8) {
84
- let outByte = currentByte >> (validBitsInCurrentByte - 8);
85
- out[outPos] = outByte;
86
- outPos += 1;
87
- validBitsInCurrentByte -= 8;
88
- }
89
- console.assert(validBitsInCurrentByte < 8);
90
- currentByte &= 0xff;
91
- }
92
- if ((currentByte & ((1 << validBitsInCurrentByte) - 1)) !== 0)
93
- throw new Error("Unexpected EOF");
94
- if (validBitsInCurrentByte >= 6)
95
- throw new Error("Unexpected EOF");
96
- return out;
97
- }