openpgp
Version:
OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.
1,267 lines (1,261 loc) • 144 kB
JavaScript
/*! OpenPGP.js v6.2.1 - 2025-08-26 - this is LGPL licensed code, see LICENSE/our website https://openpgpjs.org/ for more information. */
const globalThis = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
import { h as hexToBytes, a as abytes, b as bytesToHex, i as isBytes, c as concatBytes, d as anumber, H as Hash, e as ahash, t as toBytes, f as clean, g as aexists, r as randomBytes, s as sha256, j as sha384, k as sha512, l as createHasher, m as shake256, n as sha256$1, o as sha384$1, p as sha512$1 } from './sha512.mjs';
/**
* Hex, bytes and number utilities.
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const _0n$5 = /* @__PURE__ */ BigInt(0);
const _1n$6 = /* @__PURE__ */ BigInt(1);
// tmp name until v2
function _abool2(value, title = '') {
if (typeof value !== 'boolean') {
const prefix = title && `"${title}"`;
throw new Error(prefix + 'expected boolean, got type=' + typeof value);
}
return value;
}
// tmp name until v2
/** Asserts something is Uint8Array. */
function _abytes2(value, length, title = '') {
const bytes = isBytes(value);
const len = value?.length;
const needsLen = length !== undefined;
if (!bytes || (needsLen && len !== length)) {
const prefix = title && `"${title}" `;
const ofLen = needsLen ? ` of length ${length}` : '';
const got = bytes ? `length=${len}` : `type=${typeof value}`;
throw new Error(prefix + 'expected Uint8Array' + ofLen + ', got ' + got);
}
return value;
}
// Used in weierstrass, der
function numberToHexUnpadded(num) {
const hex = num.toString(16);
return hex.length & 1 ? '0' + hex : hex;
}
function hexToNumber(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
return hex === '' ? _0n$5 : BigInt('0x' + hex); // Big Endian
}
// BE: Big Endian, LE: Little Endian
function bytesToNumberBE(bytes) {
return hexToNumber(bytesToHex(bytes));
}
function bytesToNumberLE(bytes) {
abytes(bytes);
return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
}
function numberToBytesBE(n, len) {
return hexToBytes(n.toString(16).padStart(len * 2, '0'));
}
function numberToBytesLE(n, len) {
return numberToBytesBE(n, len).reverse();
}
/**
* Takes hex string or Uint8Array, converts to Uint8Array.
* Validates output length.
* Will throw error for other types.
* @param title descriptive title for an error e.g. 'secret key'
* @param hex hex string or Uint8Array
* @param expectedLength optional, will compare to result array's length
* @returns
*/
function ensureBytes(title, hex, expectedLength) {
let res;
if (typeof hex === 'string') {
try {
res = hexToBytes(hex);
}
catch (e) {
throw new Error(title + ' must be hex string or Uint8Array, cause: ' + e);
}
}
else if (isBytes(hex)) {
// Uint8Array.from() instead of hash.slice() because node.js Buffer
// is instance of Uint8Array, and its slice() creates **mutable** copy
res = Uint8Array.from(hex);
}
else {
throw new Error(title + ' must be hex string or Uint8Array');
}
const len = res.length;
if (typeof expectedLength === 'number' && len !== expectedLength)
throw new Error(title + ' of length ' + expectedLength + ' expected, got ' + len);
return res;
}
/**
* Copies Uint8Array. We can't use u8a.slice(), because u8a can be Buffer,
* and Buffer#slice creates mutable copy. Never use Buffers!
*/
function copyBytes(bytes) {
return Uint8Array.from(bytes);
}
/**
* Decodes 7-bit ASCII string to Uint8Array, throws on non-ascii symbols
* Should be safe to use for things expected to be ASCII.
* Returns exact same result as utf8ToBytes for ASCII or throws.
*/
function asciiToBytes(ascii) {
return Uint8Array.from(ascii, (c, i) => {
const charCode = c.charCodeAt(0);
if (c.length !== 1 || charCode > 127) {
throw new Error(`string contains non-ASCII character "${ascii[i]}" with code ${charCode} at position ${i}`);
}
return charCode;
});
}
/**
* @example utf8ToBytes('abc') // new Uint8Array([97, 98, 99])
*/
// export const utf8ToBytes: typeof utf8ToBytes_ = utf8ToBytes_;
/**
* Converts bytes to string using UTF8 encoding.
* @example bytesToUtf8(Uint8Array.from([97, 98, 99])) // 'abc'
*/
// export const bytesToUtf8: typeof bytesToUtf8_ = bytesToUtf8_;
// Is positive bigint
const isPosBig = (n) => typeof n === 'bigint' && _0n$5 <= n;
function inRange(n, min, max) {
return isPosBig(n) && isPosBig(min) && isPosBig(max) && min <= n && n < max;
}
/**
* Asserts min <= n < max. NOTE: It's < max and not <= max.
* @example
* aInRange('x', x, 1n, 256n); // would assume x is in (1n..255n)
*/
function aInRange(title, n, min, max) {
// Why min <= n < max and not a (min < n < max) OR b (min <= n <= max)?
// consider P=256n, min=0n, max=P
// - a for min=0 would require -1: `inRange('x', x, -1n, P)`
// - b would commonly require subtraction: `inRange('x', x, 0n, P - 1n)`
// - our way is the cleanest: `inRange('x', x, 0n, P)
if (!inRange(n, min, max))
throw new Error('expected valid ' + title + ': ' + min + ' <= n < ' + max + ', got ' + n);
}
// Bit operations
/**
* Calculates amount of bits in a bigint.
* Same as `n.toString(2).length`
* TODO: merge with nLength in modular
*/
function bitLen(n) {
let len;
for (len = 0; n > _0n$5; n >>= _1n$6, len += 1)
;
return len;
}
/**
* Calculate mask for N bits. Not using ** operator with bigints because of old engines.
* Same as BigInt(`0b${Array(i).fill('1').join('')}`)
*/
const bitMask = (n) => (_1n$6 << BigInt(n)) - _1n$6;
/**
* Minimal HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
* @returns function that will call DRBG until 2nd arg returns something meaningful
* @example
* const drbg = createHmacDRBG<Key>(32, 32, hmac);
* drbg(seed, bytesToKey); // bytesToKey must return Key or undefined
*/
function createHmacDrbg(hashLen, qByteLen, hmacFn) {
if (typeof hashLen !== 'number' || hashLen < 2)
throw new Error('hashLen must be a number');
if (typeof qByteLen !== 'number' || qByteLen < 2)
throw new Error('qByteLen must be a number');
if (typeof hmacFn !== 'function')
throw new Error('hmacFn must be a function');
// Step B, Step C: set hashLen to 8*ceil(hlen/8)
const u8n = (len) => new Uint8Array(len); // creates Uint8Array
const u8of = (byte) => Uint8Array.of(byte); // another shortcut
let v = u8n(hashLen); // Minimal non-full-spec HMAC-DRBG from NIST 800-90 for RFC6979 sigs.
let k = u8n(hashLen); // Steps B and C of RFC6979 3.2: set hashLen, in our case always same
let i = 0; // Iterations counter, will throw when over 1000
const reset = () => {
v.fill(1);
k.fill(0);
i = 0;
};
const h = (...b) => hmacFn(k, v, ...b); // hmac(k)(v, ...values)
const reseed = (seed = u8n(0)) => {
// HMAC-DRBG reseed() function. Steps D-G
k = h(u8of(0x00), seed); // k = hmac(k || v || 0x00 || seed)
v = h(); // v = hmac(k || v)
if (seed.length === 0)
return;
k = h(u8of(0x01), seed); // k = hmac(k || v || 0x01 || seed)
v = h(); // v = hmac(k || v)
};
const gen = () => {
// HMAC-DRBG generate() function
if (i++ >= 1000)
throw new Error('drbg: tried 1000 values');
let len = 0;
const out = [];
while (len < qByteLen) {
v = h();
const sl = v.slice();
out.push(sl);
len += v.length;
}
return concatBytes(...out);
};
const genUntil = (seed, pred) => {
reset();
reseed(seed); // Steps D-G
let res = undefined; // Step H: grind until k is in [1..n-1]
while (!(res = pred(gen())))
reseed();
reset();
return res;
};
return genUntil;
}
function _validateObject(object, fields, optFields = {}) {
if (!object || typeof object !== 'object')
throw new Error('expected valid options object');
function checkField(fieldName, expectedType, isOpt) {
const val = object[fieldName];
if (isOpt && val === undefined)
return;
const current = typeof val;
if (current !== expectedType || val === null)
throw new Error(`param "${fieldName}" is invalid: expected ${expectedType}, got ${current}`);
}
Object.entries(fields).forEach(([k, v]) => checkField(k, v, false));
Object.entries(optFields).forEach(([k, v]) => checkField(k, v, true));
}
/**
* Memoizes (caches) computation result.
* Uses WeakMap: the value is going auto-cleaned by GC after last reference is removed.
*/
function memoized(fn) {
const map = new WeakMap();
return (arg, ...args) => {
const val = map.get(arg);
if (val !== undefined)
return val;
const computed = fn(arg, ...args);
map.set(arg, computed);
return computed;
};
}
/**
* Utils for modular division and fields.
* Field over 11 is a finite (Galois) field is integer number operations `mod 11`.
* There is no division: it is replaced by modular multiplicative inverse.
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// prettier-ignore
const _0n$4 = BigInt(0), _1n$5 = BigInt(1), _2n$5 = /* @__PURE__ */ BigInt(2), _3n$2 = /* @__PURE__ */ BigInt(3);
// prettier-ignore
const _4n$1 = /* @__PURE__ */ BigInt(4), _5n = /* @__PURE__ */ BigInt(5), _7n = /* @__PURE__ */ BigInt(7);
// prettier-ignore
const _8n$1 = /* @__PURE__ */ BigInt(8), _9n = /* @__PURE__ */ BigInt(9), _16n = /* @__PURE__ */ BigInt(16);
// Calculates a modulo b
function mod(a, b) {
const result = a % b;
return result >= _0n$4 ? result : b + result;
}
/** Does `x^(2^power)` mod p. `pow2(30, 4)` == `30^(2^4)` */
function pow2(x, power, modulo) {
let res = x;
while (power-- > _0n$4) {
res *= res;
res %= modulo;
}
return res;
}
/**
* Inverses number over modulo.
* Implemented using [Euclidean GCD](https://brilliant.org/wiki/extended-euclidean-algorithm/).
*/
function invert(number, modulo) {
if (number === _0n$4)
throw new Error('invert: expected non-zero number');
if (modulo <= _0n$4)
throw new Error('invert: expected positive modulus, got ' + modulo);
// Fermat's little theorem "CT-like" version inv(n) = n^(m-2) mod m is 30x slower.
let a = mod(number, modulo);
let b = modulo;
// prettier-ignore
let x = _0n$4, u = _1n$5;
while (a !== _0n$4) {
// JIT applies optimization if those two lines follow each other
const q = b / a;
const r = b % a;
const m = x - u * q;
// prettier-ignore
b = a, a = r, x = u, u = m;
}
const gcd = b;
if (gcd !== _1n$5)
throw new Error('invert: does not exist');
return mod(x, modulo);
}
function assertIsSquare(Fp, root, n) {
if (!Fp.eql(Fp.sqr(root), n))
throw new Error('Cannot find square root');
}
// Not all roots are possible! Example which will throw:
// const NUM =
// n = 72057594037927816n;
// Fp = Field(BigInt('0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab'));
function sqrt3mod4(Fp, n) {
const p1div4 = (Fp.ORDER + _1n$5) / _4n$1;
const root = Fp.pow(n, p1div4);
assertIsSquare(Fp, root, n);
return root;
}
function sqrt5mod8(Fp, n) {
const p5div8 = (Fp.ORDER - _5n) / _8n$1;
const n2 = Fp.mul(n, _2n$5);
const v = Fp.pow(n2, p5div8);
const nv = Fp.mul(n, v);
const i = Fp.mul(Fp.mul(nv, _2n$5), v);
const root = Fp.mul(nv, Fp.sub(i, Fp.ONE));
assertIsSquare(Fp, root, n);
return root;
}
// Based on RFC9380, Kong algorithm
// prettier-ignore
function sqrt9mod16(P) {
const Fp_ = Field(P);
const tn = tonelliShanks(P);
const c1 = tn(Fp_, Fp_.neg(Fp_.ONE)); // 1. c1 = sqrt(-1) in F, i.e., (c1^2) == -1 in F
const c2 = tn(Fp_, c1); // 2. c2 = sqrt(c1) in F, i.e., (c2^2) == c1 in F
const c3 = tn(Fp_, Fp_.neg(c1)); // 3. c3 = sqrt(-c1) in F, i.e., (c3^2) == -c1 in F
const c4 = (P + _7n) / _16n; // 4. c4 = (q + 7) / 16 # Integer arithmetic
return (Fp, n) => {
let tv1 = Fp.pow(n, c4); // 1. tv1 = x^c4
let tv2 = Fp.mul(tv1, c1); // 2. tv2 = c1 * tv1
const tv3 = Fp.mul(tv1, c2); // 3. tv3 = c2 * tv1
const tv4 = Fp.mul(tv1, c3); // 4. tv4 = c3 * tv1
const e1 = Fp.eql(Fp.sqr(tv2), n); // 5. e1 = (tv2^2) == x
const e2 = Fp.eql(Fp.sqr(tv3), n); // 6. e2 = (tv3^2) == x
tv1 = Fp.cmov(tv1, tv2, e1); // 7. tv1 = CMOV(tv1, tv2, e1) # Select tv2 if (tv2^2) == x
tv2 = Fp.cmov(tv4, tv3, e2); // 8. tv2 = CMOV(tv4, tv3, e2) # Select tv3 if (tv3^2) == x
const e3 = Fp.eql(Fp.sqr(tv2), n); // 9. e3 = (tv2^2) == x
const root = Fp.cmov(tv1, tv2, e3); // 10. z = CMOV(tv1, tv2, e3) # Select sqrt from tv1 & tv2
assertIsSquare(Fp, root, n);
return root;
};
}
/**
* Tonelli-Shanks square root search algorithm.
* 1. https://eprint.iacr.org/2012/685.pdf (page 12)
* 2. Square Roots from 1; 24, 51, 10 to Dan Shanks
* @param P field order
* @returns function that takes field Fp (created from P) and number n
*/
function tonelliShanks(P) {
// Initialization (precomputation).
// Caching initialization could boost perf by 7%.
if (P < _3n$2)
throw new Error('sqrt is not defined for small field');
// Factor P - 1 = Q * 2^S, where Q is odd
let Q = P - _1n$5;
let S = 0;
while (Q % _2n$5 === _0n$4) {
Q /= _2n$5;
S++;
}
// Find the first quadratic non-residue Z >= 2
let Z = _2n$5;
const _Fp = Field(P);
while (FpLegendre(_Fp, Z) === 1) {
// Basic primality test for P. After x iterations, chance of
// not finding quadratic non-residue is 2^x, so 2^1000.
if (Z++ > 1000)
throw new Error('Cannot find square root: probably non-prime P');
}
// Fast-path; usually done before Z, but we do "primality test".
if (S === 1)
return sqrt3mod4;
// Slow-path
// TODO: test on Fp2 and others
let cc = _Fp.pow(Z, Q); // c = z^Q
const Q1div2 = (Q + _1n$5) / _2n$5;
return function tonelliSlow(Fp, n) {
if (Fp.is0(n))
return n;
// Check if n is a quadratic residue using Legendre symbol
if (FpLegendre(Fp, n) !== 1)
throw new Error('Cannot find square root');
// Initialize variables for the main loop
let M = S;
let c = Fp.mul(Fp.ONE, cc); // c = z^Q, move cc from field _Fp into field Fp
let t = Fp.pow(n, Q); // t = n^Q, first guess at the fudge factor
let R = Fp.pow(n, Q1div2); // R = n^((Q+1)/2), first guess at the square root
// Main loop
// while t != 1
while (!Fp.eql(t, Fp.ONE)) {
if (Fp.is0(t))
return Fp.ZERO; // if t=0 return R=0
let i = 1;
// Find the smallest i >= 1 such that t^(2^i) ≡ 1 (mod P)
let t_tmp = Fp.sqr(t); // t^(2^1)
while (!Fp.eql(t_tmp, Fp.ONE)) {
i++;
t_tmp = Fp.sqr(t_tmp); // t^(2^2)...
if (i === M)
throw new Error('Cannot find square root');
}
// Calculate the exponent for b: 2^(M - i - 1)
const exponent = _1n$5 << BigInt(M - i - 1); // bigint is important
const b = Fp.pow(c, exponent); // b = 2^(M - i - 1)
// Update variables
M = i;
c = Fp.sqr(b); // c = b^2
t = Fp.mul(t, c); // t = (t * b^2)
R = Fp.mul(R, b); // R = R*b
}
return R;
};
}
/**
* Square root for a finite field. Will try optimized versions first:
*
* 1. P ≡ 3 (mod 4)
* 2. P ≡ 5 (mod 8)
* 3. P ≡ 9 (mod 16)
* 4. Tonelli-Shanks algorithm
*
* Different algorithms can give different roots, it is up to user to decide which one they want.
* For example there is FpSqrtOdd/FpSqrtEven to choice root based on oddness (used for hash-to-curve).
*/
function FpSqrt(P) {
// P ≡ 3 (mod 4) => √n = n^((P+1)/4)
if (P % _4n$1 === _3n$2)
return sqrt3mod4;
// P ≡ 5 (mod 8) => Atkin algorithm, page 10 of https://eprint.iacr.org/2012/685.pdf
if (P % _8n$1 === _5n)
return sqrt5mod8;
// P ≡ 9 (mod 16) => Kong algorithm, page 11 of https://eprint.iacr.org/2012/685.pdf (algorithm 4)
if (P % _16n === _9n)
return sqrt9mod16(P);
// Tonelli-Shanks algorithm
return tonelliShanks(P);
}
// prettier-ignore
const FIELD_FIELDS = [
'create', 'isValid', 'is0', 'neg', 'inv', 'sqrt', 'sqr',
'eql', 'add', 'sub', 'mul', 'pow', 'div',
'addN', 'subN', 'mulN', 'sqrN'
];
function validateField(field) {
const initial = {
ORDER: 'bigint',
MASK: 'bigint',
BYTES: 'number',
BITS: 'number',
};
const opts = FIELD_FIELDS.reduce((map, val) => {
map[val] = 'function';
return map;
}, initial);
_validateObject(field, opts);
// const max = 16384;
// if (field.BYTES < 1 || field.BYTES > max) throw new Error('invalid field');
// if (field.BITS < 1 || field.BITS > 8 * max) throw new Error('invalid field');
return field;
}
// Generic field functions
/**
* Same as `pow` but for Fp: non-constant-time.
* Unsafe in some contexts: uses ladder, so can expose bigint bits.
*/
function FpPow(Fp, num, power) {
if (power < _0n$4)
throw new Error('invalid exponent, negatives unsupported');
if (power === _0n$4)
return Fp.ONE;
if (power === _1n$5)
return num;
let p = Fp.ONE;
let d = num;
while (power > _0n$4) {
if (power & _1n$5)
p = Fp.mul(p, d);
d = Fp.sqr(d);
power >>= _1n$5;
}
return p;
}
/**
* Efficiently invert an array of Field elements.
* Exception-free. Will return `undefined` for 0 elements.
* @param passZero map 0 to 0 (instead of undefined)
*/
function FpInvertBatch(Fp, nums, passZero = false) {
const inverted = new Array(nums.length).fill(passZero ? Fp.ZERO : undefined);
// Walk from first to last, multiply them by each other MOD p
const multipliedAcc = nums.reduce((acc, num, i) => {
if (Fp.is0(num))
return acc;
inverted[i] = acc;
return Fp.mul(acc, num);
}, Fp.ONE);
// Invert last element
const invertedAcc = Fp.inv(multipliedAcc);
// Walk from last to first, multiply them by inverted each other MOD p
nums.reduceRight((acc, num, i) => {
if (Fp.is0(num))
return acc;
inverted[i] = Fp.mul(acc, inverted[i]);
return Fp.mul(acc, num);
}, invertedAcc);
return inverted;
}
/**
* Legendre symbol.
* Legendre constant is used to calculate Legendre symbol (a | p)
* which denotes the value of a^((p-1)/2) (mod p).
*
* * (a | p) ≡ 1 if a is a square (mod p), quadratic residue
* * (a | p) ≡ -1 if a is not a square (mod p), quadratic non residue
* * (a | p) ≡ 0 if a ≡ 0 (mod p)
*/
function FpLegendre(Fp, n) {
// We can use 3rd argument as optional cache of this value
// but seems unneeded for now. The operation is very fast.
const p1mod2 = (Fp.ORDER - _1n$5) / _2n$5;
const powered = Fp.pow(n, p1mod2);
const yes = Fp.eql(powered, Fp.ONE);
const zero = Fp.eql(powered, Fp.ZERO);
const no = Fp.eql(powered, Fp.neg(Fp.ONE));
if (!yes && !zero && !no)
throw new Error('invalid Legendre symbol result');
return yes ? 1 : zero ? 0 : -1;
}
// CURVE.n lengths
function nLength(n, nBitLength) {
// Bit size, byte size of CURVE.n
if (nBitLength !== undefined)
anumber(nBitLength);
const _nBitLength = nBitLength !== undefined ? nBitLength : n.toString(2).length;
const nByteLength = Math.ceil(_nBitLength / 8);
return { nBitLength: _nBitLength, nByteLength };
}
/**
* Creates a finite field. Major performance optimizations:
* * 1. Denormalized operations like mulN instead of mul.
* * 2. Identical object shape: never add or remove keys.
* * 3. `Object.freeze`.
* Fragile: always run a benchmark on a change.
* Security note: operations don't check 'isValid' for all elements for performance reasons,
* it is caller responsibility to check this.
* This is low-level code, please make sure you know what you're doing.
*
* Note about field properties:
* * CHARACTERISTIC p = prime number, number of elements in main subgroup.
* * ORDER q = similar to cofactor in curves, may be composite `q = p^m`.
*
* @param ORDER field order, probably prime, or could be composite
* @param bitLen how many bits the field consumes
* @param isLE (default: false) if encoding / decoding should be in little-endian
* @param redef optional faster redefinitions of sqrt and other methods
*/
function Field(ORDER, bitLenOrOpts, // TODO: use opts only in v2?
isLE = false, opts = {}) {
if (ORDER <= _0n$4)
throw new Error('invalid field: expected ORDER > 0, got ' + ORDER);
let _nbitLength = undefined;
let _sqrt = undefined;
let modFromBytes = false;
let allowedLengths = undefined;
if (typeof bitLenOrOpts === 'object' && bitLenOrOpts != null) {
if (opts.sqrt || isLE)
throw new Error('cannot specify opts in two arguments');
const _opts = bitLenOrOpts;
if (_opts.BITS)
_nbitLength = _opts.BITS;
if (_opts.sqrt)
_sqrt = _opts.sqrt;
if (typeof _opts.isLE === 'boolean')
isLE = _opts.isLE;
if (typeof _opts.modFromBytes === 'boolean')
modFromBytes = _opts.modFromBytes;
allowedLengths = _opts.allowedLengths;
}
else {
if (typeof bitLenOrOpts === 'number')
_nbitLength = bitLenOrOpts;
if (opts.sqrt)
_sqrt = opts.sqrt;
}
const { nBitLength: BITS, nByteLength: BYTES } = nLength(ORDER, _nbitLength);
if (BYTES > 2048)
throw new Error('invalid field: expected ORDER of <= 2048 bytes');
let sqrtP; // cached sqrtP
const f = Object.freeze({
ORDER,
isLE,
BITS,
BYTES,
MASK: bitMask(BITS),
ZERO: _0n$4,
ONE: _1n$5,
allowedLengths: allowedLengths,
create: (num) => mod(num, ORDER),
isValid: (num) => {
if (typeof num !== 'bigint')
throw new Error('invalid field element: expected bigint, got ' + typeof num);
return _0n$4 <= num && num < ORDER; // 0 is valid element, but it's not invertible
},
is0: (num) => num === _0n$4,
// is valid and invertible
isValidNot0: (num) => !f.is0(num) && f.isValid(num),
isOdd: (num) => (num & _1n$5) === _1n$5,
neg: (num) => mod(-num, ORDER),
eql: (lhs, rhs) => lhs === rhs,
sqr: (num) => mod(num * num, ORDER),
add: (lhs, rhs) => mod(lhs + rhs, ORDER),
sub: (lhs, rhs) => mod(lhs - rhs, ORDER),
mul: (lhs, rhs) => mod(lhs * rhs, ORDER),
pow: (num, power) => FpPow(f, num, power),
div: (lhs, rhs) => mod(lhs * invert(rhs, ORDER), ORDER),
// Same as above, but doesn't normalize
sqrN: (num) => num * num,
addN: (lhs, rhs) => lhs + rhs,
subN: (lhs, rhs) => lhs - rhs,
mulN: (lhs, rhs) => lhs * rhs,
inv: (num) => invert(num, ORDER),
sqrt: _sqrt ||
((n) => {
if (!sqrtP)
sqrtP = FpSqrt(ORDER);
return sqrtP(f, n);
}),
toBytes: (num) => (isLE ? numberToBytesLE(num, BYTES) : numberToBytesBE(num, BYTES)),
fromBytes: (bytes, skipValidation = true) => {
if (allowedLengths) {
if (!allowedLengths.includes(bytes.length) || bytes.length > BYTES) {
throw new Error('Field.fromBytes: expected ' + allowedLengths + ' bytes, got ' + bytes.length);
}
const padded = new Uint8Array(BYTES);
// isLE add 0 to right, !isLE to the left.
padded.set(bytes, isLE ? 0 : padded.length - bytes.length);
bytes = padded;
}
if (bytes.length !== BYTES)
throw new Error('Field.fromBytes: expected ' + BYTES + ' bytes, got ' + bytes.length);
let scalar = isLE ? bytesToNumberLE(bytes) : bytesToNumberBE(bytes);
if (modFromBytes)
scalar = mod(scalar, ORDER);
if (!skipValidation)
if (!f.isValid(scalar))
throw new Error('invalid field element: outside of range 0..ORDER');
// NOTE: we don't validate scalar here, please use isValid. This done such way because some
// protocol may allow non-reduced scalar that reduced later or changed some other way.
return scalar;
},
// TODO: we don't need it here, move out to separate fn
invertBatch: (lst) => FpInvertBatch(f, lst),
// We can't move this out because Fp6, Fp12 implement it
// and it's unclear what to return in there.
cmov: (a, b, c) => (c ? b : a),
});
return Object.freeze(f);
}
/**
* Returns total number of bytes consumed by the field element.
* For example, 32 bytes for usual 256-bit weierstrass curve.
* @param fieldOrder number of field elements, usually CURVE.n
* @returns byte length of field
*/
function getFieldBytesLength(fieldOrder) {
if (typeof fieldOrder !== 'bigint')
throw new Error('field order must be bigint');
const bitLength = fieldOrder.toString(2).length;
return Math.ceil(bitLength / 8);
}
/**
* Returns minimal amount of bytes that can be safely reduced
* by field order.
* Should be 2^-128 for 128-bit curve such as P256.
* @param fieldOrder number of field elements, usually CURVE.n
* @returns byte length of target hash
*/
function getMinHashLength(fieldOrder) {
const length = getFieldBytesLength(fieldOrder);
return length + Math.ceil(length / 2);
}
/**
* "Constant-time" private key generation utility.
* Can take (n + n/2) or more bytes of uniform input e.g. from CSPRNG or KDF
* and convert them into private scalar, with the modulo bias being negligible.
* Needs at least 48 bytes of input for 32-byte private key.
* https://research.kudelskisecurity.com/2020/07/28/the-definitive-guide-to-modulo-bias-and-how-to-avoid-it/
* FIPS 186-5, A.2 https://csrc.nist.gov/publications/detail/fips/186/5/final
* RFC 9380, https://www.rfc-editor.org/rfc/rfc9380#section-5
* @param hash hash output from SHA3 or a similar function
* @param groupOrder size of subgroup - (e.g. secp256k1.CURVE.n)
* @param isLE interpret hash bytes as LE num
* @returns valid private scalar
*/
function mapHashToField(key, fieldOrder, isLE = false) {
const len = key.length;
const fieldLen = getFieldBytesLength(fieldOrder);
const minLen = getMinHashLength(fieldOrder);
// No small numbers: need to understand bias story. No huge numbers: easier to detect JS timings.
if (len < 16 || len < minLen || len > 1024)
throw new Error('expected ' + minLen + '-1024 bytes of input, got ' + len);
const num = isLE ? bytesToNumberLE(key) : bytesToNumberBE(key);
// `mod(x, 11)` can sometimes produce 0. `mod(x, 10) + 1` is the same, but no 0
const reduced = mod(num, fieldOrder - _1n$5) + _1n$5;
return isLE ? numberToBytesLE(reduced, fieldLen) : numberToBytesBE(reduced, fieldLen);
}
/**
* HMAC: RFC2104 message authentication code.
* @module
*/
class HMAC extends Hash {
constructor(hash, _key) {
super();
this.finished = false;
this.destroyed = false;
ahash(hash);
const key = toBytes(_key);
this.iHash = hash.create();
if (typeof this.iHash.update !== 'function')
throw new Error('Expected instance of class which extends utils.Hash');
this.blockLen = this.iHash.blockLen;
this.outputLen = this.iHash.outputLen;
const blockLen = this.blockLen;
const pad = new Uint8Array(blockLen);
// blockLen can be bigger than outputLen
pad.set(key.length > blockLen ? hash.create().update(key).digest() : key);
for (let i = 0; i < pad.length; i++)
pad[i] ^= 0x36;
this.iHash.update(pad);
// By doing update (processing of first block) of outer hash here we can re-use it between multiple calls via clone
this.oHash = hash.create();
// Undo internal XOR && apply outer XOR
for (let i = 0; i < pad.length; i++)
pad[i] ^= 0x36 ^ 0x5c;
this.oHash.update(pad);
clean(pad);
}
update(buf) {
aexists(this);
this.iHash.update(buf);
return this;
}
digestInto(out) {
aexists(this);
abytes(out, this.outputLen);
this.finished = true;
this.iHash.digestInto(out);
this.oHash.update(out);
this.oHash.digestInto(out);
this.destroy();
}
digest() {
const out = new Uint8Array(this.oHash.outputLen);
this.digestInto(out);
return out;
}
_cloneInto(to) {
// Create new instance without calling constructor since key already in state and we don't know it.
to || (to = Object.create(Object.getPrototypeOf(this), {}));
const { oHash, iHash, finished, destroyed, blockLen, outputLen } = this;
to = to;
to.finished = finished;
to.destroyed = destroyed;
to.blockLen = blockLen;
to.outputLen = outputLen;
to.oHash = oHash._cloneInto(to.oHash);
to.iHash = iHash._cloneInto(to.iHash);
return to;
}
clone() {
return this._cloneInto();
}
destroy() {
this.destroyed = true;
this.oHash.destroy();
this.iHash.destroy();
}
}
/**
* HMAC: RFC2104 message authentication code.
* @param hash - function that would be used e.g. sha256
* @param key - message key
* @param message - message data
* @example
* import { hmac } from '@noble/hashes/hmac';
* import { sha256 } from '@noble/hashes/sha2';
* const mac1 = hmac(sha256, 'key', 'message');
*/
const hmac = (hash, key, message) => new HMAC(hash, key).update(message).digest();
hmac.create = (hash, key) => new HMAC(hash, key);
/**
* Methods for elliptic curve multiplication by scalars.
* Contains wNAF, pippenger.
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
const _0n$3 = BigInt(0);
const _1n$4 = BigInt(1);
function negateCt(condition, item) {
const neg = item.negate();
return condition ? neg : item;
}
/**
* Takes a bunch of Projective Points but executes only one
* inversion on all of them. Inversion is very slow operation,
* so this improves performance massively.
* Optimization: converts a list of projective points to a list of identical points with Z=1.
*/
function normalizeZ(c, points) {
const invertedZs = FpInvertBatch(c.Fp, points.map((p) => p.Z));
return points.map((p, i) => c.fromAffine(p.toAffine(invertedZs[i])));
}
function validateW(W, bits) {
if (!Number.isSafeInteger(W) || W <= 0 || W > bits)
throw new Error('invalid window size, expected [1..' + bits + '], got W=' + W);
}
function calcWOpts(W, scalarBits) {
validateW(W, scalarBits);
const windows = Math.ceil(scalarBits / W) + 1; // W=8 33. Not 32, because we skip zero
const windowSize = 2 ** (W - 1); // W=8 128. Not 256, because we skip zero
const maxNumber = 2 ** W; // W=8 256
const mask = bitMask(W); // W=8 255 == mask 0b11111111
const shiftBy = BigInt(W); // W=8 8
return { windows, windowSize, mask, maxNumber, shiftBy };
}
function calcOffsets(n, window, wOpts) {
const { windowSize, mask, maxNumber, shiftBy } = wOpts;
let wbits = Number(n & mask); // extract W bits.
let nextN = n >> shiftBy; // shift number by W bits.
// What actually happens here:
// const highestBit = Number(mask ^ (mask >> 1n));
// let wbits2 = wbits - 1; // skip zero
// if (wbits2 & highestBit) { wbits2 ^= Number(mask); // (~);
// split if bits > max: +224 => 256-32
if (wbits > windowSize) {
// we skip zero, which means instead of `>= size-1`, we do `> size`
wbits -= maxNumber; // -32, can be maxNumber - wbits, but then we need to set isNeg here.
nextN += _1n$4; // +256 (carry)
}
const offsetStart = window * windowSize;
const offset = offsetStart + Math.abs(wbits) - 1; // -1 because we skip zero
const isZero = wbits === 0; // is current window slice a 0?
const isNeg = wbits < 0; // is current window slice negative?
const isNegF = window % 2 !== 0; // fake random statement for noise
const offsetF = offsetStart; // fake offset for noise
return { nextN, offset, isZero, isNeg, isNegF, offsetF };
}
function validateMSMPoints(points, c) {
if (!Array.isArray(points))
throw new Error('array expected');
points.forEach((p, i) => {
if (!(p instanceof c))
throw new Error('invalid point at index ' + i);
});
}
function validateMSMScalars(scalars, field) {
if (!Array.isArray(scalars))
throw new Error('array of scalars expected');
scalars.forEach((s, i) => {
if (!field.isValid(s))
throw new Error('invalid scalar at index ' + i);
});
}
// Since points in different groups cannot be equal (different object constructor),
// we can have single place to store precomputes.
// Allows to make points frozen / immutable.
const pointPrecomputes = new WeakMap();
const pointWindowSizes = new WeakMap();
function getW(P) {
// To disable precomputes:
// return 1;
return pointWindowSizes.get(P) || 1;
}
function assert0(n) {
if (n !== _0n$3)
throw new Error('invalid wNAF');
}
/**
* Elliptic curve multiplication of Point by scalar. Fragile.
* Table generation takes **30MB of ram and 10ms on high-end CPU**,
* but may take much longer on slow devices. Actual generation will happen on
* first call of `multiply()`. By default, `BASE` point is precomputed.
*
* Scalars should always be less than curve order: this should be checked inside of a curve itself.
* Creates precomputation tables for fast multiplication:
* - private scalar is split by fixed size windows of W bits
* - every window point is collected from window's table & added to accumulator
* - since windows are different, same point inside tables won't be accessed more than once per calc
* - each multiplication is 'Math.ceil(CURVE_ORDER / 𝑊) + 1' point additions (fixed for any scalar)
* - +1 window is neccessary for wNAF
* - wNAF reduces table size: 2x less memory + 2x faster generation, but 10% slower multiplication
*
* @todo Research returning 2d JS array of windows, instead of a single window.
* This would allow windows to be in different memory locations
*/
class wNAF {
// Parametrized with a given Point class (not individual point)
constructor(Point, bits) {
this.BASE = Point.BASE;
this.ZERO = Point.ZERO;
this.Fn = Point.Fn;
this.bits = bits;
}
// non-const time multiplication ladder
_unsafeLadder(elm, n, p = this.ZERO) {
let d = elm;
while (n > _0n$3) {
if (n & _1n$4)
p = p.add(d);
d = d.double();
n >>= _1n$4;
}
return p;
}
/**
* Creates a wNAF precomputation window. Used for caching.
* Default window size is set by `utils.precompute()` and is equal to 8.
* Number of precomputed points depends on the curve size:
* 2^(𝑊−1) * (Math.ceil(𝑛 / 𝑊) + 1), where:
* - 𝑊 is the window size
* - 𝑛 is the bitlength of the curve order.
* For a 256-bit curve and window size 8, the number of precomputed points is 128 * 33 = 4224.
* @param point Point instance
* @param W window size
* @returns precomputed point tables flattened to a single array
*/
precomputeWindow(point, W) {
const { windows, windowSize } = calcWOpts(W, this.bits);
const points = [];
let p = point;
let base = p;
for (let window = 0; window < windows; window++) {
base = p;
points.push(base);
// i=1, bc we skip 0
for (let i = 1; i < windowSize; i++) {
base = base.add(p);
points.push(base);
}
p = base.double();
}
return points;
}
/**
* Implements ec multiplication using precomputed tables and w-ary non-adjacent form.
* More compact implementation:
* https://github.com/paulmillr/noble-secp256k1/blob/47cb1669b6e506ad66b35fe7d76132ae97465da2/index.ts#L502-L541
* @returns real and fake (for const-time) points
*/
wNAF(W, precomputes, n) {
// Scalar should be smaller than field order
if (!this.Fn.isValid(n))
throw new Error('invalid scalar');
// Accumulators
let p = this.ZERO;
let f = this.BASE;
// This code was first written with assumption that 'f' and 'p' will never be infinity point:
// since each addition is multiplied by 2 ** W, it cannot cancel each other. However,
// there is negate now: it is possible that negated element from low value
// would be the same as high element, which will create carry into next window.
// It's not obvious how this can fail, but still worth investigating later.
const wo = calcWOpts(W, this.bits);
for (let window = 0; window < wo.windows; window++) {
// (n === _0n) is handled and not early-exited. isEven and offsetF are used for noise
const { nextN, offset, isZero, isNeg, isNegF, offsetF } = calcOffsets(n, window, wo);
n = nextN;
if (isZero) {
// bits are 0: add garbage to fake point
// Important part for const-time getPublicKey: add random "noise" point to f.
f = f.add(negateCt(isNegF, precomputes[offsetF]));
}
else {
// bits are 1: add to result point
p = p.add(negateCt(isNeg, precomputes[offset]));
}
}
assert0(n);
// Return both real and fake points: JIT won't eliminate f.
// At this point there is a way to F be infinity-point even if p is not,
// which makes it less const-time: around 1 bigint multiply.
return { p, f };
}
/**
* Implements ec unsafe (non const-time) multiplication using precomputed tables and w-ary non-adjacent form.
* @param acc accumulator point to add result of multiplication
* @returns point
*/
wNAFUnsafe(W, precomputes, n, acc = this.ZERO) {
const wo = calcWOpts(W, this.bits);
for (let window = 0; window < wo.windows; window++) {
if (n === _0n$3)
break; // Early-exit, skip 0 value
const { nextN, offset, isZero, isNeg } = calcOffsets(n, window, wo);
n = nextN;
if (isZero) {
// Window bits are 0: skip processing.
// Move to next window.
continue;
}
else {
const item = precomputes[offset];
acc = acc.add(isNeg ? item.negate() : item); // Re-using acc allows to save adds in MSM
}
}
assert0(n);
return acc;
}
getPrecomputes(W, point, transform) {
// Calculate precomputes on a first run, reuse them after
let comp = pointPrecomputes.get(point);
if (!comp) {
comp = this.precomputeWindow(point, W);
if (W !== 1) {
// Doing transform outside of if brings 15% perf hit
if (typeof transform === 'function')
comp = transform(comp);
pointPrecomputes.set(point, comp);
}
}
return comp;
}
cached(point, scalar, transform) {
const W = getW(point);
return this.wNAF(W, this.getPrecomputes(W, point, transform), scalar);
}
unsafe(point, scalar, transform, prev) {
const W = getW(point);
if (W === 1)
return this._unsafeLadder(point, scalar, prev); // For W=1 ladder is ~x2 faster
return this.wNAFUnsafe(W, this.getPrecomputes(W, point, transform), scalar, prev);
}
// We calculate precomputes for elliptic curve point multiplication
// using windowed method. This specifies window size and
// stores precomputed values. Usually only base point would be precomputed.
createCache(P, W) {
validateW(W, this.bits);
pointWindowSizes.set(P, W);
pointPrecomputes.delete(P);
}
hasCache(elm) {
return getW(elm) !== 1;
}
}
/**
* Endomorphism-specific multiplication for Koblitz curves.
* Cost: 128 dbl, 0-256 adds.
*/
function mulEndoUnsafe(Point, point, k1, k2) {
let acc = point;
let p1 = Point.ZERO;
let p2 = Point.ZERO;
while (k1 > _0n$3 || k2 > _0n$3) {
if (k1 & _1n$4)
p1 = p1.add(acc);
if (k2 & _1n$4)
p2 = p2.add(acc);
acc = acc.double();
k1 >>= _1n$4;
k2 >>= _1n$4;
}
return { p1, p2 };
}
/**
* Pippenger algorithm for multi-scalar multiplication (MSM, Pa + Qb + Rc + ...).
* 30x faster vs naive addition on L=4096, 10x faster than precomputes.
* For N=254bit, L=1, it does: 1024 ADD + 254 DBL. For L=5: 1536 ADD + 254 DBL.
* Algorithmically constant-time (for same L), even when 1 point + scalar, or when scalar = 0.
* @param c Curve Point constructor
* @param fieldN field over CURVE.N - important that it's not over CURVE.P
* @param points array of L curve points
* @param scalars array of L scalars (aka secret keys / bigints)
*/
function pippenger(c, fieldN, points, scalars) {
// If we split scalars by some window (let's say 8 bits), every chunk will only
// take 256 buckets even if there are 4096 scalars, also re-uses double.
// TODO:
// - https://eprint.iacr.org/2024/750.pdf
// - https://tches.iacr.org/index.php/TCHES/article/view/10287
// 0 is accepted in scalars
validateMSMPoints(points, c);
validateMSMScalars(scalars, fieldN);
const plength = points.length;
const slength = scalars.length;
if (plength !== slength)
throw new Error('arrays of points and scalars must have equal length');
// if (plength === 0) throw new Error('array must be of length >= 2');
const zero = c.ZERO;
const wbits = bitLen(BigInt(plength));
let windowSize = 1; // bits
if (wbits > 12)
windowSize = wbits - 3;
else if (wbits > 4)
windowSize = wbits - 2;
else if (wbits > 0)
windowSize = 2;
const MASK = bitMask(windowSize);
const buckets = new Array(Number(MASK) + 1).fill(zero); // +1 for zero array
const lastBits = Math.floor((fieldN.BITS - 1) / windowSize) * windowSize;
let sum = zero;
for (let i = lastBits; i >= 0; i -= windowSize) {
buckets.fill(zero);
for (let j = 0; j < slength; j++) {
const scalar = scalars[j];
const wbits = Number((scalar >> BigInt(i)) & MASK);
buckets[wbits] = buckets[wbits].add(points[j]);
}
let resI = zero; // not using this will do small speed-up, but will lose ct
// Skip first bucket, because it is zero
for (let j = buckets.length - 1, sumI = zero; j > 0; j--) {
sumI = sumI.add(buckets[j]);
resI = resI.add(sumI);
}
sum = sum.add(resI);
if (i !== 0)
for (let j = 0; j < windowSize; j++)
sum = sum.double();
}
return sum;
}
function createField(order, field, isLE) {
if (field) {
if (field.ORDER !== order)
throw new Error('Field.ORDER must match order: Fp == p, Fn == n');
validateField(field);
return field;
}
else {
return Field(order, { isLE });
}
}
/** Validates CURVE opts and creates fields */
function _createCurveFields(type, CURVE, curveOpts = {}, FpFnLE) {
if (FpFnLE === undefined)
FpFnLE = type === 'edwards';
if (!CURVE || typeof CURVE !== 'object')
throw new Error(`expected valid ${type} CURVE object`);
for (const p of ['p', 'n', 'h']) {
const val = CURVE[p];
if (!(typeof val === 'bigint' && val > _0n$3))
throw new Error(`CURVE.${p} must be positive bigint`);
}
const Fp = createField(CURVE.p, curveOpts.Fp, FpFnLE);
const Fn = createField(CURVE.n, curveOpts.Fn, FpFnLE);
const _b = type === 'weierstrass' ? 'b' : 'd';
const params = ['Gx', 'Gy', 'a', _b];
for (const p of params) {
// @ts-ignore
if (!Fp.isValid(CURVE[p]))
throw new Error(`CURVE.${p} must be valid field element of CURVE.Fp`);
}
CURVE = Object.freeze(Object.assign({}, CURVE));
return { CURVE, Fp, Fn };
}
/**
* Short Weierstrass curve methods. The formula is: y² = x³ + ax + b.
*
* ### Design rationale for types
*
* * Interaction between classes from different curves should fail:
* `k256.Point.BASE.add(p256.Point.BASE)`
* * For this purpose we want to use `instanceof` operator, which is fast and works during runtime
* * Different calls of `curve()` would return different classes -
* `curve(params) !== curve(params)`: if somebody decided to monkey-patch their curve,
* it won't affect others
*
* TypeScript can't infer types for classes created inside a function. Classes is one instance
* of nominative types in TypeScript and interfaces only check for shape, so it's hard to create
* unique type for every function call.
*
* We can use generic types via some param, like curve opts, but that would:
* 1. Enable interaction between `curve(params)` and `curve(params)` (curves of same params)
* which is hard to debug.
* 2. Params can be generic and we can't enforce them to be constant value:
* if somebody creates curve from non-constant params,
* it would be allowed to interact with other curves with non-constant params
*
* @todo https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#unique-symbol
* @module
*/
/*! noble-curves - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// We construct basis in such way that den is always positive and equals n, but num sign depends on basis (not on secret value)
const divNearest = (num, den) => (num + (num >= 0 ? den : -den) / _2n$4) / den;
/**
* Splits scalar for GLV endomorphism.
*/
function _splitEndoScalar(k, basis, n) {
// Split scalar into two such that part is ~half bits: `abs(part) < sqrt(N)`
// Since part can be negative, we need to do this on point.
// TODO: verifyScalar function which consumes lambda
const [[a1, b1], [a2, b2]] = basis;
const c1 = divNearest(b2 * k, n);
const c2 = divNearest(-b1 * k, n);
// |k1|/|k2| is < sqrt(N), but can be negative.
// If we do `k1 mod N`, we'll get big scalar (`> sqrt(N)`): so, we do cheaper negation instead.
let k1 = k - c1 * a1 - c2 * a2;
let k2 = -c1 * b1 - c2 * b2;
const k1neg = k1 < _0n$2;
const k2neg = k2 < _0n$2;
if (k1neg)
k1 = -k1;
if (k2neg)
k2 = -k2;
// Double check that resulting scalar less than half bits of N: otherwise wNAF will fail.
// This should only happen on wrong basises. Also, math inside is too complex and I don't trust it.
const MAX_NUM = bitMask(Math.ceil(bitLen(n) / 2)) + _1n$3; // Half bits of N
if (k1 < _0n$2 || k1 >= MAX_NUM || k2 < _0n$2 || k2 >= MAX_NUM) {
throw new Error('splitScalar (endomorphism): failed, k=' + k);
}
return { k1neg, k1, k2neg, k2 };
}
function validateSigFormat(format) {
if (!['compact', 'recovered', 'der'].includes(format))
throw new Error('Signature format must be "compact", "recovered", or "der"');
return format;
}
function validateSigOpts(opts, def) {
const optsn = {};
for (let optName of Object.keys(def)) {
// @ts-ignore
optsn[optName] = opts[optName] === undefined ? def[optName] : opts[optName];
}
_abool2(optsn.lowS, 'lowS');
_abool2(optsn.prehash, 'prehash');
if (optsn.format !== undefined)
validateSigFormat(optsn.format);
return optsn;
}
class DERErr extends Error {
constructor(m = '') {
super(m);
}
}
/**
* ASN.1 DER encoding utilities. ASN is very complex & fragile. Format:
*
* [0x30 (SEQUENCE), bytelength, 0x02 (INTEGER), intLength, R, 0x02 (INTEGER), intLength, S]
*
* Docs: https://letsencrypt.org/docs/a-warm-welcome-to-asn1-and-der/, https://luca.ntop.org/Teaching/Appunti/asn1.html
*/
const DER = {
// asn.1 DER encoding utils
Err: DERErr,
// Basic building block is TLV (Tag-Length-Value)
_tlv: {
encode: (tag, data) => {
const { Err: E } = DER;
if (tag < 0 || tag > 256)
throw new E('tlv.encode: wrong tag');
if (data.length & 1)
throw new E('tlv.encode: unpadded data');
const dataLen = data.length / 2;
const len = numberToHexUnpadded(dataLen);
if ((len.length / 2) & 128)
throw new E('tlv.encode: long form length too big');
// length of length with long form flag
const lenLen = dataLen > 127 ? numberToHexUnpadded((len.length / 2) | 128) : '';
const t =