From 12f2a09b8528bd524ea1aefffded4a96990af10b Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Fri, 21 Jan 2022 15:50:01 -0500
Subject: [PATCH 01/60] add CSP types
---
packages/kit/types/config.d.ts | 4 ++
packages/kit/types/csp.d.ts | 115 +++++++++++++++++++++++++++++++++
2 files changed, 119 insertions(+)
create mode 100644 packages/kit/types/csp.d.ts
diff --git a/packages/kit/types/config.d.ts b/packages/kit/types/config.d.ts
index 788bef4cea3b..8095c492867f 100644
--- a/packages/kit/types/config.d.ts
+++ b/packages/kit/types/config.d.ts
@@ -1,5 +1,6 @@
import { CompileOptions } from 'svelte/types/compiler/interfaces';
import { UserConfig as ViteConfig } from 'vite';
+import { CspDirectives } from './csp';
import { RecursiveRequired } from './helper';
import { HttpMethod, Logger, RouteSegment, TrailingSlash } from './internal';
@@ -117,6 +118,9 @@ export interface Config {
adapter?: Adapter;
amp?: boolean;
appDir?: string;
+ csp?: {
+ directives?: CspDirectives;
+ };
files?: {
assets?: string;
hooks?: string;
diff --git a/packages/kit/types/csp.d.ts b/packages/kit/types/csp.d.ts
new file mode 100644
index 000000000000..5b390dd12a9f
--- /dev/null
+++ b/packages/kit/types/csp.d.ts
@@ -0,0 +1,115 @@
+/**
+ * Based on https://github.com/josh-hemphill/csp-typed-directives/blob/latest/src/csp.types.ts
+ *
+ * MIT License
+ *
+ * Copyright (c) 2021-present, Joshua Hemphill
+ * Copyright (c) 2021, Tecnico Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+type SchemeSource = 'http:' | 'https:' | 'data:' | 'mediastream:' | 'blob:' | 'filesystem:';
+
+type HostProtocolSchemes = `${string}://` | '';
+type PortScheme = `:${number}` | '' | ':*';
+/** Can actually be any string, but typed more explicitly to
+ * restrict the combined optional types of Source from collapsing to just bing `string` */
+type HostNameScheme = `${string}.${string}` | `localhost`;
+type HostSource = `${HostProtocolSchemes}${HostNameScheme}${PortScheme}`;
+
+type CryptoSource = `${'nonce' | 'sha256' | 'sha384' | 'sha512'}-${string}`;
+
+type BaseSource = 'self' | 'unsafe-eval' | 'unsafe-hashes' | 'unsafe-inline' | 'none';
+
+type Source = HostSource | SchemeSource | CryptoSource | BaseSource;
+type Sources = Source[];
+
+type ActionSource = 'strict-dynamic' | 'report-sample';
+
+type FrameSource = HostSource | SchemeSource | 'self' | 'none';
+
+type HttpDelineator = '/' | '?' | '#' | '\\';
+type UriPath = `${HttpDelineator}${string}`;
+
+export type CspDirectives = {
+ 'child-src'?: Sources;
+ 'default-src'?: Sources;
+ 'frame-src'?: Sources;
+ 'worker-src'?: Sources;
+ 'connect-src'?: Sources;
+ 'font-src'?: Sources;
+ 'img-src'?: Sources;
+ 'manifest-src'?: Sources;
+ 'media-src'?: Sources;
+ 'object-src'?: Sources;
+ 'prefetch-src'?: Sources;
+ 'script-src'?: Sources;
+ 'script-src-elem'?: Sources;
+ 'script-src-attr'?: Sources;
+ 'style-src'?: Sources;
+ 'style-src-elem'?: Sources;
+ 'style-src-attr'?: Sources;
+ 'base-uri'?: Array
*/
-sjcl.bitArray = {
- /**
- * Array slices in units of bits.
- * @param {bitArray} a The array to slice.
- * @param {Number} bstart The offset to the start of the slice, in bits.
- * @param {Number} bend The offset to the end of the slice, in bits. If this is undefined,
- * slice until the end of the array.
- * @return {bitArray} The requested slice.
- */
- bitSlice: function (a, bstart, bend) {
- a = sjcl.bitArray._shiftRight(a.slice(bstart / 32), 32 - (bstart & 31)).slice(1);
- return bend === undefined ? a : sjcl.bitArray.clamp(a, bend - bstart);
- },
-
- /**
- * Extract a number packed into a bit array.
- * @param {bitArray} a The array to slice.
- * @param {Number} bstart The offset to the start of the slice, in bits.
- * @param {Number} blength The length of the number to extract.
- * @return {Number} The requested slice.
- */
- extract: function (a, bstart, blength) {
- // FIXME: this Math.floor is not necessary at all, but for some reason
- // seems to suppress a bug in the Chromium JIT.
- var x,
- sh = Math.floor((-bstart - blength) & 31);
-
- if (((bstart + blength - 1) ^ bstart) & -32) {
- // it crosses a boundary
- x = (a[(bstart / 32) | 0] << (32 - sh)) ^ (a[(bstart / 32 + 1) | 0] >>> sh);
- } else {
- // within a single word
- x = a[(bstart / 32) | 0] >>> sh;
- }
-
- return x & ((1 << blength) - 1);
- },
+/** @typedef {number[]} bitArray */
+const BitArray = {
/**
* Concatenate two bit arrays.
* @param {bitArray} a1 The first array.
@@ -121,12 +86,12 @@ sjcl.bitArray = {
}
var last = a1[a1.length - 1],
- shift = sjcl.bitArray.getPartial(last);
+ shift = BitArray.getPartial(last);
if (shift === 32) {
return a1.concat(a2);
} else {
- return sjcl.bitArray._shiftRight(a2, shift, last | 0, a1.slice(0, a1.length - 1));
+ return BitArray._shiftRight(a2, shift, last | 0, a1.slice(0, a1.length - 1));
}
},
@@ -145,31 +110,7 @@ sjcl.bitArray = {
x = a[l - 1];
- return (l - 1) * 32 + sjcl.bitArray.getPartial(x);
- },
-
- /**
- * Truncate an array.
- * @param {bitArray} a The array.
- * @param {Number} len The length to truncate to, in bits.
- * @return {bitArray} A new array, truncated to len bits.
- */
- clamp: function (a, len) {
- if (a.length * 32 < len) {
- return a;
- }
-
- a = a.slice(0, Math.ceil(len / 32));
-
- var l = a.length;
-
- len = len & 31;
-
- if (l > 0 && len) {
- a[l - 1] = sjcl.bitArray.partial(len, a[l - 1] & (0x80000000 >> (len - 1)), 1);
- }
-
- return a;
+ return (l - 1) * 32 + BitArray.getPartial(x);
},
/**
@@ -196,44 +137,18 @@ sjcl.bitArray = {
return Math.round(x / 0x10000000000) || 32;
},
- /**
- * Compare two arrays for equality in a predictable amount of time.
- * @param {bitArray} a The first array.
- * @param {bitArray} b The second array.
- * @return {boolean} true if a == b; false otherwise.
- */
-
- equal: function (a, b) {
- if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) {
- return false;
- }
-
- var x = 0,
- i;
-
- for (i = 0; i < a.length; i++) {
- x |= a[i] ^ b[i];
- }
-
- return x === 0;
- },
-
/** Shift an array right.
* @param {bitArray} a The array to shift.
- * @param {Number} shift The number of bits to shift.
- * @param {Number} [carry=0] A byte to carry in
- * @param {bitArray} [out=[]] An array to prepend to the output.
+ * @param {number} shift The number of bits to shift.
+ * @param {number} [carry] A byte to carry in
+ * @param {bitArray} [out] An array to prepend to the output.
* @private
*/
- _shiftRight: function (a, shift, carry, out) {
+ _shiftRight: function (a, shift, carry = 0, out = []) {
var i,
last2 = 0,
shift2;
- if (out === undefined) {
- out = [];
- }
-
for (; shift >= 32; shift -= 32) {
out.push(carry);
@@ -252,39 +167,11 @@ sjcl.bitArray = {
last2 = a.length ? a[a.length - 1] : 0;
- shift2 = sjcl.bitArray.getPartial(last2);
+ shift2 = BitArray.getPartial(last2);
- out.push(
- sjcl.bitArray.partial((shift + shift2) & 31, shift + shift2 > 32 ? carry : out.pop(), 1)
- );
+ out.push(BitArray.partial((shift + shift2) & 31, shift + shift2 > 32 ? carry : out.pop(), 1));
return out;
- },
-
- /** xor a block of 4 words together.
- * @private
- */
- _xor4: function (x, y) {
- return [x[0] ^ y[0], x[1] ^ y[1], x[2] ^ y[2], x[3] ^ y[3]];
- },
-
- /** byteswap a word array inplace.
- * (does not handle partial words)
- * @param {sjcl.bitArray} a word array
- * @return {sjcl.bitArray} byteswapped array
- */
- byteswapM: function (a) {
- var i,
- v,
- m = 0xff00;
-
- for (i = 0; i < a.length; ++i) {
- v = a[i];
-
- a[i] = (v >>> 24) | ((v >>> 8) & m) | ((v & m) << 8) | (v << 24);
- }
-
- return a;
}
};
@@ -362,9 +249,9 @@ sjcl.hash.sha256.prototype = {
}
var i,
- b = (this._buffer = sjcl.bitArray.concat(this._buffer, data)),
+ b = (this._buffer = BitArray.concat(this._buffer, data)),
ol = this._length,
- nl = (this._length = ol + sjcl.bitArray.bitLength(data));
+ nl = (this._length = ol + BitArray.bitLength(data));
if (nl > 9007199254740991) {
throw new sjcl.exception.invalid('Cannot hash more than 2^53 - 1 bits');
@@ -402,7 +289,7 @@ sjcl.hash.sha256.prototype = {
// Round out and push the buffer
- b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1, 1)]);
+ b = BitArray.concat(b, [BitArray.partial(1, 1)]);
// Round out the buffer to a multiple of 16 words, less the 2 length words.
From cc9aca87a5332c6e5aabaf33028c2aa0d9b415cf Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 10:27:05 -0500
Subject: [PATCH 20/60] move some stuff out of the prototype
---
packages/kit/src/runtime/server/page/sjcl.js | 72 +++++++++-----------
1 file changed, 33 insertions(+), 39 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 7fd1c5d2a23c..56e38036e340 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -175,6 +175,34 @@ const BitArray = {
}
};
+/**
+ * The SHA-256 initialization vector, to be precomputed.
+ * @private
+ */
+const init = [];
+
+/*
+ _init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19],
+ */
+
+/**
+ * The SHA-256 hash key, to be precomputed.
+ * @private
+ */
+const key = [];
+
+/*
+ _key:
+ [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2],
+ */
+
/** @fileOverview Javascript SHA-256 implementation.
*
* An older version of this implementation is available in the public
@@ -195,7 +223,7 @@ const BitArray = {
*/
sjcl.hash.sha256 = function (hash) {
- if (!this._key[0]) {
+ if (!key[0]) {
this._precompute();
}
@@ -221,18 +249,12 @@ sjcl.hash.sha256.hash = function (data) {
};
sjcl.hash.sha256.prototype = {
- /**
- * The hash's block size, in bits.
- * @constant
- */
- blockSize: 512,
-
/**
* Reset the hash state.
* @return this
*/
reset: function () {
- this._h = this._init.slice(0);
+ this._h = init.slice(0);
this._buffer = [];
this._length = 0;
return this;
@@ -312,34 +334,6 @@ sjcl.hash.sha256.prototype = {
return h;
},
- /**
- * The SHA-256 initialization vector, to be precomputed.
- * @private
- */
- _init: [],
-
- /*
- _init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19],
- */
-
- /**
- * The SHA-256 hash key, to be precomputed.
- * @private
- */
- _key: [],
-
- /*
- _key:
- [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2],
- */
-
/**
* Function to precompute _init and _key.
* @private
@@ -367,10 +361,10 @@ sjcl.hash.sha256.prototype = {
if (isPrime) {
if (i < 8) {
- this._init[i] = frac(Math.pow(prime, 1 / 2));
+ init[i] = frac(Math.pow(prime, 1 / 2));
}
- this._key[i] = frac(Math.pow(prime, 1 / 3));
+ key[i] = frac(Math.pow(prime, 1 / 3));
i++;
}
@@ -388,7 +382,7 @@ sjcl.hash.sha256.prototype = {
a,
b,
h = this._h,
- k = this._key,
+ k = key,
h0 = h[0],
h1 = h[1],
h2 = h[2],
From 07f86fc59eaa53369dbefc37088449e7345d7a26 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 10:29:27 -0500
Subject: [PATCH 21/60] use a class
---
.../kit/src/runtime/server/page/crypto.js | 4 +-
packages/kit/src/runtime/server/page/sjcl.js | 62 +++++++++----------
2 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/crypto.js b/packages/kit/src/runtime/server/page/crypto.js
index 450cc310a808..f2e5a57ac55f 100644
--- a/packages/kit/src/runtime/server/page/crypto.js
+++ b/packages/kit/src/runtime/server/page/crypto.js
@@ -1,11 +1,11 @@
-import { sjcl } from './sjcl.js';
+import { sjcl, Sha256 } from './sjcl.js';
// adapted from https://bitwiseshiftleft.github.io/sjcl/,
// modified and redistributed under BSD license
/** @param {string} text */
export function sha256(text) {
- const hashed = new Uint32Array(sjcl.hash.sha256.hash(text));
+ const hashed = new Uint32Array(Sha256.hash(text));
const buffer = hashed.buffer;
const uint8array = new Uint8Array(buffer);
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 56e38036e340..7b0a28dec086 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -222,50 +222,50 @@ const key = [];
* @constructor
*/
-sjcl.hash.sha256 = function (hash) {
- if (!key[0]) {
- this._precompute();
- }
+export class Sha256 {
+ constructor(hash) {
+ if (!key[0]) {
+ this._precompute();
+ }
- if (hash) {
- this._h = hash._h.slice(0);
+ if (hash) {
+ this._h = hash._h.slice(0);
- this._buffer = hash._buffer.slice(0);
+ this._buffer = hash._buffer.slice(0);
- this._length = hash._length;
- } else {
- this.reset();
+ this._length = hash._length;
+ } else {
+ this.reset();
+ }
}
-};
-/**
- * Hash a string or an array of words.
- * @static
- * @param {bitArray|String} data the data to hash.
- * @return {bitArray} The hash value, an array of 16 big-endian words.
- */
-sjcl.hash.sha256.hash = function (data) {
- return new sjcl.hash.sha256().update(data).finalize();
-};
+ /**
+ * Hash a string or an array of words.
+ * @static
+ * @param {bitArray|String} data the data to hash.
+ * @return {bitArray} The hash value, an array of 16 big-endian words.
+ */
+ static hash(data) {
+ return new Sha256().update(data).finalize();
+ }
-sjcl.hash.sha256.prototype = {
/**
* Reset the hash state.
* @return this
*/
- reset: function () {
+ reset() {
this._h = init.slice(0);
this._buffer = [];
this._length = 0;
return this;
- },
+ }
/**
* Input several words to the hash.
* @param {bitArray|String} data the data to hash.
* @return this
*/
- update: function (data) {
+ update(data) {
if (typeof data === 'string') {
data = toBits(data);
}
@@ -298,13 +298,13 @@ sjcl.hash.sha256.prototype = {
}
return this;
- },
+ }
/**
* Complete hashing and output the hash value.
* @return {bitArray} The hash value, an array of 8 big-endian words.
*/
- finalize: function () {
+ finalize() {
var i,
b = this._buffer,
h = this._h;
@@ -332,13 +332,13 @@ sjcl.hash.sha256.prototype = {
this.reset();
return h;
- },
+ }
/**
* Function to precompute _init and _key.
* @private
*/
- _precompute: function () {
+ _precompute() {
var i = 0,
prime = 2,
factor,
@@ -369,14 +369,14 @@ sjcl.hash.sha256.prototype = {
i++;
}
}
- },
+ }
/**
* Perform one cycle of SHA-256.
* @param {Uint32Array|bitArray} w one block of words.
* @private
*/
- _block: function (w) {
+ _block(w) {
var i,
tmp,
a,
@@ -459,4 +459,4 @@ sjcl.hash.sha256.prototype = {
h[6] = (h[6] + h6) | 0;
h[7] = (h[7] + h7) | 0;
}
-};
+}
From 3ec91c6558fb0b7f8b109b48a0e025045c08577e Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 10:31:24 -0500
Subject: [PATCH 22/60] more tidying
---
packages/kit/src/runtime/server/page/sjcl.js | 70 +++++---------------
1 file changed, 16 insertions(+), 54 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 7b0a28dec086..62b9fa10512e 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -177,66 +177,41 @@ const BitArray = {
/**
* The SHA-256 initialization vector, to be precomputed.
- * @private
+ * @type {number[]}
*/
const init = [];
/*
- _init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19],
+ * init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19],
*/
/**
* The SHA-256 hash key, to be precomputed.
- * @private
+ * @type {number[]}
*/
const key = [];
/*
- _key:
- [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2],
- */
-
-/** @fileOverview Javascript SHA-256 implementation.
- *
- * An older version of this implementation is available in the public
- * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh,
- * Stanford University 2008-2010 and BSD-licensed for liability
- * reasons.
- *
- * Special thanks to Aldo Cortesi for pointing out several bugs in
- * this code.
- *
- * @author Emily Stark
- * @author Mike Hamburg
- * @author Dan Boneh
- */
-/**
- * Context for a SHA-256 operation in progress.
- * @constructor
+ * key:
+ * [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ * 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ * 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ * 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ * 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ * 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ * 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ * 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2],
*/
export class Sha256 {
- constructor(hash) {
+ constructor() {
if (!key[0]) {
this._precompute();
}
- if (hash) {
- this._h = hash._h.slice(0);
-
- this._buffer = hash._buffer.slice(0);
-
- this._length = hash._length;
- } else {
- this.reset();
- }
+ this._h = init.slice(0);
+ this._buffer = [];
+ this._length = 0;
}
/**
@@ -249,17 +224,6 @@ export class Sha256 {
return new Sha256().update(data).finalize();
}
- /**
- * Reset the hash state.
- * @return this
- */
- reset() {
- this._h = init.slice(0);
- this._buffer = [];
- this._length = 0;
- return this;
- }
-
/**
* Input several words to the hash.
* @param {bitArray|String} data the data to hash.
@@ -329,8 +293,6 @@ export class Sha256 {
this._block(b.splice(0, 16));
}
- this.reset();
-
return h;
}
From 527bb33fc748b52df08e3fd2317ebffe70778c8d Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 10:33:27 -0500
Subject: [PATCH 23/60] more tidying
---
packages/kit/src/runtime/server/page/sjcl.js | 87 ++++++++++----------
1 file changed, 43 insertions(+), 44 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 62b9fa10512e..a95f46c767d5 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -203,10 +203,47 @@ const key = [];
* 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2],
*/
+/**
+ * Function to precompute _init and _key.
+ */
+function precompute() {
+ var i = 0,
+ prime = 2,
+ factor,
+ isPrime;
+
+ /** @param {number} x */
+ function frac(x) {
+ return ((x - Math.floor(x)) * 0x100000000) | 0;
+ }
+
+ for (; i < 64; prime++) {
+ isPrime = true;
+
+ for (factor = 2; factor * factor <= prime; factor++) {
+ if (prime % factor === 0) {
+ isPrime = false;
+
+ break;
+ }
+ }
+
+ if (isPrime) {
+ if (i < 8) {
+ init[i] = frac(Math.pow(prime, 1 / 2));
+ }
+
+ key[i] = frac(Math.pow(prime, 1 / 3));
+
+ i++;
+ }
+ }
+}
+
export class Sha256 {
constructor() {
if (!key[0]) {
- this._precompute();
+ precompute();
}
this._h = init.slice(0);
@@ -240,7 +277,7 @@ export class Sha256 {
nl = (this._length = ol + BitArray.bitLength(data));
if (nl > 9007199254740991) {
- throw new sjcl.exception.invalid('Cannot hash more than 2^53 - 1 bits');
+ throw new Error('Cannot hash more than 2^53 - 1 bits');
}
if (typeof Uint32Array !== 'undefined') {
@@ -249,7 +286,7 @@ export class Sha256 {
var j = 0;
for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {
- this._block(c.subarray(16 * j, 16 * (j + 1)));
+ this.#block(c.subarray(16 * j, 16 * (j + 1)));
j += 1;
}
@@ -257,7 +294,7 @@ export class Sha256 {
b.splice(0, 16 * j);
} else {
for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {
- this._block(b.splice(0, 16));
+ this.#block(b.splice(0, 16));
}
}
@@ -290,55 +327,17 @@ export class Sha256 {
b.push(this._length | 0);
while (b.length) {
- this._block(b.splice(0, 16));
+ this.#block(b.splice(0, 16));
}
return h;
}
- /**
- * Function to precompute _init and _key.
- * @private
- */
- _precompute() {
- var i = 0,
- prime = 2,
- factor,
- isPrime;
-
- function frac(x) {
- return ((x - Math.floor(x)) * 0x100000000) | 0;
- }
-
- for (; i < 64; prime++) {
- isPrime = true;
-
- for (factor = 2; factor * factor <= prime; factor++) {
- if (prime % factor === 0) {
- isPrime = false;
-
- break;
- }
- }
-
- if (isPrime) {
- if (i < 8) {
- init[i] = frac(Math.pow(prime, 1 / 2));
- }
-
- key[i] = frac(Math.pow(prime, 1 / 3));
-
- i++;
- }
- }
- }
-
/**
* Perform one cycle of SHA-256.
* @param {Uint32Array|bitArray} w one block of words.
- * @private
*/
- _block(w) {
+ #block(w) {
var i,
tmp,
a,
From e5cf81dc05d7b5305c504d522e9d0052f02e19fd Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 10:38:21 -0500
Subject: [PATCH 24/60] more tidying
---
.../kit/src/runtime/server/page/crypto.js | 2 +-
packages/kit/src/runtime/server/page/sjcl.js | 29 ++++++-------------
2 files changed, 10 insertions(+), 21 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/crypto.js b/packages/kit/src/runtime/server/page/crypto.js
index f2e5a57ac55f..57bf63ca2479 100644
--- a/packages/kit/src/runtime/server/page/crypto.js
+++ b/packages/kit/src/runtime/server/page/crypto.js
@@ -1,4 +1,4 @@
-import { sjcl, Sha256 } from './sjcl.js';
+import { Sha256 } from './sjcl.js';
// adapted from https://bitwiseshiftleft.github.io/sjcl/,
// modified and redistributed under BSD license
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index a95f46c767d5..0e3ba3744b4f 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -1,7 +1,3 @@
-export const sjcl = {
- hash: {}
-};
-
/** @param {Uint32Array} uint32array' */
function swap_endianness(uint32array) {
const uint8array = new Uint8Array(uint32array.buffer);
@@ -254,7 +250,7 @@ export class Sha256 {
/**
* Hash a string or an array of words.
* @static
- * @param {bitArray|String} data the data to hash.
+ * @param {bitArray | string} data the data to hash.
* @return {bitArray} The hash value, an array of 16 big-endian words.
*/
static hash(data) {
@@ -263,8 +259,7 @@ export class Sha256 {
/**
* Input several words to the hash.
- * @param {bitArray|String} data the data to hash.
- * @return this
+ * @param {bitArray | string} data the data to hash.
*/
update(data) {
if (typeof data === 'string') {
@@ -280,24 +275,18 @@ export class Sha256 {
throw new Error('Cannot hash more than 2^53 - 1 bits');
}
- if (typeof Uint32Array !== 'undefined') {
- var c = new Uint32Array(b);
+ var c = new Uint32Array(b);
- var j = 0;
+ var j = 0;
- for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {
- this.#block(c.subarray(16 * j, 16 * (j + 1)));
-
- j += 1;
- }
+ for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {
+ this.#block(c.subarray(16 * j, 16 * (j + 1)));
- b.splice(0, 16 * j);
- } else {
- for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {
- this.#block(b.splice(0, 16));
- }
+ j += 1;
}
+ b.splice(0, 16 * j);
+
return this;
}
From 1f2015a9ce04f94873ccabef542a63c0090e8ba4 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 10:40:49 -0500
Subject: [PATCH 25/60] fix all type errors
---
packages/kit/src/runtime/server/page/sjcl.js | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 0e3ba3744b4f..677282c94d56 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -94,7 +94,7 @@ const BitArray = {
/**
* Find the length of an array of bits.
* @param {bitArray} a The array.
- * @return {Number} The length of a, in bits.
+ * @return {number} The length of a, in bits.
*/
bitLength: function (a) {
var l = a.length,
@@ -165,7 +165,13 @@ const BitArray = {
shift2 = BitArray.getPartial(last2);
- out.push(BitArray.partial((shift + shift2) & 31, shift + shift2 > 32 ? carry : out.pop(), 1));
+ out.push(
+ BitArray.partial(
+ (shift + shift2) & 31,
+ shift + shift2 > 32 ? carry : /** @type {number} */ (out.pop()),
+ 1
+ )
+ );
return out;
}
@@ -243,6 +249,7 @@ export class Sha256 {
}
this._h = init.slice(0);
+ /** @type {bitArray} */
this._buffer = [];
this._length = 0;
}
From d226b5b2b296f4b8d4483c45d299cc526fe0ff4a Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 10:44:47 -0500
Subject: [PATCH 26/60] store init vector and hash key as typed arrays
---
packages/kit/src/runtime/server/page/sjcl.js | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 677282c94d56..459f242d41df 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -179,9 +179,8 @@ const BitArray = {
/**
* The SHA-256 initialization vector, to be precomputed.
- * @type {number[]}
*/
-const init = [];
+const init = new Uint32Array(8);
/*
* init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19],
@@ -189,9 +188,8 @@ const init = [];
/**
* The SHA-256 hash key, to be precomputed.
- * @type {number[]}
*/
-const key = [];
+const key = new Uint32Array(64);
/*
* key:
@@ -216,7 +214,7 @@ function precompute() {
/** @param {number} x */
function frac(x) {
- return ((x - Math.floor(x)) * 0x100000000) | 0;
+ return (x - Math.floor(x)) * 0x100000000;
}
for (; i < 64; prime++) {
From 7dc289b2fc8c8c0e1ed0d99dab25c60557c1f3f5 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 10:53:50 -0500
Subject: [PATCH 27/60] convert to closure
---
.../kit/src/runtime/server/page/crypto.js | 5 +-
packages/kit/src/runtime/server/page/sjcl.js | 137 ++++++++----------
2 files changed, 60 insertions(+), 82 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/crypto.js b/packages/kit/src/runtime/server/page/crypto.js
index 57bf63ca2479..ebceeb5e2902 100644
--- a/packages/kit/src/runtime/server/page/crypto.js
+++ b/packages/kit/src/runtime/server/page/crypto.js
@@ -1,11 +1,12 @@
-import { Sha256 } from './sjcl.js';
+import { hash } from './sjcl.js';
// adapted from https://bitwiseshiftleft.github.io/sjcl/,
// modified and redistributed under BSD license
/** @param {string} text */
export function sha256(text) {
- const hashed = new Uint32Array(Sha256.hash(text));
+ // const hashed = new Uint32Array(Sha256.hash(text));
+ const hashed = new Uint32Array(hash(text));
const buffer = hashed.buffer;
const uint8array = new Uint8Array(buffer);
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 459f242d41df..d4841ea1eb74 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -240,103 +240,45 @@ function precompute() {
}
}
-export class Sha256 {
- constructor() {
- if (!key[0]) {
- precompute();
- }
-
- this._h = init.slice(0);
- /** @type {bitArray} */
- this._buffer = [];
- this._length = 0;
- }
-
- /**
- * Hash a string or an array of words.
- * @static
- * @param {bitArray | string} data the data to hash.
- * @return {bitArray} The hash value, an array of 16 big-endian words.
- */
- static hash(data) {
- return new Sha256().update(data).finalize();
+/** @param {bitArray | string} data */
+export function hash(data) {
+ if (!key[0]) {
+ precompute();
}
- /**
- * Input several words to the hash.
- * @param {bitArray | string} data the data to hash.
- */
- update(data) {
- if (typeof data === 'string') {
- data = toBits(data);
- }
-
- var i,
- b = (this._buffer = BitArray.concat(this._buffer, data)),
- ol = this._length,
- nl = (this._length = ol + BitArray.bitLength(data));
-
- if (nl > 9007199254740991) {
- throw new Error('Cannot hash more than 2^53 - 1 bits');
- }
-
- var c = new Uint32Array(b);
-
- var j = 0;
-
- for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {
- this.#block(c.subarray(16 * j, 16 * (j + 1)));
-
- j += 1;
- }
+ const _h = init.slice(0);
+ /** @type {bitArray} */
+ let _buffer = [];
+ let _length = 0;
- b.splice(0, 16 * j);
-
- return this;
+ if (typeof data === 'string') {
+ data = toBits(data);
}
- /**
- * Complete hashing and output the hash value.
- * @return {bitArray} The hash value, an array of 8 big-endian words.
- */
- finalize() {
- var i,
- b = this._buffer,
- h = this._h;
-
- // Round out and push the buffer
-
- b = BitArray.concat(b, [BitArray.partial(1, 1)]);
+ // update
+ var i,
+ b = (_buffer = BitArray.concat(_buffer, data)),
+ ol = _length,
+ nl = (_length = ol + BitArray.bitLength(data));
- // Round out the buffer to a multiple of 16 words, less the 2 length words.
-
- for (i = b.length + 2; i & 15; i++) {
- b.push(0);
- }
-
- // append the length
-
- b.push(Math.floor(this._length / 0x100000000));
-
- b.push(this._length | 0);
+ if (nl > 9007199254740991) {
+ throw new Error('Cannot hash more than 2^53 - 1 bits');
+ }
- while (b.length) {
- this.#block(b.splice(0, 16));
- }
+ var c = new Uint32Array(b);
- return h;
- }
+ var j = 0;
/**
* Perform one cycle of SHA-256.
* @param {Uint32Array|bitArray} w one block of words.
*/
- #block(w) {
+ const block = (w) => {
var i,
tmp,
a,
b,
- h = this._h,
+ h = _h,
k = key,
h0 = h[0],
h1 = h[1],
@@ -413,5 +355,40 @@ export class Sha256 {
h[5] = (h[5] + h5) | 0;
h[6] = (h[6] + h6) | 0;
h[7] = (h[7] + h7) | 0;
+ };
+
+ for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {
+ block(c.subarray(16 * j, 16 * (j + 1)));
+
+ j += 1;
}
+
+ b.splice(0, 16 * j);
+
+ // finalize
+ var i,
+ b = _buffer,
+ h = _h;
+
+ // Round out and push the buffer
+
+ b = BitArray.concat(b, [BitArray.partial(1, 1)]);
+
+ // Round out the buffer to a multiple of 16 words, less the 2 length words.
+
+ for (i = b.length + 2; i & 15; i++) {
+ b.push(0);
+ }
+
+ // append the length
+
+ b.push(Math.floor(_length / 0x100000000));
+
+ b.push(_length | 0);
+
+ while (b.length) {
+ block(b.splice(0, 16));
+ }
+
+ return h;
}
From b2f77bc28c230c77499760911b8726088c9b40aa Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 11:11:01 -0500
Subject: [PATCH 28/60] more tidying
---
packages/kit/src/runtime/server/page/sjcl.js | 140 ++++++++-----------
1 file changed, 62 insertions(+), 78 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index d4841ea1eb74..328687fb3ccb 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -242,52 +242,38 @@ function precompute() {
/** @param {bitArray | string} data */
export function hash(data) {
- if (!key[0]) {
- precompute();
- }
-
- const _h = init.slice(0);
- /** @type {bitArray} */
- let _buffer = [];
- let _length = 0;
+ if (!key[0]) precompute();
if (typeof data === 'string') {
data = toBits(data);
}
- // update
- var i,
- b = (_buffer = BitArray.concat(_buffer, data)),
- ol = _length,
- nl = (_length = ol + BitArray.bitLength(data));
-
- if (nl > 9007199254740991) {
- throw new Error('Cannot hash more than 2^53 - 1 bits');
- }
+ const out = init.slice(0);
- var c = new Uint32Array(b);
+ /** @type {bitArray} */
+ let _buffer = data;
+ let _length = BitArray.bitLength(data);
- var j = 0;
+ // update
+ const c = new Uint32Array(_buffer);
/**
* Perform one cycle of SHA-256.
* @param {Uint32Array|bitArray} w one block of words.
*/
const block = (w) => {
- var i,
- tmp,
- a,
- b,
- h = _h,
- k = key,
- h0 = h[0],
- h1 = h[1],
- h2 = h[2],
- h3 = h[3],
- h4 = h[4],
- h5 = h[5],
- h6 = h[6],
- h7 = h[7];
+ var tmp;
+ var a;
+ var b;
+
+ let out0 = out[0];
+ let out1 = out[1];
+ let out2 = out[2];
+ let out3 = out[3];
+ let out4 = out[4];
+ let out5 = out[5];
+ let out6 = out[6];
+ let out7 = out[7];
/* Rationale for placement of |0 :
* If a value can overflow is original 32 bits by a factor of more than a few
@@ -295,15 +281,15 @@ export function hash(data) {
* 53-bit mantissa and lose precision.
*
* To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that
- * propagates around the loop, and on the hash state h[]. I don't believe
- * that the clamps on h4 and on h0 are strictly necessary, but it's close
- * (for h4 anyway), and better safe than sorry.
+ * propagates around the loop, and on the hash state out[]. I don't believe
+ * that the clamps on out4 and on out0 are strictly necessary, but it's close
+ * (for out4 anyway), and better safe than sorry.
*
- * The clamps on h[] are necessary for the output to be correct even in the
+ * The clamps on out[] are necessary for the output to be correct even in the
* common case and for short inputs.
*/
- for (i = 0; i < 64; i++) {
+ for (let i = 0; i < 64; i++) {
// load up the input word for this round
if (i < 16) {
@@ -323,72 +309,70 @@ export function hash(data) {
tmp =
tmp +
- h7 +
- ((h4 >>> 6) ^ (h4 >>> 11) ^ (h4 >>> 25) ^ (h4 << 26) ^ (h4 << 21) ^ (h4 << 7)) +
- (h6 ^ (h4 & (h5 ^ h6))) +
- k[i]; // | 0;
+ out7 +
+ ((out4 >>> 6) ^ (out4 >>> 11) ^ (out4 >>> 25) ^ (out4 << 26) ^ (out4 << 21) ^ (out4 << 7)) +
+ (out6 ^ (out4 & (out5 ^ out6))) +
+ key[i]; // | 0;
// shift register
+ out7 = out6;
+ out6 = out5;
+ out5 = out4;
- h7 = h6;
- h6 = h5;
- h5 = h4;
-
- h4 = (h3 + tmp) | 0;
+ out4 = (out3 + tmp) | 0;
- h3 = h2;
- h2 = h1;
- h1 = h0;
+ out3 = out2;
+ out2 = out1;
+ out1 = out0;
- h0 =
+ out0 =
(tmp +
- ((h1 & h2) ^ (h3 & (h1 ^ h2))) +
- ((h1 >>> 2) ^ (h1 >>> 13) ^ (h1 >>> 22) ^ (h1 << 30) ^ (h1 << 19) ^ (h1 << 10))) |
+ ((out1 & out2) ^ (out3 & (out1 ^ out2))) +
+ ((out1 >>> 2) ^
+ (out1 >>> 13) ^
+ (out1 >>> 22) ^
+ (out1 << 30) ^
+ (out1 << 19) ^
+ (out1 << 10))) |
0;
}
- h[0] = (h[0] + h0) | 0;
- h[1] = (h[1] + h1) | 0;
- h[2] = (h[2] + h2) | 0;
- h[3] = (h[3] + h3) | 0;
- h[4] = (h[4] + h4) | 0;
- h[5] = (h[5] + h5) | 0;
- h[6] = (h[6] + h6) | 0;
- h[7] = (h[7] + h7) | 0;
+ out[0] = (out[0] + out0) | 0;
+ out[1] = (out[1] + out1) | 0;
+ out[2] = (out[2] + out2) | 0;
+ out[3] = (out[3] + out3) | 0;
+ out[4] = (out[4] + out4) | 0;
+ out[5] = (out[5] + out5) | 0;
+ out[6] = (out[6] + out6) | 0;
+ out[7] = (out[7] + out7) | 0;
};
- for (i = 512 + ol - ((512 + ol) & 511); i <= nl; i += 512) {
+ let j = 0;
+ for (let i = 512; i <= _length; i += 512) {
block(c.subarray(16 * j, 16 * (j + 1)));
j += 1;
}
- b.splice(0, 16 * j);
+ _buffer.splice(0, 16 * j);
// finalize
- var i,
- b = _buffer,
- h = _h;
// Round out and push the buffer
-
- b = BitArray.concat(b, [BitArray.partial(1, 1)]);
+ _buffer = BitArray.concat(_buffer, [BitArray.partial(1, 1)]);
// Round out the buffer to a multiple of 16 words, less the 2 length words.
-
- for (i = b.length + 2; i & 15; i++) {
- b.push(0);
+ for (let i = _buffer.length + 2; i & 15; i++) {
+ _buffer.push(0);
}
// append the length
+ _buffer.push(Math.floor(_length / 0x100000000));
+ _buffer.push(_length | 0);
- b.push(Math.floor(_length / 0x100000000));
-
- b.push(_length | 0);
-
- while (b.length) {
- block(b.splice(0, 16));
+ while (_buffer.length) {
+ block(_buffer.splice(0, 16));
}
- return h;
+ return out;
}
From 58a22a1a1122eb2a89b5f87877dacdc84aec308b Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 11:14:20 -0500
Subject: [PATCH 29/60] hoist block
---
packages/kit/src/runtime/server/page/sjcl.js | 186 +++++++++----------
1 file changed, 93 insertions(+), 93 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 328687fb3ccb..5eb690a0de63 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -240,6 +240,97 @@ function precompute() {
}
}
+/**
+ * Perform one cycle of SHA-256.
+ * @param {Uint32Array} out
+ * @param {Uint32Array | bitArray} w one block of words.
+ */
+const block = (out, w) => {
+ var tmp;
+ var a;
+ var b;
+
+ let out0 = out[0];
+ let out1 = out[1];
+ let out2 = out[2];
+ let out3 = out[3];
+ let out4 = out[4];
+ let out5 = out[5];
+ let out6 = out[6];
+ let out7 = out[7];
+
+ /* Rationale for placement of |0 :
+ * If a value can overflow is original 32 bits by a factor of more than a few
+ * million (2^23 ish), there is a possibility that it might overflow the
+ * 53-bit mantissa and lose precision.
+ *
+ * To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that
+ * propagates around the loop, and on the hash state out[]. I don't believe
+ * that the clamps on out4 and on out0 are strictly necessary, but it's close
+ * (for out4 anyway), and better safe than sorry.
+ *
+ * The clamps on out[] are necessary for the output to be correct even in the
+ * common case and for short inputs.
+ */
+
+ for (let i = 0; i < 64; i++) {
+ // load up the input word for this round
+
+ if (i < 16) {
+ tmp = w[i];
+ } else {
+ a = w[(i + 1) & 15];
+
+ b = w[(i + 14) & 15];
+
+ tmp = w[i & 15] =
+ (((a >>> 7) ^ (a >>> 18) ^ (a >>> 3) ^ (a << 25) ^ (a << 14)) +
+ ((b >>> 17) ^ (b >>> 19) ^ (b >>> 10) ^ (b << 15) ^ (b << 13)) +
+ w[i & 15] +
+ w[(i + 9) & 15]) |
+ 0;
+ }
+
+ tmp =
+ tmp +
+ out7 +
+ ((out4 >>> 6) ^ (out4 >>> 11) ^ (out4 >>> 25) ^ (out4 << 26) ^ (out4 << 21) ^ (out4 << 7)) +
+ (out6 ^ (out4 & (out5 ^ out6))) +
+ key[i]; // | 0;
+
+ // shift register
+ out7 = out6;
+ out6 = out5;
+ out5 = out4;
+
+ out4 = (out3 + tmp) | 0;
+
+ out3 = out2;
+ out2 = out1;
+ out1 = out0;
+
+ out0 =
+ (tmp +
+ ((out1 & out2) ^ (out3 & (out1 ^ out2))) +
+ ((out1 >>> 2) ^
+ (out1 >>> 13) ^
+ (out1 >>> 22) ^
+ (out1 << 30) ^
+ (out1 << 19) ^
+ (out1 << 10))) |
+ 0;
+ }
+
+ out[0] = (out[0] + out0) | 0;
+ out[1] = (out[1] + out1) | 0;
+ out[2] = (out[2] + out2) | 0;
+ out[3] = (out[3] + out3) | 0;
+ out[4] = (out[4] + out4) | 0;
+ out[5] = (out[5] + out5) | 0;
+ out[6] = (out[6] + out6) | 0;
+ out[7] = (out[7] + out7) | 0;
+};
+
/** @param {bitArray | string} data */
export function hash(data) {
if (!key[0]) precompute();
@@ -257,100 +348,9 @@ export function hash(data) {
// update
const c = new Uint32Array(_buffer);
- /**
- * Perform one cycle of SHA-256.
- * @param {Uint32Array|bitArray} w one block of words.
- */
- const block = (w) => {
- var tmp;
- var a;
- var b;
-
- let out0 = out[0];
- let out1 = out[1];
- let out2 = out[2];
- let out3 = out[3];
- let out4 = out[4];
- let out5 = out[5];
- let out6 = out[6];
- let out7 = out[7];
-
- /* Rationale for placement of |0 :
- * If a value can overflow is original 32 bits by a factor of more than a few
- * million (2^23 ish), there is a possibility that it might overflow the
- * 53-bit mantissa and lose precision.
- *
- * To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that
- * propagates around the loop, and on the hash state out[]. I don't believe
- * that the clamps on out4 and on out0 are strictly necessary, but it's close
- * (for out4 anyway), and better safe than sorry.
- *
- * The clamps on out[] are necessary for the output to be correct even in the
- * common case and for short inputs.
- */
-
- for (let i = 0; i < 64; i++) {
- // load up the input word for this round
-
- if (i < 16) {
- tmp = w[i];
- } else {
- a = w[(i + 1) & 15];
-
- b = w[(i + 14) & 15];
-
- tmp = w[i & 15] =
- (((a >>> 7) ^ (a >>> 18) ^ (a >>> 3) ^ (a << 25) ^ (a << 14)) +
- ((b >>> 17) ^ (b >>> 19) ^ (b >>> 10) ^ (b << 15) ^ (b << 13)) +
- w[i & 15] +
- w[(i + 9) & 15]) |
- 0;
- }
-
- tmp =
- tmp +
- out7 +
- ((out4 >>> 6) ^ (out4 >>> 11) ^ (out4 >>> 25) ^ (out4 << 26) ^ (out4 << 21) ^ (out4 << 7)) +
- (out6 ^ (out4 & (out5 ^ out6))) +
- key[i]; // | 0;
-
- // shift register
- out7 = out6;
- out6 = out5;
- out5 = out4;
-
- out4 = (out3 + tmp) | 0;
-
- out3 = out2;
- out2 = out1;
- out1 = out0;
-
- out0 =
- (tmp +
- ((out1 & out2) ^ (out3 & (out1 ^ out2))) +
- ((out1 >>> 2) ^
- (out1 >>> 13) ^
- (out1 >>> 22) ^
- (out1 << 30) ^
- (out1 << 19) ^
- (out1 << 10))) |
- 0;
- }
-
- out[0] = (out[0] + out0) | 0;
- out[1] = (out[1] + out1) | 0;
- out[2] = (out[2] + out2) | 0;
- out[3] = (out[3] + out3) | 0;
- out[4] = (out[4] + out4) | 0;
- out[5] = (out[5] + out5) | 0;
- out[6] = (out[6] + out6) | 0;
- out[7] = (out[7] + out7) | 0;
- };
-
let j = 0;
for (let i = 512; i <= _length; i += 512) {
- block(c.subarray(16 * j, 16 * (j + 1)));
-
+ block(out, c.subarray(16 * j, 16 * (j + 1)));
j += 1;
}
@@ -371,7 +371,7 @@ export function hash(data) {
_buffer.push(_length | 0);
while (_buffer.length) {
- block(_buffer.splice(0, 16));
+ block(out, _buffer.splice(0, 16));
}
return out;
From 277658e2252678e6b834af68fbd53818f97a06ad Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 11:33:53 -0500
Subject: [PATCH 30/60] more tidying
---
packages/kit/src/runtime/server/page/sjcl.js | 37 +++++++++-----------
1 file changed, 17 insertions(+), 20 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 5eb690a0de63..8e3b41ef0de1 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -21,9 +21,10 @@ function swap_endianness(uint32array) {
function toBits(str) {
str = unescape(encodeURIComponent(str));
- var out = [],
- i,
- tmp = 0;
+ const out = [];
+
+ let i;
+ let tmp = 0;
for (i = 0; i < str.length; i++) {
tmp = (tmp << 8) | str.charCodeAt(i);
@@ -331,47 +332,43 @@ const block = (out, w) => {
out[7] = (out[7] + out7) | 0;
};
-/** @param {bitArray | string} data */
+/** @param {string} data */
export function hash(data) {
if (!key[0]) precompute();
- if (typeof data === 'string') {
- data = toBits(data);
- }
-
const out = init.slice(0);
/** @type {bitArray} */
- let _buffer = data;
- let _length = BitArray.bitLength(data);
+ let buffer = toBits(data);
+ let length = BitArray.bitLength(buffer);
// update
- const c = new Uint32Array(_buffer);
+ const c = new Uint32Array(buffer);
let j = 0;
- for (let i = 512; i <= _length; i += 512) {
+ for (let i = 512; i <= length; i += 512) {
block(out, c.subarray(16 * j, 16 * (j + 1)));
j += 1;
}
- _buffer.splice(0, 16 * j);
+ buffer.splice(0, 16 * j);
// finalize
// Round out and push the buffer
- _buffer = BitArray.concat(_buffer, [BitArray.partial(1, 1)]);
+ buffer = BitArray.concat(buffer, [BitArray.partial(1, 1)]);
// Round out the buffer to a multiple of 16 words, less the 2 length words.
- for (let i = _buffer.length + 2; i & 15; i++) {
- _buffer.push(0);
+ for (let i = buffer.length + 2; i & 15; i++) {
+ buffer.push(0);
}
// append the length
- _buffer.push(Math.floor(_length / 0x100000000));
- _buffer.push(_length | 0);
+ buffer.push(Math.floor(length / 0x100000000));
+ buffer.push(length | 0);
- while (_buffer.length) {
- block(out, _buffer.splice(0, 16));
+ while (buffer.length) {
+ block(out, buffer.splice(0, 16));
}
return out;
From 4517b3bedc9fd14a16e6fb1f198e91909b5f5599 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 12:18:51 -0500
Subject: [PATCH 31/60] use textdecoder
---
packages/kit/src/runtime/server/page/crypto.spec.js | 6 +++++-
packages/kit/src/runtime/server/page/sjcl.js | 10 +++++-----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/crypto.spec.js b/packages/kit/src/runtime/server/page/crypto.spec.js
index d620f39b514a..dda6e0985302 100644
--- a/packages/kit/src/runtime/server/page/crypto.spec.js
+++ b/packages/kit/src/runtime/server/page/crypto.spec.js
@@ -3,7 +3,11 @@ import * as assert from 'uvu/assert';
import crypto from 'crypto';
import { sha256 } from './crypto.js';
-const inputs = ['the quick brown fox jumps over the lazy dog', '工欲善其事,必先利其器'];
+const inputs = [
+ 'abc',
+ 'the quick brown fox jumps over the lazy dog',
+ '工欲善其事,必先利其器'
+].slice(0);
inputs.forEach((input) => {
test(input, () => {
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 8e3b41ef0de1..4e2af2477139 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -19,25 +19,25 @@ function swap_endianness(uint32array) {
/** @param {string} str */
function toBits(str) {
- str = unescape(encodeURIComponent(str));
+ let uint8array = new TextEncoder().encode(str);
const out = [];
let i;
let tmp = 0;
- for (i = 0; i < str.length; i++) {
- tmp = (tmp << 8) | str.charCodeAt(i);
+ for (i = 0; i < uint8array.length; i++) {
+ tmp = (tmp << 8) | uint8array[i];
if ((i & 3) === 3) {
out.push(tmp);
-
tmp = 0;
}
}
if (i & 3) {
- out.push(BitArray.partial(8 * (i & 3), tmp));
+ let partial = BitArray.partial(8 * (i & 3), tmp);
+ out.push(partial);
}
return out;
From 90de3b5f731f1f1c151243261bd27ec1f45253d1 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 12:19:12 -0500
Subject: [PATCH 32/60] create textencoder once
---
packages/kit/src/runtime/server/page/sjcl.js | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 4e2af2477139..eff5a0e51a16 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -17,9 +17,11 @@ function swap_endianness(uint32array) {
return uint32array;
}
+const encoder = new TextEncoder();
+
/** @param {string} str */
function toBits(str) {
- let uint8array = new TextEncoder().encode(str);
+ let uint8array = encoder.encode(str);
const out = [];
From 532471c65a40247c0ba0b73d4c6704a80818c6d5 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 12:21:02 -0500
Subject: [PATCH 33/60] more tidying
---
packages/kit/src/runtime/server/page/sjcl.js | 21 +++++++++-----------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index eff5a0e51a16..bd4a8204f344 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -210,33 +210,30 @@ const key = new Uint32Array(64);
* Function to precompute _init and _key.
*/
function precompute() {
- var i = 0,
- prime = 2,
- factor,
- isPrime;
-
/** @param {number} x */
function frac(x) {
return (x - Math.floor(x)) * 0x100000000;
}
- for (; i < 64; prime++) {
- isPrime = true;
+ let prime = 2;
+
+ for (let i = 0; i < 64; prime++) {
+ let is_prime = true;
- for (factor = 2; factor * factor <= prime; factor++) {
+ for (let factor = 2; factor * factor <= prime; factor++) {
if (prime % factor === 0) {
- isPrime = false;
+ is_prime = false;
break;
}
}
- if (isPrime) {
+ if (is_prime) {
if (i < 8) {
- init[i] = frac(Math.pow(prime, 1 / 2));
+ init[i] = frac(prime ** (1 / 2));
}
- key[i] = frac(Math.pow(prime, 1 / 3));
+ key[i] = frac(prime ** (1 / 3));
i++;
}
From 9b87c5444c30dff3ab017ed05d983b6851c204ea Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 13:52:39 -0500
Subject: [PATCH 34/60] simplify further
---
.../src/runtime/server/page/crypto.spec.js | 3 ++-
packages/kit/src/runtime/server/page/sjcl.js | 21 +++++--------------
2 files changed, 7 insertions(+), 17 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/crypto.spec.js b/packages/kit/src/runtime/server/page/crypto.spec.js
index dda6e0985302..a46a84018462 100644
--- a/packages/kit/src/runtime/server/page/crypto.spec.js
+++ b/packages/kit/src/runtime/server/page/crypto.spec.js
@@ -14,7 +14,8 @@ inputs.forEach((input) => {
const expected_bytes = crypto.createHash('sha256').update(input, 'utf-8').digest();
const expected = expected_bytes.toString('base64');
- assert.equal(sha256(input), expected);
+ const actual = sha256(input);
+ assert.equal(actual, expected);
});
});
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index bd4a8204f344..7aba1cae7b8f 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -243,7 +243,7 @@ function precompute() {
/**
* Perform one cycle of SHA-256.
* @param {Uint32Array} out
- * @param {Uint32Array | bitArray} w one block of words.
+ * @param {Uint32Array} w one block of words.
*/
const block = (out, w) => {
var tmp;
@@ -341,19 +341,6 @@ export function hash(data) {
let buffer = toBits(data);
let length = BitArray.bitLength(buffer);
- // update
- const c = new Uint32Array(buffer);
-
- let j = 0;
- for (let i = 512; i <= length; i += 512) {
- block(out, c.subarray(16 * j, 16 * (j + 1)));
- j += 1;
- }
-
- buffer.splice(0, 16 * j);
-
- // finalize
-
// Round out and push the buffer
buffer = BitArray.concat(buffer, [BitArray.partial(1, 1)]);
@@ -366,8 +353,10 @@ export function hash(data) {
buffer.push(Math.floor(length / 0x100000000));
buffer.push(length | 0);
- while (buffer.length) {
- block(out, buffer.splice(0, 16));
+ const uint32array = new Uint32Array(buffer);
+
+ for (let i = 0; i < uint32array.length; i += 16) {
+ block(out, uint32array.subarray(i, i + 16));
}
return out;
From dfb2c5d7979a79fbf845b3c11bcae29efb2f15d4 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 14:45:33 -0500
Subject: [PATCH 35/60] more tidying
---
packages/kit/src/runtime/server/page/sjcl.js | 106 +++++++++----------
1 file changed, 48 insertions(+), 58 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 7aba1cae7b8f..ee9d3838b073 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -1,3 +1,5 @@
+const encoder = new TextEncoder();
+
/** @param {Uint32Array} uint32array' */
function swap_endianness(uint32array) {
const uint8array = new Uint8Array(uint32array.buffer);
@@ -17,7 +19,27 @@ function swap_endianness(uint32array) {
return uint32array;
}
-const encoder = new TextEncoder();
+/**
+ * Get the number of bits used by a partial word.
+ * @param {number} n The partial word.
+ * @return {number} The number of bits used by the partial word.
+ */
+function count_bits_in_word(n) {
+ return Math.round(n / 0x10000000000) || 32;
+}
+
+/**
+ * Find the length of an array of bits.
+ * @param {Uint32Array} a The array.
+ * @return {number} The length of a, in bits.
+ */
+function count_bits_in_buffer(a) {
+ const l = a.length;
+ if (l === 0) return 0;
+
+ const x = a[l - 1];
+ return (l - 1) * 32 + count_bits_in_word(x);
+}
/** @param {string} str */
function toBits(str) {
@@ -75,41 +97,19 @@ function toBits(str) {
const BitArray = {
/**
* Concatenate two bit arrays.
- * @param {bitArray} a1 The first array.
- * @param {bitArray} a2 The second array.
- * @return {bitArray} The concatenation of a1 and a2.
- */
- concat: function (a1, a2) {
- if (a1.length === 0 || a2.length === 0) {
- return a1.concat(a2);
- }
-
- var last = a1[a1.length - 1],
- shift = BitArray.getPartial(last);
-
- if (shift === 32) {
- return a1.concat(a2);
- } else {
- return BitArray._shiftRight(a2, shift, last | 0, a1.slice(0, a1.length - 1));
- }
- },
-
- /**
- * Find the length of an array of bits.
- * @param {bitArray} a The array.
- * @return {number} The length of a, in bits.
+ * @param {bitArray} array The array.
+ * @param {number} n The number.
+ * @return {bitArray} The concatenation of a1 and n.
*/
- bitLength: function (a) {
- var l = a.length,
- x;
+ concat: function (array, n) {
+ if (array.length === 0) return [n];
- if (l === 0) {
- return 0;
- }
+ const last = array[array.length - 1];
+ const shift = count_bits_in_word(last);
- x = a[l - 1];
+ if (shift === 32) return array.concat(n);
- return (l - 1) * 32 + BitArray.getPartial(x);
+ return BitArray._shiftRight(n, shift, last, array.slice(0, array.length - 1));
},
/**
@@ -127,27 +127,14 @@ const BitArray = {
return (_end ? x | 0 : x << (32 - len)) + len * 0x10000000000;
},
- /**
- * Get the number of bits used by a partial word.
- * @param {Number} x The partial word.
- * @return {Number} The number of bits used by the partial word.
- */
- getPartial: function (x) {
- return Math.round(x / 0x10000000000) || 32;
- },
-
/** Shift an array right.
- * @param {bitArray} a The array to shift.
+ * @param {number} a The array to shift.
* @param {number} shift The number of bits to shift.
* @param {number} [carry] A byte to carry in
* @param {bitArray} [out] An array to prepend to the output.
* @private
*/
_shiftRight: function (a, shift, carry = 0, out = []) {
- var i,
- last2 = 0,
- shift2;
-
for (; shift >= 32; shift -= 32) {
out.push(carry);
@@ -158,15 +145,11 @@ const BitArray = {
return out.concat(a);
}
- for (i = 0; i < a.length; i++) {
- out.push(carry | (a[i] >>> shift));
+ out.push(carry | (a >>> shift));
+ carry = a << (32 - shift);
- carry = a[i] << (32 - shift);
- }
-
- last2 = a.length ? a[a.length - 1] : 0;
-
- shift2 = BitArray.getPartial(last2);
+ const last2 = a;
+ const shift2 = count_bits_in_word(last2);
out.push(
BitArray.partial(
@@ -337,12 +320,19 @@ export function hash(data) {
const out = init.slice(0);
+ let uint8array = encoder.encode(data);
+ let l = 4 * Math.ceil(uint8array.length / 4);
+ if (uint8array.length < l) {
+ const tmp = new Uint8Array(l);
+ tmp.set(uint8array);
+ uint8array = tmp;
+ }
/** @type {bitArray} */
let buffer = toBits(data);
- let length = BitArray.bitLength(buffer);
+ let bits = count_bits_in_buffer(buffer);
// Round out and push the buffer
- buffer = BitArray.concat(buffer, [BitArray.partial(1, 1)]);
+ buffer = BitArray.concat(buffer, 0xff80000000);
// Round out the buffer to a multiple of 16 words, less the 2 length words.
for (let i = buffer.length + 2; i & 15; i++) {
@@ -350,8 +340,8 @@ export function hash(data) {
}
// append the length
- buffer.push(Math.floor(length / 0x100000000));
- buffer.push(length | 0);
+ buffer.push(Math.floor(bits / 0x100000000)); // this will always be zero for us
+ buffer.push(bits | 0);
const uint32array = new Uint32Array(buffer);
From 720ad9191bc98b8fd98533744d9199d6de44c952 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 15:23:57 -0500
Subject: [PATCH 36/60] more tidying
---
.../src/runtime/server/page/crypto.spec.js | 3 +-
packages/kit/src/runtime/server/page/sjcl.js | 69 ++++++++++++-------
2 files changed, 48 insertions(+), 24 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/crypto.spec.js b/packages/kit/src/runtime/server/page/crypto.spec.js
index a46a84018462..115986537a86 100644
--- a/packages/kit/src/runtime/server/page/crypto.spec.js
+++ b/packages/kit/src/runtime/server/page/crypto.spec.js
@@ -4,7 +4,8 @@ import crypto from 'crypto';
import { sha256 } from './crypto.js';
const inputs = [
- 'abc',
+ '',
+ 'abcd',
'the quick brown fox jumps over the lazy dog',
'工欲善其事,必先利其器'
].slice(0);
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index ee9d3838b073..16be9fe6412c 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -42,7 +42,7 @@ function count_bits_in_buffer(a) {
}
/** @param {string} str */
-function toBits(str) {
+function to_bits(str) {
let uint8array = encoder.encode(str);
const out = [];
@@ -97,19 +97,41 @@ function toBits(str) {
const BitArray = {
/**
* Concatenate two bit arrays.
- * @param {bitArray} array The array.
- * @param {number} n The number.
- * @return {bitArray} The concatenation of a1 and n.
+ * @param {bitArray} a1 The first array.
+ * @param {bitArray} a2 The second array.
+ * @return {bitArray} The concatenation of a1 and a2.
*/
- concat: function (array, n) {
- if (array.length === 0) return [n];
+ concat: function (a1, a2) {
+ if (a1.length === 0) {
+ return a1.concat(a2);
+ }
- const last = array[array.length - 1];
+ const last = a1[a1.length - 1];
const shift = count_bits_in_word(last);
- if (shift === 32) return array.concat(n);
+ if (shift === 32) {
+ return a1.concat(a2);
+ } else {
+ return BitArray._shiftRight(a2, shift, last | 0, a1.slice(0, a1.length - 1));
+ }
+ },
+
+ /**
+ * Find the length of an array of bits.
+ * @param {bitArray} a The array.
+ * @return {number} The length of a, in bits.
+ */
+ bitLength: function (a) {
+ var l = a.length,
+ x;
- return BitArray._shiftRight(n, shift, last, array.slice(0, array.length - 1));
+ if (l === 0) {
+ return 0;
+ }
+
+ x = a[l - 1];
+
+ return (l - 1) * 32 + count_bits_in_word(x);
},
/**
@@ -128,13 +150,17 @@ const BitArray = {
},
/** Shift an array right.
- * @param {number} a The array to shift.
+ * @param {bitArray} a The array to shift.
* @param {number} shift The number of bits to shift.
* @param {number} [carry] A byte to carry in
* @param {bitArray} [out] An array to prepend to the output.
* @private
*/
_shiftRight: function (a, shift, carry = 0, out = []) {
+ var i,
+ last2 = 0,
+ shift2;
+
for (; shift >= 32; shift -= 32) {
out.push(carry);
@@ -145,11 +171,15 @@ const BitArray = {
return out.concat(a);
}
- out.push(carry | (a >>> shift));
- carry = a << (32 - shift);
+ for (i = 0; i < a.length; i++) {
+ out.push(carry | (a[i] >>> shift));
- const last2 = a;
- const shift2 = count_bits_in_word(last2);
+ carry = a[i] << (32 - shift);
+ }
+
+ last2 = a.length ? a[a.length - 1] : 0;
+
+ shift2 = count_bits_in_word(last2);
out.push(
BitArray.partial(
@@ -320,19 +350,12 @@ export function hash(data) {
const out = init.slice(0);
- let uint8array = encoder.encode(data);
- let l = 4 * Math.ceil(uint8array.length / 4);
- if (uint8array.length < l) {
- const tmp = new Uint8Array(l);
- tmp.set(uint8array);
- uint8array = tmp;
- }
/** @type {bitArray} */
- let buffer = toBits(data);
+ let buffer = to_bits(data);
let bits = count_bits_in_buffer(buffer);
// Round out and push the buffer
- buffer = BitArray.concat(buffer, 0xff80000000);
+ buffer = BitArray.concat(buffer, [0xff80000000]);
// Round out the buffer to a multiple of 16 words, less the 2 length words.
for (let i = buffer.length + 2; i & 15; i++) {
From 1dc0a050ae1c12828117badf5d44bf549ec3d459 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 15:36:04 -0500
Subject: [PATCH 37/60] simplify
---
packages/kit/src/runtime/server/page/sjcl.js | 25 ++++----------------
1 file changed, 5 insertions(+), 20 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 16be9fe6412c..e2bc2b058b69 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -28,24 +28,11 @@ function count_bits_in_word(n) {
return Math.round(n / 0x10000000000) || 32;
}
-/**
- * Find the length of an array of bits.
- * @param {Uint32Array} a The array.
- * @return {number} The length of a, in bits.
- */
-function count_bits_in_buffer(a) {
- const l = a.length;
- if (l === 0) return 0;
-
- const x = a[l - 1];
- return (l - 1) * 32 + count_bits_in_word(x);
-}
-
/** @param {string} str */
function to_bits(str) {
let uint8array = encoder.encode(str);
- const out = [];
+ const buffer = [];
let i;
let tmp = 0;
@@ -54,17 +41,17 @@ function to_bits(str) {
tmp = (tmp << 8) | uint8array[i];
if ((i & 3) === 3) {
- out.push(tmp);
+ buffer.push(tmp);
tmp = 0;
}
}
if (i & 3) {
let partial = BitArray.partial(8 * (i & 3), tmp);
- out.push(partial);
+ buffer.push(partial);
}
- return out;
+ return { buffer, bits: uint8array.length * 8 };
}
/**
@@ -350,9 +337,7 @@ export function hash(data) {
const out = init.slice(0);
- /** @type {bitArray} */
- let buffer = to_bits(data);
- let bits = count_bits_in_buffer(buffer);
+ let { buffer, bits } = to_bits(data);
// Round out and push the buffer
buffer = BitArray.concat(buffer, [0xff80000000]);
From 2b3ca8fa857c742e5a1d126a323c52a0106cfc2f Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 16:39:43 -0500
Subject: [PATCH 38/60] radically simplify
---
.../src/runtime/server/page/crypto.spec.js | 1 +
packages/kit/src/runtime/server/page/sjcl.js | 187 ++----------------
2 files changed, 16 insertions(+), 172 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/crypto.spec.js b/packages/kit/src/runtime/server/page/crypto.spec.js
index 115986537a86..cf967e184c97 100644
--- a/packages/kit/src/runtime/server/page/crypto.spec.js
+++ b/packages/kit/src/runtime/server/page/crypto.spec.js
@@ -4,6 +4,7 @@ import crypto from 'crypto';
import { sha256 } from './crypto.js';
const inputs = [
+ 'hello world',
'',
'abcd',
'the quick brown fox jumps over the lazy dog',
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index e2bc2b058b69..2e27bbd4eaad 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -1,6 +1,6 @@
const encoder = new TextEncoder();
-/** @param {Uint32Array} uint32array' */
+/** @param {Uint32Array} uint32array */
function swap_endianness(uint32array) {
const uint8array = new Uint8Array(uint32array.buffer);
@@ -19,167 +19,25 @@ function swap_endianness(uint32array) {
return uint32array;
}
-/**
- * Get the number of bits used by a partial word.
- * @param {number} n The partial word.
- * @return {number} The number of bits used by the partial word.
- */
-function count_bits_in_word(n) {
- return Math.round(n / 0x10000000000) || 32;
-}
-
/** @param {string} str */
function to_bits(str) {
- let uint8array = encoder.encode(str);
+ const encoded = encoder.encode(str);
+ const length = encoded.length * 8;
- const buffer = [];
+ const size = 512 * Math.ceil((length + 129) / 512);
+ const bytes = new Uint8Array(size / 8);
+ bytes.set(encoded);
+ bytes[encoded.length] = 0b10000000;
- let i;
- let tmp = 0;
+ swap_endianness(new Uint32Array(bytes.buffer));
- for (i = 0; i < uint8array.length; i++) {
- tmp = (tmp << 8) | uint8array[i];
-
- if ((i & 3) === 3) {
- buffer.push(tmp);
- tmp = 0;
- }
- }
+ const words = new Uint32Array(bytes.buffer);
+ words[words.length - 2] = Math.floor(length / 0x100000000); // this will always be zero for us
+ words[words.length - 1] = length;
- if (i & 3) {
- let partial = BitArray.partial(8 * (i & 3), tmp);
- buffer.push(partial);
- }
-
- return { buffer, bits: uint8array.length * 8 };
+ return words;
}
-/**
- * Arrays of bits, encoded as arrays of Numbers.
- * @namespace
- * @description
- *
- * These objects are the currency accepted by SJCL's crypto functions.
- *
- *
- *
- * Most of our crypto primitives operate on arrays of 4-byte words internally,
- * but many of them can take arguments that are not a multiple of 4 bytes.
- * This library encodes arrays of bits (whose size need not be a multiple of 8
- * bits) as arrays of 32-bit words. The bits are packed, big-endian, into an
- * array of words, 32 bits at a time. Since the words are double-precision
- * floating point numbers, they fit some extra data. We use this (in a private,
- * possibly-changing manner) to encode the number of bits actually present
- * in the last word of the array.
- *
- *
- *
- * Because bitwise ops clear this out-of-band data, these arrays can be passed
- * to ciphers like AES which want arrays of words.
- *
- */
-
-/** @typedef {number[]} bitArray */
-
-const BitArray = {
- /**
- * Concatenate two bit arrays.
- * @param {bitArray} a1 The first array.
- * @param {bitArray} a2 The second array.
- * @return {bitArray} The concatenation of a1 and a2.
- */
- concat: function (a1, a2) {
- if (a1.length === 0) {
- return a1.concat(a2);
- }
-
- const last = a1[a1.length - 1];
- const shift = count_bits_in_word(last);
-
- if (shift === 32) {
- return a1.concat(a2);
- } else {
- return BitArray._shiftRight(a2, shift, last | 0, a1.slice(0, a1.length - 1));
- }
- },
-
- /**
- * Find the length of an array of bits.
- * @param {bitArray} a The array.
- * @return {number} The length of a, in bits.
- */
- bitLength: function (a) {
- var l = a.length,
- x;
-
- if (l === 0) {
- return 0;
- }
-
- x = a[l - 1];
-
- return (l - 1) * 32 + count_bits_in_word(x);
- },
-
- /**
- * Make a partial word for a bit array.
- * @param {Number} len The number of bits in the word.
- * @param {Number} x The bits.
- * @param {Number} [_end=0] Pass 1 if x has already been shifted to the high side.
- * @return {Number} The partial word.
- */
- partial: function (len, x, _end) {
- if (len === 32) {
- return x;
- }
-
- return (_end ? x | 0 : x << (32 - len)) + len * 0x10000000000;
- },
-
- /** Shift an array right.
- * @param {bitArray} a The array to shift.
- * @param {number} shift The number of bits to shift.
- * @param {number} [carry] A byte to carry in
- * @param {bitArray} [out] An array to prepend to the output.
- * @private
- */
- _shiftRight: function (a, shift, carry = 0, out = []) {
- var i,
- last2 = 0,
- shift2;
-
- for (; shift >= 32; shift -= 32) {
- out.push(carry);
-
- carry = 0;
- }
-
- if (shift === 0) {
- return out.concat(a);
- }
-
- for (i = 0; i < a.length; i++) {
- out.push(carry | (a[i] >>> shift));
-
- carry = a[i] << (32 - shift);
- }
-
- last2 = a.length ? a[a.length - 1] : 0;
-
- shift2 = count_bits_in_word(last2);
-
- out.push(
- BitArray.partial(
- (shift + shift2) & 31,
- shift + shift2 > 32 ? carry : /** @type {number} */ (out.pop()),
- 1
- )
- );
-
- return out;
- }
-};
-
/**
* The SHA-256 initialization vector, to be precomputed.
*/
@@ -336,25 +194,10 @@ export function hash(data) {
if (!key[0]) precompute();
const out = init.slice(0);
+ const array = to_bits(data);
- let { buffer, bits } = to_bits(data);
-
- // Round out and push the buffer
- buffer = BitArray.concat(buffer, [0xff80000000]);
-
- // Round out the buffer to a multiple of 16 words, less the 2 length words.
- for (let i = buffer.length + 2; i & 15; i++) {
- buffer.push(0);
- }
-
- // append the length
- buffer.push(Math.floor(bits / 0x100000000)); // this will always be zero for us
- buffer.push(bits | 0);
-
- const uint32array = new Uint32Array(buffer);
-
- for (let i = 0; i < uint32array.length; i += 16) {
- block(out, uint32array.subarray(i, i + 16));
+ for (let i = 0; i < array.length; i += 16) {
+ block(out, array.subarray(i, i + 16));
}
return out;
From 65be836569d4b4bdd67829e487de9e93734317d3 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 16:48:16 -0500
Subject: [PATCH 39/60] simplify further
---
packages/kit/src/runtime/server/page/sjcl.js | 273 ++++++++-----------
1 file changed, 121 insertions(+), 152 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
index 2e27bbd4eaad..b26f49271cc5 100644
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ b/packages/kit/src/runtime/server/page/sjcl.js
@@ -1,72 +1,12 @@
const encoder = new TextEncoder();
-/** @param {Uint32Array} uint32array */
-function swap_endianness(uint32array) {
- const uint8array = new Uint8Array(uint32array.buffer);
-
- for (let i = 0; i < uint8array.length; i += 4) {
- const a = uint8array[i + 0];
- const b = uint8array[i + 1];
- const c = uint8array[i + 2];
- const d = uint8array[i + 3];
-
- uint8array[i + 0] = d;
- uint8array[i + 1] = c;
- uint8array[i + 2] = b;
- uint8array[i + 3] = a;
- }
-
- return uint32array;
-}
-
-/** @param {string} str */
-function to_bits(str) {
- const encoded = encoder.encode(str);
- const length = encoded.length * 8;
-
- const size = 512 * Math.ceil((length + 129) / 512);
- const bytes = new Uint8Array(size / 8);
- bytes.set(encoded);
- bytes[encoded.length] = 0b10000000;
-
- swap_endianness(new Uint32Array(bytes.buffer));
-
- const words = new Uint32Array(bytes.buffer);
- words[words.length - 2] = Math.floor(length / 0x100000000); // this will always be zero for us
- words[words.length - 1] = length;
-
- return words;
-}
-
-/**
- * The SHA-256 initialization vector, to be precomputed.
- */
+/** The SHA-256 initialization vector */
const init = new Uint32Array(8);
-/*
- * init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19],
- */
-
-/**
- * The SHA-256 hash key, to be precomputed.
- */
+/** The SHA-256 hash key */
const key = new Uint32Array(64);
-/*
- * key:
- * [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- * 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- * 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- * 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- * 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- * 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- * 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- * 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2],
- */
-
-/**
- * Function to precompute _init and _key.
- */
+/** Function to precompute init and key. */
function precompute() {
/** @param {number} x */
function frac(x) {
@@ -98,106 +38,135 @@ function precompute() {
}
}
-/**
- * Perform one cycle of SHA-256.
- * @param {Uint32Array} out
- * @param {Uint32Array} w one block of words.
- */
-const block = (out, w) => {
- var tmp;
- var a;
- var b;
-
- let out0 = out[0];
- let out1 = out[1];
- let out2 = out[2];
- let out3 = out[3];
- let out4 = out[4];
- let out5 = out[5];
- let out6 = out[6];
- let out7 = out[7];
-
- /* Rationale for placement of |0 :
- * If a value can overflow is original 32 bits by a factor of more than a few
- * million (2^23 ish), there is a possibility that it might overflow the
- * 53-bit mantissa and lose precision.
- *
- * To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that
- * propagates around the loop, and on the hash state out[]. I don't believe
- * that the clamps on out4 and on out0 are strictly necessary, but it's close
- * (for out4 anyway), and better safe than sorry.
- *
- * The clamps on out[] are necessary for the output to be correct even in the
- * common case and for short inputs.
- */
-
- for (let i = 0; i < 64; i++) {
- // load up the input word for this round
-
- if (i < 16) {
- tmp = w[i];
- } else {
- a = w[(i + 1) & 15];
-
- b = w[(i + 14) & 15];
-
- tmp = w[i & 15] =
- (((a >>> 7) ^ (a >>> 18) ^ (a >>> 3) ^ (a << 25) ^ (a << 14)) +
- ((b >>> 17) ^ (b >>> 19) ^ (b >>> 10) ^ (b << 15) ^ (b << 13)) +
- w[i & 15] +
- w[(i + 9) & 15]) |
- 0;
- }
+/** @param {string} str */
+function encode(str) {
+ const encoded = encoder.encode(str);
+ const length = encoded.length * 8;
- tmp =
- tmp +
- out7 +
- ((out4 >>> 6) ^ (out4 >>> 11) ^ (out4 >>> 25) ^ (out4 << 26) ^ (out4 << 21) ^ (out4 << 7)) +
- (out6 ^ (out4 & (out5 ^ out6))) +
- key[i]; // | 0;
-
- // shift register
- out7 = out6;
- out6 = out5;
- out5 = out4;
-
- out4 = (out3 + tmp) | 0;
-
- out3 = out2;
- out2 = out1;
- out1 = out0;
-
- out0 =
- (tmp +
- ((out1 & out2) ^ (out3 & (out1 ^ out2))) +
- ((out1 >>> 2) ^
- (out1 >>> 13) ^
- (out1 >>> 22) ^
- (out1 << 30) ^
- (out1 << 19) ^
- (out1 << 10))) |
- 0;
+ // result should be a multiple of 512 bits in length,
+ // with room for a 1 (after the data) and two 32-bit
+ // words containing the original input bit length
+ const size = 512 * Math.ceil((length + 65) / 512);
+ const bytes = new Uint8Array(size / 8);
+ bytes.set(encoded);
+
+ // append a 1
+ bytes[encoded.length] = 0b10000000;
+
+ // swap endianness
+ for (let i = 0; i < bytes.length; i += 4) {
+ const a = bytes[i + 0];
+ const b = bytes[i + 1];
+ const c = bytes[i + 2];
+ const d = bytes[i + 3];
+
+ bytes[i + 0] = d;
+ bytes[i + 1] = c;
+ bytes[i + 2] = b;
+ bytes[i + 3] = a;
}
- out[0] = (out[0] + out0) | 0;
- out[1] = (out[1] + out1) | 0;
- out[2] = (out[2] + out2) | 0;
- out[3] = (out[3] + out3) | 0;
- out[4] = (out[4] + out4) | 0;
- out[5] = (out[5] + out5) | 0;
- out[6] = (out[6] + out6) | 0;
- out[7] = (out[7] + out7) | 0;
-};
+ // add the input bit length
+ const words = new Uint32Array(bytes.buffer);
+ words[words.length - 2] = Math.floor(length / 0x100000000); // this will always be zero for us
+ words[words.length - 1] = length;
+
+ return words;
+}
/** @param {string} data */
export function hash(data) {
if (!key[0]) precompute();
const out = init.slice(0);
- const array = to_bits(data);
+ const array = encode(data);
for (let i = 0; i < array.length; i += 16) {
- block(out, array.subarray(i, i + 16));
+ const w = array.subarray(i, i + 16);
+
+ let tmp;
+ let a;
+ let b;
+
+ let out0 = out[0];
+ let out1 = out[1];
+ let out2 = out[2];
+ let out3 = out[3];
+ let out4 = out[4];
+ let out5 = out[5];
+ let out6 = out[6];
+ let out7 = out[7];
+
+ /* Rationale for placement of |0 :
+ * If a value can overflow is original 32 bits by a factor of more than a few
+ * million (2^23 ish), there is a possibility that it might overflow the
+ * 53-bit mantissa and lose precision.
+ *
+ * To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that
+ * propagates around the loop, and on the hash state out[]. I don't believe
+ * that the clamps on out4 and on out0 are strictly necessary, but it's close
+ * (for out4 anyway), and better safe than sorry.
+ *
+ * The clamps on out[] are necessary for the output to be correct even in the
+ * common case and for short inputs.
+ */
+
+ for (let i = 0; i < 64; i++) {
+ // load up the input word for this round
+
+ if (i < 16) {
+ tmp = w[i];
+ } else {
+ a = w[(i + 1) & 15];
+
+ b = w[(i + 14) & 15];
+
+ tmp = w[i & 15] =
+ (((a >>> 7) ^ (a >>> 18) ^ (a >>> 3) ^ (a << 25) ^ (a << 14)) +
+ ((b >>> 17) ^ (b >>> 19) ^ (b >>> 10) ^ (b << 15) ^ (b << 13)) +
+ w[i & 15] +
+ w[(i + 9) & 15]) |
+ 0;
+ }
+
+ tmp =
+ tmp +
+ out7 +
+ ((out4 >>> 6) ^ (out4 >>> 11) ^ (out4 >>> 25) ^ (out4 << 26) ^ (out4 << 21) ^ (out4 << 7)) +
+ (out6 ^ (out4 & (out5 ^ out6))) +
+ key[i]; // | 0;
+
+ // shift register
+ out7 = out6;
+ out6 = out5;
+ out5 = out4;
+
+ out4 = (out3 + tmp) | 0;
+
+ out3 = out2;
+ out2 = out1;
+ out1 = out0;
+
+ out0 =
+ (tmp +
+ ((out1 & out2) ^ (out3 & (out1 ^ out2))) +
+ ((out1 >>> 2) ^
+ (out1 >>> 13) ^
+ (out1 >>> 22) ^
+ (out1 << 30) ^
+ (out1 << 19) ^
+ (out1 << 10))) |
+ 0;
+ }
+
+ out[0] = (out[0] + out0) | 0;
+ out[1] = (out[1] + out1) | 0;
+ out[2] = (out[2] + out2) | 0;
+ out[3] = (out[3] + out3) | 0;
+ out[4] = (out[4] + out4) | 0;
+ out[5] = (out[5] + out5) | 0;
+ out[6] = (out[6] + out6) | 0;
+ out[7] = (out[7] + out7) | 0;
}
return out;
From 3126da02c6e797037b42d4da8c16a7e94080aebc Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 17:38:28 -0500
Subject: [PATCH 40/60] more crypto stuff
---
packages/adapter-netlify/src/shims.js | 2 -
packages/adapter-node/src/shims.js | 2 -
packages/adapter-vercel/files/shims.js | 2 -
packages/kit/package.json | 3 -
packages/kit/rollup.config.js | 1 -
.../kit/src/core/adapt/prerender/prerender.js | 2 -
packages/kit/src/core/dev/plugin.js | 2 -
packages/kit/src/core/preview/index.js | 2 -
packages/kit/src/install-crypto.js | 12 -
.../kit/src/runtime/server/page/crypto.js | 205 ++++++++++++++++--
packages/kit/src/runtime/server/page/csp.js | 46 ++--
.../kit/src/runtime/server/page/render.js | 3 +-
packages/kit/src/runtime/server/page/sjcl.js | 173 ---------------
13 files changed, 208 insertions(+), 247 deletions(-)
delete mode 100644 packages/kit/src/install-crypto.js
delete mode 100644 packages/kit/src/runtime/server/page/sjcl.js
diff --git a/packages/adapter-netlify/src/shims.js b/packages/adapter-netlify/src/shims.js
index 2e7f455d2b07..8bd70fe8b758 100644
--- a/packages/adapter-netlify/src/shims.js
+++ b/packages/adapter-netlify/src/shims.js
@@ -1,5 +1,3 @@
-import { install_crypto } from '@sveltejs/kit/install-crypto';
import { install_fetch } from '@sveltejs/kit/install-fetch';
-install_crypto();
install_fetch();
diff --git a/packages/adapter-node/src/shims.js b/packages/adapter-node/src/shims.js
index 2e7f455d2b07..8bd70fe8b758 100644
--- a/packages/adapter-node/src/shims.js
+++ b/packages/adapter-node/src/shims.js
@@ -1,5 +1,3 @@
-import { install_crypto } from '@sveltejs/kit/install-crypto';
import { install_fetch } from '@sveltejs/kit/install-fetch';
-install_crypto();
install_fetch();
diff --git a/packages/adapter-vercel/files/shims.js b/packages/adapter-vercel/files/shims.js
index 2e7f455d2b07..8bd70fe8b758 100644
--- a/packages/adapter-vercel/files/shims.js
+++ b/packages/adapter-vercel/files/shims.js
@@ -1,5 +1,3 @@
-import { install_crypto } from '@sveltejs/kit/install-crypto';
import { install_fetch } from '@sveltejs/kit/install-fetch';
-install_crypto();
install_fetch();
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 9042aebc85b9..9bdf36eb11c8 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -86,9 +86,6 @@
"./hooks": {
"import": "./dist/hooks.js"
},
- "./install-crypto": {
- "import": "./dist/install-crypto.js"
- },
"./install-fetch": {
"import": "./dist/install-fetch.js"
}
diff --git a/packages/kit/rollup.config.js b/packages/kit/rollup.config.js
index 1da2dd0fbee9..9361bebb710b 100644
--- a/packages/kit/rollup.config.js
+++ b/packages/kit/rollup.config.js
@@ -57,7 +57,6 @@ export default [
cli: 'src/cli.js',
node: 'src/node.js',
hooks: 'src/hooks.js',
- 'install-crypto': 'src/install-crypto.js',
'install-fetch': 'src/install-fetch.js'
},
output: {
diff --git a/packages/kit/src/core/adapt/prerender/prerender.js b/packages/kit/src/core/adapt/prerender/prerender.js
index 5bc1ec76ad1f..368a8d01d6de 100644
--- a/packages/kit/src/core/adapt/prerender/prerender.js
+++ b/packages/kit/src/core/adapt/prerender/prerender.js
@@ -3,7 +3,6 @@ import { dirname, join, resolve as resolve_path } from 'path';
import { pathToFileURL, URL } from 'url';
import { mkdirp } from '../../../utils/filesystem.js';
import { install_fetch } from '../../../install-fetch.js';
-import { install_crypto } from '../../../install-crypto.js';
import { SVELTE_KIT } from '../../constants.js';
import { is_root_relative, resolve } from '../../../utils/url.js';
import { queue } from './queue.js';
@@ -58,7 +57,6 @@ export async function prerender({ cwd, out, log, config, build_data, fallback, a
}
install_fetch();
- install_crypto();
mkdirp(out);
diff --git a/packages/kit/src/core/dev/plugin.js b/packages/kit/src/core/dev/plugin.js
index a50ca589b79e..8aa8560c83c0 100644
--- a/packages/kit/src/core/dev/plugin.js
+++ b/packages/kit/src/core/dev/plugin.js
@@ -5,7 +5,6 @@ import colors from 'kleur';
import sirv from 'sirv';
import { respond } from '../../runtime/server/index.js';
import { install_fetch } from '../../install-fetch.js';
-import { install_crypto } from '../../install-crypto.js';
import { create_app } from '../create_app/index.js';
import create_manifest_data from '../create_manifest_data/index.js';
import { getRequest, setResponse } from '../../node.js';
@@ -33,7 +32,6 @@ export async function create_plugin(config, cwd) {
configureServer(vite) {
install_fetch();
- install_crypto();
/** @type {import('types/app').SSRManifest} */
let manifest;
diff --git a/packages/kit/src/core/preview/index.js b/packages/kit/src/core/preview/index.js
index 10f0565177ab..7136edd96a55 100644
--- a/packages/kit/src/core/preview/index.js
+++ b/packages/kit/src/core/preview/index.js
@@ -6,7 +6,6 @@ import sirv from 'sirv';
import { pathToFileURL } from 'url';
import { getRequest, setResponse } from '../../node.js';
import { install_fetch } from '../../install-fetch.js';
-import { install_crypto } from '../../install-crypto.js';
import { SVELTE_KIT, SVELTE_KIT_ASSETS } from '../constants.js';
/** @param {string} dir */
@@ -33,7 +32,6 @@ export async function preview({
cwd = process.cwd()
}) {
install_fetch();
- install_crypto();
const app_file = resolve(cwd, `${SVELTE_KIT}/output/server/app.js`);
const manifest_file = resolve(cwd, `${SVELTE_KIT}/output/server/manifest.js`);
diff --git a/packages/kit/src/install-crypto.js b/packages/kit/src/install-crypto.js
deleted file mode 100644
index 9eee91b082f5..000000000000
--- a/packages/kit/src/install-crypto.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { webcrypto } from 'crypto';
-
-// exported for dev/preview and node environments
-export function install_crypto() {
- Object.defineProperties(globalThis, {
- crypto: {
- enumerable: true,
- configurable: true,
- value: webcrypto
- }
- });
-}
diff --git a/packages/kit/src/runtime/server/page/crypto.js b/packages/kit/src/runtime/server/page/crypto.js
index ebceeb5e2902..4c6998a8f38a 100644
--- a/packages/kit/src/runtime/server/page/crypto.js
+++ b/packages/kit/src/runtime/server/page/crypto.js
@@ -1,29 +1,186 @@
-import { hash } from './sjcl.js';
-
-// adapted from https://bitwiseshiftleft.github.io/sjcl/,
-// modified and redistributed under BSD license
-
-/** @param {string} text */
-export function sha256(text) {
- // const hashed = new Uint32Array(Sha256.hash(text));
- const hashed = new Uint32Array(hash(text));
- const buffer = hashed.buffer;
- const uint8array = new Uint8Array(buffer);
-
- // bitArray is big endian, uint32array is little endian, so we need to do this:
- for (let i = 0; i < uint8array.length; i += 4) {
- const a = uint8array[i + 0];
- const b = uint8array[i + 1];
- const c = uint8array[i + 2];
- const d = uint8array[i + 3];
-
- uint8array[i + 0] = d;
- uint8array[i + 1] = c;
- uint8array[i + 2] = b;
- uint8array[i + 3] = a;
+const encoder = new TextEncoder();
+
+/**
+ * SHA-256 hashing function adapted from https://bitwiseshiftleft.github.io/sjcl
+ * modified and redistributed under BSD license
+ * @param {string} data
+ */
+export function sha256(data) {
+ if (!key[0]) precompute();
+
+ const out = init.slice(0);
+ const array = encode(data);
+
+ for (let i = 0; i < array.length; i += 16) {
+ const w = array.subarray(i, i + 16);
+
+ let tmp;
+ let a;
+ let b;
+
+ let out0 = out[0];
+ let out1 = out[1];
+ let out2 = out[2];
+ let out3 = out[3];
+ let out4 = out[4];
+ let out5 = out[5];
+ let out6 = out[6];
+ let out7 = out[7];
+
+ /* Rationale for placement of |0 :
+ * If a value can overflow is original 32 bits by a factor of more than a few
+ * million (2^23 ish), there is a possibility that it might overflow the
+ * 53-bit mantissa and lose precision.
+ *
+ * To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that
+ * propagates around the loop, and on the hash state out[]. I don't believe
+ * that the clamps on out4 and on out0 are strictly necessary, but it's close
+ * (for out4 anyway), and better safe than sorry.
+ *
+ * The clamps on out[] are necessary for the output to be correct even in the
+ * common case and for short inputs.
+ */
+
+ for (let i = 0; i < 64; i++) {
+ // load up the input word for this round
+
+ if (i < 16) {
+ tmp = w[i];
+ } else {
+ a = w[(i + 1) & 15];
+
+ b = w[(i + 14) & 15];
+
+ tmp = w[i & 15] =
+ (((a >>> 7) ^ (a >>> 18) ^ (a >>> 3) ^ (a << 25) ^ (a << 14)) +
+ ((b >>> 17) ^ (b >>> 19) ^ (b >>> 10) ^ (b << 15) ^ (b << 13)) +
+ w[i & 15] +
+ w[(i + 9) & 15]) |
+ 0;
+ }
+
+ tmp =
+ tmp +
+ out7 +
+ ((out4 >>> 6) ^ (out4 >>> 11) ^ (out4 >>> 25) ^ (out4 << 26) ^ (out4 << 21) ^ (out4 << 7)) +
+ (out6 ^ (out4 & (out5 ^ out6))) +
+ key[i]; // | 0;
+
+ // shift register
+ out7 = out6;
+ out6 = out5;
+ out5 = out4;
+
+ out4 = (out3 + tmp) | 0;
+
+ out3 = out2;
+ out2 = out1;
+ out1 = out0;
+
+ out0 =
+ (tmp +
+ ((out1 & out2) ^ (out3 & (out1 ^ out2))) +
+ ((out1 >>> 2) ^
+ (out1 >>> 13) ^
+ (out1 >>> 22) ^
+ (out1 << 30) ^
+ (out1 << 19) ^
+ (out1 << 10))) |
+ 0;
+ }
+
+ out[0] = (out[0] + out0) | 0;
+ out[1] = (out[1] + out1) | 0;
+ out[2] = (out[2] + out2) | 0;
+ out[3] = (out[3] + out3) | 0;
+ out[4] = (out[4] + out4) | 0;
+ out[5] = (out[5] + out5) | 0;
+ out[6] = (out[6] + out6) | 0;
+ out[7] = (out[7] + out7) | 0;
+ }
+
+ const bytes = new Uint8Array(out.buffer);
+ reverse_endianness(bytes);
+
+ return base64(bytes);
+}
+
+/** The SHA-256 initialization vector */
+const init = new Uint32Array(8);
+
+/** The SHA-256 hash key */
+const key = new Uint32Array(64);
+
+/** Function to precompute init and key. */
+function precompute() {
+ /** @param {number} x */
+ function frac(x) {
+ return (x - Math.floor(x)) * 0x100000000;
}
- return base64(uint8array);
+ let prime = 2;
+
+ for (let i = 0; i < 64; prime++) {
+ let is_prime = true;
+
+ for (let factor = 2; factor * factor <= prime; factor++) {
+ if (prime % factor === 0) {
+ is_prime = false;
+
+ break;
+ }
+ }
+
+ if (is_prime) {
+ if (i < 8) {
+ init[i] = frac(prime ** (1 / 2));
+ }
+
+ key[i] = frac(prime ** (1 / 3));
+
+ i++;
+ }
+ }
+}
+
+/** @param {Uint8Array} bytes */
+function reverse_endianness(bytes) {
+ for (let i = 0; i < bytes.length; i += 4) {
+ const a = bytes[i + 0];
+ const b = bytes[i + 1];
+ const c = bytes[i + 2];
+ const d = bytes[i + 3];
+
+ bytes[i + 0] = d;
+ bytes[i + 1] = c;
+ bytes[i + 2] = b;
+ bytes[i + 3] = a;
+ }
+}
+
+/** @param {string} str */
+function encode(str) {
+ const encoded = encoder.encode(str);
+ const length = encoded.length * 8;
+
+ // result should be a multiple of 512 bits in length,
+ // with room for a 1 (after the data) and two 32-bit
+ // words containing the original input bit length
+ const size = 512 * Math.ceil((length + 65) / 512);
+ const bytes = new Uint8Array(size / 8);
+ bytes.set(encoded);
+
+ // append a 1
+ bytes[encoded.length] = 0b10000000;
+
+ reverse_endianness(bytes);
+
+ // add the input bit length
+ const words = new Uint32Array(bytes.buffer);
+ words[words.length - 2] = Math.floor(length / 0x100000000); // this will always be zero for us
+ words[words.length - 1] = length;
+
+ return words;
}
/*
diff --git a/packages/kit/src/runtime/server/page/csp.js b/packages/kit/src/runtime/server/page/csp.js
index 4b9b6da58175..57a31a7926ec 100644
--- a/packages/kit/src/runtime/server/page/csp.js
+++ b/packages/kit/src/runtime/server/page/csp.js
@@ -1,21 +1,25 @@
import { escape_html_attr } from '../../../utils/escape.js';
-
-const array = new Uint8Array(16);
-
-export function generate_nonce() {
- crypto.getRandomValues(array);
- return base64(array);
-}
-
-/**
- * @param {string} contents
- * @param {string} algorithm
- * @returns
- */
-async function generate_hash(contents, algorithm = 'sha-256') {
- const bytes = new TextEncoder().encode(contents);
- const digest = new Uint8Array(await crypto.subtle.digest(algorithm, bytes));
- return base64(digest);
+import { sha256 } from './crypto.js';
+
+/** @type {Promise} */
+export let csp_ready;
+
+/** @type {() => string} */
+let generate_nonce;
+
+if (typeof crypto !== 'undefined') {
+ const array = new Uint8Array(16);
+
+ generate_nonce = () => {
+ crypto.getRandomValues(array);
+ return base64(array);
+ };
+} else {
+ csp_ready = import('crypto').then((crypto) => {
+ generate_nonce = () => {
+ return crypto.randomBytes(16).toString('base64');
+ };
+ });
}
const quoted = new Set([
@@ -78,10 +82,10 @@ export class Csp {
// TODO would be great if these methods weren't async
/** @param {string} content */
- async add_script(content) {
+ add_script(content) {
if (this.script_needs_csp) {
if (this.#use_hashes) {
- this.#script_src.push(`sha256-${await generate_hash(content)}`);
+ this.#script_src.push(`sha256-${sha256(content)}`);
} else if (this.#script_src.length === 0) {
this.#script_src.push(`nonce-${this.nonce}`);
}
@@ -89,10 +93,10 @@ export class Csp {
}
/** @param {string} content */
- async add_style(content) {
+ add_style(content) {
if (this.style_needs_csp) {
if (this.#use_hashes) {
- this.#style_src.push(`sha256-${await generate_hash(content)}`);
+ this.#style_src.push(`sha256-${sha256(content)}`);
} else if (this.#style_src.length === 0) {
this.#style_src.push(`nonce-${this.nonce}`);
}
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index 39a13eec5534..456f1dffbd15 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -5,7 +5,7 @@ import { hash } from '../../hash.js';
import { escape_html_attr } from '../../../utils/escape.js';
import { s } from '../../../utils/misc.js';
import { create_prerendering_url_proxy } from './utils.js';
-import { Csp } from './csp.js';
+import { Csp, csp_ready } from './csp.js';
// TODO rename this function/module
@@ -139,6 +139,7 @@ export async function render_response({
const inlined_style = Array.from(styles.values()).join('\n');
+ await csp_ready;
const csp = new Csp(options.csp, !!state.prerender);
// prettier-ignore
diff --git a/packages/kit/src/runtime/server/page/sjcl.js b/packages/kit/src/runtime/server/page/sjcl.js
deleted file mode 100644
index b26f49271cc5..000000000000
--- a/packages/kit/src/runtime/server/page/sjcl.js
+++ /dev/null
@@ -1,173 +0,0 @@
-const encoder = new TextEncoder();
-
-/** The SHA-256 initialization vector */
-const init = new Uint32Array(8);
-
-/** The SHA-256 hash key */
-const key = new Uint32Array(64);
-
-/** Function to precompute init and key. */
-function precompute() {
- /** @param {number} x */
- function frac(x) {
- return (x - Math.floor(x)) * 0x100000000;
- }
-
- let prime = 2;
-
- for (let i = 0; i < 64; prime++) {
- let is_prime = true;
-
- for (let factor = 2; factor * factor <= prime; factor++) {
- if (prime % factor === 0) {
- is_prime = false;
-
- break;
- }
- }
-
- if (is_prime) {
- if (i < 8) {
- init[i] = frac(prime ** (1 / 2));
- }
-
- key[i] = frac(prime ** (1 / 3));
-
- i++;
- }
- }
-}
-
-/** @param {string} str */
-function encode(str) {
- const encoded = encoder.encode(str);
- const length = encoded.length * 8;
-
- // result should be a multiple of 512 bits in length,
- // with room for a 1 (after the data) and two 32-bit
- // words containing the original input bit length
- const size = 512 * Math.ceil((length + 65) / 512);
- const bytes = new Uint8Array(size / 8);
- bytes.set(encoded);
-
- // append a 1
- bytes[encoded.length] = 0b10000000;
-
- // swap endianness
- for (let i = 0; i < bytes.length; i += 4) {
- const a = bytes[i + 0];
- const b = bytes[i + 1];
- const c = bytes[i + 2];
- const d = bytes[i + 3];
-
- bytes[i + 0] = d;
- bytes[i + 1] = c;
- bytes[i + 2] = b;
- bytes[i + 3] = a;
- }
-
- // add the input bit length
- const words = new Uint32Array(bytes.buffer);
- words[words.length - 2] = Math.floor(length / 0x100000000); // this will always be zero for us
- words[words.length - 1] = length;
-
- return words;
-}
-
-/** @param {string} data */
-export function hash(data) {
- if (!key[0]) precompute();
-
- const out = init.slice(0);
- const array = encode(data);
-
- for (let i = 0; i < array.length; i += 16) {
- const w = array.subarray(i, i + 16);
-
- let tmp;
- let a;
- let b;
-
- let out0 = out[0];
- let out1 = out[1];
- let out2 = out[2];
- let out3 = out[3];
- let out4 = out[4];
- let out5 = out[5];
- let out6 = out[6];
- let out7 = out[7];
-
- /* Rationale for placement of |0 :
- * If a value can overflow is original 32 bits by a factor of more than a few
- * million (2^23 ish), there is a possibility that it might overflow the
- * 53-bit mantissa and lose precision.
- *
- * To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that
- * propagates around the loop, and on the hash state out[]. I don't believe
- * that the clamps on out4 and on out0 are strictly necessary, but it's close
- * (for out4 anyway), and better safe than sorry.
- *
- * The clamps on out[] are necessary for the output to be correct even in the
- * common case and for short inputs.
- */
-
- for (let i = 0; i < 64; i++) {
- // load up the input word for this round
-
- if (i < 16) {
- tmp = w[i];
- } else {
- a = w[(i + 1) & 15];
-
- b = w[(i + 14) & 15];
-
- tmp = w[i & 15] =
- (((a >>> 7) ^ (a >>> 18) ^ (a >>> 3) ^ (a << 25) ^ (a << 14)) +
- ((b >>> 17) ^ (b >>> 19) ^ (b >>> 10) ^ (b << 15) ^ (b << 13)) +
- w[i & 15] +
- w[(i + 9) & 15]) |
- 0;
- }
-
- tmp =
- tmp +
- out7 +
- ((out4 >>> 6) ^ (out4 >>> 11) ^ (out4 >>> 25) ^ (out4 << 26) ^ (out4 << 21) ^ (out4 << 7)) +
- (out6 ^ (out4 & (out5 ^ out6))) +
- key[i]; // | 0;
-
- // shift register
- out7 = out6;
- out6 = out5;
- out5 = out4;
-
- out4 = (out3 + tmp) | 0;
-
- out3 = out2;
- out2 = out1;
- out1 = out0;
-
- out0 =
- (tmp +
- ((out1 & out2) ^ (out3 & (out1 ^ out2))) +
- ((out1 >>> 2) ^
- (out1 >>> 13) ^
- (out1 >>> 22) ^
- (out1 << 30) ^
- (out1 << 19) ^
- (out1 << 10))) |
- 0;
- }
-
- out[0] = (out[0] + out0) | 0;
- out[1] = (out[1] + out1) | 0;
- out[2] = (out[2] + out2) | 0;
- out[3] = (out[3] + out3) | 0;
- out[4] = (out[4] + out4) | 0;
- out[5] = (out[5] + out5) | 0;
- out[6] = (out[6] + out6) | 0;
- out[7] = (out[7] + out7) | 0;
- }
-
- return out;
-}
From 969a77116ed64a3cadcb029fde206e8165a955ea Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 17:50:41 -0500
Subject: [PATCH 41/60] use node crypto module to generate hashes where
possible
---
packages/kit/src/runtime/server/page/csp.js | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/csp.js b/packages/kit/src/runtime/server/page/csp.js
index 57a31a7926ec..0252020e8ee7 100644
--- a/packages/kit/src/runtime/server/page/csp.js
+++ b/packages/kit/src/runtime/server/page/csp.js
@@ -7,6 +7,9 @@ export let csp_ready;
/** @type {() => string} */
let generate_nonce;
+/** @type {(input: string) => string} */
+let generate_hash;
+
if (typeof crypto !== 'undefined') {
const array = new Uint8Array(16);
@@ -14,11 +17,17 @@ if (typeof crypto !== 'undefined') {
crypto.getRandomValues(array);
return base64(array);
};
+
+ generate_hash = sha256;
} else {
csp_ready = import('crypto').then((crypto) => {
generate_nonce = () => {
return crypto.randomBytes(16).toString('base64');
};
+
+ generate_hash = (input) => {
+ return crypto.createHash('sha256').update(input, 'utf-8').digest().toString('base64');
+ };
});
}
@@ -85,7 +94,7 @@ export class Csp {
add_script(content) {
if (this.script_needs_csp) {
if (this.#use_hashes) {
- this.#script_src.push(`sha256-${sha256(content)}`);
+ this.#script_src.push(`sha256-${generate_hash(content)}`);
} else if (this.#script_src.length === 0) {
this.#script_src.push(`nonce-${this.nonce}`);
}
@@ -96,7 +105,7 @@ export class Csp {
add_style(content) {
if (this.style_needs_csp) {
if (this.#use_hashes) {
- this.#style_src.push(`sha256-${sha256(content)}`);
+ this.#style_src.push(`sha256-${generate_hash(content)}`);
} else if (this.#style_src.length === 0) {
this.#style_src.push(`nonce-${this.nonce}`);
}
From 6e31aa0574da5d9092e397cbd1aec6aa29294911 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 17:55:50 -0500
Subject: [PATCH 42/60] remove unnecessary awaits
---
packages/kit/src/runtime/server/page/render.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js
index 456f1dffbd15..763ef25dc058 100644
--- a/packages/kit/src/runtime/server/page/render.js
+++ b/packages/kit/src/runtime/server/page/render.js
@@ -194,7 +194,7 @@ export async function render_response({
if (options.dev) attributes.push(' data-svelte');
if (csp.style_needs_nonce) attributes.push(` nonce="${csp.nonce}"`);
- await csp.add_style(inlined_style);
+ csp.add_style(inlined_style);
head += `\n\t`;
}
@@ -226,7 +226,7 @@ export async function render_response({
const attributes = ['type="module"'];
- await csp.add_script(init_app);
+ csp.add_script(init_app);
if (csp.script_needs_nonce) {
attributes.push(`nonce="${csp.nonce}"`);
@@ -247,7 +247,7 @@ export async function render_response({
if (options.service_worker) {
// always include service worker unless it's turned off explicitly
- await csp.add_script(init_service_worker);
+ csp.add_script(init_service_worker);
head += `
`;
From 591663678938506ec4907704650f2c5262a622d4 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 18:13:04 -0500
Subject: [PATCH 43/60] fix mutation bug
---
packages/kit/src/runtime/server/page/csp.js | 28 ++++++++++-----------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/csp.js b/packages/kit/src/runtime/server/page/csp.js
index 0252020e8ee7..648d3bb12f39 100644
--- a/packages/kit/src/runtime/server/page/csp.js
+++ b/packages/kit/src/runtime/server/page/csp.js
@@ -65,7 +65,7 @@ export class Csp {
*/
constructor({ mode, directives }, prerender) {
this.#use_hashes = mode === 'hash' || (mode === 'auto' && prerender);
- this.#directives = { ...directives };
+ this.#directives = directives;
this.#script_src = [];
this.#style_src = [];
@@ -120,31 +120,31 @@ export class Csp {
// (specifically, Firefox appears to not ignore nonce-{nonce} directives
// on default-src), so we ensure that script-src and style-src exist
- if (this.#script_src.length > 0) {
- if (!this.#directives['script-src']) {
- this.#directives['script-src'] = [...(this.#directives['default-src'] || [])];
- }
+ const directives = { ...this.#directives };
- this.#directives['script-src'].push(...this.#script_src);
+ if (this.#script_src.length > 0) {
+ directives['script-src'] = [
+ ...(directives['script-src'] || directives['default-src'] || []),
+ ...this.#script_src
+ ];
}
if (this.#style_src.length > 0) {
- if (!this.#directives['style-src']) {
- this.#directives['style-src'] = [...(this.#directives['default-src'] || [])];
- }
-
- this.#directives['style-src'].push(...this.#style_src);
+ directives['style-src'] = [
+ ...(directives['style-src'] || directives['default-src'] || []),
+ ...this.#style_src
+ ];
}
- for (const key in this.#directives) {
+ for (const key in directives) {
if (is_meta && (key === 'frame-ancestors' || key === 'report-uri' || key === 'sandbox')) {
// these values cannot be used with a tag
// TODO warn?
continue;
}
- // @ts-expect-error gimme a break typescript, `key` is obviously a member of this.#directives
- const value = /** @type {string[] | true} */ (this.#directives[key]);
+ // @ts-expect-error gimme a break typescript, `key` is obviously a member of directives
+ const value = /** @type {string[] | true} */ (directives[key]);
if (!value) continue;
From 17afa3ee0ecb6ec75808f552103ce15f0b111f93 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 18:17:09 -0500
Subject: [PATCH 44/60] trick esbuild
---
packages/kit/src/runtime/server/page/csp.js | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/kit/src/runtime/server/page/csp.js b/packages/kit/src/runtime/server/page/csp.js
index 648d3bb12f39..8dc39168a8ef 100644
--- a/packages/kit/src/runtime/server/page/csp.js
+++ b/packages/kit/src/runtime/server/page/csp.js
@@ -20,7 +20,8 @@ if (typeof crypto !== 'undefined') {
generate_hash = sha256;
} else {
- csp_ready = import('crypto').then((crypto) => {
+ const name = 'crypto'; // store in a variable to fool esbuild when adapters bundle kit
+ csp_ready = import(name).then((crypto) => {
generate_nonce = () => {
return crypto.randomBytes(16).toString('base64');
};
From cb0968e30dbaa0095fd45fd7c35650ff81019b7c Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 18:21:59 -0500
Subject: [PATCH 45/60] windows fix, hopefully
---
packages/kit/src/core/config/index.spec.js | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js
index 7f33e3452506..0ef2d5f9fe9d 100644
--- a/packages/kit/src/core/config/index.spec.js
+++ b/packages/kit/src/core/config/index.spec.js
@@ -51,12 +51,12 @@ const get_defaults = (prefix = '') => ({
}
},
files: {
- assets: prefix + 'static',
- hooks: prefix + 'src/hooks',
- lib: prefix + 'src/lib',
- routes: prefix + 'src/routes',
- serviceWorker: prefix + 'src/service-worker',
- template: prefix + 'src/app.html'
+ assets: join(prefix, 'static'),
+ hooks: join(prefix, 'src/hooks'),
+ lib: join(prefix, 'src/lib'),
+ routes: join(prefix, 'src/routes'),
+ serviceWorker: join(prefix, 'src/service-worker'),
+ template: join(prefix, 'src/app.html')
},
floc: false,
headers: undefined,
From 7587a35ce5518b13211fbf640f713b6c1557b357 Mon Sep 17 00:00:00 2001
From: Rich Harris
Date: Tue, 25 Jan 2022 18:46:09 -0500
Subject: [PATCH 46/60] add unsafe-inline styles in dev
---
packages/kit/src/runtime/server/page/csp.js | 44 ++++++++++++++-----
.../kit/src/runtime/server/page/render.js | 2 +-
2 files changed, 35 insertions(+), 11 deletions(-)
diff --git a/packages/kit/src/runtime/server/page/csp.js b/packages/kit/src/runtime/server/page/csp.js
index 8dc39168a8ef..3ac8a918bc70 100644
--- a/packages/kit/src/runtime/server/page/csp.js
+++ b/packages/kit/src/runtime/server/page/csp.js
@@ -48,6 +48,15 @@ export class Csp {
/** @type {boolean} */
#use_hashes;
+ /** @type {boolean} */
+ #dev;
+
+ /** @type {boolean} */
+ #script_needs_csp;
+
+ /** @type {boolean} */
+ #style_needs_csp;
+
/** @type {import('types/csp').CspDirectives} */
#directives;
@@ -62,11 +71,13 @@ export class Csp {
* mode: string,
* directives: import('types/csp').CspDirectives
* }} opts
+ * @param {boolean} dev
* @param {boolean} prerender
*/
- constructor({ mode, directives }, prerender) {
+ constructor({ mode, directives }, dev, prerender) {
this.#use_hashes = mode === 'hash' || (mode === 'auto' && prerender);
this.#directives = directives;
+ this.#dev = dev;
this.#script_src = [];
this.#style_src = [];
@@ -74,16 +85,17 @@ export class Csp {
const effective_script_src = directives['script-src'] || directives['default-src'];
const effective_style_src = directives['style-src'] || directives['default-src'];
- this.script_needs_csp =
- effective_script_src &&
+ this.#script_needs_csp =
+ !!effective_script_src &&
effective_script_src.filter((value) => value !== 'unsafe-inline').length > 0;
- this.style_needs_csp =
- effective_style_src &&
+ this.#style_needs_csp =
+ !dev &&
+ !!effective_style_src &&
effective_style_src.filter((value) => value !== 'unsafe-inline').length > 0;
- this.script_needs_nonce = this.script_needs_csp && !this.#use_hashes;
- this.style_needs_nonce = this.style_needs_csp && !this.#use_hashes;
+ this.script_needs_nonce = this.#script_needs_csp && !this.#use_hashes;
+ this.style_needs_nonce = this.#style_needs_csp && !this.#use_hashes;
if (this.script_needs_nonce || this.style_needs_nonce) {
this.nonce = generate_nonce();
@@ -93,7 +105,7 @@ export class Csp {
// TODO would be great if these methods weren't async
/** @param {string} content */
add_script(content) {
- if (this.script_needs_csp) {
+ if (this.#script_needs_csp) {
if (this.#use_hashes) {
this.#script_src.push(`sha256-${generate_hash(content)}`);
} else if (this.#script_src.length === 0) {
@@ -104,7 +116,7 @@ export class Csp {
/** @param {string} content */
add_style(content) {
- if (this.style_needs_csp) {
+ if (this.#style_needs_csp) {
if (this.#use_hashes) {
this.#style_src.push(`sha256-${generate_hash(content)}`);
} else if (this.#style_src.length === 0) {
@@ -130,7 +142,19 @@ export class Csp {
];
}
- if (this.#style_src.length > 0) {
+ if (this.#dev) {
+ const effective_style_src = directives['style-src'] || directives['default-src'];
+
+ // in development, we need to be able to inject