subtls
Version:
Proof-of-concept TLS 1.3 in pure JS using SubtleCrypto
1,498 lines (1,488 loc) • 106 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true,
writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// node_modules/hextreme/index.mjs
var __defProp2 = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, {
enumerable: true,
configurable: true,
writable: true,
value
}) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var chunkBytes = 1008e3;
var littleEndian = new Uint8Array(new Uint16Array([258]).buffer)[0] === 2;
var td = new TextDecoder();
var te = new TextEncoder();
var hexCharsLower = te.encode("0123456789abcdef");
var hexCharsUpper = te.encode("0123456789ABCDEF");
var b64ChStd = te.encode("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
var b64ChPad = 61;
var b64ChUrl = b64ChStd.slice();
b64ChUrl[62] = 45;
b64ChUrl[63] = 95;
var ccl;
var ccu;
function _toHex(in8, { alphabet, scratchArr } = {}) {
if (!ccl) {
ccl = new Uint16Array(256);
ccu = new Uint16Array(256);
if (littleEndian) for (let i2 = 0; i2 < 256; i2++) {
ccl[i2] = hexCharsLower[i2 & 15] << 8 | hexCharsLower[i2 >>> 4];
ccu[i2] = hexCharsUpper[i2 & 15] << 8 | hexCharsUpper[i2 >>> 4];
}
else for (let i2 = 0; i2 < 256; i2++) {
ccl[i2] = hexCharsLower[i2 & 15] | hexCharsLower[i2 >>> 4] << 8;
ccu[i2] = hexCharsUpper[i2 & 15] | hexCharsUpper[i2 >>> 4] << 8;
}
}
if (in8.byteOffset % 4 !== 0) in8 = new Uint8Array(in8);
const len = in8.length, halfLen = len >>> 1, quarterLen = len >>> 2, out16 = scratchArr || new Uint16Array(len),
in32 = new Uint32Array(
in8.buffer,
in8.byteOffset,
quarterLen
), out32 = new Uint32Array(out16.buffer, out16.byteOffset, halfLen), cc = alphabet === "upper" ? ccu : ccl;
let i = 0, j = 0, v;
if (littleEndian) while (i < quarterLen) {
v = in32[i++];
out32[j++] = cc[v >>> 8 & 255] << 16 | cc[v & 255];
out32[j++] = cc[v >>> 24] << 16 | cc[v >>> 16 & 255];
}
else while (i < quarterLen) {
v = in32[i++];
out32[j++] = cc[v >>> 24] << 16 | cc[v >>> 16 & 255];
out32[j++] = cc[v >>> 8 & 255] << 16 | cc[v & 255];
}
i <<= 2;
while (i < len) out16[i] = cc[in8[i++]];
const hex = td.decode(out16.subarray(0, len));
return hex;
}
function _toHexChunked(d, options = {}) {
let hex = "", len = d.length, chunkWords = chunkBytes >>> 1, chunks = Math.ceil(len / chunkWords), scratchArr = new Uint16Array(
chunks > 1 ? chunkWords : len
);
for (let i = 0; i < chunks; i++) {
const start = i * chunkWords, end = start + chunkWords;
hex += _toHex(d.subarray(start, end), __spreadProps(__spreadValues({}, options), { scratchArr }));
}
return hex;
}
function toHex(d, options = {}) {
return options.alphabet !== "upper" && typeof d.toHex === "function" ? d.toHex() : _toHexChunked(d, options);
}
var vff = 26214;
var hl;
function _fromHex(s, { onInvalidInput, scratchArray: scratchArr, outArray: outArr, indexOffset } = {}) {
if (!hl) {
hl = new Uint8Array(vff + 1);
for (let l = 0; l < 22; l++) for (let r = 0; r < 22; r++) {
const cl = l + (l < 10 ? 48 : l < 16 ? 55 : 81), cr = r + (r < 10 ? 48 : r < 16 ? 55 : 81), vin = littleEndian ?
cr << 8 | cl : cr | cl << 8, vout = (l < 16 ? l : l - 6) << 4 | (r < 16 ? r : r - 6);
hl[vin] = vout;
}
}
const lax = onInvalidInput === "truncate", slen = s.length;
if (!lax && slen & 1) throw new Error("Hex input is an odd number of characters");
const bytelen = slen >>> 1, last7 = bytelen - 7, h16len = bytelen + 2, h16 = scratchArr || new Uint16Array(h16len),
h8 = new Uint8Array(
h16.buffer,
h16.byteOffset
), out = outArr || new Uint8Array(bytelen);
if (h16.length < h16len) throw new Error(`Wrong-sized scratch array supplied (was ${h16.length}, expected at\
least ${h16len})`);
if (out.length != bytelen) throw new Error(`Wrong-sized output array supplied (was ${out.length}, expected ${bytelen}\
)`);
te.encodeInto(s, h8);
let i = 0, ok = false;
e: {
let vin, vout;
while (i < last7) {
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
}
while (i < bytelen) {
vin = h16[i];
vout = hl[vin];
if (!vout && vin !== 12336) break e;
out[i++] = vout;
}
ok = true;
}
if (!ok && !lax) throw new Error(`Invalid pair in hex input at index ${(indexOffset || 0) + i << 1}`);
return i < bytelen ? out.subarray(0, i) : out;
}
function _fromHexChunked(s, { onInvalidInput, outArray } = {}) {
const lax = onInvalidInput === "truncate", slen = s.length;
if (!lax && slen & 1) throw new Error("Hex input is an odd number of characters");
const byteLength = slen >>> 1, chunkInts = chunkBytes >>> 1, chunksCount = Math.ceil(byteLength / chunkInts),
scratchArr = new Uint16Array(
(chunksCount > 1 ? chunkInts : byteLength) + 2
), outArr = outArray || new Uint8Array(byteLength);
if (outArr.length !== byteLength) throw new Error(`Provided output array is of wrong length: expected ${byteLength}\
, got ${outArr.length}`);
for (let i = 0; i < chunksCount; i++) {
const chunkStartByte = i * chunkInts, chunkEndByte = chunkStartByte + chunkInts, result = _fromHex(s.slice(
chunkStartByte << 1,
chunkEndByte << 1
), {
onInvalidInput,
scratchArray: scratchArr,
outArray: outArr.subarray(chunkStartByte, chunkEndByte),
indexOffset: chunkStartByte
});
if (lax && result.length < chunkEndByte - chunkStartByte) {
return outArr.subarray(0, chunkStartByte + result.length);
}
}
return outArr;
}
function fromHex(s, options = {}) {
if (typeof Uint8Array.fromHex === "function" && options.onInvalidInput !== "truncate" && !options.outArray) return Uint8Array.
fromHex(
s
);
return _fromHexChunked(s, options);
}
var chpairsStd;
var chpairsUrl;
function _toBase64(d, { omitPadding, alphabet, scratchArr } = {}) {
if (!chpairsStd) {
chpairsStd = new Uint16Array(4096);
if (littleEndian) for (let i2 = 0; i2 < 64; i2++) for (let j2 = 0; j2 < 64; j2++) chpairsStd[i2 << 6 | j2] =
b64ChStd[i2] | b64ChStd[j2] << 8;
else for (let i2 = 0; i2 < 64; i2++) for (let j2 = 0; j2 < 64; j2++) chpairsStd[i2 << 6 | j2] = b64ChStd[i2] <<
8 | b64ChStd[j2];
chpairsUrl = chpairsStd.slice();
if (littleEndian) {
for (let i2 = 0; i2 < 64; i2++) for (let j2 = 62; j2 < 64; j2++) chpairsUrl[i2 << 6 | j2] = b64ChUrl[i2] |
b64ChUrl[j2] << 8;
for (let i2 = 62; i2 < 64; i2++) for (let j2 = 0; j2 < 62; j2++) chpairsUrl[i2 << 6 | j2] = b64ChUrl[i2] |
b64ChUrl[j2] << 8;
} else {
for (let i2 = 0; i2 < 64; i2++) for (let j2 = 62; j2 < 64; j2++) chpairsUrl[i2 << 6 | j2] = b64ChUrl[i2] <<
8 | b64ChUrl[j2];
for (let i2 = 62; i2 < 64; i2++) for (let j2 = 0; j2 < 62; j2++) chpairsUrl[i2 << 6 | j2] = b64ChUrl[i2] <<
8 | b64ChUrl[j2];
}
}
if (d.byteOffset % 4 !== 0) d = new Uint8Array(d);
const urlsafe = alphabet === "base64url", ch = urlsafe ? b64ChUrl : b64ChStd, chpairs = urlsafe ? chpairsUrl :
chpairsStd, inlen = d.length, last2 = inlen - 2, inints = inlen >>> 2, intlast3 = inints - 3, d32 = new Uint32Array(
d.buffer, d.byteOffset, inints), outints = Math.ceil(inlen / 3), out = scratchArr || new Uint32Array(outints);
let i = 0, j = 0, u1, u2, u3, b1, b2, b3;
if (littleEndian) while (i < intlast3) {
u1 = d32[i++];
u2 = d32[i++];
u3 = d32[i++];
b1 = u1 & 255;
b2 = u1 >>> 8 & 255;
b3 = u1 >>> 16 & 255;
out[j++] = chpairs[b1 << 4 | b2 >>> 4] | chpairs[(b2 & 15) << 8 | b3] << 16;
b1 = u1 >>> 24;
b2 = u2 & 255;
b3 = u2 >>> 8 & 255;
out[j++] = chpairs[b1 << 4 | b2 >>> 4] | chpairs[(b2 & 15) << 8 | b3] << 16;
b1 = u2 >>> 16 & 255;
b2 = u2 >>> 24;
b3 = u3 & 255;
out[j++] = chpairs[b1 << 4 | b2 >>> 4] | chpairs[(b2 & 15) << 8 | b3] << 16;
b1 = u3 >>> 8 & 255;
b2 = u3 >>> 16 & 255;
b3 = u3 >>> 24;
out[j++] = chpairs[b1 << 4 | b2 >>> 4] | chpairs[(b2 & 15) << 8 | b3] << 16;
}
else while (i < intlast3) {
u1 = d32[i++];
u2 = d32[i++];
u3 = d32[i++];
out[j++] = chpairs[u1 >>> 20] << 16 | chpairs[u1 >>> 8 & 4095];
out[j++] = chpairs[(u1 & 255) << 4 | u2 >>> 28] << 16 | chpairs[u2 >>> 16 & 4095];
out[j++] = chpairs[u2 >>> 4 & 4095] << 16 | chpairs[(u2 & 15) << 8 | u3 >>> 24];
out[j++] = chpairs[u3 >>> 12 & 4095] << 16 | chpairs[u3 & 4095];
}
i = i << 2;
while (i < last2) {
b1 = d[i++];
b2 = d[i++];
b3 = d[i++];
out[j++] = chpairs[b1 << 4 | b2 >>> 4] << (littleEndian ? 0 : 16) | chpairs[(b2 & 15) << 8 | b3] << (littleEndian ?
16 : 0);
}
if (i === inlen) return td.decode(out);
b1 = d[i++];
b2 = d[i++];
out[j++] = chpairs[b1 << 4 | (b2 || 0) >>> 4] << (littleEndian ? 0 : 16) | // first 16 bits (no padding)
(b2 === void 0 ? b64ChPad : ch[((b2 || 0) & 15) << 2]) << (littleEndian ? 16 : 8) | // next 8 bits
b64ChPad << (littleEndian ? 24 : 0);
if (!omitPadding) return td.decode(out);
let out8 = new Uint8Array(out.buffer, 0, (outints << 2) - (b2 === void 0 ? 2 : 1));
return td.decode(out8);
}
function _toBase64Chunked(d, options = {}) {
const inBytes = d.length, outInts = Math.ceil(inBytes / 3), outChunkInts = chunkBytes >>> 2, chunksCount = Math.
ceil(outInts / outChunkInts), inChunkBytes = outChunkInts * 3, scratchArr = new Uint32Array(chunksCount > 1 ?
outChunkInts : outInts);
let b64 = "";
for (let i = 0; i < chunksCount; i++) {
const startInBytes = i * inChunkBytes, endInBytes = startInBytes + inChunkBytes, startOutInts = i * outChunkInts,
endOutInts = Math.min(startOutInts + outChunkInts, outInts);
b64 += _toBase64(d.subarray(startInBytes, endInBytes), __spreadProps(__spreadValues({}, options), {
scratchArr: scratchArr.subarray(0, endOutInts - startOutInts)
}));
}
return b64;
}
function toBase64(d, options = {}) {
return typeof d.toBase64 === "function" ? d.toBase64(options) : _toBase64Chunked(d, options);
}
var vzz = 31354;
var stdWordLookup;
var urlWordLookup;
var anyWordLookup;
var stdByteLookup;
var urlByteLookup;
var anyByteLookup;
function _fromBase64(s, { alphabet, onInvalidInput } = {}) {
const lax = onInvalidInput === "skip";
if (!stdWordLookup && alphabet !== "base64url" && alphabet !== "base64any") {
stdWordLookup = new Uint16Array(vzz + 1);
for (let l = 0; l < 64; l++) for (let r = 0; r < 64; r++) {
const cl = b64ChStd[l], cr = b64ChStd[r], vin = littleEndian ? cr << 8 | cl : cr | cl << 8, vout = l << 6 |
r;
stdWordLookup[vin] = vout;
}
}
if (!urlWordLookup && alphabet === "base64url") {
urlWordLookup = new Uint16Array(vzz + 1);
for (let l = 0; l < 64; l++) for (let r = 0; r < 64; r++) {
const cl = b64ChUrl[l], cr = b64ChUrl[r], vin = littleEndian ? cr << 8 | cl : cr | cl << 8, vout = l << 6 |
r;
urlWordLookup[vin] = vout;
}
}
if (!anyWordLookup && alphabet === "base64any") {
anyWordLookup = new Uint16Array(vzz + 1);
for (let l = 0; l < 64; l++) for (let r = 0; r < 64; r++) {
const cl = b64ChStd[l], cr = b64ChStd[r], vin = littleEndian ? cr << 8 | cl : cr | cl << 8, vout = l << 6 |
r;
anyWordLookup[vin] = vout;
if (l > 61 || r > 61) {
const cl2 = b64ChUrl[l], cr2 = b64ChUrl[r], vin2 = littleEndian ? cr2 << 8 | cl2 : cr2 | cl2 << 8;
anyWordLookup[vin2] = vout;
}
}
}
if (!stdByteLookup) {
stdByteLookup = new Uint8Array(256).fill(66);
urlByteLookup = new Uint8Array(256).fill(66);
anyByteLookup = new Uint8Array(256).fill(66);
stdByteLookup[b64ChPad] = urlByteLookup[b64ChPad] = anyByteLookup[b64ChPad] = 65;
stdByteLookup[9] = stdByteLookup[10] = stdByteLookup[13] = stdByteLookup[32] = // tab, \r, \n, space
urlByteLookup[9] = urlByteLookup[10] = urlByteLookup[13] = urlByteLookup[32] = anyByteLookup[9] = anyByteLookup[10] =
anyByteLookup[13] = anyByteLookup[32] = 64;
for (let i2 = 0; i2 < 64; i2++) {
const chStdI = b64ChStd[i2], chUrlI = b64ChUrl[i2];
stdByteLookup[chStdI] = urlByteLookup[chUrlI] = anyByteLookup[chStdI] = anyByteLookup[chUrlI] = i2;
}
}
const inBytes = te.encode(s), inBytesLen = inBytes.length, inIntsLen = inBytesLen >>> 2, inInts = new Uint32Array(
inBytes.buffer,
inBytes.byteOffset,
inIntsLen
), last3 = inIntsLen - 3, maxOutBytesLen = inIntsLen * 3 + inBytesLen % 4, outBytes = new Uint8Array(
maxOutBytesLen
), outInts = new Uint32Array(outBytes.buffer, 0, maxOutBytesLen >>> 2), wl = alphabet === "base64url" ? urlWordLookup :
alphabet === "base64any" ? anyWordLookup : stdWordLookup, bl = alphabet === "base64url" ? urlByteLookup : alphabet ===
"base64any" ? anyByteLookup : stdByteLookup;
let i = 0, j = 0, inInt, inL, inR, vL1, vR1, vL2, vR2, vL3, vR3, vL4, vR4;
if (littleEndian) while (i < last3) {
inInt = inInts[i++];
inL = inInt & 65535;
inR = inInt >>> 16;
vL1 = wl[inL];
vR1 = wl[inR];
if (!((vL1 || inL === 16705) && (vR1 || inR === 16705))) {
i -= 1;
break;
}
inInt = inInts[i++];
inL = inInt & 65535;
inR = inInt >>> 16;
vL2 = wl[inL];
vR2 = wl[inR];
if (!((vL2 || inL === 16705) && (vR2 || inR === 16705))) {
i -= 2;
break;
}
inInt = inInts[i++];
inL = inInt & 65535;
inR = inInt >>> 16;
vL3 = wl[inL];
vR3 = wl[inR];
if (!((vL3 || inL === 16705) && (vR3 || inR === 16705))) {
i -= 3;
break;
}
inInt = inInts[i++];
inL = inInt & 65535;
inR = inInt >>> 16;
vL4 = wl[inL];
vR4 = wl[inR];
if (!((vL4 || inL === 16705) && (vR4 || inR === 16705))) {
i -= 4;
break;
}
outInts[j++] = vL1 >>> 4 | (vL1 & 15) << 12 | vR1 & 65280 | (vR1 & 255) << 16 | (vL2 & 4080) << 20;
outInts[j++] = (vL2 & 15) << 4 | (vR2 & 65280) >>> 8 | (vR2 & 255) << 8 | (vL3 & 4080) << 12 | (vL3 & 15) <<
28 | (vR3 & 65280) << 16;
outInts[j++] = vR3 & 255 | (vL4 & 4080) << 4 | (vL4 & 15) << 20 | (vR4 & 3840) << 8 | vR4 << 24;
}
else while (i < last3) {
inInt = inInts[i++];
inL = inInt >>> 16;
inR = inInt & 65535;
vL1 = wl[inL];
vR1 = wl[inR];
if (!((vL1 || inL === 16705) && (vR1 || inR === 16705))) {
i -= 1;
break;
}
inInt = inInts[i++];
inL = inInt >>> 16;
inR = inInt & 65535;
vL2 = wl[inL];
vR2 = wl[inR];
if (!((vL2 || inL === 16705) && (vR2 || inR === 16705))) {
i -= 2;
break;
}
inInt = inInts[i++];
inL = inInt >>> 16;
inR = inInt & 65535;
vL3 = wl[inL];
vR3 = wl[inR];
if (!((vL3 || inL === 16705) && (vR3 || inR === 16705))) {
i -= 3;
break;
}
inInt = inInts[i++];
inL = inInt >>> 16;
inR = inInt & 65535;
vL4 = wl[inL];
vR4 = wl[inR];
if (!((vL4 || inL === 16705) && (vR4 || inR === 16705))) {
i -= 4;
break;
}
outInts[j++] = vL1 << 20 | vR1 << 8 | vL2 >>> 4;
outInts[j++] = (vL2 & 15) << 28 | vR2 << 16 | vL3 << 4 | vR3 >>> 8;
outInts[j++] = (vR3 & 255) << 24 | vL4 << 12 | vR4;
}
i <<= 2;
j <<= 2;
if (i === inBytesLen) return outBytes;
let i0 = i, ok = false;
e: {
if (lax) while (i < inBytesLen) {
i0 = i;
while ((vL1 = bl[inBytes[i++]]) > 63) if (vL1 === 65) ok = true;
while ((vL2 = bl[inBytes[i++]]) > 63) if (vL2 === 65) ok = true;
while ((vL3 = bl[inBytes[i++]]) > 63) if (vL3 === 65) ok = true;
while ((vL4 = bl[inBytes[i++]]) > 63) if (vL4 === 65) ok = true;
outBytes[j++] = vL1 << 2 | vL2 >>> 4;
outBytes[j++] = (vL2 << 4 | vL3 >>> 2) & 255;
outBytes[j++] = (vL3 << 6 | vL4) & 255;
if (ok) break;
}
else while (i < inBytesLen) {
i0 = i;
while ((vL1 = bl[inBytes[i++]]) > 63) if (vL1 === 66) break e;
else if (vL1 === 65) ok = true;
while ((vL2 = bl[inBytes[i++]]) > 63) if (vL2 === 66) break e;
else if (vL2 === 65) ok = true;
while ((vL3 = bl[inBytes[i++]]) > 63) if (vL3 === 66) break e;
else if (vL3 === 65) ok = true;
while ((vL4 = bl[inBytes[i++]]) > 63) if (vL4 === 66) break e;
else if (vL4 === 65) ok = true;
outBytes[j++] = vL1 << 2 | vL2 >>> 4;
outBytes[j++] = (vL2 << 4 | vL3 >>> 2) & 255;
outBytes[j++] = (vL3 << 6 | vL4) & 255;
if (ok) break;
}
ok = true;
}
if (!ok) throw new Error(`Invalid character in base64 at index ${i - 1}`);
let validChars = 0;
for (i = i0; i < inBytesLen; i++) {
const v = bl[inBytes[i]];
if (v < 64) validChars++;
if (v === 65) break;
}
if (!lax) for (i = i0; i < inBytesLen; i++) {
const v = bl[inBytes[i]];
if (v > 65) throw new Error(`Invalid character in base64 after padding`);
}
const truncateBytes = { 4: 0, 3: 1, 2: 2, 1: 3, 0: 3 }[validChars];
return outBytes.subarray(0, j - truncateBytes);
}
function fromBase64(s, options = {}) {
if (typeof Uint8Array.fromBase64 === "function" && options.onInvalidInput !== "skip" && options.alphabet !==
"base64any") return Uint8Array.fromBase64(s, options);
return _fromBase64(s, options);
}
// src/util/array.ts
function concat(...arrs) {
if (arrs.length === 1 && arrs[0] instanceof Uint8Array) return arrs[0];
const length = arrs.reduce((memo, arr) => memo + arr.length, 0);
const result = new Uint8Array(length);
let offset = 0;
for (const arr of arrs) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
function equal(a, b) {
const aLength = a.length;
if (aLength !== b.length) return false;
for (let i = 0; i < aLength; i++) if (a[i] !== b[i]) return false;
return true;
}
var GrowableData = class {
constructor() {
__publicField(this, "length");
__publicField(this, "data");
this.length = 0;
this.data = new Uint8Array();
}
append(newData) {
const newDataLength = newData.length;
if (this.length + newDataLength > this.data.length) {
const prevData = this.data;
this.data = new Uint8Array(this.length * 2 + newDataLength);
this.data.set(prevData);
}
this.data.set(newData, this.length);
this.length += newData.length;
}
getData() {
return this.data.subarray(0, this.length);
}
};
// src/presentation/appearance.ts
var indentChars = "\xB7\xB7 ";
// src/util/bytes.ts
var initialSize = 1024;
var growthFactor = 2;
var txtEnc = new TextEncoder();
var txtDec = new TextDecoder();
var emptyArray = new Uint8Array(0);
var hexLookup = [];
for (let i = 0; i < 256; i++) hexLookup[i] = i.toString(16).padStart(2, "0") + " ";
var Bytes = class {
/**
* @param data -
* * If data is a `Uint8Array`, this is the initial data
* * If data is a `number`, this is the initial size in bytes (all zeroes)
* * If data is a `function`, this function is called to retrieve data when required
*/
constructor(data, indent = 0) {
this.indent = indent;
__publicField(this, "fetchFn");
__publicField(this, "endOfReadableData");
// how much data exists to read (not used for writing)
__publicField(this, "offset");
// current read/write cursor
__publicField(this, "dataView");
__publicField(this, "data");
__publicField(this, "comments");
__publicField(this, "indents");
this.endOfReadableData = this.offset = 0;
this.comments = {};
this.indents = { 0: indent };
if (typeof data === "number") {
this.data = new Uint8Array(data);
} else if (data === void 0 || typeof data === "function") {
this.data = emptyArray;
this.fetchFn = data;
} else {
this.data = data;
this.endOfReadableData = data.length;
}
this.dataView = new DataView(this.data.buffer, this.data.byteOffset, this.data.byteLength);
}
readRemaining() {
return this.endOfReadableData - this.offset;
}
resizeTo(newSize) {
const newData = new Uint8Array(newSize);
newData.set(this.data);
this.data = newData;
this.dataView = new DataView(this.data.buffer, this.data.byteOffset, this.data.byteLength);
}
async ensureReadAvailable(bytes) {
if (bytes <= this.readRemaining()) return;
if (this.fetchFn === void 0) throw new Error("Not enough data and no read function supplied");
const freeSpace = this.data.length - this.endOfReadableData;
if (bytes > freeSpace) {
const newSize = Math.max(
initialSize,
this.data.length * growthFactor,
this.endOfReadableData + bytes
);
this.resizeTo(newSize);
}
const newData = await this.fetchFn(bytes);
if (newData === void 0 || newData.length < bytes) {
const e = new Error(`Not enough data returned by read function.
data.length: ${this.data.length}
endOfReadableData: ${this.endOfReadableData}
offset: ${this.offset}
bytes requested: ${bytes}
bytes returned: ${newData && newData.length}`);
e._bytes_error_reason = "EOF";
throw e;
}
this.data.set(newData, this.endOfReadableData);
this.endOfReadableData += newData.length;
}
ensureWriteAvailable(bytes) {
if (this.offset + bytes < this.data.length) return;
const newSize = Math.max(
initialSize,
this.data.length * growthFactor,
this.offset + bytes
);
this.resizeTo(newSize);
}
expectLength(length, indentDelta = 1) {
const startOffset = this.offset;
const endOffset = startOffset + length;
this.indent += indentDelta;
this.indents[startOffset] = this.indent;
return [
() => {
this.indent -= indentDelta;
this.indents[this.offset] = this.indent;
if (this.offset !== endOffset) throw new Error(`${length} bytes expected but ${this.offset - startOffset}\
advanced`);
},
() => endOffset - this.offset
];
}
comment(s, offset = this.offset) {
if (true) throw new Error("No comments should be emitted outside of chatty mode");
const existing = this.comments[offset];
const result = (existing === void 0 ? "" : existing + " ") + s;
this.comments[offset] = result;
return this;
}
lengthComment(length, comment, inclusive = false) {
return length === 1 ? `${length} byte${comment ? ` of ${comment}` : ""} ${inclusive ? "starts here" : "fol\
lows"}` : `${length === 0 ? "no" : length} bytes${comment ? ` of ${comment}` : ""} ${inclusive ? "start here" :
"follow"}`;
}
// reading
async subarrayForRead(length) {
await this.ensureReadAvailable(length);
return this.data.subarray(this.offset, this.offset += length);
}
async skipRead(length, comment) {
await this.ensureReadAvailable(length);
this.offset += length;
if (comment) this.comment(comment);
return this;
}
async readBytes(length) {
await this.ensureReadAvailable(length);
return this.data.slice(this.offset, this.offset += length);
}
async readUTF8String(length) {
await this.ensureReadAvailable(length);
const bytes = await this.subarrayForRead(length);
const s = txtDec.decode(bytes);
return s;
}
async readUTF8StringNullTerminated() {
let i = 0;
while (true) {
await this.ensureReadAvailable(i + 1);
const charCode = this.data[this.offset + i];
if (charCode === 0) break;
i++;
}
;
const str = await this.readUTF8String(i);
await this.expectUint8(0, "end of string");
return str;
}
async readUint8(comment) {
await this.ensureReadAvailable(1);
const result = this.dataView.getUint8(this.offset);
this.offset += 1;
if (0) this.comment(comment.replace(/%/g, String(result)));
return result;
}
async readUint16(comment) {
await this.ensureReadAvailable(2);
const result = this.dataView.getUint16(this.offset);
this.offset += 2;
if (0) this.comment(comment.replace(/%/g, String(result)));
return result;
}
async readUint24(comment) {
await this.ensureReadAvailable(3);
const msb = await this.readUint8();
const lsbs = await this.readUint16();
const result = (msb << 16) + lsbs;
if (0) this.comment(comment.replace(/%/g, String(result)));
return result;
}
async readUint32(comment) {
await this.ensureReadAvailable(4);
const result = this.dataView.getUint32(this.offset);
this.offset += 4;
if (0) this.comment(comment.replace(/%/g, String(result)));
return result;
}
async expectBytes(expected, comment) {
await this.ensureReadAvailable(expected.length);
const actual = await this.readBytes(expected.length);
if (0) this.comment(comment);
if (!equal(actual, expected)) throw new Error("Unexpected bytes");
}
async expectUint8(expectedValue, comment) {
const actualValue = await this.readUint8();
if (0) this.comment(comment);
if (actualValue !== expectedValue) throw new Error(`Expected ${expectedValue}, got ${actualValue}`);
}
async expectUint16(expectedValue, comment) {
const actualValue = await this.readUint16();
if (0) this.comment(comment);
if (actualValue !== expectedValue) throw new Error(`Expected ${expectedValue}, got ${actualValue}`);
}
async expectUint24(expectedValue, comment) {
const actualValue = await this.readUint24();
if (0) this.comment(comment);
if (actualValue !== expectedValue) throw new Error(`Expected ${expectedValue}, got ${actualValue}`);
}
async expectUint32(expectedValue, comment) {
const actualValue = await this.readUint32();
if (0) this.comment(comment);
if (actualValue !== expectedValue) throw new Error(`Expected ${expectedValue}, got ${actualValue}`);
}
async expectReadLength(length, indentDelta = 1) {
await this.ensureReadAvailable(length);
return this.expectLength(length, indentDelta);
}
async expectLengthUint8(comment) {
const length = await this.readUint8();
return this.expectReadLength(length);
}
async expectLengthUint16(comment) {
const length = await this.readUint16();
return this.expectReadLength(length);
}
async expectLengthUint24(comment) {
const length = await this.readUint24();
return this.expectReadLength(length);
}
async expectLengthUint32(comment) {
const length = await this.readUint32();
return this.expectReadLength(length);
}
async expectLengthUint8Incl(comment) {
const length = await this.readUint8();
return this.expectReadLength(length - 1);
}
async expectLengthUint16Incl(comment) {
const length = await this.readUint16();
return this.expectReadLength(length - 2);
}
async expectLengthUint24Incl(comment) {
const length = await this.readUint24();
return this.expectReadLength(length - 3);
}
async expectLengthUint32Incl(comment) {
const length = await this.readUint32();
return this.expectReadLength(length - 4);
}
// writing
subarrayForWrite(length) {
this.ensureWriteAvailable(length);
return this.data.subarray(this.offset, this.offset += length);
}
skipWrite(length, comment) {
this.ensureWriteAvailable(length);
this.offset += length;
if (comment) this.comment(comment);
return this;
}
writeBytes(bytes) {
this.ensureWriteAvailable(bytes.length);
this.data.set(bytes, this.offset);
this.offset += bytes.length;
return this;
}
writeUTF8String(s) {
const bytes = txtEnc.encode(s);
this.writeBytes(bytes);
return this;
}
writeUTF8StringNullTerminated(s) {
const bytes = txtEnc.encode(s);
this.writeBytes(bytes);
this.writeUint8(0);
return this;
}
writeUint8(value, comment) {
this.ensureWriteAvailable(1);
this.dataView.setUint8(this.offset, value);
this.offset += 1;
if (0) this.comment(comment);
return this;
}
writeUint16(value, comment) {
this.ensureWriteAvailable(2);
this.dataView.setUint16(this.offset, value);
this.offset += 2;
if (0) this.comment(comment);
return this;
}
writeUint24(value, comment) {
this.writeUint8((value & 16711680) >> 16);
this.writeUint16(value & 65535, comment);
return this;
}
writeUint32(value, comment) {
this.ensureWriteAvailable(4);
this.dataView.setUint32(this.offset, value);
this.offset += 4;
if (0) this.comment(comment);
return this;
}
// forward-looking lengths
_writeLengthGeneric(lengthBytes, inclusive, comment) {
this.ensureWriteAvailable(lengthBytes);
const startOffset = this.offset;
this.offset += lengthBytes;
const endOffset = this.offset;
this.indent += 1;
this.indents[endOffset] = this.indent;
return () => {
const length = this.offset - (inclusive ? startOffset : endOffset);
switch (lengthBytes) {
case 1:
this.dataView.setUint8(startOffset, length);
break;
case 2:
this.dataView.setUint16(startOffset, length);
break;
case 3:
this.dataView.setUint8(startOffset, (length & 16711680) >> 16);
this.dataView.setUint16(startOffset + 1, length & 65535);
break;
case 4:
this.dataView.setUint32(startOffset, length);
break;
default:
throw new Error(`Invalid length for length field: ${lengthBytes}`);
}
this.indent -= 1;
this.indents[this.offset] = this.indent;
};
}
writeLengthUint8(comment) {
return this._writeLengthGeneric(1, false, comment);
}
writeLengthUint16(comment) {
return this._writeLengthGeneric(2, false, comment);
}
writeLengthUint24(comment) {
return this._writeLengthGeneric(3, false, comment);
}
writeLengthUint32(comment) {
return this._writeLengthGeneric(4, false, comment);
}
writeLengthUint8Incl(comment) {
return this._writeLengthGeneric(1, true, comment);
}
writeLengthUint16Incl(comment) {
return this._writeLengthGeneric(2, true, comment);
}
writeLengthUint24Incl(comment) {
return this._writeLengthGeneric(3, true, comment);
}
writeLengthUint32Incl(comment) {
return this._writeLengthGeneric(4, true, comment);
}
expectWriteLength(length, indentDelta = 1) {
this.ensureWriteAvailable(length);
return this.expectLength(length, indentDelta);
}
// output
array() {
return this.data.subarray(0, this.offset);
}
commentedString(all = false) {
let indent = this.indents[0] ?? 0;
let s = indentChars.repeat(indent);
const len = all ? this.data.length : this.offset;
for (let i = 0; i < len; i++) {
s += hexLookup[this.data[i]];
const comment = this.comments[i + 1];
indent = this.indents[i + 1] ?? indent;
if (comment) {
s += ` ${comment}`;
if (i < len - 1) s += `
${indentChars.repeat(indent)}`;
}
}
return s;
}
};
// src/util/cryptoRandom.ts
var cryptoPromise = typeof crypto !== "undefined" ? Promise.resolve(crypto) : (
// browsers and Node 19+
import("crypto").then((c) => c.webcrypto)
);
async function getRandomValues(...args) {
const c = await cryptoPromise;
return c.getRandomValues(...args);
}
// src/tls/makeClientHello.ts
async function makeClientHello(host, publicKey, sessionId, useSNI = true) {
const h = new Bytes();
h.writeUint8(22, 0);
h.writeUint16(769, 0);
const endRecordHeader = h.writeLengthUint16("TLS record");
h.writeUint8(1, 0);
const endHandshakeHeader = h.writeLengthUint24();
h.writeUint16(771, 0);
await getRandomValues(h.subarrayForWrite(32));
const endSessionId = h.writeLengthUint8(0);
h.writeBytes(sessionId);
endSessionId();
const endCiphers = h.writeLengthUint16(0);
h.writeUint16(4865, 0);
endCiphers();
const endCompressionMethods = h.writeLengthUint8(0);
h.writeUint8(0, 0);
endCompressionMethods();
const endExtensions = h.writeLengthUint16(0);
if (useSNI) {
h.writeUint16(0, 0);
const endSNIExt = h.writeLengthUint16(0);
const endSNI = h.writeLengthUint16(0);
h.writeUint8(0, 0);
const endHostname = h.writeLengthUint16(0);
h.writeUTF8String(host);
endHostname();
endSNI();
endSNIExt();
}
h.writeUint16(11, 0);
const endFormatTypesExt = h.writeLengthUint16(0);
const endFormatTypes = h.writeLengthUint8(0);
h.writeUint8(0, 0);
endFormatTypes();
endFormatTypesExt();
h.writeUint16(10, 0);
const endGroupsExt = h.writeLengthUint16(0);
const endGroups = h.writeLengthUint16(0);
h.writeUint16(23, 0);
endGroups();
endGroupsExt();
h.writeUint16(13, 0);
const endSigsExt = h.writeLengthUint16(0);
const endSigs = h.writeLengthUint16(0);
h.writeUint16(1027, 0);
h.writeUint16(2052, 0);
endSigs();
endSigsExt();
h.writeUint16(43, 0);
const endVersionsExt = h.writeLengthUint16(0);
const endVersions = h.writeLengthUint8(0);
h.writeUint16(772, 0);
endVersions();
endVersionsExt();
h.writeUint16(51, 0);
const endKeyShareExt = h.writeLengthUint16(0);
const endKeyShares = h.writeLengthUint16(0);
h.writeUint16(23, 0);
const endKeyShare = h.writeLengthUint16(0);
if (0) {
h.writeUint8(publicKey[0], "legacy point format: always 4, which means uncompressed ([RFC 8446 \xA74.2.8.2](h\
ttps://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8.2) and [RFC 8422 \xA75.4.1](https://datatracker.ietf.o\
rg/doc/html/rfc8422#section-5.4.1))");
h.writeBytes(publicKey.subarray(1, 33));
h.comment("x coordinate");
h.writeBytes(publicKey.subarray(33, 65));
h.comment("y coordinate");
} else {
h.writeBytes(publicKey);
}
endKeyShare();
endKeyShares();
endKeyShareExt();
endExtensions();
endHandshakeHeader();
endRecordHeader();
return h;
}
// src/util/hex.ts
function u8FromHex(hex) {
return new Uint8Array(Array.from(hex.matchAll(/[0-9a-f]/g)).map((hex2) => parseInt(hex2[0], 16)));
}
function hexFromU8(u8, spacer = "") {
if (!(u8 instanceof Uint8Array)) u8 = new Uint8Array(u8);
if (spacer === "") return toHex(u8);
if (spacer === " ") return toHexSpaced(u8);
throw new Error("Spacer may only be empty or a single space");
}
var te2 = new TextEncoder();
var td2 = new TextDecoder();
var littleEndian2 = new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
var hexChars = te2.encode("0123456789abcdef");
var ccEvens = new Uint16Array(256);
var ccOdds = new Uint32Array(256);
if (littleEndian2) for (let i = 0; i < 256; i++) {
ccEvens[i] = hexChars[i & 15] << 8 | hexChars[i >>> 4];
ccOdds[i] = 32 << 16 | hexChars[i >>> 4] << 24 | hexChars[i & 15] | 32 << 8;
}
else for (let i = 0; i < 256; i++) {
ccEvens[i] = hexChars[i & 15] | hexChars[i >>> 4] << 8;
ccOdds[i] = 32 << 24 | hexChars[i >>> 4] << 16 | hexChars[i & 15] << 8 | 32;
}
function toHexSpaced(in8) {
const bytes = in8.length;
const out16 = new Uint16Array(bytes * 1.5 << 0);
let outIndex = 0;
for (let i = 0; i < bytes; i += 2) {
out16[outIndex++] = ccEvens[in8[i]];
const ccOdd = ccOdds[in8[i + 1]];
out16[outIndex++] = ccOdd >>> 16;
out16[outIndex++] = ccOdd & 65535;
}
const out8 = new Uint8Array(out16.buffer);
return td2.decode(out8.subarray(0, bytes * 3 - 1));
}
// src/tls/parseServerHello.ts
async function parseServerHello(h, sessionId) {
let serverPublicKey;
let tlsVersionSpecified;
await h.expectUint8(2, 0);
const [endServerHello] = await h.expectLengthUint24(0);
await h.expectUint16(771, 0);
const serverRandom = await h.readBytes(32);
if (equal(serverRandom, [
// SHA-256 of "HelloRetryRequest", https://datatracker.ietf.org/doc/html/rfc8446#page-32
// see also: echo -n "HelloRetryRequest" | openssl dgst -sha256 -hex
207,
33,
173,
116,
229,
154,
97,
17,
190,
29,
140,
2,
30,
101,
184,
145,
194,
162,
17,
22,
122,
187,
140,
94,
7,
158,
9,
226,
200,
168,
51,
156
])) throw new Error("Unexpected HelloRetryRequest");
await h.expectUint8(sessionId.length, 0);
await h.expectBytes(sessionId, 0);
await h.expectUint16(4865, 0);
await h.expectUint8(0, 0);
const [endExtensions, extensionsRemaining] = await h.expectLengthUint16(0);
while (extensionsRemaining() > 0) {
const extensionType = await h.readUint16(0);
const [endExtension] = await h.expectLengthUint16(0);
if (extensionType === 43) {
await h.expectUint16(772, 0);
tlsVersionSpecified = true;
} else if (extensionType === 51) {
await h.expectUint16(23, 0);
const [endKeyShare, keyShareRemaining] = await h.expectLengthUint16("key share");
const keyShareLength = keyShareRemaining();
if (keyShareLength !== 65) throw new Error(`Expected 65 bytes of key share, but got ${keyShareLength}`);
if (0) {
await h.expectUint8(4, "legacy point format: always 4, which means uncompressed ([RFC 8446 \xA74.2.8.2](h\
ttps://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8.2) and [RFC 8422 \xA75.4.1](https://datatracker.ietf.o\
rg/doc/html/rfc8422#section-5.4.1))");
const x = await h.readBytes(32);
h.comment("x coordinate");
const y = await h.readBytes(32);
h.comment("y coordinate");
serverPublicKey = concat2([4], x, y);
} else {
serverPublicKey = await h.readBytes(keyShareLength);
}
endKeyShare();
} else {
throw new Error(`Unexpected extension 0x${hexFromU8([extensionType])}`);
}
endExtension();
}
endExtensions();
endServerHello();
if (tlsVersionSpecified !== true) throw new Error("No TLS version provided");
if (serverPublicKey === void 0) throw new Error("No key provided");
return serverPublicKey;
}
// src/tls/certUtils.ts
var universalTypeBoolean = 1;
var universalTypeInteger = 2;
var constructedUniversalTypeSequence = 48;
var constructedUniversalTypeSet = 49;
var universalTypeOID = 6;
var universalTypePrintableString = 19;
var universalTypeTeletexString = 20;
var universalTypeUTF8String = 12;
var universalTypeIA5String = 22;
var universalTypeUTCTime = 23;
var universalTypeGeneralizedTime = 24;
var universalTypeNull = 5;
var universalTypeOctetString = 4;
var universalTypeBitString = 3;
var constructedContextSpecificType = 163;
var contextSpecificType = 128;
var DNOIDMap = {
"2.5.4.6": "C",
// country
"2.5.4.10": "O",
// organisation
"2.5.4.11": "OU",
// organisational unit
"2.5.4.3": "CN",
// common name
"2.5.4.7": "L",
// locality
"2.5.4.8": "ST",
// state/province
"2.5.4.12": "T",
// title
"2.5.4.42": "GN",
// given name
"2.5.4.43": "I",
// initials
"2.5.4.4": "SN",
// surname
"1.2.840.113549.1.9.1": "MAIL",
"2.5.4.5": "SERIALNUMBER"
};
function intFromBitString(bs) {
const { length } = bs;
if (length > 4) throw new Error(`Bit string length ${length} would overflow JS bit operators`);
let result = 0;
let leftShift = 0;
for (let i = bs.length - 1; i >= 0; i--) {
result |= bs[i] << leftShift;
leftShift += 8;
}
return result;
}
async function readSeqOfSetOfSeq(cb, seqType) {
const result = {};
const [endSeq, seqRemaining] = await cb.expectASN1Sequence(seqType);
while (seqRemaining() > 0) {
await cb.expectUint8(constructedUniversalTypeSet, 0);
const [endItemSet] = await cb.expectASN1Length(0);
const [endItemSeq] = await cb.expectASN1Sequence();
const itemOID = await cb.readASN1OID();
const itemName = DNOIDMap[itemOID] ?? itemOID;
const valueType = await cb.readUint8();
if (valueType === universalTypePrintableString) {
} else if (valueType === universalTypeUTF8String) {
} else if (valueType === universalTypeIA5String) {
} else if (valueType === universalTypeTeletexString) {
} else {
throw new Error(`Unexpected item type in certificate ${seqType}: 0x${hexFromU8([valueType])}`);
}
const [endItemString, itemStringRemaining] = await cb.expectASN1Length(0);
const itemValue = await cb.readUTF8String(itemStringRemaining());
endItemString();
endItemSeq();
endItemSet();
const existingValue = result[itemName];
if (existingValue === void 0) result[itemName] = itemValue;
else if (typeof existingValue === "string") result[itemName] = [existingValue, itemValue];
else existingValue.push(itemValue);
}
endSeq();
return result;
}
async function readNamesSeq(cb, typeUnionBits = 0) {
const names = [];
const [endNamesSeq, namesSeqRemaining] = await cb.expectASN1Sequence(0);
while (namesSeqRemaining() > 0) {
const type = await cb.readUint8(0);
const [endName, nameRemaining] = await cb.expectASN1Length(0);
let name;
if (type === (typeUnionBits | 2 /* dNSName */)) {
name = await cb.readUTF8String(nameRemaining());
} else {
name = await cb.readBytes(nameRemaining());
}
names.push({ name, type });
endName();
}
endNamesSeq();
return names;
}
function algorithmWithOID(oid) {
const algo = {
"1.2.840.113549.1.1.1": {
name: "RSAES-PKCS1-v1_5"
},
"1.2.840.113549.1.1.5": {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-1"
}
},
"1.2.840.113549.1.1.11": {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-256"
}
},
"1.2.840.113549.1.1.12": {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-384"
}
},
"1.2.840.113549.1.1.13": {
name: "RSASSA-PKCS1-v1_5",
hash: {
name: "SHA-512"
}
},
"1.2.840.113549.1.1.10": {
name: "RSA-PSS"
},
"1.2.840.113549.1.1.7": {
name: "RSA-OAEP"
},
"1.2.840.10045.2.1": {
// dupes
name: "ECDSA",
hash: {
name: "SHA-1"
}
},
"1.2.840.10045.4.1": {
// dupes
name: "ECDSA",
hash: {
name: "SHA-1"
}
},
"1.2.840.10045.4.3.2": {
name: "ECDSA",
hash: {
name: "SHA-256"
}
},
"1.2.840.10045.4.3.3": {
name: "ECDSA",
hash: {
name: "SHA-384"
}
},
"1.2.840.10045.4.3.4": {
name: "ECDSA",
hash: {
name: "SHA-512"
}
},
"1.3.133.16.840.63.0.2": {
name: "ECDH",
kdf: "SHA-1"
},
"1.3.132.1.11.1": {
name: "ECDH",
kdf: "SHA-256"
},
"1.3.132.1.11.2": {
name: "ECDH",
kdf: "SHA-384"
},
"1.3.132.1.11.3": {
name: "ECDH",
kdf: "SHA-512"
},
"2.16.840.1.101.3.4.1.2": {
name: "AES-CBC",
length: 128
},
"2.16.840.1.101.3.4.1.22": {
name: "AES-CBC",
length: 192
},
"2.16.840.1.101.3.4.1.42": {
name: "AES-CBC",
length: 256
},
"2.16.840.1.101.3.4.1.6": {
name: "AES-GCM",
length: 128
},
"2.16.840.1.101.3.4.1.26": {
name: "AES-GCM",
length: 192
},
"2.16.840.1.101.3.4.1.46": {
name: "AES-GCM",
length: 256
},
"2.16.840.1.101.3.4.1.4": {
name: "AES-CFB",
length: 128
},
"2.16.840.1.101.3.4.1.24": {
name: "AES-CFB",
length: 192
},
"2.16.840.1.101.3.4.1.44": {
name: "AES-CFB",
length: 256
},
"2.16.840.1.101.3.4.1.5": {
name: "AES-KW",
length: 128
},
"2.16.840.1.101.3.4.1.25": {
name: "AES-KW",
length: 192
},
"2.16.840.1.101.3.4.1.45": {
name: "AES-KW",
length: 256
},
"1.2.840.113549.2.7": {
name: "HMAC",
hash: {
name: "SHA-1"
}
},
"1.2.840.113549.2.9": {
name: "HMAC",
hash: {
name: "SHA-256"
}
},
"1.2.840.113549.2.10": {
name: "HMAC",
hash: {
name: "SHA-384"
}
},
"1.2.840.113549.2.11": {
name: "HMAC",
hash: {
name: "SHA-512"
}
},
"1.2.840.113549.1.9.16.3.5": {
name: "DH"
},
"1.3.14.3.2.26": {
name: "SHA-1"
},
"2.16.840.1.101.3.4.2.1": {
name: "SHA-256"
},
"2.16.840.1.101.3.4.2.2": {
name: "SHA-384"
},
"2.16.840.1.101.3.4.2.3": {
name: "SHA-512"
},
"1.2.840.113549.1.5.12": {
name: "PBKDF2"
},
// special case: OIDs for ECC curves
"1.2.840.10045.3.1.7": {
name: "P-256"
},
"1.3.132.0.34": {
name: "P-384"
},
"1.3.132.0.35": {
name: "P-521"
}
}[oid];
if (algo === void 0) throw new Error(`Unsupported algorithm identifier: ${oid}`);
return algo;
}
function _descriptionForAlgorithm(algo, desc = []) {
Object.values(algo).forEach((value) => {
if (typeof value === "string") desc = [...desc, value];
else desc = _descriptionForAlgorithm(value, desc);
});
return desc;
}
function descriptionForAlgorithm(algo) {
return _descriptionForAlgorithm(algo).join(" / ");
}
// src/util/asn1bytes.ts
var ASN1Bytes = class extends Bytes {
async readASN1Length(comment) {
const byte1 = await this.readUint8();
if (byte1 < 128) {
return byte1;
}
const lengthBytes = byte1 & 127;
const fullComment = 0;
if (lengthBytes === 1) return this.readUint8(fullComment);
if (lengthBytes === 2) return this.readUint16(fullComment);
if (lengthBytes === 3) return this.readUint24(fullComment);
if (lengthBytes === 4) return this.readUint32(fullComment);
throw new Error(`ASN.1 length fields are only supported up to 4 bytes (this one is ${lengthBytes} bytes)`);
}
async expectASN1Length(comment) {
const length = await this.readASN1Length(comment);
return this.expectReadLength(length);
}
async expectASN1TypeAndLength(typeNum, typeDesc, comment) {
await this.expectUint8(typeNum, 0);
return this.expectASN1Length(0);
}
async readASN1OID(comment) {
const [endOID, OIDRemaining] = await this.expectASN1TypeAndLength(universalTypeOID, "OID", comment);
const byte1 = await this.readUint8();
let oid = `${Math.floor(byte1 / 40)}.${byte1 % 40}`;
while (OIDRemaining() > 0) {
let value = 0;
while (true) {
const nextByte = await this.readUint8();
value <<= 7;
value += nextByte & 127;
if (nextByte < 128) break;
}
oid += `.${value}`;
}
endOID();
return oid;
}
async readASN1Boolean(comment) {
const [endBoolean, booleanRemaining] = await this.expectASN1TypeAndLength(universalTypeBoolean, "boolean",
comment);
const length = booleanRemaining();
if (length !== 1) throw new Error(`Boolean has unexpected length: ${length}`);
const byte = await this.readUint8();
const result = {
255: true,
0: false
}[byte];
if (result === void 0) throw new Error(`Boolean has unexpected value: 0x${hexFromU8([byte])}`);
endBoolean();
return result;
}
async readASN1UTCTime(comment) {
const [endTime, timeRemaining] = await this.expectASN1TypeAndLength(universalTypeUTCTime, "UTC time", comment);
const timeStr = await this.readUTF8String(timeRemaining());
const parts = timeStr.match(/^(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)Z$/);
if (!parts) throw new Error("Unrecognised ASN.1 UTC time format");
const [, yr2dstr, mth, dy, hr, min, sec] = parts;
const yr2d = parseInt(yr2dstr, 10);
const yr = yr2d + (yr2d >= 50 ? 1900 : 2e3);
const time = /* @__PURE__ */ new Date(`${yr}-${mth}-${dy}T${hr}:${min}:${sec}Z`);
endTime();
return time;
}
async readASN1GeneralizedTime(comment) {
const [endTime, timeRemaining] = await this.expectASN1TypeAndLength(universalTypeGeneralizedTime, "general\
ized time", comment);
const timeStr = await this.readUTF8String(timeRemaining());
const parts = timeStr.match(/^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})?([0-9]{2})?([.][0-9]+)?(Z)?([-+][0-9]+)?$/);
if (!parts) throw new Error("Unrecognised ASN.1 generalized time format");
const [, yr, mth, dy, hr, min, sec, fracsec, z, tz] = parts;
if (sec === void 0 && fracsec !== void 0) throw new Error("Invalid ASN.1 generalized time format (fraction\
without seconds)");
if (z !== void 0 && tz !== void 0) throw new Error("Invalid ASN.1 generalized time format (Z and timezone)");
const time = /* @__PURE__ */ new Date(`${yr}-${mth}-${dy}T${hr}:${min ?? "00"}:${sec ?? "00"}${fracsec ?? ""}${tz ??
"Z"}`);
endTime();
return time;
}
async readASN1Time(comment) {
const startTimeType = await this.readUint8();
this.offset--;
let t;
if (startTimeType === universalTypeUTCTime) {
t = await this.readASN1UTCTime(comment);
} else if (startTimeType === universalTypeGeneralizedTime) {
t = await this.readASN1Generali