Delete 100 files via Git Manager GUI

This commit is contained in:
2026-10-22 09:14:02 +00:00
parent 7fa0db02b5
commit a902a0c5d7
100 changed files with 0 additions and 6991 deletions
-10
View File
@@ -1,10 +0,0 @@
/**
* SHA1 (RFC 3174) legacy hash function.
* @module
* @deprecated
*/
import { SHA1 as SHA1n, sha1 as sha1n } from './legacy.ts';
/** @deprecated Use import from `noble/hashes/legacy` module */
export const SHA1: typeof SHA1n = SHA1n;
/** @deprecated Use import from `noble/hashes/legacy` module */
export const sha1: typeof sha1n = sha1n;
-402
View File
@@ -1,402 +0,0 @@
/**
* SHA2 hash function. A.k.a. sha256, sha384, sha512, sha512_224, sha512_256.
* SHA256 is the fastest hash implementable in JS, even faster than Blake3.
* Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and
* [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
* @module
*/
import { Chi, HashMD, Maj, SHA224_IV, SHA256_IV, SHA384_IV, SHA512_IV } from './_md.ts';
import * as u64 from './_u64.ts';
import { type CHash, clean, createHasher, rotr } from './utils.ts';
/**
* Round constants:
* First 32 bits of fractional parts of the cube roots of the first 64 primes 2..311)
*/
// prettier-ignore
const SHA256_K = /* @__PURE__ */ Uint32Array.from([
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
]);
/** Reusable temporary buffer. "W" comes straight from spec. */
const SHA256_W = /* @__PURE__ */ new Uint32Array(64);
export class SHA256 extends HashMD<SHA256> {
// We cannot use array here since array allows indexing by variable
// which means optimizer/compiler cannot use registers.
protected A: number = SHA256_IV[0] | 0;
protected B: number = SHA256_IV[1] | 0;
protected C: number = SHA256_IV[2] | 0;
protected D: number = SHA256_IV[3] | 0;
protected E: number = SHA256_IV[4] | 0;
protected F: number = SHA256_IV[5] | 0;
protected G: number = SHA256_IV[6] | 0;
protected H: number = SHA256_IV[7] | 0;
constructor(outputLen: number = 32) {
super(64, outputLen, 8, false);
}
protected get(): [number, number, number, number, number, number, number, number] {
const { A, B, C, D, E, F, G, H } = this;
return [A, B, C, D, E, F, G, H];
}
// prettier-ignore
protected set(
A: number, B: number, C: number, D: number, E: number, F: number, G: number, H: number
): void {
this.A = A | 0;
this.B = B | 0;
this.C = C | 0;
this.D = D | 0;
this.E = E | 0;
this.F = F | 0;
this.G = G | 0;
this.H = H | 0;
}
protected process(view: DataView, offset: number): void {
// Extend the first 16 words into the remaining 48 words w[16..63] of the message schedule array
for (let i = 0; i < 16; i++, offset += 4) SHA256_W[i] = view.getUint32(offset, false);
for (let i = 16; i < 64; i++) {
const W15 = SHA256_W[i - 15];
const W2 = SHA256_W[i - 2];
const s0 = rotr(W15, 7) ^ rotr(W15, 18) ^ (W15 >>> 3);
const s1 = rotr(W2, 17) ^ rotr(W2, 19) ^ (W2 >>> 10);
SHA256_W[i] = (s1 + SHA256_W[i - 7] + s0 + SHA256_W[i - 16]) | 0;
}
// Compression function main loop, 64 rounds
let { A, B, C, D, E, F, G, H } = this;
for (let i = 0; i < 64; i++) {
const sigma1 = rotr(E, 6) ^ rotr(E, 11) ^ rotr(E, 25);
const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
const sigma0 = rotr(A, 2) ^ rotr(A, 13) ^ rotr(A, 22);
const T2 = (sigma0 + Maj(A, B, C)) | 0;
H = G;
G = F;
F = E;
E = (D + T1) | 0;
D = C;
C = B;
B = A;
A = (T1 + T2) | 0;
}
// Add the compressed chunk to the current hash value
A = (A + this.A) | 0;
B = (B + this.B) | 0;
C = (C + this.C) | 0;
D = (D + this.D) | 0;
E = (E + this.E) | 0;
F = (F + this.F) | 0;
G = (G + this.G) | 0;
H = (H + this.H) | 0;
this.set(A, B, C, D, E, F, G, H);
}
protected roundClean(): void {
clean(SHA256_W);
}
destroy(): void {
this.set(0, 0, 0, 0, 0, 0, 0, 0);
clean(this.buffer);
}
}
export class SHA224 extends SHA256 {
protected A: number = SHA224_IV[0] | 0;
protected B: number = SHA224_IV[1] | 0;
protected C: number = SHA224_IV[2] | 0;
protected D: number = SHA224_IV[3] | 0;
protected E: number = SHA224_IV[4] | 0;
protected F: number = SHA224_IV[5] | 0;
protected G: number = SHA224_IV[6] | 0;
protected H: number = SHA224_IV[7] | 0;
constructor() {
super(28);
}
}
// SHA2-512 is slower than sha256 in js because u64 operations are slow.
// Round contants
// First 32 bits of the fractional parts of the cube roots of the first 80 primes 2..409
// prettier-ignore
const K512 = /* @__PURE__ */ (() => u64.split([
'0x428a2f98d728ae22', '0x7137449123ef65cd', '0xb5c0fbcfec4d3b2f', '0xe9b5dba58189dbbc',
'0x3956c25bf348b538', '0x59f111f1b605d019', '0x923f82a4af194f9b', '0xab1c5ed5da6d8118',
'0xd807aa98a3030242', '0x12835b0145706fbe', '0x243185be4ee4b28c', '0x550c7dc3d5ffb4e2',
'0x72be5d74f27b896f', '0x80deb1fe3b1696b1', '0x9bdc06a725c71235', '0xc19bf174cf692694',
'0xe49b69c19ef14ad2', '0xefbe4786384f25e3', '0x0fc19dc68b8cd5b5', '0x240ca1cc77ac9c65',
'0x2de92c6f592b0275', '0x4a7484aa6ea6e483', '0x5cb0a9dcbd41fbd4', '0x76f988da831153b5',
'0x983e5152ee66dfab', '0xa831c66d2db43210', '0xb00327c898fb213f', '0xbf597fc7beef0ee4',
'0xc6e00bf33da88fc2', '0xd5a79147930aa725', '0x06ca6351e003826f', '0x142929670a0e6e70',
'0x27b70a8546d22ffc', '0x2e1b21385c26c926', '0x4d2c6dfc5ac42aed', '0x53380d139d95b3df',
'0x650a73548baf63de', '0x766a0abb3c77b2a8', '0x81c2c92e47edaee6', '0x92722c851482353b',
'0xa2bfe8a14cf10364', '0xa81a664bbc423001', '0xc24b8b70d0f89791', '0xc76c51a30654be30',
'0xd192e819d6ef5218', '0xd69906245565a910', '0xf40e35855771202a', '0x106aa07032bbd1b8',
'0x19a4c116b8d2d0c8', '0x1e376c085141ab53', '0x2748774cdf8eeb99', '0x34b0bcb5e19b48a8',
'0x391c0cb3c5c95a63', '0x4ed8aa4ae3418acb', '0x5b9cca4f7763e373', '0x682e6ff3d6b2b8a3',
'0x748f82ee5defb2fc', '0x78a5636f43172f60', '0x84c87814a1f0ab72', '0x8cc702081a6439ec',
'0x90befffa23631e28', '0xa4506cebde82bde9', '0xbef9a3f7b2c67915', '0xc67178f2e372532b',
'0xca273eceea26619c', '0xd186b8c721c0c207', '0xeada7dd6cde0eb1e', '0xf57d4f7fee6ed178',
'0x06f067aa72176fba', '0x0a637dc5a2c898a6', '0x113f9804bef90dae', '0x1b710b35131c471b',
'0x28db77f523047d84', '0x32caab7b40c72493', '0x3c9ebe0a15c9bebc', '0x431d67c49c100d4c',
'0x4cc5d4becb3e42b6', '0x597f299cfc657e2a', '0x5fcb6fab3ad6faec', '0x6c44198c4a475817'
].map(n => BigInt(n))))();
const SHA512_Kh = /* @__PURE__ */ (() => K512[0])();
const SHA512_Kl = /* @__PURE__ */ (() => K512[1])();
// Reusable temporary buffers
const SHA512_W_H = /* @__PURE__ */ new Uint32Array(80);
const SHA512_W_L = /* @__PURE__ */ new Uint32Array(80);
export class SHA512 extends HashMD<SHA512> {
// We cannot use array here since array allows indexing by variable
// which means optimizer/compiler cannot use registers.
// h -- high 32 bits, l -- low 32 bits
protected Ah: number = SHA512_IV[0] | 0;
protected Al: number = SHA512_IV[1] | 0;
protected Bh: number = SHA512_IV[2] | 0;
protected Bl: number = SHA512_IV[3] | 0;
protected Ch: number = SHA512_IV[4] | 0;
protected Cl: number = SHA512_IV[5] | 0;
protected Dh: number = SHA512_IV[6] | 0;
protected Dl: number = SHA512_IV[7] | 0;
protected Eh: number = SHA512_IV[8] | 0;
protected El: number = SHA512_IV[9] | 0;
protected Fh: number = SHA512_IV[10] | 0;
protected Fl: number = SHA512_IV[11] | 0;
protected Gh: number = SHA512_IV[12] | 0;
protected Gl: number = SHA512_IV[13] | 0;
protected Hh: number = SHA512_IV[14] | 0;
protected Hl: number = SHA512_IV[15] | 0;
constructor(outputLen: number = 64) {
super(128, outputLen, 16, false);
}
// prettier-ignore
protected get(): [
number, number, number, number, number, number, number, number,
number, number, number, number, number, number, number, number
] {
const { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
return [Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl];
}
// prettier-ignore
protected set(
Ah: number, Al: number, Bh: number, Bl: number, Ch: number, Cl: number, Dh: number, Dl: number,
Eh: number, El: number, Fh: number, Fl: number, Gh: number, Gl: number, Hh: number, Hl: number
): void {
this.Ah = Ah | 0;
this.Al = Al | 0;
this.Bh = Bh | 0;
this.Bl = Bl | 0;
this.Ch = Ch | 0;
this.Cl = Cl | 0;
this.Dh = Dh | 0;
this.Dl = Dl | 0;
this.Eh = Eh | 0;
this.El = El | 0;
this.Fh = Fh | 0;
this.Fl = Fl | 0;
this.Gh = Gh | 0;
this.Gl = Gl | 0;
this.Hh = Hh | 0;
this.Hl = Hl | 0;
}
protected process(view: DataView, offset: number): void {
// Extend the first 16 words into the remaining 64 words w[16..79] of the message schedule array
for (let i = 0; i < 16; i++, offset += 4) {
SHA512_W_H[i] = view.getUint32(offset);
SHA512_W_L[i] = view.getUint32((offset += 4));
}
for (let i = 16; i < 80; i++) {
// s0 := (w[i-15] rightrotate 1) xor (w[i-15] rightrotate 8) xor (w[i-15] rightshift 7)
const W15h = SHA512_W_H[i - 15] | 0;
const W15l = SHA512_W_L[i - 15] | 0;
const s0h = u64.rotrSH(W15h, W15l, 1) ^ u64.rotrSH(W15h, W15l, 8) ^ u64.shrSH(W15h, W15l, 7);
const s0l = u64.rotrSL(W15h, W15l, 1) ^ u64.rotrSL(W15h, W15l, 8) ^ u64.shrSL(W15h, W15l, 7);
// s1 := (w[i-2] rightrotate 19) xor (w[i-2] rightrotate 61) xor (w[i-2] rightshift 6)
const W2h = SHA512_W_H[i - 2] | 0;
const W2l = SHA512_W_L[i - 2] | 0;
const s1h = u64.rotrSH(W2h, W2l, 19) ^ u64.rotrBH(W2h, W2l, 61) ^ u64.shrSH(W2h, W2l, 6);
const s1l = u64.rotrSL(W2h, W2l, 19) ^ u64.rotrBL(W2h, W2l, 61) ^ u64.shrSL(W2h, W2l, 6);
// SHA256_W[i] = s0 + s1 + SHA256_W[i - 7] + SHA256_W[i - 16];
const SUMl = u64.add4L(s0l, s1l, SHA512_W_L[i - 7], SHA512_W_L[i - 16]);
const SUMh = u64.add4H(SUMl, s0h, s1h, SHA512_W_H[i - 7], SHA512_W_H[i - 16]);
SHA512_W_H[i] = SUMh | 0;
SHA512_W_L[i] = SUMl | 0;
}
let { Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl } = this;
// Compression function main loop, 80 rounds
for (let i = 0; i < 80; i++) {
// S1 := (e rightrotate 14) xor (e rightrotate 18) xor (e rightrotate 41)
const sigma1h = u64.rotrSH(Eh, El, 14) ^ u64.rotrSH(Eh, El, 18) ^ u64.rotrBH(Eh, El, 41);
const sigma1l = u64.rotrSL(Eh, El, 14) ^ u64.rotrSL(Eh, El, 18) ^ u64.rotrBL(Eh, El, 41);
//const T1 = (H + sigma1 + Chi(E, F, G) + SHA256_K[i] + SHA256_W[i]) | 0;
const CHIh = (Eh & Fh) ^ (~Eh & Gh);
const CHIl = (El & Fl) ^ (~El & Gl);
// T1 = H + sigma1 + Chi(E, F, G) + SHA512_K[i] + SHA512_W[i]
// prettier-ignore
const T1ll = u64.add5L(Hl, sigma1l, CHIl, SHA512_Kl[i], SHA512_W_L[i]);
const T1h = u64.add5H(T1ll, Hh, sigma1h, CHIh, SHA512_Kh[i], SHA512_W_H[i]);
const T1l = T1ll | 0;
// S0 := (a rightrotate 28) xor (a rightrotate 34) xor (a rightrotate 39)
const sigma0h = u64.rotrSH(Ah, Al, 28) ^ u64.rotrBH(Ah, Al, 34) ^ u64.rotrBH(Ah, Al, 39);
const sigma0l = u64.rotrSL(Ah, Al, 28) ^ u64.rotrBL(Ah, Al, 34) ^ u64.rotrBL(Ah, Al, 39);
const MAJh = (Ah & Bh) ^ (Ah & Ch) ^ (Bh & Ch);
const MAJl = (Al & Bl) ^ (Al & Cl) ^ (Bl & Cl);
Hh = Gh | 0;
Hl = Gl | 0;
Gh = Fh | 0;
Gl = Fl | 0;
Fh = Eh | 0;
Fl = El | 0;
({ h: Eh, l: El } = u64.add(Dh | 0, Dl | 0, T1h | 0, T1l | 0));
Dh = Ch | 0;
Dl = Cl | 0;
Ch = Bh | 0;
Cl = Bl | 0;
Bh = Ah | 0;
Bl = Al | 0;
const All = u64.add3L(T1l, sigma0l, MAJl);
Ah = u64.add3H(All, T1h, sigma0h, MAJh);
Al = All | 0;
}
// Add the compressed chunk to the current hash value
({ h: Ah, l: Al } = u64.add(this.Ah | 0, this.Al | 0, Ah | 0, Al | 0));
({ h: Bh, l: Bl } = u64.add(this.Bh | 0, this.Bl | 0, Bh | 0, Bl | 0));
({ h: Ch, l: Cl } = u64.add(this.Ch | 0, this.Cl | 0, Ch | 0, Cl | 0));
({ h: Dh, l: Dl } = u64.add(this.Dh | 0, this.Dl | 0, Dh | 0, Dl | 0));
({ h: Eh, l: El } = u64.add(this.Eh | 0, this.El | 0, Eh | 0, El | 0));
({ h: Fh, l: Fl } = u64.add(this.Fh | 0, this.Fl | 0, Fh | 0, Fl | 0));
({ h: Gh, l: Gl } = u64.add(this.Gh | 0, this.Gl | 0, Gh | 0, Gl | 0));
({ h: Hh, l: Hl } = u64.add(this.Hh | 0, this.Hl | 0, Hh | 0, Hl | 0));
this.set(Ah, Al, Bh, Bl, Ch, Cl, Dh, Dl, Eh, El, Fh, Fl, Gh, Gl, Hh, Hl);
}
protected roundClean(): void {
clean(SHA512_W_H, SHA512_W_L);
}
destroy(): void {
clean(this.buffer);
this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
}
export class SHA384 extends SHA512 {
protected Ah: number = SHA384_IV[0] | 0;
protected Al: number = SHA384_IV[1] | 0;
protected Bh: number = SHA384_IV[2] | 0;
protected Bl: number = SHA384_IV[3] | 0;
protected Ch: number = SHA384_IV[4] | 0;
protected Cl: number = SHA384_IV[5] | 0;
protected Dh: number = SHA384_IV[6] | 0;
protected Dl: number = SHA384_IV[7] | 0;
protected Eh: number = SHA384_IV[8] | 0;
protected El: number = SHA384_IV[9] | 0;
protected Fh: number = SHA384_IV[10] | 0;
protected Fl: number = SHA384_IV[11] | 0;
protected Gh: number = SHA384_IV[12] | 0;
protected Gl: number = SHA384_IV[13] | 0;
protected Hh: number = SHA384_IV[14] | 0;
protected Hl: number = SHA384_IV[15] | 0;
constructor() {
super(48);
}
}
/**
* Truncated SHA512/256 and SHA512/224.
* SHA512_IV is XORed with 0xa5a5a5a5a5a5a5a5, then used as "intermediary" IV of SHA512/t.
* Then t hashes string to produce result IV.
* See `test/misc/sha2-gen-iv.js`.
*/
/** SHA512/224 IV */
const T224_IV = /* @__PURE__ */ Uint32Array.from([
0x8c3d37c8, 0x19544da2, 0x73e19966, 0x89dcd4d6, 0x1dfab7ae, 0x32ff9c82, 0x679dd514, 0x582f9fcf,
0x0f6d2b69, 0x7bd44da8, 0x77e36f73, 0x04c48942, 0x3f9d85a8, 0x6a1d36c8, 0x1112e6ad, 0x91d692a1,
]);
/** SHA512/256 IV */
const T256_IV = /* @__PURE__ */ Uint32Array.from([
0x22312194, 0xfc2bf72c, 0x9f555fa3, 0xc84c64c2, 0x2393b86b, 0x6f53b151, 0x96387719, 0x5940eabd,
0x96283ee2, 0xa88effe3, 0xbe5e1e25, 0x53863992, 0x2b0199fc, 0x2c85b8aa, 0x0eb72ddc, 0x81c52ca2,
]);
export class SHA512_224 extends SHA512 {
protected Ah: number = T224_IV[0] | 0;
protected Al: number = T224_IV[1] | 0;
protected Bh: number = T224_IV[2] | 0;
protected Bl: number = T224_IV[3] | 0;
protected Ch: number = T224_IV[4] | 0;
protected Cl: number = T224_IV[5] | 0;
protected Dh: number = T224_IV[6] | 0;
protected Dl: number = T224_IV[7] | 0;
protected Eh: number = T224_IV[8] | 0;
protected El: number = T224_IV[9] | 0;
protected Fh: number = T224_IV[10] | 0;
protected Fl: number = T224_IV[11] | 0;
protected Gh: number = T224_IV[12] | 0;
protected Gl: number = T224_IV[13] | 0;
protected Hh: number = T224_IV[14] | 0;
protected Hl: number = T224_IV[15] | 0;
constructor() {
super(28);
}
}
export class SHA512_256 extends SHA512 {
protected Ah: number = T256_IV[0] | 0;
protected Al: number = T256_IV[1] | 0;
protected Bh: number = T256_IV[2] | 0;
protected Bl: number = T256_IV[3] | 0;
protected Ch: number = T256_IV[4] | 0;
protected Cl: number = T256_IV[5] | 0;
protected Dh: number = T256_IV[6] | 0;
protected Dl: number = T256_IV[7] | 0;
protected Eh: number = T256_IV[8] | 0;
protected El: number = T256_IV[9] | 0;
protected Fh: number = T256_IV[10] | 0;
protected Fl: number = T256_IV[11] | 0;
protected Gh: number = T256_IV[12] | 0;
protected Gl: number = T256_IV[13] | 0;
protected Hh: number = T256_IV[14] | 0;
protected Hl: number = T256_IV[15] | 0;
constructor() {
super(32);
}
}
/**
* SHA2-256 hash function from RFC 4634.
*
* It is the fastest JS hash, even faster than Blake3.
* To break sha256 using birthday attack, attackers need to try 2^128 hashes.
* BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
*/
export const sha256: CHash = /* @__PURE__ */ createHasher(() => new SHA256());
/** SHA2-224 hash function from RFC 4634 */
export const sha224: CHash = /* @__PURE__ */ createHasher(() => new SHA224());
/** SHA2-512 hash function from RFC 4634. */
export const sha512: CHash = /* @__PURE__ */ createHasher(() => new SHA512());
/** SHA2-384 hash function from RFC 4634. */
export const sha384: CHash = /* @__PURE__ */ createHasher(() => new SHA384());
/**
* SHA2-512/256 "truncated" hash function, with improved resistance to length extension attacks.
* See the paper on [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).
*/
export const sha512_256: CHash = /* @__PURE__ */ createHasher(() => new SHA512_256());
/**
* SHA2-512/224 "truncated" hash function, with improved resistance to length extension attacks.
* See the paper on [truncated SHA512](https://eprint.iacr.org/2010/548.pdf).
*/
export const sha512_224: CHash = /* @__PURE__ */ createHasher(() => new SHA512_224());
-24
View File
@@ -1,24 +0,0 @@
/**
* SHA2-256 a.k.a. sha256. In JS, it is the fastest hash, even faster than Blake3.
*
* To break sha256 using birthday attack, attackers need to try 2^128 hashes.
* BTC network is doing 2^70 hashes/sec (2^95 hashes/year) as per 2025.
*
* Check out [FIPS 180-4](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
* @module
* @deprecated
*/
import {
SHA224 as SHA224n,
sha224 as sha224n,
SHA256 as SHA256n,
sha256 as sha256n,
} from './sha2.ts';
/** @deprecated Use import from `noble/hashes/sha2` module */
export const SHA256: typeof SHA256n = SHA256n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const sha256: typeof sha256n = sha256n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const SHA224: typeof SHA224n = SHA224n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const sha224: typeof sha224n = sha224n;
-499
View File
@@ -1,499 +0,0 @@
/**
* SHA3 (keccak) addons.
*
* * Full [NIST SP 800-185](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-185.pdf):
* cSHAKE, KMAC, TupleHash, ParallelHash + XOF variants
* * Reduced-round Keccak [(draft)](https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/):
* * 🦘 K12 aka KangarooTwelve
* * M14 aka MarsupilamiFourteen
* * TurboSHAKE
* * KeccakPRG: Pseudo-random generator based on Keccak [(pdf)](https://keccak.team/files/CSF-0.1.pdf)
* @module
*/
import { Keccak, type ShakeOpts } from './sha3.ts';
import {
abytes,
anumber,
type CHashO,
type CHashXO,
createOptHasher,
createXOFer,
Hash,
type HashXOF,
type Input,
toBytes,
u32,
} from './utils.ts';
// cSHAKE && KMAC (NIST SP800-185)
const _8n = BigInt(8);
const _ffn = BigInt(0xff);
// NOTE: it is safe to use bigints here, since they used only for length encoding (not actual data).
// We use bigints in sha256 for lengths too.
function leftEncode(n: number | bigint): Uint8Array {
n = BigInt(n);
const res = [Number(n & _ffn)];
n >>= _8n;
for (; n > 0; n >>= _8n) res.unshift(Number(n & _ffn));
res.unshift(res.length);
return new Uint8Array(res);
}
function rightEncode(n: number | bigint): Uint8Array {
n = BigInt(n);
const res = [Number(n & _ffn)];
n >>= _8n;
for (; n > 0; n >>= _8n) res.unshift(Number(n & _ffn));
res.push(res.length);
return new Uint8Array(res);
}
function chooseLen(opts: ShakeOpts, outputLen: number): number {
return opts.dkLen === undefined ? outputLen : opts.dkLen;
}
const abytesOrZero = (buf?: Input) => {
if (buf === undefined) return Uint8Array.of();
return toBytes(buf);
};
// NOTE: second modulo is necessary since we don't need to add padding if current element takes whole block
const getPadding = (len: number, block: number) => new Uint8Array((block - (len % block)) % block);
export type cShakeOpts = ShakeOpts & { personalization?: Input; NISTfn?: Input };
// Personalization
function cshakePers(hash: Keccak, opts: cShakeOpts = {}): Keccak {
if (!opts || (!opts.personalization && !opts.NISTfn)) return hash;
// Encode and pad inplace to avoid unneccesary memory copies/slices (so we don't need to zero them later)
// bytepad(encode_string(N) || encode_string(S), 168)
const blockLenBytes = leftEncode(hash.blockLen);
const fn = abytesOrZero(opts.NISTfn);
const fnLen = leftEncode(_8n * BigInt(fn.length)); // length in bits
const pers = abytesOrZero(opts.personalization);
const persLen = leftEncode(_8n * BigInt(pers.length)); // length in bits
if (!fn.length && !pers.length) return hash;
hash.suffix = 0x04;
hash.update(blockLenBytes).update(fnLen).update(fn).update(persLen).update(pers);
let totalLen = blockLenBytes.length + fnLen.length + fn.length + persLen.length + pers.length;
hash.update(getPadding(totalLen, hash.blockLen));
return hash;
}
const gencShake = (suffix: number, blockLen: number, outputLen: number) =>
createXOFer<Keccak, cShakeOpts>((opts: cShakeOpts = {}) =>
cshakePers(new Keccak(blockLen, suffix, chooseLen(opts, outputLen), true), opts)
);
// TODO: refactor
export type ICShake = {
(msg: Input, opts?: cShakeOpts): Uint8Array;
outputLen: number;
blockLen: number;
create(opts: cShakeOpts): HashXOF<Keccak>;
};
export type ITupleHash = {
(messages: Input[], opts?: cShakeOpts): Uint8Array;
create(opts?: cShakeOpts): TupleHash;
};
export type IParHash = {
(message: Input, opts?: ParallelOpts): Uint8Array;
create(opts?: ParallelOpts): ParallelHash;
};
export const cshake128: ICShake = /* @__PURE__ */ (() => gencShake(0x1f, 168, 128 / 8))();
export const cshake256: ICShake = /* @__PURE__ */ (() => gencShake(0x1f, 136, 256 / 8))();
export class KMAC extends Keccak implements HashXOF<KMAC> {
constructor(
blockLen: number,
outputLen: number,
enableXOF: boolean,
key: Input,
opts: cShakeOpts = {}
) {
super(blockLen, 0x1f, outputLen, enableXOF);
cshakePers(this, { NISTfn: 'KMAC', personalization: opts.personalization });
key = toBytes(key);
abytes(key);
// 1. newX = bytepad(encode_string(K), 168) || X || right_encode(L).
const blockLenBytes = leftEncode(this.blockLen);
const keyLen = leftEncode(_8n * BigInt(key.length));
this.update(blockLenBytes).update(keyLen).update(key);
const totalLen = blockLenBytes.length + keyLen.length + key.length;
this.update(getPadding(totalLen, this.blockLen));
}
protected finish(): void {
if (!this.finished) this.update(rightEncode(this.enableXOF ? 0 : _8n * BigInt(this.outputLen))); // outputLen in bits
super.finish();
}
_cloneInto(to?: KMAC): KMAC {
// Create new instance without calling constructor since key already in state and we don't know it.
// Force "to" to be instance of KMAC instead of Sha3.
if (!to) {
to = Object.create(Object.getPrototypeOf(this), {}) as KMAC;
to.state = this.state.slice();
to.blockLen = this.blockLen;
to.state32 = u32(to.state);
}
return super._cloneInto(to) as KMAC;
}
clone(): KMAC {
return this._cloneInto();
}
}
function genKmac(blockLen: number, outputLen: number, xof = false) {
const kmac = (key: Input, message: Input, opts?: cShakeOpts): Uint8Array =>
kmac.create(key, opts).update(message).digest();
kmac.create = (key: Input, opts: cShakeOpts = {}) =>
new KMAC(blockLen, chooseLen(opts, outputLen), xof, key, opts);
return kmac;
}
export const kmac128: {
(key: Input, message: Input, opts?: cShakeOpts): Uint8Array;
create(key: Input, opts?: cShakeOpts): KMAC;
} = /* @__PURE__ */ (() => genKmac(168, 128 / 8))();
export const kmac256: {
(key: Input, message: Input, opts?: cShakeOpts): Uint8Array;
create(key: Input, opts?: cShakeOpts): KMAC;
} = /* @__PURE__ */ (() => genKmac(136, 256 / 8))();
export const kmac128xof: {
(key: Input, message: Input, opts?: cShakeOpts): Uint8Array;
create(key: Input, opts?: cShakeOpts): KMAC;
} = /* @__PURE__ */ (() => genKmac(168, 128 / 8, true))();
export const kmac256xof: {
(key: Input, message: Input, opts?: cShakeOpts): Uint8Array;
create(key: Input, opts?: cShakeOpts): KMAC;
} = /* @__PURE__ */ (() => genKmac(136, 256 / 8, true))();
// TupleHash
// Usage: tuple(['ab', 'cd']) != tuple(['a', 'bcd'])
export class TupleHash extends Keccak implements HashXOF<TupleHash> {
constructor(blockLen: number, outputLen: number, enableXOF: boolean, opts: cShakeOpts = {}) {
super(blockLen, 0x1f, outputLen, enableXOF);
cshakePers(this, { NISTfn: 'TupleHash', personalization: opts.personalization });
// Change update after cshake processed
this.update = (data: Input) => {
data = toBytes(data);
abytes(data);
super.update(leftEncode(_8n * BigInt(data.length)));
super.update(data);
return this;
};
}
protected finish(): void {
if (!this.finished)
super.update(rightEncode(this.enableXOF ? 0 : _8n * BigInt(this.outputLen))); // outputLen in bits
super.finish();
}
_cloneInto(to?: TupleHash): TupleHash {
to ||= new TupleHash(this.blockLen, this.outputLen, this.enableXOF);
return super._cloneInto(to) as TupleHash;
}
clone(): TupleHash {
return this._cloneInto();
}
}
function genTuple(blockLen: number, outputLen: number, xof = false) {
const tuple = (messages: Input[], opts?: cShakeOpts): Uint8Array => {
const h = tuple.create(opts);
for (const msg of messages) h.update(msg);
return h.digest();
};
tuple.create = (opts: cShakeOpts = {}) =>
new TupleHash(blockLen, chooseLen(opts, outputLen), xof, opts);
return tuple;
}
/** 128-bit TupleHASH. */
export const tuplehash128: ITupleHash = /* @__PURE__ */ (() => genTuple(168, 128 / 8))();
/** 256-bit TupleHASH. */
export const tuplehash256: ITupleHash = /* @__PURE__ */ (() => genTuple(136, 256 / 8))();
/** 128-bit TupleHASH XOF. */
export const tuplehash128xof: ITupleHash = /* @__PURE__ */ (() => genTuple(168, 128 / 8, true))();
/** 256-bit TupleHASH XOF. */
export const tuplehash256xof: ITupleHash = /* @__PURE__ */ (() => genTuple(136, 256 / 8, true))();
// ParallelHash (same as K12/M14, but without speedup for inputs less 8kb, reduced number of rounds and more simple)
type ParallelOpts = cShakeOpts & { blockLen?: number };
export class ParallelHash extends Keccak implements HashXOF<ParallelHash> {
private leafHash?: Hash<Keccak>;
protected leafCons: () => Hash<Keccak>;
private chunkPos = 0; // Position of current block in chunk
private chunksDone = 0; // How many chunks we already have
private chunkLen: number;
constructor(
blockLen: number,
outputLen: number,
leafCons: () => Hash<Keccak>,
enableXOF: boolean,
opts: ParallelOpts = {}
) {
super(blockLen, 0x1f, outputLen, enableXOF);
cshakePers(this, { NISTfn: 'ParallelHash', personalization: opts.personalization });
this.leafCons = leafCons;
let { blockLen: B } = opts;
B ||= 8;
anumber(B);
this.chunkLen = B;
super.update(leftEncode(B));
// Change update after cshake processed
this.update = (data: Input) => {
data = toBytes(data);
abytes(data);
const { chunkLen, leafCons } = this;
for (let pos = 0, len = data.length; pos < len; ) {
if (this.chunkPos == chunkLen || !this.leafHash) {
if (this.leafHash) {
super.update(this.leafHash.digest());
this.chunksDone++;
}
this.leafHash = leafCons();
this.chunkPos = 0;
}
const take = Math.min(chunkLen - this.chunkPos, len - pos);
this.leafHash.update(data.subarray(pos, pos + take));
this.chunkPos += take;
pos += take;
}
return this;
};
}
protected finish(): void {
if (this.finished) return;
if (this.leafHash) {
super.update(this.leafHash.digest());
this.chunksDone++;
}
super.update(rightEncode(this.chunksDone));
super.update(rightEncode(this.enableXOF ? 0 : _8n * BigInt(this.outputLen))); // outputLen in bits
super.finish();
}
_cloneInto(to?: ParallelHash): ParallelHash {
to ||= new ParallelHash(this.blockLen, this.outputLen, this.leafCons, this.enableXOF);
if (this.leafHash) to.leafHash = this.leafHash._cloneInto(to.leafHash as Keccak);
to.chunkPos = this.chunkPos;
to.chunkLen = this.chunkLen;
to.chunksDone = this.chunksDone;
return super._cloneInto(to) as ParallelHash;
}
destroy(): void {
super.destroy.call(this);
if (this.leafHash) this.leafHash.destroy();
}
clone(): ParallelHash {
return this._cloneInto();
}
}
function genPrl(
blockLen: number,
outputLen: number,
leaf: ReturnType<typeof gencShake>,
xof = false
) {
const parallel = (message: Input, opts?: ParallelOpts): Uint8Array =>
parallel.create(opts).update(message).digest();
parallel.create = (opts: ParallelOpts = {}) =>
new ParallelHash(
blockLen,
chooseLen(opts, outputLen),
() => leaf.create({ dkLen: 2 * outputLen }),
xof,
opts
);
return parallel;
}
/** 128-bit ParallelHash. In JS, it is not parallel. */
export const parallelhash128: IParHash = /* @__PURE__ */ (() => genPrl(168, 128 / 8, cshake128))();
/** 256-bit ParallelHash. In JS, it is not parallel. */
export const parallelhash256: IParHash = /* @__PURE__ */ (() => genPrl(136, 256 / 8, cshake256))();
/** 128-bit ParallelHash XOF. In JS, it is not parallel. */
export const parallelhash128xof: IParHash = /* @__PURE__ */ (() =>
genPrl(168, 128 / 8, cshake128, true))();
/** 256-bit ParallelHash. In JS, it is not parallel. */
export const parallelhash256xof: IParHash = /* @__PURE__ */ (() =>
genPrl(136, 256 / 8, cshake256, true))();
// Should be simple 'shake with 12 rounds', but no, we got whole new spec about Turbo SHAKE Pro MAX.
export type TurboshakeOpts = ShakeOpts & {
D?: number; // Domain separation byte
};
const genTurboshake = (blockLen: number, outputLen: number) =>
createXOFer<HashXOF<Keccak>, TurboshakeOpts>((opts: TurboshakeOpts = {}) => {
const D = opts.D === undefined ? 0x1f : opts.D;
// Section 2.1 of https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/
if (!Number.isSafeInteger(D) || D < 0x01 || D > 0x7f)
throw new Error('invalid domain separation byte must be 0x01..0x7f, got: ' + D);
return new Keccak(blockLen, D, opts.dkLen === undefined ? outputLen : opts.dkLen, true, 12);
});
/** TurboSHAKE 128-bit: reduced 12-round keccak. */
export const turboshake128: CHashXO = /* @__PURE__ */ genTurboshake(168, 256 / 8);
/** TurboSHAKE 256-bit: reduced 12-round keccak. */
export const turboshake256: CHashXO = /* @__PURE__ */ genTurboshake(136, 512 / 8);
// Kangaroo
// Same as NIST rightEncode, but returns [0] for zero string
function rightEncodeK12(n: number | bigint): Uint8Array {
n = BigInt(n);
const res: number[] = [];
for (; n > 0; n >>= _8n) res.unshift(Number(n & _ffn));
res.push(res.length);
return Uint8Array.from(res);
}
export type KangarooOpts = { dkLen?: number; personalization?: Input };
const EMPTY_BUFFER = /* @__PURE__ */ Uint8Array.of();
export class KangarooTwelve extends Keccak implements HashXOF<KangarooTwelve> {
readonly chunkLen = 8192;
private leafHash?: Keccak;
protected leafLen: number;
private personalization: Uint8Array;
private chunkPos = 0; // Position of current block in chunk
private chunksDone = 0; // How many chunks we already have
constructor(
blockLen: number,
leafLen: number,
outputLen: number,
rounds: number,
opts: KangarooOpts
) {
super(blockLen, 0x07, outputLen, true, rounds);
this.leafLen = leafLen;
this.personalization = abytesOrZero(opts.personalization);
}
update(data: Input): this {
data = toBytes(data);
abytes(data);
const { chunkLen, blockLen, leafLen, rounds } = this;
for (let pos = 0, len = data.length; pos < len; ) {
if (this.chunkPos == chunkLen) {
if (this.leafHash) super.update(this.leafHash.digest());
else {
this.suffix = 0x06; // Its safe to change suffix here since its used only in digest()
super.update(Uint8Array.from([3, 0, 0, 0, 0, 0, 0, 0]));
}
this.leafHash = new Keccak(blockLen, 0x0b, leafLen, false, rounds);
this.chunksDone++;
this.chunkPos = 0;
}
const take = Math.min(chunkLen - this.chunkPos, len - pos);
const chunk = data.subarray(pos, pos + take);
if (this.leafHash) this.leafHash.update(chunk);
else super.update(chunk);
this.chunkPos += take;
pos += take;
}
return this;
}
protected finish(): void {
if (this.finished) return;
const { personalization } = this;
this.update(personalization).update(rightEncodeK12(personalization.length));
// Leaf hash
if (this.leafHash) {
super.update(this.leafHash.digest());
super.update(rightEncodeK12(this.chunksDone));
super.update(Uint8Array.from([0xff, 0xff]));
}
super.finish.call(this);
}
destroy(): void {
super.destroy.call(this);
if (this.leafHash) this.leafHash.destroy();
// We cannot zero personalization buffer since it is user provided and we don't want to mutate user input
this.personalization = EMPTY_BUFFER;
}
_cloneInto(to?: KangarooTwelve): KangarooTwelve {
const { blockLen, leafLen, leafHash, outputLen, rounds } = this;
to ||= new KangarooTwelve(blockLen, leafLen, outputLen, rounds, {});
super._cloneInto(to);
if (leafHash) to.leafHash = leafHash._cloneInto(to.leafHash);
to.personalization.set(this.personalization);
to.leafLen = this.leafLen;
to.chunkPos = this.chunkPos;
to.chunksDone = this.chunksDone;
return to;
}
clone(): KangarooTwelve {
return this._cloneInto();
}
}
/** KangarooTwelve: reduced 12-round keccak. */
export const k12: CHashO = /* @__PURE__ */ (() =>
createOptHasher<KangarooTwelve, KangarooOpts>(
(opts: KangarooOpts = {}) => new KangarooTwelve(168, 32, chooseLen(opts, 32), 12, opts)
))();
/** MarsupilamiFourteen: reduced 14-round keccak. */
export const m14: CHashO = /* @__PURE__ */ (() =>
createOptHasher<KangarooTwelve, KangarooOpts>(
(opts: KangarooOpts = {}) => new KangarooTwelve(136, 64, chooseLen(opts, 64), 14, opts)
))();
/**
* More at https://github.com/XKCP/XKCP/tree/master/lib/high/Keccak/PRG.
*/
export class KeccakPRG extends Keccak {
protected rate: number;
constructor(capacity: number) {
anumber(capacity);
// Rho should be full bytes
if (capacity < 0 || capacity > 1600 - 10 || (1600 - capacity - 2) % 8)
throw new Error('invalid capacity');
// blockLen = rho in bytes
super((1600 - capacity - 2) / 8, 0, 0, true);
this.rate = 1600 - capacity;
this.posOut = Math.floor((this.rate + 7) / 8);
}
keccak(): void {
// Duplex padding
this.state[this.pos] ^= 0x01;
this.state[this.blockLen] ^= 0x02; // Rho is full bytes
super.keccak();
this.pos = 0;
this.posOut = 0;
}
update(data: Input): this {
super.update(data);
this.posOut = this.blockLen;
return this;
}
feed(data: Input): this {
return this.update(data);
}
protected finish(): void {}
digestInto(_out: Uint8Array): Uint8Array {
throw new Error('digest is not allowed, use .fetch instead');
}
fetch(bytes: number): Uint8Array {
return this.xof(bytes);
}
// Ensure irreversibility (even if state leaked previous outputs cannot be computed)
forget(): void {
if (this.rate < 1600 / 2 + 1) throw new Error('rate is too low to use .forget()');
this.keccak();
for (let i = 0; i < this.blockLen; i++) this.state[i] = 0;
this.pos = this.blockLen;
this.keccak();
this.posOut = this.blockLen;
}
_cloneInto(to?: KeccakPRG): KeccakPRG {
const { rate } = this;
to ||= new KeccakPRG(1600 - rate);
super._cloneInto(to);
to.rate = rate;
return to;
}
clone(): KeccakPRG {
return this._cloneInto();
}
}
/** KeccakPRG: Pseudo-random generator based on Keccak. https://keccak.team/files/CSF-0.1.pdf */
export const keccakprg = (capacity = 254): KeccakPRG => new KeccakPRG(capacity);
-258
View File
@@ -1,258 +0,0 @@
/**
* SHA3 (keccak) hash function, based on a new "Sponge function" design.
* Different from older hashes, the internal state is bigger than output size.
*
* Check out [FIPS-202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf),
* [Website](https://keccak.team/keccak.html),
* [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub).
*
* Check out `sha3-addons` module for cSHAKE, k12, and others.
* @module
*/
import { rotlBH, rotlBL, rotlSH, rotlSL, split } from './_u64.ts';
// prettier-ignore
import {
abytes, aexists, anumber, aoutput,
clean, createHasher, createXOFer, Hash,
swap32IfBE,
toBytes, u32,
type CHash, type CHashXO, type HashXOF, type Input
} from './utils.ts';
// No __PURE__ annotations in sha3 header:
// EVERYTHING is in fact used on every export.
// Various per round constants calculations
const _0n = BigInt(0);
const _1n = BigInt(1);
const _2n = BigInt(2);
const _7n = BigInt(7);
const _256n = BigInt(256);
const _0x71n = BigInt(0x71);
const SHA3_PI: number[] = [];
const SHA3_ROTL: number[] = [];
const _SHA3_IOTA: bigint[] = [];
for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
// Pi
[x, y] = [y, (2 * x + 3 * y) % 5];
SHA3_PI.push(2 * (5 * y + x));
// Rotational
SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);
// Iota
let t = _0n;
for (let j = 0; j < 7; j++) {
R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
if (R & _2n) t ^= _1n << ((_1n << /* @__PURE__ */ BigInt(j)) - _1n);
}
_SHA3_IOTA.push(t);
}
const IOTAS = split(_SHA3_IOTA, true);
const SHA3_IOTA_H = IOTAS[0];
const SHA3_IOTA_L = IOTAS[1];
// Left rotation (without 0, 32, 64)
const rotlH = (h: number, l: number, s: number) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s));
const rotlL = (h: number, l: number, s: number) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s));
/** `keccakf1600` internal function, additionally allows to adjust round count. */
export function keccakP(s: Uint32Array, rounds: number = 24): void {
const B = new Uint32Array(5 * 2);
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
for (let round = 24 - rounds; round < 24; round++) {
// Theta θ
for (let x = 0; x < 10; x++) B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
for (let x = 0; x < 10; x += 2) {
const idx1 = (x + 8) % 10;
const idx0 = (x + 2) % 10;
const B0 = B[idx0];
const B1 = B[idx0 + 1];
const Th = rotlH(B0, B1, 1) ^ B[idx1];
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
for (let y = 0; y < 50; y += 10) {
s[x + y] ^= Th;
s[x + y + 1] ^= Tl;
}
}
// Rho (ρ) and Pi (π)
let curH = s[2];
let curL = s[3];
for (let t = 0; t < 24; t++) {
const shift = SHA3_ROTL[t];
const Th = rotlH(curH, curL, shift);
const Tl = rotlL(curH, curL, shift);
const PI = SHA3_PI[t];
curH = s[PI];
curL = s[PI + 1];
s[PI] = Th;
s[PI + 1] = Tl;
}
// Chi (χ)
for (let y = 0; y < 50; y += 10) {
for (let x = 0; x < 10; x++) B[x] = s[y + x];
for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
}
// Iota (ι)
s[0] ^= SHA3_IOTA_H[round];
s[1] ^= SHA3_IOTA_L[round];
}
clean(B);
}
/** Keccak sponge function. */
export class Keccak extends Hash<Keccak> implements HashXOF<Keccak> {
protected state: Uint8Array;
protected pos = 0;
protected posOut = 0;
protected finished = false;
protected state32: Uint32Array;
protected destroyed = false;
public blockLen: number;
public suffix: number;
public outputLen: number;
protected enableXOF = false;
protected rounds: number;
// NOTE: we accept arguments in bytes instead of bits here.
constructor(
blockLen: number,
suffix: number,
outputLen: number,
enableXOF = false,
rounds: number = 24
) {
super();
this.blockLen = blockLen;
this.suffix = suffix;
this.outputLen = outputLen;
this.enableXOF = enableXOF;
this.rounds = rounds;
// Can be passed from user as dkLen
anumber(outputLen);
// 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes
// 0 < blockLen < 200
if (!(0 < blockLen && blockLen < 200))
throw new Error('only keccak-f1600 function is supported');
this.state = new Uint8Array(200);
this.state32 = u32(this.state);
}
clone(): Keccak {
return this._cloneInto();
}
protected keccak(): void {
swap32IfBE(this.state32);
keccakP(this.state32, this.rounds);
swap32IfBE(this.state32);
this.posOut = 0;
this.pos = 0;
}
update(data: Input): this {
aexists(this);
data = toBytes(data);
abytes(data);
const { blockLen, state } = this;
const len = data.length;
for (let pos = 0; pos < len; ) {
const take = Math.min(blockLen - this.pos, len - pos);
for (let i = 0; i < take; i++) state[this.pos++] ^= data[pos++];
if (this.pos === blockLen) this.keccak();
}
return this;
}
protected finish(): void {
if (this.finished) return;
this.finished = true;
const { state, suffix, pos, blockLen } = this;
// Do the padding
state[pos] ^= suffix;
if ((suffix & 0x80) !== 0 && pos === blockLen - 1) this.keccak();
state[blockLen - 1] ^= 0x80;
this.keccak();
}
protected writeInto(out: Uint8Array): Uint8Array {
aexists(this, false);
abytes(out);
this.finish();
const bufferOut = this.state;
const { blockLen } = this;
for (let pos = 0, len = out.length; pos < len; ) {
if (this.posOut >= blockLen) this.keccak();
const take = Math.min(blockLen - this.posOut, len - pos);
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
this.posOut += take;
pos += take;
}
return out;
}
xofInto(out: Uint8Array): Uint8Array {
// Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
if (!this.enableXOF) throw new Error('XOF is not possible for this instance');
return this.writeInto(out);
}
xof(bytes: number): Uint8Array {
anumber(bytes);
return this.xofInto(new Uint8Array(bytes));
}
digestInto(out: Uint8Array): Uint8Array {
aoutput(out, this);
if (this.finished) throw new Error('digest() was already called');
this.writeInto(out);
this.destroy();
return out;
}
digest(): Uint8Array {
return this.digestInto(new Uint8Array(this.outputLen));
}
destroy(): void {
this.destroyed = true;
clean(this.state);
}
_cloneInto(to?: Keccak): Keccak {
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
to.state32.set(this.state32);
to.pos = this.pos;
to.posOut = this.posOut;
to.finished = this.finished;
to.rounds = rounds;
// Suffix can change in cSHAKE
to.suffix = suffix;
to.outputLen = outputLen;
to.enableXOF = enableXOF;
to.destroyed = this.destroyed;
return to;
}
}
const gen = (suffix: number, blockLen: number, outputLen: number) =>
createHasher(() => new Keccak(blockLen, suffix, outputLen));
/** SHA3-224 hash function. */
export const sha3_224: CHash = /* @__PURE__ */ (() => gen(0x06, 144, 224 / 8))();
/** SHA3-256 hash function. Different from keccak-256. */
export const sha3_256: CHash = /* @__PURE__ */ (() => gen(0x06, 136, 256 / 8))();
/** SHA3-384 hash function. */
export const sha3_384: CHash = /* @__PURE__ */ (() => gen(0x06, 104, 384 / 8))();
/** SHA3-512 hash function. */
export const sha3_512: CHash = /* @__PURE__ */ (() => gen(0x06, 72, 512 / 8))();
/** keccak-224 hash function. */
export const keccak_224: CHash = /* @__PURE__ */ (() => gen(0x01, 144, 224 / 8))();
/** keccak-256 hash function. Different from SHA3-256. */
export const keccak_256: CHash = /* @__PURE__ */ (() => gen(0x01, 136, 256 / 8))();
/** keccak-384 hash function. */
export const keccak_384: CHash = /* @__PURE__ */ (() => gen(0x01, 104, 384 / 8))();
/** keccak-512 hash function. */
export const keccak_512: CHash = /* @__PURE__ */ (() => gen(0x01, 72, 512 / 8))();
export type ShakeOpts = { dkLen?: number };
const genShake = (suffix: number, blockLen: number, outputLen: number) =>
createXOFer<HashXOF<Keccak>, ShakeOpts>(
(opts: ShakeOpts = {}) =>
new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true)
);
/** SHAKE128 XOF with 128-bit security. */
export const shake128: CHashXO = /* @__PURE__ */ (() => genShake(0x1f, 168, 128 / 8))();
/** SHAKE256 XOF with 256-bit security. */
export const shake256: CHashXO = /* @__PURE__ */ (() => genShake(0x1f, 136, 256 / 8))();
-34
View File
@@ -1,34 +0,0 @@
/**
* SHA2-512 a.k.a. sha512 and sha384. It is slower than sha256 in js because u64 operations are slow.
*
* Check out [RFC 4634](https://datatracker.ietf.org/doc/html/rfc4634) and
* [the paper on truncated SHA512/256](https://eprint.iacr.org/2010/548.pdf).
* @module
* @deprecated
*/
import {
SHA384 as SHA384n,
sha384 as sha384n,
sha512_224 as sha512_224n,
SHA512_224 as SHA512_224n,
sha512_256 as sha512_256n,
SHA512_256 as SHA512_256n,
SHA512 as SHA512n,
sha512 as sha512n,
} from './sha2.ts';
/** @deprecated Use import from `noble/hashes/sha2` module */
export const SHA512: typeof SHA512n = SHA512n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const sha512: typeof sha512n = sha512n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const SHA384: typeof SHA384n = SHA384n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const sha384: typeof sha384n = sha384n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const SHA512_224: typeof SHA512_224n = SHA512_224n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const sha512_224: typeof sha512_224n = sha512_224n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const SHA512_256: typeof SHA512_256n = SHA512_256n;
/** @deprecated Use import from `noble/hashes/sha2` module */
export const sha512_256: typeof sha512_256n = sha512_256n;
-395
View File
@@ -1,395 +0,0 @@
/**
* Utilities for hex, bytes, CSPRNG.
* @module
*/
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
// node.js versions earlier than v19 don't declare it in global scope.
// For node.js, package.json#exports field mapping rewrites import
// from `crypto` to `cryptoNode`, which imports native module.
// Makes the utils un-importable in browsers without a bundler.
// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.
import { crypto } from '@noble/hashes/crypto';
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
export function isBytes(a: unknown): a is Uint8Array {
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
}
/** Asserts something is positive integer. */
export function anumber(n: number): void {
if (!Number.isSafeInteger(n) || n < 0) throw new Error('positive integer expected, got ' + n);
}
/** Asserts something is Uint8Array. */
export function abytes(b: Uint8Array | undefined, ...lengths: number[]): void {
if (!isBytes(b)) throw new Error('Uint8Array expected');
if (lengths.length > 0 && !lengths.includes(b.length))
throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);
}
/** Asserts something is hash */
export function ahash(h: IHash): void {
if (typeof h !== 'function' || typeof h.create !== 'function')
throw new Error('Hash should be wrapped by utils.createHasher');
anumber(h.outputLen);
anumber(h.blockLen);
}
/** Asserts a hash instance has not been destroyed / finished */
export function aexists(instance: any, checkFinished = true): void {
if (instance.destroyed) throw new Error('Hash instance has been destroyed');
if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');
}
/** Asserts output is properly-sized byte array */
export function aoutput(out: any, instance: any): void {
abytes(out);
const min = instance.outputLen;
if (out.length < min) {
throw new Error('digestInto() expects output buffer of length at least ' + min);
}
}
/** Generic type encompassing 8/16/32-byte arrays - but not 64-byte. */
// prettier-ignore
export type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |
Uint16Array | Int16Array | Uint32Array | Int32Array;
/** Cast u8 / u16 / u32 to u8. */
export function u8(arr: TypedArray): Uint8Array {
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
}
/** Cast u8 / u16 / u32 to u32. */
export function u32(arr: TypedArray): Uint32Array {
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
}
/** Zeroize a byte array. Warning: JS provides no guarantees. */
export function clean(...arrays: TypedArray[]): void {
for (let i = 0; i < arrays.length; i++) {
arrays[i].fill(0);
}
}
/** Create DataView of an array for easy byte-level manipulation. */
export function createView(arr: TypedArray): DataView {
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
}
/** The rotate right (circular right shift) operation for uint32 */
export function rotr(word: number, shift: number): number {
return (word << (32 - shift)) | (word >>> shift);
}
/** The rotate left (circular left shift) operation for uint32 */
export function rotl(word: number, shift: number): number {
return (word << shift) | ((word >>> (32 - shift)) >>> 0);
}
/** Is current platform little-endian? Most are. Big-Endian platform: IBM */
export const isLE: boolean = /* @__PURE__ */ (() =>
new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();
/** The byte swap operation for uint32 */
export function byteSwap(word: number): number {
return (
((word << 24) & 0xff000000) |
((word << 8) & 0xff0000) |
((word >>> 8) & 0xff00) |
((word >>> 24) & 0xff)
);
}
/** Conditionally byte swap if on a big-endian platform */
export const swap8IfBE: (n: number) => number = isLE
? (n: number) => n
: (n: number) => byteSwap(n);
/** @deprecated */
export const byteSwapIfBE: typeof swap8IfBE = swap8IfBE;
/** In place byte swap for Uint32Array */
export function byteSwap32(arr: Uint32Array): Uint32Array {
for (let i = 0; i < arr.length; i++) {
arr[i] = byteSwap(arr[i]);
}
return arr;
}
export const swap32IfBE: (u: Uint32Array) => Uint32Array = isLE
? (u: Uint32Array) => u
: byteSwap32;
// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex
const hasHexBuiltin: boolean = /* @__PURE__ */ (() =>
// @ts-ignore
typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();
// Array where index 0xf0 (240) is mapped to string 'f0'
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>
i.toString(16).padStart(2, '0')
);
/**
* Convert byte array to hex string. Uses built-in function, when available.
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
*/
export function bytesToHex(bytes: Uint8Array): string {
abytes(bytes);
// @ts-ignore
if (hasHexBuiltin) return bytes.toHex();
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++) {
hex += hexes[bytes[i]];
}
return hex;
}
// We use optimized technique to convert hex string to byte array
const asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;
function asciiToBase16(ch: number): number | undefined {
if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48
if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)
if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)
return;
}
/**
* Convert hex string to byte array. Uses built-in function, when available.
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
*/
export function hexToBytes(hex: string): Uint8Array {
if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);
// @ts-ignore
if (hasHexBuiltin) return Uint8Array.fromHex(hex);
const hl = hex.length;
const al = hl / 2;
if (hl % 2) throw new Error('hex string expected, got unpadded hex of length ' + hl);
const array = new Uint8Array(al);
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
const n1 = asciiToBase16(hex.charCodeAt(hi));
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
if (n1 === undefined || n2 === undefined) {
const char = hex[hi] + hex[hi + 1];
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
}
array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163
}
return array;
}
/**
* There is no setImmediate in browser and setTimeout is slow.
* Call of async fn will return Promise, which will be fullfiled only on
* next scheduler queue processing step and this is exactly what we need.
*/
export const nextTick = async (): Promise<void> => {};
/** Returns control to thread each 'tick' ms to avoid blocking. */
export async function asyncLoop(
iters: number,
tick: number,
cb: (i: number) => void
): Promise<void> {
let ts = Date.now();
for (let i = 0; i < iters; i++) {
cb(i);
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
const diff = Date.now() - ts;
if (diff >= 0 && diff < tick) continue;
await nextTick();
ts += diff;
}
}
// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535
declare const TextEncoder: any;
declare const TextDecoder: any;
/**
* Converts string to bytes using UTF8 encoding.
* @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])
*/
export function utf8ToBytes(str: string): Uint8Array {
if (typeof str !== 'string') throw new Error('string expected');
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
}
/**
* Converts bytes to string using UTF8 encoding.
* @example bytesToUtf8(Uint8Array.from([97, 98, 99])) // 'abc'
*/
export function bytesToUtf8(bytes: Uint8Array): string {
return new TextDecoder().decode(bytes);
}
/** Accepted input of hash functions. Strings are converted to byte arrays. */
export type Input = string | Uint8Array;
/**
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
* Warning: when Uint8Array is passed, it would NOT get copied.
* Keep in mind for future mutable operations.
*/
export function toBytes(data: Input): Uint8Array {
if (typeof data === 'string') data = utf8ToBytes(data);
abytes(data);
return data;
}
/** KDFs can accept string or Uint8Array for user convenience. */
export type KDFInput = string | Uint8Array;
/**
* Helper for KDFs: consumes uint8array or string.
* When string is passed, does utf8 decoding, using TextDecoder.
*/
export function kdfInputToBytes(data: KDFInput): Uint8Array {
if (typeof data === 'string') data = utf8ToBytes(data);
abytes(data);
return data;
}
/** Copies several Uint8Arrays into one. */
export function concatBytes(...arrays: Uint8Array[]): Uint8Array {
let sum = 0;
for (let i = 0; i < arrays.length; i++) {
const a = arrays[i];
abytes(a);
sum += a.length;
}
const res = new Uint8Array(sum);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const a = arrays[i];
res.set(a, pad);
pad += a.length;
}
return res;
}
type EmptyObj = {};
export function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(
defaults: T1,
opts?: T2
): T1 & T2 {
if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')
throw new Error('options should be object or undefined');
const merged = Object.assign(defaults, opts);
return merged as T1 & T2;
}
/** Hash interface. */
export type IHash = {
(data: Uint8Array): Uint8Array;
blockLen: number;
outputLen: number;
create: any;
};
/** For runtime check if class implements interface */
export abstract class Hash<T extends Hash<T>> {
abstract blockLen: number; // Bytes per block
abstract outputLen: number; // Bytes in output
abstract update(buf: Input): this;
// Writes digest into buf
abstract digestInto(buf: Uint8Array): void;
abstract digest(): Uint8Array;
/**
* Resets internal state. Makes Hash instance unusable.
* Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed
* by user, they will need to manually call `destroy()` when zeroing is necessary.
*/
abstract destroy(): void;
/**
* Clones hash instance. Unsafe: doesn't check whether `to` is valid. Can be used as `clone()`
* when no options are passed.
* Reasons to use `_cloneInto` instead of clone: 1) performance 2) reuse instance => all internal
* buffers are overwritten => causes buffer overwrite which is used for digest in some cases.
* There are no guarantees for clean-up because it's impossible in JS.
*/
abstract _cloneInto(to?: T): T;
// Safe version that clones internal state
abstract clone(): T;
}
/**
* XOF: streaming API to read digest in chunks.
* Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.
* When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot
* destroy state, next call can require more bytes.
*/
export type HashXOF<T extends Hash<T>> = Hash<T> & {
xof(bytes: number): Uint8Array; // Read 'bytes' bytes from digest stream
xofInto(buf: Uint8Array): Uint8Array; // read buf.length bytes from digest stream into buf
};
/** Hash function */
export type CHash = ReturnType<typeof createHasher>;
/** Hash function with output */
export type CHashO = ReturnType<typeof createOptHasher>;
/** XOF with output */
export type CHashXO = ReturnType<typeof createXOFer>;
/** Wraps hash function, creating an interface on top of it */
export function createHasher<T extends Hash<T>>(
hashCons: () => Hash<T>
): {
(msg: Input): Uint8Array;
outputLen: number;
blockLen: number;
create(): Hash<T>;
} {
const hashC = (msg: Input): Uint8Array => hashCons().update(toBytes(msg)).digest();
const tmp = hashCons();
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = () => hashCons();
return hashC;
}
export function createOptHasher<H extends Hash<H>, T extends Object>(
hashCons: (opts?: T) => Hash<H>
): {
(msg: Input, opts?: T): Uint8Array;
outputLen: number;
blockLen: number;
create(opts?: T): Hash<H>;
} {
const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();
const tmp = hashCons({} as T);
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (opts?: T) => hashCons(opts);
return hashC;
}
export function createXOFer<H extends HashXOF<H>, T extends Object>(
hashCons: (opts?: T) => HashXOF<H>
): {
(msg: Input, opts?: T): Uint8Array;
outputLen: number;
blockLen: number;
create(opts?: T): HashXOF<H>;
} {
const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();
const tmp = hashCons({} as T);
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (opts?: T) => hashCons(opts);
return hashC;
}
export const wrapConstructor: typeof createHasher = createHasher;
export const wrapConstructorWithOpts: typeof createOptHasher = createOptHasher;
export const wrapXOFConstructorWithOpts: typeof createXOFer = createXOFer;
/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */
export function randomBytes(bytesLength = 32): Uint8Array {
if (crypto && typeof crypto.getRandomValues === 'function') {
return crypto.getRandomValues(new Uint8Array(bytesLength));
}
// Legacy Node.js compatibility
if (crypto && typeof crypto.randomBytes === 'function') {
return Uint8Array.from(crypto.randomBytes(bytesLength));
}
throw new Error('crypto.getRandomValues must be defined');
}
-161
View File
@@ -1,161 +0,0 @@
/**
* Utilities for hex, bytes, CSPRNG.
* @module
*/
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
export declare function isBytes(a: unknown): a is Uint8Array;
/** Asserts something is positive integer. */
export declare function anumber(n: number): void;
/** Asserts something is Uint8Array. */
export declare function abytes(b: Uint8Array | undefined, ...lengths: number[]): void;
/** Asserts something is hash */
export declare function ahash(h: IHash): void;
/** Asserts a hash instance has not been destroyed / finished */
export declare function aexists(instance: any, checkFinished?: boolean): void;
/** Asserts output is properly-sized byte array */
export declare function aoutput(out: any, instance: any): void;
/** Generic type encompassing 8/16/32-byte arrays - but not 64-byte. */
export type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array | Uint16Array | Int16Array | Uint32Array | Int32Array;
/** Cast u8 / u16 / u32 to u8. */
export declare function u8(arr: TypedArray): Uint8Array;
/** Cast u8 / u16 / u32 to u32. */
export declare function u32(arr: TypedArray): Uint32Array;
/** Zeroize a byte array. Warning: JS provides no guarantees. */
export declare function clean(...arrays: TypedArray[]): void;
/** Create DataView of an array for easy byte-level manipulation. */
export declare function createView(arr: TypedArray): DataView;
/** The rotate right (circular right shift) operation for uint32 */
export declare function rotr(word: number, shift: number): number;
/** The rotate left (circular left shift) operation for uint32 */
export declare function rotl(word: number, shift: number): number;
/** Is current platform little-endian? Most are. Big-Endian platform: IBM */
export declare const isLE: boolean;
/** The byte swap operation for uint32 */
export declare function byteSwap(word: number): number;
/** Conditionally byte swap if on a big-endian platform */
export declare const swap8IfBE: (n: number) => number;
/** @deprecated */
export declare const byteSwapIfBE: typeof swap8IfBE;
/** In place byte swap for Uint32Array */
export declare function byteSwap32(arr: Uint32Array): Uint32Array;
export declare const swap32IfBE: (u: Uint32Array) => Uint32Array;
/**
* Convert byte array to hex string. Uses built-in function, when available.
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
*/
export declare function bytesToHex(bytes: Uint8Array): string;
/**
* Convert hex string to byte array. Uses built-in function, when available.
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
*/
export declare function hexToBytes(hex: string): Uint8Array;
/**
* There is no setImmediate in browser and setTimeout is slow.
* Call of async fn will return Promise, which will be fullfiled only on
* next scheduler queue processing step and this is exactly what we need.
*/
export declare const nextTick: () => Promise<void>;
/** Returns control to thread each 'tick' ms to avoid blocking. */
export declare function asyncLoop(iters: number, tick: number, cb: (i: number) => void): Promise<void>;
/**
* Converts string to bytes using UTF8 encoding.
* @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])
*/
export declare function utf8ToBytes(str: string): Uint8Array;
/**
* Converts bytes to string using UTF8 encoding.
* @example bytesToUtf8(Uint8Array.from([97, 98, 99])) // 'abc'
*/
export declare function bytesToUtf8(bytes: Uint8Array): string;
/** Accepted input of hash functions. Strings are converted to byte arrays. */
export type Input = string | Uint8Array;
/**
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
* Warning: when Uint8Array is passed, it would NOT get copied.
* Keep in mind for future mutable operations.
*/
export declare function toBytes(data: Input): Uint8Array;
/** KDFs can accept string or Uint8Array for user convenience. */
export type KDFInput = string | Uint8Array;
/**
* Helper for KDFs: consumes uint8array or string.
* When string is passed, does utf8 decoding, using TextDecoder.
*/
export declare function kdfInputToBytes(data: KDFInput): Uint8Array;
/** Copies several Uint8Arrays into one. */
export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
type EmptyObj = {};
export declare function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(defaults: T1, opts?: T2): T1 & T2;
/** Hash interface. */
export type IHash = {
(data: Uint8Array): Uint8Array;
blockLen: number;
outputLen: number;
create: any;
};
/** For runtime check if class implements interface */
export declare abstract class Hash<T extends Hash<T>> {
abstract blockLen: number;
abstract outputLen: number;
abstract update(buf: Input): this;
abstract digestInto(buf: Uint8Array): void;
abstract digest(): Uint8Array;
/**
* Resets internal state. Makes Hash instance unusable.
* Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed
* by user, they will need to manually call `destroy()` when zeroing is necessary.
*/
abstract destroy(): void;
/**
* Clones hash instance. Unsafe: doesn't check whether `to` is valid. Can be used as `clone()`
* when no options are passed.
* Reasons to use `_cloneInto` instead of clone: 1) performance 2) reuse instance => all internal
* buffers are overwritten => causes buffer overwrite which is used for digest in some cases.
* There are no guarantees for clean-up because it's impossible in JS.
*/
abstract _cloneInto(to?: T): T;
abstract clone(): T;
}
/**
* XOF: streaming API to read digest in chunks.
* Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.
* When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot
* destroy state, next call can require more bytes.
*/
export type HashXOF<T extends Hash<T>> = Hash<T> & {
xof(bytes: number): Uint8Array;
xofInto(buf: Uint8Array): Uint8Array;
};
/** Hash function */
export type CHash = ReturnType<typeof createHasher>;
/** Hash function with output */
export type CHashO = ReturnType<typeof createOptHasher>;
/** XOF with output */
export type CHashXO = ReturnType<typeof createXOFer>;
/** Wraps hash function, creating an interface on top of it */
export declare function createHasher<T extends Hash<T>>(hashCons: () => Hash<T>): {
(msg: Input): Uint8Array;
outputLen: number;
blockLen: number;
create(): Hash<T>;
};
export declare function createOptHasher<H extends Hash<H>, T extends Object>(hashCons: (opts?: T) => Hash<H>): {
(msg: Input, opts?: T): Uint8Array;
outputLen: number;
blockLen: number;
create(opts?: T): Hash<H>;
};
export declare function createXOFer<H extends HashXOF<H>, T extends Object>(hashCons: (opts?: T) => HashXOF<H>): {
(msg: Input, opts?: T): Uint8Array;
outputLen: number;
blockLen: number;
create(opts?: T): HashXOF<H>;
};
export declare const wrapConstructor: typeof createHasher;
export declare const wrapConstructorWithOpts: typeof createOptHasher;
export declare const wrapXOFConstructorWithOpts: typeof createXOFer;
/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */
export declare function randomBytes(bytesLength?: number): Uint8Array;
export {};
//# sourceMappingURL=utils.d.ts.map
-1
View File
@@ -1 +0,0 @@
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,sEAAsE;AAUtE,qFAAqF;AACrF,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,UAAU,CAEnD;AAED,6CAA6C;AAC7C,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAEvC;AAED,uCAAuC;AACvC,wBAAgB,MAAM,CAAC,CAAC,EAAE,UAAU,GAAG,SAAS,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAI5E;AAED,gCAAgC;AAChC,wBAAgB,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAKpC;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CAAC,QAAQ,EAAE,GAAG,EAAE,aAAa,UAAO,GAAG,IAAI,CAGjE;AAED,kDAAkD;AAClD,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,GAAG,IAAI,CAMrD;AAED,uEAAuE;AAEvE,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,iBAAiB,GAAG,UAAU,GACjE,WAAW,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;AAEtD,iCAAiC;AACjC,wBAAgB,EAAE,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CAE9C;AAED,kCAAkC;AAClC,wBAAgB,GAAG,CAAC,GAAG,EAAE,UAAU,GAAG,WAAW,CAEhD;AAED,gEAAgE;AAChE,wBAAgB,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAInD;AAED,oEAAoE;AACpE,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,QAAQ,CAEpD;AAED,mEAAmE;AACnE,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,iEAAiE;AACjE,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,4EAA4E;AAC5E,eAAO,MAAM,IAAI,EAAE,OACkD,CAAC;AAEtE,yCAAyC;AACzC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO7C;AACD,0DAA0D;AAC1D,eAAO,MAAM,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAET,CAAC;AAE/B,kBAAkB;AAClB,eAAO,MAAM,YAAY,EAAE,OAAO,SAAqB,CAAC;AACxD,yCAAyC;AACzC,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,WAAW,CAKxD;AAED,eAAO,MAAM,UAAU,EAAE,CAAC,CAAC,EAAE,WAAW,KAAK,WAE/B,CAAC;AAYf;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAUpD;AAWD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAkBlD;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,QAAa,OAAO,CAAC,IAAI,CAAO,CAAC;AAEtD,kEAAkE;AAClE,wBAAsB,SAAS,CAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GACtB,OAAO,CAAC,IAAI,CAAC,CAUf;AAMD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAGnD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAErD;AAED,8EAA8E;AAC9E,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;AACxC;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG,UAAU,CAI/C;AAED,iEAAiE;AACjE,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;AAC3C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAI1D;AAED,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAc/D;AAED,KAAK,QAAQ,GAAG,EAAE,CAAC;AACnB,wBAAgB,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,EAAE,SAAS,QAAQ,EAChE,QAAQ,EAAE,EAAE,EACZ,IAAI,CAAC,EAAE,EAAE,GACR,EAAE,GAAG,EAAE,CAKT;AAED,sBAAsB;AACtB,MAAM,MAAM,KAAK,GAAG;IAClB,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,GAAG,CAAC;CACb,CAAC;AAEF,sDAAsD;AACtD,8BAAsB,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI;IAEjC,QAAQ,CAAC,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAC1C,QAAQ,CAAC,MAAM,IAAI,UAAU;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,OAAO,IAAI,IAAI;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC;IAE9B,QAAQ,CAAC,KAAK,IAAI,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,MAAM,OAAO,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG;IACjD,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,UAAU,CAAC;CACtC,CAAC;AAEF,oBAAoB;AACpB,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,gCAAgC;AAChC,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,sBAAsB;AACtB,MAAM,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAErD,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAC5C,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,CAAC,GACtB;IACD,CAAC,GAAG,EAAE,KAAK,GAAG,UAAU,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;CACnB,CAOA;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,EACjE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,GAC9B;IACD,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;CAC3B,CAOA;AAED,wBAAgB,WAAW,CAAC,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,EAChE,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GACjC;IACD,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC9B,CAOA;AACD,eAAO,MAAM,eAAe,EAAE,OAAO,YAA2B,CAAC;AACjE,eAAO,MAAM,uBAAuB,EAAE,OAAO,eAAiC,CAAC;AAC/E,eAAO,MAAM,0BAA0B,EAAE,OAAO,WAAyB,CAAC;AAE1E,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,WAAW,SAAK,GAAG,UAAU,CASxD"}
-313
View File
@@ -1,313 +0,0 @@
"use strict";
/**
* Utilities for hex, bytes, CSPRNG.
* @module
*/
/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrapXOFConstructorWithOpts = exports.wrapConstructorWithOpts = exports.wrapConstructor = exports.Hash = exports.nextTick = exports.swap32IfBE = exports.byteSwapIfBE = exports.swap8IfBE = exports.isLE = void 0;
exports.isBytes = isBytes;
exports.anumber = anumber;
exports.abytes = abytes;
exports.ahash = ahash;
exports.aexists = aexists;
exports.aoutput = aoutput;
exports.u8 = u8;
exports.u32 = u32;
exports.clean = clean;
exports.createView = createView;
exports.rotr = rotr;
exports.rotl = rotl;
exports.byteSwap = byteSwap;
exports.byteSwap32 = byteSwap32;
exports.bytesToHex = bytesToHex;
exports.hexToBytes = hexToBytes;
exports.asyncLoop = asyncLoop;
exports.utf8ToBytes = utf8ToBytes;
exports.bytesToUtf8 = bytesToUtf8;
exports.toBytes = toBytes;
exports.kdfInputToBytes = kdfInputToBytes;
exports.concatBytes = concatBytes;
exports.checkOpts = checkOpts;
exports.createHasher = createHasher;
exports.createOptHasher = createOptHasher;
exports.createXOFer = createXOFer;
exports.randomBytes = randomBytes;
// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.
// node.js versions earlier than v19 don't declare it in global scope.
// For node.js, package.json#exports field mapping rewrites import
// from `crypto` to `cryptoNode`, which imports native module.
// Makes the utils un-importable in browsers without a bundler.
// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.
const crypto_1 = require("@noble/hashes/crypto");
/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
function isBytes(a) {
return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
}
/** Asserts something is positive integer. */
function anumber(n) {
if (!Number.isSafeInteger(n) || n < 0)
throw new Error('positive integer expected, got ' + n);
}
/** Asserts something is Uint8Array. */
function abytes(b, ...lengths) {
if (!isBytes(b))
throw new Error('Uint8Array expected');
if (lengths.length > 0 && !lengths.includes(b.length))
throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);
}
/** Asserts something is hash */
function ahash(h) {
if (typeof h !== 'function' || typeof h.create !== 'function')
throw new Error('Hash should be wrapped by utils.createHasher');
anumber(h.outputLen);
anumber(h.blockLen);
}
/** Asserts a hash instance has not been destroyed / finished */
function aexists(instance, checkFinished = true) {
if (instance.destroyed)
throw new Error('Hash instance has been destroyed');
if (checkFinished && instance.finished)
throw new Error('Hash#digest() has already been called');
}
/** Asserts output is properly-sized byte array */
function aoutput(out, instance) {
abytes(out);
const min = instance.outputLen;
if (out.length < min) {
throw new Error('digestInto() expects output buffer of length at least ' + min);
}
}
/** Cast u8 / u16 / u32 to u8. */
function u8(arr) {
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
}
/** Cast u8 / u16 / u32 to u32. */
function u32(arr) {
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
}
/** Zeroize a byte array. Warning: JS provides no guarantees. */
function clean(...arrays) {
for (let i = 0; i < arrays.length; i++) {
arrays[i].fill(0);
}
}
/** Create DataView of an array for easy byte-level manipulation. */
function createView(arr) {
return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);
}
/** The rotate right (circular right shift) operation for uint32 */
function rotr(word, shift) {
return (word << (32 - shift)) | (word >>> shift);
}
/** The rotate left (circular left shift) operation for uint32 */
function rotl(word, shift) {
return (word << shift) | ((word >>> (32 - shift)) >>> 0);
}
/** Is current platform little-endian? Most are. Big-Endian platform: IBM */
exports.isLE = (() => new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();
/** The byte swap operation for uint32 */
function byteSwap(word) {
return (((word << 24) & 0xff000000) |
((word << 8) & 0xff0000) |
((word >>> 8) & 0xff00) |
((word >>> 24) & 0xff));
}
/** Conditionally byte swap if on a big-endian platform */
exports.swap8IfBE = exports.isLE
? (n) => n
: (n) => byteSwap(n);
/** @deprecated */
exports.byteSwapIfBE = exports.swap8IfBE;
/** In place byte swap for Uint32Array */
function byteSwap32(arr) {
for (let i = 0; i < arr.length; i++) {
arr[i] = byteSwap(arr[i]);
}
return arr;
}
exports.swap32IfBE = exports.isLE
? (u) => u
: byteSwap32;
// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex
const hasHexBuiltin = /* @__PURE__ */ (() =>
// @ts-ignore
typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();
// Array where index 0xf0 (240) is mapped to string 'f0'
const hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'));
/**
* Convert byte array to hex string. Uses built-in function, when available.
* @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'
*/
function bytesToHex(bytes) {
abytes(bytes);
// @ts-ignore
if (hasHexBuiltin)
return bytes.toHex();
// pre-caching improves the speed 6x
let hex = '';
for (let i = 0; i < bytes.length; i++) {
hex += hexes[bytes[i]];
}
return hex;
}
// We use optimized technique to convert hex string to byte array
const asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 };
function asciiToBase16(ch) {
if (ch >= asciis._0 && ch <= asciis._9)
return ch - asciis._0; // '2' => 50-48
if (ch >= asciis.A && ch <= asciis.F)
return ch - (asciis.A - 10); // 'B' => 66-(65-10)
if (ch >= asciis.a && ch <= asciis.f)
return ch - (asciis.a - 10); // 'b' => 98-(97-10)
return;
}
/**
* Convert hex string to byte array. Uses built-in function, when available.
* @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
*/
function hexToBytes(hex) {
if (typeof hex !== 'string')
throw new Error('hex string expected, got ' + typeof hex);
// @ts-ignore
if (hasHexBuiltin)
return Uint8Array.fromHex(hex);
const hl = hex.length;
const al = hl / 2;
if (hl % 2)
throw new Error('hex string expected, got unpadded hex of length ' + hl);
const array = new Uint8Array(al);
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
const n1 = asciiToBase16(hex.charCodeAt(hi));
const n2 = asciiToBase16(hex.charCodeAt(hi + 1));
if (n1 === undefined || n2 === undefined) {
const char = hex[hi] + hex[hi + 1];
throw new Error('hex string expected, got non-hex character "' + char + '" at index ' + hi);
}
array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163
}
return array;
}
/**
* There is no setImmediate in browser and setTimeout is slow.
* Call of async fn will return Promise, which will be fullfiled only on
* next scheduler queue processing step and this is exactly what we need.
*/
const nextTick = async () => { };
exports.nextTick = nextTick;
/** Returns control to thread each 'tick' ms to avoid blocking. */
async function asyncLoop(iters, tick, cb) {
let ts = Date.now();
for (let i = 0; i < iters; i++) {
cb(i);
// Date.now() is not monotonic, so in case if clock goes backwards we return return control too
const diff = Date.now() - ts;
if (diff >= 0 && diff < tick)
continue;
await (0, exports.nextTick)();
ts += diff;
}
}
/**
* Converts string to bytes using UTF8 encoding.
* @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])
*/
function utf8ToBytes(str) {
if (typeof str !== 'string')
throw new Error('string expected');
return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809
}
/**
* Converts bytes to string using UTF8 encoding.
* @example bytesToUtf8(Uint8Array.from([97, 98, 99])) // 'abc'
*/
function bytesToUtf8(bytes) {
return new TextDecoder().decode(bytes);
}
/**
* Normalizes (non-hex) string or Uint8Array to Uint8Array.
* Warning: when Uint8Array is passed, it would NOT get copied.
* Keep in mind for future mutable operations.
*/
function toBytes(data) {
if (typeof data === 'string')
data = utf8ToBytes(data);
abytes(data);
return data;
}
/**
* Helper for KDFs: consumes uint8array or string.
* When string is passed, does utf8 decoding, using TextDecoder.
*/
function kdfInputToBytes(data) {
if (typeof data === 'string')
data = utf8ToBytes(data);
abytes(data);
return data;
}
/** Copies several Uint8Arrays into one. */
function concatBytes(...arrays) {
let sum = 0;
for (let i = 0; i < arrays.length; i++) {
const a = arrays[i];
abytes(a);
sum += a.length;
}
const res = new Uint8Array(sum);
for (let i = 0, pad = 0; i < arrays.length; i++) {
const a = arrays[i];
res.set(a, pad);
pad += a.length;
}
return res;
}
function checkOpts(defaults, opts) {
if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')
throw new Error('options should be object or undefined');
const merged = Object.assign(defaults, opts);
return merged;
}
/** For runtime check if class implements interface */
class Hash {
}
exports.Hash = Hash;
/** Wraps hash function, creating an interface on top of it */
function createHasher(hashCons) {
const hashC = (msg) => hashCons().update(toBytes(msg)).digest();
const tmp = hashCons();
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = () => hashCons();
return hashC;
}
function createOptHasher(hashCons) {
const hashC = (msg, opts) => hashCons(opts).update(toBytes(msg)).digest();
const tmp = hashCons({});
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (opts) => hashCons(opts);
return hashC;
}
function createXOFer(hashCons) {
const hashC = (msg, opts) => hashCons(opts).update(toBytes(msg)).digest();
const tmp = hashCons({});
hashC.outputLen = tmp.outputLen;
hashC.blockLen = tmp.blockLen;
hashC.create = (opts) => hashCons(opts);
return hashC;
}
exports.wrapConstructor = createHasher;
exports.wrapConstructorWithOpts = createOptHasher;
exports.wrapXOFConstructorWithOpts = createXOFer;
/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */
function randomBytes(bytesLength = 32) {
if (crypto_1.crypto && typeof crypto_1.crypto.getRandomValues === 'function') {
return crypto_1.crypto.getRandomValues(new Uint8Array(bytesLength));
}
// Legacy Node.js compatibility
if (crypto_1.crypto && typeof crypto_1.crypto.randomBytes === 'function') {
return Uint8Array.from(crypto_1.crypto.randomBytes(bytesLength));
}
throw new Error('crypto.getRandomValues must be defined');
}
//# sourceMappingURL=utils.js.map
-1
View File
File diff suppressed because one or more lines are too long
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020
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.
-45
View File
@@ -1,45 +0,0 @@
# `@peculiar/asn1-schema`
[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://github.com/PeculiarVentures/asn1-schema/blob/master/packages/schema/LICENSE)
[![npm version](https://badge.fury.io/js/%40peculiar%2Fasn1-schema.svg)](https://badge.fury.io/js/%40peculiar%2Fasn1-schema)
[![NPM](https://nodei.co/npm/@peculiar/asn1-schema.png)](https://nodei.co/npm/@peculiar/asn1-schema/)
Decorators and helper APIs for declaring ASN.1 schemas in TypeScript.
This is the core package used by the schema modules in this repository. It lets you describe DER structures with decorators and convert them with `AsnConvert`, `AsnParser`, and `AsnSerializer`.
## Installation
```bash
npm install @peculiar/asn1-schema
```
## Overview
Abstract Syntax Notation One (ASN.1) is widely used across X.509, PKCS, CMS, OCSP, and related standards. This package keeps those mappings declarative and type-friendly by attaching schema metadata directly to classes.
## TypeScript Example
```ts
import { AsnConvert, AsnProp, AsnPropTypes, AsnType, AsnTypeTypes } from "@peculiar/asn1-schema";
@AsnType({ type: AsnTypeTypes.Sequence })
class BasicConstraints {
@AsnProp({ type: AsnPropTypes.Boolean, defaultValue: false })
public cA = false;
}
const basicConstraints = new BasicConstraints();
basicConstraints.cA = true;
const encoded = AsnConvert.serialize(basicConstraints);
const decoded = AsnConvert.parse(encoded, BasicConstraints);
console.log(decoded.cA);
```
## Related Links
- [Decorators overview](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841)
- [ASN.1 playground](http://lapo.it/asn1js/)
-25
View File
@@ -1,25 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnConvert = void 0;
const tslib_1 = require("tslib");
const asn1js = tslib_1.__importStar(require("asn1js"));
const utils_1 = require("@peculiar/utils");
const parser_1 = require("./parser");
const serializer_1 = require("./serializer");
class AsnConvert {
static serialize(obj) {
return serializer_1.AsnSerializer.serialize(obj);
}
static parse(data, target, options) {
return parser_1.AsnParser.parse(data, target, options);
}
static toString(data, options) {
const buf = (0, utils_1.isBufferSource)(data) ? (0, utils_1.toArrayBuffer)(data) : AsnConvert.serialize(data);
const asn = asn1js.fromBER(buf, options?.berOptions);
if (asn.offset === -1) {
throw new Error(`Cannot decode ASN.1 data. ${asn.result.error}`);
}
return asn.result.toString();
}
}
exports.AsnConvert = AsnConvert;
-148
View File
@@ -1,148 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnNullConverter = exports.AsnGeneralizedTimeConverter = exports.AsnUTCTimeConverter = exports.AsnCharacterStringConverter = exports.AsnGeneralStringConverter = exports.AsnVisibleStringConverter = exports.AsnGraphicStringConverter = exports.AsnIA5StringConverter = exports.AsnVideotexStringConverter = exports.AsnTeletexStringConverter = exports.AsnPrintableStringConverter = exports.AsnNumericStringConverter = exports.AsnUniversalStringConverter = exports.AsnBmpStringConverter = exports.AsnUtf8StringConverter = exports.AsnConstructedOctetStringConverter = exports.AsnOctetStringConverter = exports.AsnBooleanConverter = exports.AsnRelativeObjectIdentifierConverter = exports.AsnObjectIdentifierConverter = exports.AsnBitStringConverter = exports.AsnIntegerBigIntConverter = exports.AsnIntegerArrayBufferConverter = exports.AsnEnumeratedConverter = exports.AsnIntegerConverter = exports.AsnAnyConverter = void 0;
exports.defaultConverter = defaultConverter;
const tslib_1 = require("tslib");
const asn1js = tslib_1.__importStar(require("asn1js"));
const utils_1 = require("@peculiar/utils");
const enums_1 = require("./enums");
const index_1 = require("./types/index");
exports.AsnAnyConverter = {
fromASN: (value) => (value instanceof asn1js.Null ? null : (0, utils_1.toArrayBuffer)(value.valueBeforeDecodeView)),
toASN: (value) => {
if (value === null) {
return new asn1js.Null();
}
const schema = asn1js.fromBER(value, { maxDepth: Infinity, maxNodes: Infinity, maxContentLength: Infinity });
if (schema.result.error) {
throw new Error(schema.result.error);
}
return schema.result;
},
};
exports.AsnIntegerConverter = {
fromASN: (value) => value.valueBlock.valueHexView.byteLength >= 4
? value.valueBlock.toString()
: value.valueBlock.valueDec,
toASN: (value) => new asn1js.Integer({ value: +value }),
};
exports.AsnEnumeratedConverter = {
fromASN: (value) => value.valueBlock.valueDec,
toASN: (value) => new asn1js.Enumerated({ value }),
};
exports.AsnIntegerArrayBufferConverter = {
fromASN: (value) => (0, utils_1.toArrayBuffer)(value.valueBlock.valueHexView),
toASN: (value) => new asn1js.Integer({ valueHex: value }),
};
exports.AsnIntegerBigIntConverter = {
fromASN: (value) => value.toBigInt(),
toASN: (value) => asn1js.Integer.fromBigInt(value),
};
exports.AsnBitStringConverter = {
fromASN: (value) => (0, utils_1.toArrayBuffer)(value.valueBlock.valueHexView),
toASN: (value) => new asn1js.BitString({ valueHex: value }),
};
exports.AsnObjectIdentifierConverter = {
fromASN: (value) => value.valueBlock.toString(),
toASN: (value) => new asn1js.ObjectIdentifier({ value }),
};
exports.AsnRelativeObjectIdentifierConverter = {
fromASN: (value) => value.valueBlock.toString(),
toASN: (value) => new asn1js.RelativeObjectIdentifier({ value }),
};
exports.AsnBooleanConverter = {
fromASN: (value) => value.valueBlock.value,
toASN: (value) => new asn1js.Boolean({ value }),
};
exports.AsnOctetStringConverter = {
fromASN: (value) => (0, utils_1.toArrayBuffer)(value.valueBlock.valueHexView),
toASN: (value) => new asn1js.OctetString({ valueHex: value }),
};
exports.AsnConstructedOctetStringConverter = {
fromASN: (value) => new index_1.OctetString(value.getValue()),
toASN: (value) => value.toASN(),
};
function createStringConverter(Asn1Type) {
return {
fromASN: (value) => value.valueBlock.value,
toASN: (value) => new Asn1Type({ value }),
};
}
exports.AsnUtf8StringConverter = createStringConverter(asn1js.Utf8String);
exports.AsnBmpStringConverter = createStringConverter(asn1js.BmpString);
exports.AsnUniversalStringConverter = createStringConverter(asn1js.UniversalString);
exports.AsnNumericStringConverter = createStringConverter(asn1js.NumericString);
exports.AsnPrintableStringConverter = createStringConverter(asn1js.PrintableString);
exports.AsnTeletexStringConverter = createStringConverter(asn1js.TeletexString);
exports.AsnVideotexStringConverter = createStringConverter(asn1js.VideotexString);
exports.AsnIA5StringConverter = createStringConverter(asn1js.IA5String);
exports.AsnGraphicStringConverter = createStringConverter(asn1js.GraphicString);
exports.AsnVisibleStringConverter = createStringConverter(asn1js.VisibleString);
exports.AsnGeneralStringConverter = createStringConverter(asn1js.GeneralString);
exports.AsnCharacterStringConverter = createStringConverter(asn1js.CharacterString);
exports.AsnUTCTimeConverter = {
fromASN: (value) => value.toDate(),
toASN: (value) => new asn1js.UTCTime({ valueDate: value }),
};
exports.AsnGeneralizedTimeConverter = {
fromASN: (value) => value.toDate(),
toASN: (value) => new asn1js.GeneralizedTime({ valueDate: value }),
};
exports.AsnNullConverter = {
fromASN: () => null,
toASN: () => {
return new asn1js.Null();
},
};
function defaultConverter(type) {
switch (type) {
case enums_1.AsnPropTypes.Any:
return exports.AsnAnyConverter;
case enums_1.AsnPropTypes.BitString:
return exports.AsnBitStringConverter;
case enums_1.AsnPropTypes.BmpString:
return exports.AsnBmpStringConverter;
case enums_1.AsnPropTypes.Boolean:
return exports.AsnBooleanConverter;
case enums_1.AsnPropTypes.CharacterString:
return exports.AsnCharacterStringConverter;
case enums_1.AsnPropTypes.Enumerated:
return exports.AsnEnumeratedConverter;
case enums_1.AsnPropTypes.GeneralString:
return exports.AsnGeneralStringConverter;
case enums_1.AsnPropTypes.GeneralizedTime:
return exports.AsnGeneralizedTimeConverter;
case enums_1.AsnPropTypes.GraphicString:
return exports.AsnGraphicStringConverter;
case enums_1.AsnPropTypes.IA5String:
return exports.AsnIA5StringConverter;
case enums_1.AsnPropTypes.Integer:
return exports.AsnIntegerConverter;
case enums_1.AsnPropTypes.Null:
return exports.AsnNullConverter;
case enums_1.AsnPropTypes.NumericString:
return exports.AsnNumericStringConverter;
case enums_1.AsnPropTypes.ObjectIdentifier:
return exports.AsnObjectIdentifierConverter;
case enums_1.AsnPropTypes.RelativeObjectIdentifier:
return exports.AsnRelativeObjectIdentifierConverter;
case enums_1.AsnPropTypes.OctetString:
return exports.AsnOctetStringConverter;
case enums_1.AsnPropTypes.PrintableString:
return exports.AsnPrintableStringConverter;
case enums_1.AsnPropTypes.TeletexString:
return exports.AsnTeletexStringConverter;
case enums_1.AsnPropTypes.UTCTime:
return exports.AsnUTCTimeConverter;
case enums_1.AsnPropTypes.UniversalString:
return exports.AsnUniversalStringConverter;
case enums_1.AsnPropTypes.Utf8String:
return exports.AsnUtf8StringConverter;
case enums_1.AsnPropTypes.VideotexString:
return exports.AsnVideotexStringConverter;
case enums_1.AsnPropTypes.VisibleString:
return exports.AsnVisibleStringConverter;
default:
return null;
}
}
-52
View File
@@ -1,52 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnProp = exports.AsnSequenceType = exports.AsnSetType = exports.AsnChoiceType = exports.AsnType = void 0;
const tslib_1 = require("tslib");
const converters = tslib_1.__importStar(require("./converters"));
const enums_1 = require("./enums");
const storage_1 = require("./storage");
const AsnType = (options) => (target) => {
let schema;
if (!storage_1.schemaStorage.has(target)) {
schema = storage_1.schemaStorage.createDefault(target);
storage_1.schemaStorage.set(target, schema);
}
else {
schema = storage_1.schemaStorage.get(target);
}
Object.assign(schema, options);
};
exports.AsnType = AsnType;
const AsnChoiceType = () => (0, exports.AsnType)({ type: enums_1.AsnTypeTypes.Choice });
exports.AsnChoiceType = AsnChoiceType;
const AsnSetType = (options) => (0, exports.AsnType)({
type: enums_1.AsnTypeTypes.Set,
...options,
});
exports.AsnSetType = AsnSetType;
const AsnSequenceType = (options) => (0, exports.AsnType)({
type: enums_1.AsnTypeTypes.Sequence,
...options,
});
exports.AsnSequenceType = AsnSequenceType;
const AsnProp = (options) => (target, propertyKey) => {
let schema;
if (!storage_1.schemaStorage.has(target.constructor)) {
schema = storage_1.schemaStorage.createDefault(target.constructor);
storage_1.schemaStorage.set(target.constructor, schema);
}
else {
schema = storage_1.schemaStorage.get(target.constructor);
}
const copyOptions = Object.assign({}, options);
if (typeof copyOptions.type === "number" && !copyOptions.converter) {
const defaultConverter = converters.defaultConverter(options.type);
if (!defaultConverter) {
throw new Error(`Cannot get default converter for property '${propertyKey}' of ${target.constructor.name}`);
}
copyOptions.converter = defaultConverter;
}
copyOptions.raw = options.raw;
schema.items[propertyKey] = copyOptions;
};
exports.AsnProp = AsnProp;
-40
View File
@@ -1,40 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnPropTypes = exports.AsnTypeTypes = void 0;
var AsnTypeTypes;
(function (AsnTypeTypes) {
AsnTypeTypes[AsnTypeTypes["Sequence"] = 0] = "Sequence";
AsnTypeTypes[AsnTypeTypes["Set"] = 1] = "Set";
AsnTypeTypes[AsnTypeTypes["Choice"] = 2] = "Choice";
})(AsnTypeTypes || (exports.AsnTypeTypes = AsnTypeTypes = {}));
var AsnPropTypes;
(function (AsnPropTypes) {
AsnPropTypes[AsnPropTypes["Any"] = 1] = "Any";
AsnPropTypes[AsnPropTypes["Boolean"] = 2] = "Boolean";
AsnPropTypes[AsnPropTypes["OctetString"] = 3] = "OctetString";
AsnPropTypes[AsnPropTypes["BitString"] = 4] = "BitString";
AsnPropTypes[AsnPropTypes["Integer"] = 5] = "Integer";
AsnPropTypes[AsnPropTypes["Enumerated"] = 6] = "Enumerated";
AsnPropTypes[AsnPropTypes["ObjectIdentifier"] = 7] = "ObjectIdentifier";
AsnPropTypes[AsnPropTypes["Utf8String"] = 8] = "Utf8String";
AsnPropTypes[AsnPropTypes["BmpString"] = 9] = "BmpString";
AsnPropTypes[AsnPropTypes["UniversalString"] = 10] = "UniversalString";
AsnPropTypes[AsnPropTypes["NumericString"] = 11] = "NumericString";
AsnPropTypes[AsnPropTypes["PrintableString"] = 12] = "PrintableString";
AsnPropTypes[AsnPropTypes["TeletexString"] = 13] = "TeletexString";
AsnPropTypes[AsnPropTypes["VideotexString"] = 14] = "VideotexString";
AsnPropTypes[AsnPropTypes["IA5String"] = 15] = "IA5String";
AsnPropTypes[AsnPropTypes["GraphicString"] = 16] = "GraphicString";
AsnPropTypes[AsnPropTypes["VisibleString"] = 17] = "VisibleString";
AsnPropTypes[AsnPropTypes["GeneralString"] = 18] = "GeneralString";
AsnPropTypes[AsnPropTypes["CharacterString"] = 19] = "CharacterString";
AsnPropTypes[AsnPropTypes["UTCTime"] = 20] = "UTCTime";
AsnPropTypes[AsnPropTypes["GeneralizedTime"] = 21] = "GeneralizedTime";
AsnPropTypes[AsnPropTypes["DATE"] = 22] = "DATE";
AsnPropTypes[AsnPropTypes["TimeOfDay"] = 23] = "TimeOfDay";
AsnPropTypes[AsnPropTypes["DateTime"] = 24] = "DateTime";
AsnPropTypes[AsnPropTypes["Duration"] = 25] = "Duration";
AsnPropTypes[AsnPropTypes["TIME"] = 26] = "TIME";
AsnPropTypes[AsnPropTypes["Null"] = 27] = "Null";
AsnPropTypes[AsnPropTypes["RelativeObjectIdentifier"] = 28] = "RelativeObjectIdentifier";
})(AsnPropTypes || (exports.AsnPropTypes = AsnPropTypes = {}));
-4
View File
@@ -1,4 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./schema_validation"), exports);
@@ -1,7 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnSchemaValidationError = void 0;
class AsnSchemaValidationError extends Error {
schemas = [];
}
exports.AsnSchemaValidationError = AsnSchemaValidationError;
-44
View File
@@ -1,44 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isConvertible = isConvertible;
exports.isTypeOfArray = isTypeOfArray;
exports.isArrayEqual = isArrayEqual;
function isConvertible(target) {
if (typeof target === "function" && target.prototype) {
if (target.prototype.toASN && target.prototype.fromASN) {
return true;
}
else {
return isConvertible(target.prototype);
}
}
else {
return !!(target && typeof target === "object" && "toASN" in target && "fromASN" in target);
}
}
function isTypeOfArray(target) {
if (target) {
const proto = Object.getPrototypeOf(target);
if (proto?.prototype?.constructor === Array) {
return true;
}
return isTypeOfArray(proto);
}
return false;
}
function isArrayEqual(bytes1, bytes2) {
if (!(bytes1 && bytes2)) {
return false;
}
if (bytes1.byteLength !== bytes2.byteLength) {
return false;
}
const b1 = new Uint8Array(bytes1);
const b2 = new Uint8Array(bytes2);
for (let i = 0; i < bytes1.byteLength; i++) {
if (b1[i] !== b2[i]) {
return false;
}
}
return true;
}
-22
View File
@@ -1,22 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnSerializer = exports.AsnParser = exports.AsnPropTypes = exports.AsnTypeTypes = exports.AsnSetType = exports.AsnSequenceType = exports.AsnChoiceType = exports.AsnType = exports.AsnProp = void 0;
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./converters"), exports);
tslib_1.__exportStar(require("./types/index"), exports);
var decorators_1 = require("./decorators");
Object.defineProperty(exports, "AsnProp", { enumerable: true, get: function () { return decorators_1.AsnProp; } });
Object.defineProperty(exports, "AsnType", { enumerable: true, get: function () { return decorators_1.AsnType; } });
Object.defineProperty(exports, "AsnChoiceType", { enumerable: true, get: function () { return decorators_1.AsnChoiceType; } });
Object.defineProperty(exports, "AsnSequenceType", { enumerable: true, get: function () { return decorators_1.AsnSequenceType; } });
Object.defineProperty(exports, "AsnSetType", { enumerable: true, get: function () { return decorators_1.AsnSetType; } });
var enums_1 = require("./enums");
Object.defineProperty(exports, "AsnTypeTypes", { enumerable: true, get: function () { return enums_1.AsnTypeTypes; } });
Object.defineProperty(exports, "AsnPropTypes", { enumerable: true, get: function () { return enums_1.AsnPropTypes; } });
var parser_1 = require("./parser");
Object.defineProperty(exports, "AsnParser", { enumerable: true, get: function () { return parser_1.AsnParser; } });
var serializer_1 = require("./serializer");
Object.defineProperty(exports, "AsnSerializer", { enumerable: true, get: function () { return serializer_1.AsnSerializer; } });
tslib_1.__exportStar(require("./errors"), exports);
tslib_1.__exportStar(require("./objects"), exports);
tslib_1.__exportStar(require("./convert"), exports);
-17
View File
@@ -1,17 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnArray = void 0;
class AsnArray extends Array {
constructor(items = []) {
if (typeof items === "number") {
super(items);
}
else {
super();
for (const item of items) {
this.push(item);
}
}
}
}
exports.AsnArray = AsnArray;
-313
View File
@@ -1,313 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnParser = void 0;
const tslib_1 = require("tslib");
const asn1js = tslib_1.__importStar(require("asn1js"));
const utils_1 = require("@peculiar/utils");
const enums_1 = require("./enums");
const converters = tslib_1.__importStar(require("./converters"));
const errors_1 = require("./errors");
const helper_1 = require("./helper");
const storage_1 = require("./storage");
class AsnParser {
static parse(data, target, options) {
const asn1Parsed = asn1js.fromBER((0, utils_1.toArrayBuffer)(data), options?.berOptions);
if (asn1Parsed.result.error) {
throw new Error(asn1Parsed.result.error);
}
const res = this.fromASN(asn1Parsed.result, target, options);
return res;
}
static fromASN(asn1Schema, target, options) {
try {
if ((0, helper_1.isConvertible)(target)) {
const value = new target();
return value.fromASN(asn1Schema);
}
const schema = storage_1.schemaStorage.get(target);
storage_1.schemaStorage.cache(target);
let targetSchema = schema.schema;
const choiceResult = this.handleChoiceTypes(asn1Schema, schema, target, targetSchema, options);
if (choiceResult?.result) {
return choiceResult.result;
}
if (choiceResult?.targetSchema) {
targetSchema = choiceResult.targetSchema;
}
const sequenceResult = this.handleSequenceTypes(asn1Schema, schema, target, targetSchema);
const res = new target();
if ((0, helper_1.isTypeOfArray)(target)) {
return this.handleArrayTypes(asn1Schema, schema, target, options);
}
this.processSchemaItems(schema, sequenceResult, res, options);
return res;
}
catch (error) {
if (error instanceof errors_1.AsnSchemaValidationError) {
error.schemas.push(target.name);
}
throw error;
}
}
static handleChoiceTypes(asn1Schema, schema, target, targetSchema, options) {
if (asn1Schema.constructor === asn1js.Constructed && schema.type === enums_1.AsnTypeTypes.Choice && asn1Schema.idBlock.tagClass === 3) {
for (const key in schema.items) {
const schemaItem = schema.items[key];
if (schemaItem.context === asn1Schema.idBlock.tagNumber && schemaItem.implicit) {
if (typeof schemaItem.type === "function" && storage_1.schemaStorage.has(schemaItem.type)) {
const fieldSchema = storage_1.schemaStorage.get(schemaItem.type);
if (fieldSchema && fieldSchema.type === enums_1.AsnTypeTypes.Sequence) {
const newSeq = new asn1js.Sequence();
if ("value" in asn1Schema.valueBlock && Array.isArray(asn1Schema.valueBlock.value) && "value" in newSeq.valueBlock) {
newSeq.valueBlock.value = asn1Schema.valueBlock.value;
const fieldValue = this.fromASN(newSeq, schemaItem.type, options);
const res = new target();
res[key] = fieldValue;
return { result: res };
}
}
}
}
}
}
else if (asn1Schema.constructor === asn1js.Constructed && schema.type !== enums_1.AsnTypeTypes.Choice) {
const newTargetSchema = new asn1js.Constructed({
idBlock: {
tagClass: 3,
tagNumber: asn1Schema.idBlock.tagNumber,
},
value: schema.schema.valueBlock.value,
});
for (const key in schema.items) {
delete asn1Schema[key];
}
return { targetSchema: newTargetSchema };
}
return null;
}
static handleSequenceTypes(asn1Schema, schema, target, targetSchema) {
if (schema.type === enums_1.AsnTypeTypes.Sequence) {
const asn1ComparedSchema = asn1js.compareSchema({}, asn1Schema, targetSchema);
if (!asn1ComparedSchema.verified) {
throw new errors_1.AsnSchemaValidationError(`Data does not match to ${target.name} ASN1 schema.${asn1ComparedSchema.result.error ? ` ${asn1ComparedSchema.result.error}` : ""}`);
}
return asn1ComparedSchema;
}
else {
const asn1ComparedSchema = asn1js.compareSchema({}, asn1Schema, targetSchema);
if (!asn1ComparedSchema.verified) {
throw new errors_1.AsnSchemaValidationError(`Data does not match to ${target.name} ASN1 schema.${asn1ComparedSchema.result.error ? ` ${asn1ComparedSchema.result.error}` : ""}`);
}
return asn1ComparedSchema;
}
}
static processRepeatedField(asn1Elements, asn1Index, schemaItem) {
let elementsToProcess = asn1Elements.slice(asn1Index);
if (elementsToProcess.length === 1 && elementsToProcess[0].constructor.name === "Sequence") {
const seq = elementsToProcess[0];
if (seq.valueBlock && seq.valueBlock.value && Array.isArray(seq.valueBlock.value)) {
elementsToProcess = seq.valueBlock.value;
}
}
if (typeof schemaItem.type === "number") {
const converter = converters.defaultConverter(schemaItem.type);
if (!converter)
throw new Error(`No converter for ASN.1 type ${schemaItem.type}`);
return elementsToProcess
.filter((el) => el && el.valueBlock)
.map((el) => {
try {
return converter.fromASN(el);
}
catch {
return undefined;
}
})
.filter((v) => v !== undefined);
}
else {
return elementsToProcess
.filter((el) => el && el.valueBlock)
.map((el) => {
try {
return this.fromASN(el, schemaItem.type);
}
catch {
return undefined;
}
})
.filter((v) => v !== undefined);
}
}
static processPrimitiveField(asn1Element, schemaItem) {
const converter = converters.defaultConverter(schemaItem.type);
if (!converter)
throw new Error(`No converter for ASN.1 type ${schemaItem.type}`);
return converter.fromASN(asn1Element);
}
static isOptionalChoiceField(schemaItem) {
return (schemaItem.optional &&
typeof schemaItem.type === "function" &&
storage_1.schemaStorage.has(schemaItem.type) &&
storage_1.schemaStorage.get(schemaItem.type).type === enums_1.AsnTypeTypes.Choice);
}
static processOptionalChoiceField(asn1Element, schemaItem) {
try {
const value = this.fromASN(asn1Element, schemaItem.type);
return {
processed: true,
value,
};
}
catch (err) {
if (err instanceof errors_1.AsnSchemaValidationError && /Wrong values for Choice type/.test(err.message)) {
return { processed: false };
}
throw err;
}
}
static handleArrayTypes(asn1Schema, schema, target, options) {
if (!("value" in asn1Schema.valueBlock && Array.isArray(asn1Schema.valueBlock.value))) {
throw new Error("Cannot get items from the ASN.1 parsed value. ASN.1 object is not constructed.");
}
const itemType = schema.itemType;
if (typeof itemType === "number") {
const converter = converters.defaultConverter(itemType);
if (!converter) {
throw new Error(`Cannot get default converter for array item of ${target.name} ASN1 schema`);
}
return target.from(asn1Schema.valueBlock.value, (element) => converter.fromASN(element));
}
else {
return target.from(asn1Schema.valueBlock.value, (element) => this.fromASN(element, itemType, options));
}
}
static processSchemaItems(schema, asn1ComparedSchema, res, options) {
for (const key in schema.items) {
const asn1SchemaValue = asn1ComparedSchema.result[key];
if (!asn1SchemaValue) {
continue;
}
const schemaItem = schema.items[key];
const schemaItemType = schemaItem.type;
let parsedValue;
if (typeof schemaItemType === "number" || (0, helper_1.isConvertible)(schemaItemType)) {
parsedValue = this.processPrimitiveSchemaItem(asn1SchemaValue, schemaItem, schemaItemType, options);
}
else {
parsedValue = this.processComplexSchemaItem(asn1SchemaValue, schemaItem, schemaItemType, options);
}
if (parsedValue && typeof parsedValue === "object" && "value" in parsedValue && "raw" in parsedValue) {
res[key] = parsedValue.value;
res[`${key}Raw`] = parsedValue.raw;
}
else {
res[key] = parsedValue;
}
}
}
static processPrimitiveSchemaItem(asn1SchemaValue, schemaItem, schemaItemType, options) {
const converter = schemaItem.converter ?? ((0, helper_1.isConvertible)(schemaItemType) ? new schemaItemType() : null);
if (!converter) {
throw new Error("Converter is empty");
}
if (schemaItem.repeated) {
return this.processRepeatedPrimitiveItem(asn1SchemaValue, schemaItem, converter, options);
}
else {
return this.processSinglePrimitiveItem(asn1SchemaValue, schemaItem, schemaItemType, converter, options);
}
}
static processRepeatedPrimitiveItem(asn1SchemaValue, schemaItem, converter, options) {
if (schemaItem.implicit) {
const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
const newItem = new Container();
newItem.valueBlock = asn1SchemaValue.valueBlock;
const newItemAsn = asn1js.fromBER(newItem.toBER(false), options?.berOptions);
if (newItemAsn.offset === -1) {
throw new Error(`Cannot parse the child item. ${newItemAsn.result.error}`);
}
if (!("value" in newItemAsn.result.valueBlock && Array.isArray(newItemAsn.result.valueBlock.value))) {
throw new Error("Cannot get items from the ASN.1 parsed value. ASN.1 object is not constructed.");
}
const value = newItemAsn.result.valueBlock.value;
return Array.from(value, (element) => converter.fromASN(element));
}
else {
return Array.from(asn1SchemaValue, (element) => converter.fromASN(element));
}
}
static processSinglePrimitiveItem(asn1SchemaValue, schemaItem, schemaItemType, converter, options) {
let value = asn1SchemaValue;
if (schemaItem.implicit) {
let newItem;
if ((0, helper_1.isConvertible)(schemaItemType)) {
newItem = new schemaItemType().toSchema("");
}
else {
const Asn1TypeName = enums_1.AsnPropTypes[schemaItemType];
const Asn1Type = asn1js[Asn1TypeName];
if (!Asn1Type) {
throw new Error(`Cannot get '${Asn1TypeName}' class from asn1js module`);
}
newItem = new Asn1Type();
}
newItem.valueBlock = value.valueBlock;
value = asn1js.fromBER(newItem.toBER(false), options?.berOptions).result;
}
return converter.fromASN(value);
}
static processComplexSchemaItem(asn1SchemaValue, schemaItem, schemaItemType, options) {
if (schemaItem.repeated) {
if (!Array.isArray(asn1SchemaValue)) {
throw new Error("Cannot get list of items from the ASN.1 parsed value. ASN.1 value should be iterable.");
}
return Array.from(asn1SchemaValue, (element) => this.fromASN(element, schemaItemType, options));
}
else {
const valueToProcess = this.handleImplicitTagging(asn1SchemaValue, schemaItem, schemaItemType);
if (this.isOptionalChoiceField(schemaItem)) {
try {
return this.fromASN(valueToProcess, schemaItemType, options);
}
catch (err) {
if (err instanceof errors_1.AsnSchemaValidationError && /Wrong values for Choice type/.test(err.message)) {
return undefined;
}
throw err;
}
}
else {
const parsedValue = this.fromASN(valueToProcess, schemaItemType, options);
if (schemaItem.raw) {
return {
value: parsedValue,
raw: asn1SchemaValue.valueBeforeDecodeView,
};
}
return parsedValue;
}
}
}
static handleImplicitTagging(asn1SchemaValue, schemaItem, schemaItemType) {
if (schemaItem.implicit && typeof schemaItem.context === "number") {
const schema = storage_1.schemaStorage.get(schemaItemType);
if (schema.type === enums_1.AsnTypeTypes.Sequence) {
const newSeq = new asn1js.Sequence();
if ("value" in asn1SchemaValue.valueBlock && Array.isArray(asn1SchemaValue.valueBlock.value) && "value" in newSeq.valueBlock) {
newSeq.valueBlock.value = asn1SchemaValue.valueBlock.value;
return newSeq;
}
}
else if (schema.type === enums_1.AsnTypeTypes.Set) {
const newSet = new asn1js.Set();
if ("value" in asn1SchemaValue.valueBlock && Array.isArray(asn1SchemaValue.valueBlock.value) && "value" in newSet.valueBlock) {
newSet.valueBlock.value = asn1SchemaValue.valueBlock.value;
return newSet;
}
}
}
return asn1SchemaValue;
}
}
exports.AsnParser = AsnParser;
-165
View File
@@ -1,165 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnSchemaStorage = void 0;
const tslib_1 = require("tslib");
const asn1js = tslib_1.__importStar(require("asn1js"));
const enums_1 = require("./enums");
const helper_1 = require("./helper");
class AsnSchemaStorage {
items = new WeakMap();
has(target) {
return this.items.has(target);
}
get(target, checkSchema = false) {
const schema = this.items.get(target);
if (!schema) {
throw new Error(`Cannot get schema for '${target.prototype.constructor.name}' target`);
}
if (checkSchema && !schema.schema) {
throw new Error(`Schema '${target.prototype.constructor.name}' doesn't contain ASN.1 schema. Call 'AsnSchemaStorage.cache'.`);
}
return schema;
}
cache(target) {
const schema = this.get(target);
if (!schema.schema) {
schema.schema = this.create(target, true);
}
}
createDefault(target) {
const schema = {
type: enums_1.AsnTypeTypes.Sequence,
items: {},
};
const parentSchema = this.findParentSchema(target);
if (parentSchema) {
Object.assign(schema, parentSchema);
schema.items = Object.assign({}, schema.items, parentSchema.items);
}
return schema;
}
create(target, useNames) {
const schema = this.items.get(target) || this.createDefault(target);
const asn1Value = [];
for (const key in schema.items) {
const item = schema.items[key];
const name = useNames ? key : "";
let asn1Item;
if (typeof item.type === "number") {
const Asn1TypeName = enums_1.AsnPropTypes[item.type];
const Asn1Type = asn1js[Asn1TypeName];
if (!Asn1Type) {
throw new Error(`Cannot get ASN1 class by name '${Asn1TypeName}'`);
}
asn1Item = new Asn1Type({ name });
}
else if ((0, helper_1.isConvertible)(item.type)) {
const instance = new item.type();
asn1Item = instance.toSchema(name);
}
else if (item.optional) {
const itemSchema = this.get(item.type);
if (itemSchema.type === enums_1.AsnTypeTypes.Choice) {
asn1Item = new asn1js.Any({ name });
}
else {
asn1Item = this.create(item.type, false);
asn1Item.name = name;
}
}
else {
asn1Item = new asn1js.Any({ name });
}
const optional = !!item.optional || item.defaultValue !== undefined;
if (item.repeated) {
asn1Item.name = "";
const Container = item.repeated === "set" ? asn1js.Set : asn1js.Sequence;
asn1Item = new Container({
name: "",
value: [
new asn1js.Repeated({
name,
value: asn1Item,
}),
],
});
}
if (item.context !== null && item.context !== undefined) {
if (item.implicit) {
if (typeof item.type === "number" || (0, helper_1.isConvertible)(item.type)) {
const Container = item.repeated ? asn1js.Constructed : asn1js.Primitive;
asn1Value.push(new Container({
name,
optional,
idBlock: {
tagClass: 3,
tagNumber: item.context,
},
}));
}
else {
this.cache(item.type);
const isRepeated = !!item.repeated;
let value = !isRepeated ? this.get(item.type, true).schema : asn1Item;
value = "valueBlock" in value ? value.valueBlock.value : value.value;
asn1Value.push(new asn1js.Constructed({
name: !isRepeated ? name : "",
optional,
idBlock: {
tagClass: 3,
tagNumber: item.context,
},
value: value,
}));
}
}
else {
asn1Value.push(new asn1js.Constructed({
optional,
idBlock: {
tagClass: 3,
tagNumber: item.context,
},
value: [asn1Item],
}));
}
}
else {
asn1Item.optional = optional;
asn1Value.push(asn1Item);
}
}
switch (schema.type) {
case enums_1.AsnTypeTypes.Sequence:
return new asn1js.Sequence({
value: asn1Value,
name: "",
});
case enums_1.AsnTypeTypes.Set:
return new asn1js.Set({
value: asn1Value,
name: "",
});
case enums_1.AsnTypeTypes.Choice:
return new asn1js.Choice({
value: asn1Value,
name: "",
});
default:
throw new Error("Unsupported ASN1 type in use");
}
}
set(target, schema) {
this.items.set(target, schema);
return this;
}
findParentSchema(target) {
const parent = Object.getPrototypeOf(target);
if (parent) {
const schema = this.items.get(parent);
return schema || this.findParentSchema(parent);
}
return null;
}
}
exports.AsnSchemaStorage = AsnSchemaStorage;
-150
View File
@@ -1,150 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsnSerializer = void 0;
const tslib_1 = require("tslib");
const asn1js = tslib_1.__importStar(require("asn1js"));
const utils_1 = require("@peculiar/utils");
const converters = tslib_1.__importStar(require("./converters"));
const enums_1 = require("./enums");
const helper_1 = require("./helper");
const storage_1 = require("./storage");
class AsnSerializer {
static serialize(obj) {
if (obj instanceof asn1js.BaseBlock) {
return obj.toBER(false);
}
return this.toASN(obj).toBER(false);
}
static toASN(obj) {
if (obj && typeof obj === "object" && (0, helper_1.isConvertible)(obj)) {
return obj.toASN();
}
if (!(obj && typeof obj === "object")) {
throw new TypeError("Parameter 1 should be type of Object.");
}
const target = obj.constructor;
const schema = storage_1.schemaStorage.get(target);
storage_1.schemaStorage.cache(target);
let asn1Value = [];
if (schema.itemType) {
if (!Array.isArray(obj)) {
throw new TypeError("Parameter 1 should be type of Array.");
}
if (typeof schema.itemType === "number") {
const converter = converters.defaultConverter(schema.itemType);
if (!converter) {
throw new Error(`Cannot get default converter for array item of ${target.name} ASN1 schema`);
}
asn1Value = obj.map((o) => converter.toASN(o));
}
else {
asn1Value = obj.map((o) => this.toAsnItem({ type: schema.itemType }, "[]", target, o));
}
}
else {
for (const key in schema.items) {
const schemaItem = schema.items[key];
const objProp = obj[key];
if (objProp === undefined ||
schemaItem.defaultValue === objProp ||
(typeof schemaItem.defaultValue === "object" && typeof objProp === "object" && (0, helper_1.isArrayEqual)(this.serialize(schemaItem.defaultValue), this.serialize(objProp)))) {
continue;
}
const asn1Item = AsnSerializer.toAsnItem(schemaItem, key, target, objProp);
if (typeof schemaItem.context === "number") {
if (schemaItem.implicit) {
if (!schemaItem.repeated && (typeof schemaItem.type === "number" || (0, helper_1.isConvertible)(schemaItem.type))) {
const value = {};
value.valueHex = asn1Item instanceof asn1js.Null ? (0, utils_1.toArrayBuffer)(asn1Item.valueBeforeDecodeView) : asn1Item.valueBlock.toBER();
asn1Value.push(new asn1js.Primitive({
optional: schemaItem.optional,
idBlock: {
tagClass: 3,
tagNumber: schemaItem.context,
},
...value,
}));
}
else {
asn1Value.push(new asn1js.Constructed({
optional: schemaItem.optional,
idBlock: {
tagClass: 3,
tagNumber: schemaItem.context,
},
value: asn1Item.valueBlock.value,
}));
}
}
else {
asn1Value.push(new asn1js.Constructed({
optional: schemaItem.optional,
idBlock: {
tagClass: 3,
tagNumber: schemaItem.context,
},
value: [asn1Item],
}));
}
}
else if (schemaItem.repeated) {
asn1Value = asn1Value.concat(asn1Item);
}
else {
asn1Value.push(asn1Item);
}
}
}
let asnSchema;
switch (schema.type) {
case enums_1.AsnTypeTypes.Sequence:
asnSchema = new asn1js.Sequence({ value: asn1Value });
break;
case enums_1.AsnTypeTypes.Set:
asnSchema = new asn1js.Set({ value: asn1Value });
break;
case enums_1.AsnTypeTypes.Choice:
if (!asn1Value[0]) {
throw new Error(`Schema '${target.name}' has wrong data. Choice cannot be empty.`);
}
asnSchema = asn1Value[0];
break;
}
return asnSchema;
}
static toAsnItem(schemaItem, key, target, objProp) {
let asn1Item;
if (typeof schemaItem.type === "number") {
const converter = schemaItem.converter;
if (!converter) {
throw new Error(`Property '${key}' doesn't have converter for type ${enums_1.AsnPropTypes[schemaItem.type]} in schema '${target.name}'`);
}
if (schemaItem.repeated) {
if (!Array.isArray(objProp)) {
throw new TypeError("Parameter 'objProp' should be type of Array.");
}
const items = Array.from(objProp, (element) => converter.toASN(element));
const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
asn1Item = new Container({ value: items });
}
else {
asn1Item = converter.toASN(objProp);
}
}
else {
if (schemaItem.repeated) {
if (!Array.isArray(objProp)) {
throw new TypeError("Parameter 'objProp' should be type of Array.");
}
const items = Array.from(objProp, (element) => this.toASN(element));
const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
asn1Item = new Container({ value: items });
}
else {
asn1Item = this.toASN(objProp);
}
}
return asn1Item;
}
}
exports.AsnSerializer = AsnSerializer;
-5
View File
@@ -1,5 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.schemaStorage = void 0;
const schema_1 = require("./schema");
exports.schemaStorage = new schema_1.AsnSchemaStorage();
-2
View File
@@ -1,2 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
-71
View File
@@ -1,71 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BitString = void 0;
const tslib_1 = require("tslib");
const asn1js = tslib_1.__importStar(require("asn1js"));
const utils_1 = require("@peculiar/utils");
class BitString {
unusedBits = 0;
value = new ArrayBuffer(0);
constructor(params, unusedBits = 0) {
if (params) {
if (typeof params === "number") {
this.fromNumber(params);
}
else if ((0, utils_1.isBufferSource)(params)) {
this.unusedBits = unusedBits;
this.value = (0, utils_1.toArrayBuffer)(params);
}
else {
throw TypeError("Unsupported type of 'params' argument for BitString");
}
}
}
fromASN(asn) {
if (!(asn instanceof asn1js.BitString)) {
throw new TypeError("Argument 'asn' is not instance of ASN.1 BitString");
}
this.unusedBits = asn.valueBlock.unusedBits;
this.value = (0, utils_1.toArrayBuffer)(asn.valueBlock.valueHex);
return this;
}
toASN() {
return new asn1js.BitString({
unusedBits: this.unusedBits,
valueHex: this.value,
});
}
toSchema(name) {
return new asn1js.BitString({ name });
}
toNumber() {
let res = "";
const uintArray = new Uint8Array(this.value);
for (const octet of uintArray) {
res += octet.toString(2).padStart(8, "0");
}
res = res.split("").reverse().join("");
if (this.unusedBits) {
res = res.slice(this.unusedBits).padStart(this.unusedBits, "0");
}
return parseInt(res, 2);
}
fromNumber(value) {
let bits = value.toString(2);
const octetSize = (bits.length + 7) >> 3;
this.unusedBits = (octetSize << 3) - bits.length;
const octets = new Uint8Array(octetSize);
bits = bits
.padStart(octetSize << 3, "0")
.split("")
.reverse()
.join("");
let index = 0;
while (index < octetSize) {
octets[index] = parseInt(bits.slice(index << 3, (index << 3) + 8), 2);
index++;
}
this.value = octets.buffer;
}
}
exports.BitString = BitString;
-5
View File
@@ -1,5 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./bit_string"), exports);
tslib_1.__exportStar(require("./octet_string"), exports);
-45
View File
@@ -1,45 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OctetString = void 0;
const tslib_1 = require("tslib");
const asn1js = tslib_1.__importStar(require("asn1js"));
const utils_1 = require("@peculiar/utils");
class OctetString {
buffer;
get byteLength() {
return this.buffer.byteLength;
}
get byteOffset() {
return 0;
}
constructor(param) {
if (typeof param === "number") {
this.buffer = new ArrayBuffer(param);
}
else {
if ((0, utils_1.isBufferSource)(param)) {
this.buffer = (0, utils_1.toArrayBuffer)(param);
}
else if (Array.isArray(param)) {
this.buffer = new Uint8Array(param).buffer;
}
else {
this.buffer = new ArrayBuffer(0);
}
}
}
fromASN(asn) {
if (!(asn instanceof asn1js.OctetString)) {
throw new TypeError("Argument 'asn' is not instance of ASN.1 OctetString");
}
this.buffer = (0, utils_1.toArrayBuffer)(asn.valueBlock.valueHex);
return this;
}
toASN() {
return new asn1js.OctetString({ valueHex: this.buffer });
}
toSchema(name) {
return new asn1js.OctetString({ name });
}
}
exports.OctetString = OctetString;
-20
View File
@@ -1,20 +0,0 @@
import * as asn1js from "asn1js";
import { isBufferSource, toArrayBuffer } from "@peculiar/utils";
import { AsnParser } from "./parser";
import { AsnSerializer } from "./serializer";
export class AsnConvert {
static serialize(obj) {
return AsnSerializer.serialize(obj);
}
static parse(data, target, options) {
return AsnParser.parse(data, target, options);
}
static toString(data, options) {
const buf = isBufferSource(data) ? toArrayBuffer(data) : AsnConvert.serialize(data);
const asn = asn1js.fromBER(buf, options?.berOptions);
if (asn.offset === -1) {
throw new Error(`Cannot decode ASN.1 data. ${asn.result.error}`);
}
return asn.result.toString();
}
}
-143
View File
@@ -1,143 +0,0 @@
import * as asn1js from "asn1js";
import { toArrayBuffer } from "@peculiar/utils";
import { AsnPropTypes } from "./enums";
import { OctetString } from "./types/index";
export const AsnAnyConverter = {
fromASN: (value) => (value instanceof asn1js.Null ? null : toArrayBuffer(value.valueBeforeDecodeView)),
toASN: (value) => {
if (value === null) {
return new asn1js.Null();
}
const schema = asn1js.fromBER(value, { maxDepth: Infinity, maxNodes: Infinity, maxContentLength: Infinity });
if (schema.result.error) {
throw new Error(schema.result.error);
}
return schema.result;
},
};
export const AsnIntegerConverter = {
fromASN: (value) => value.valueBlock.valueHexView.byteLength >= 4
? value.valueBlock.toString()
: value.valueBlock.valueDec,
toASN: (value) => new asn1js.Integer({ value: +value }),
};
export const AsnEnumeratedConverter = {
fromASN: (value) => value.valueBlock.valueDec,
toASN: (value) => new asn1js.Enumerated({ value }),
};
export const AsnIntegerArrayBufferConverter = {
fromASN: (value) => toArrayBuffer(value.valueBlock.valueHexView),
toASN: (value) => new asn1js.Integer({ valueHex: value }),
};
export const AsnIntegerBigIntConverter = {
fromASN: (value) => value.toBigInt(),
toASN: (value) => asn1js.Integer.fromBigInt(value),
};
export const AsnBitStringConverter = {
fromASN: (value) => toArrayBuffer(value.valueBlock.valueHexView),
toASN: (value) => new asn1js.BitString({ valueHex: value }),
};
export const AsnObjectIdentifierConverter = {
fromASN: (value) => value.valueBlock.toString(),
toASN: (value) => new asn1js.ObjectIdentifier({ value }),
};
export const AsnRelativeObjectIdentifierConverter = {
fromASN: (value) => value.valueBlock.toString(),
toASN: (value) => new asn1js.RelativeObjectIdentifier({ value }),
};
export const AsnBooleanConverter = {
fromASN: (value) => value.valueBlock.value,
toASN: (value) => new asn1js.Boolean({ value }),
};
export const AsnOctetStringConverter = {
fromASN: (value) => toArrayBuffer(value.valueBlock.valueHexView),
toASN: (value) => new asn1js.OctetString({ valueHex: value }),
};
export const AsnConstructedOctetStringConverter = {
fromASN: (value) => new OctetString(value.getValue()),
toASN: (value) => value.toASN(),
};
function createStringConverter(Asn1Type) {
return {
fromASN: (value) => value.valueBlock.value,
toASN: (value) => new Asn1Type({ value }),
};
}
export const AsnUtf8StringConverter = createStringConverter(asn1js.Utf8String);
export const AsnBmpStringConverter = createStringConverter(asn1js.BmpString);
export const AsnUniversalStringConverter = createStringConverter(asn1js.UniversalString);
export const AsnNumericStringConverter = createStringConverter(asn1js.NumericString);
export const AsnPrintableStringConverter = createStringConverter(asn1js.PrintableString);
export const AsnTeletexStringConverter = createStringConverter(asn1js.TeletexString);
export const AsnVideotexStringConverter = createStringConverter(asn1js.VideotexString);
export const AsnIA5StringConverter = createStringConverter(asn1js.IA5String);
export const AsnGraphicStringConverter = createStringConverter(asn1js.GraphicString);
export const AsnVisibleStringConverter = createStringConverter(asn1js.VisibleString);
export const AsnGeneralStringConverter = createStringConverter(asn1js.GeneralString);
export const AsnCharacterStringConverter = createStringConverter(asn1js.CharacterString);
export const AsnUTCTimeConverter = {
fromASN: (value) => value.toDate(),
toASN: (value) => new asn1js.UTCTime({ valueDate: value }),
};
export const AsnGeneralizedTimeConverter = {
fromASN: (value) => value.toDate(),
toASN: (value) => new asn1js.GeneralizedTime({ valueDate: value }),
};
export const AsnNullConverter = {
fromASN: () => null,
toASN: () => {
return new asn1js.Null();
},
};
export function defaultConverter(type) {
switch (type) {
case AsnPropTypes.Any:
return AsnAnyConverter;
case AsnPropTypes.BitString:
return AsnBitStringConverter;
case AsnPropTypes.BmpString:
return AsnBmpStringConverter;
case AsnPropTypes.Boolean:
return AsnBooleanConverter;
case AsnPropTypes.CharacterString:
return AsnCharacterStringConverter;
case AsnPropTypes.Enumerated:
return AsnEnumeratedConverter;
case AsnPropTypes.GeneralString:
return AsnGeneralStringConverter;
case AsnPropTypes.GeneralizedTime:
return AsnGeneralizedTimeConverter;
case AsnPropTypes.GraphicString:
return AsnGraphicStringConverter;
case AsnPropTypes.IA5String:
return AsnIA5StringConverter;
case AsnPropTypes.Integer:
return AsnIntegerConverter;
case AsnPropTypes.Null:
return AsnNullConverter;
case AsnPropTypes.NumericString:
return AsnNumericStringConverter;
case AsnPropTypes.ObjectIdentifier:
return AsnObjectIdentifierConverter;
case AsnPropTypes.RelativeObjectIdentifier:
return AsnRelativeObjectIdentifierConverter;
case AsnPropTypes.OctetString:
return AsnOctetStringConverter;
case AsnPropTypes.PrintableString:
return AsnPrintableStringConverter;
case AsnPropTypes.TeletexString:
return AsnTeletexStringConverter;
case AsnPropTypes.UTCTime:
return AsnUTCTimeConverter;
case AsnPropTypes.UniversalString:
return AsnUniversalStringConverter;
case AsnPropTypes.Utf8String:
return AsnUtf8StringConverter;
case AsnPropTypes.VideotexString:
return AsnVideotexStringConverter;
case AsnPropTypes.VisibleString:
return AsnVisibleStringConverter;
default:
return null;
}
}
-43
View File
@@ -1,43 +0,0 @@
import * as converters from "./converters";
import { AsnTypeTypes } from "./enums";
import { schemaStorage } from "./storage";
export const AsnType = (options) => (target) => {
let schema;
if (!schemaStorage.has(target)) {
schema = schemaStorage.createDefault(target);
schemaStorage.set(target, schema);
}
else {
schema = schemaStorage.get(target);
}
Object.assign(schema, options);
};
export const AsnChoiceType = () => AsnType({ type: AsnTypeTypes.Choice });
export const AsnSetType = (options) => AsnType({
type: AsnTypeTypes.Set,
...options,
});
export const AsnSequenceType = (options) => AsnType({
type: AsnTypeTypes.Sequence,
...options,
});
export const AsnProp = (options) => (target, propertyKey) => {
let schema;
if (!schemaStorage.has(target.constructor)) {
schema = schemaStorage.createDefault(target.constructor);
schemaStorage.set(target.constructor, schema);
}
else {
schema = schemaStorage.get(target.constructor);
}
const copyOptions = Object.assign({}, options);
if (typeof copyOptions.type === "number" && !copyOptions.converter) {
const defaultConverter = converters.defaultConverter(options.type);
if (!defaultConverter) {
throw new Error(`Cannot get default converter for property '${propertyKey}' of ${target.constructor.name}`);
}
copyOptions.converter = defaultConverter;
}
copyOptions.raw = options.raw;
schema.items[propertyKey] = copyOptions;
};
-37
View File
@@ -1,37 +0,0 @@
export var AsnTypeTypes;
(function (AsnTypeTypes) {
AsnTypeTypes[AsnTypeTypes["Sequence"] = 0] = "Sequence";
AsnTypeTypes[AsnTypeTypes["Set"] = 1] = "Set";
AsnTypeTypes[AsnTypeTypes["Choice"] = 2] = "Choice";
})(AsnTypeTypes || (AsnTypeTypes = {}));
export var AsnPropTypes;
(function (AsnPropTypes) {
AsnPropTypes[AsnPropTypes["Any"] = 1] = "Any";
AsnPropTypes[AsnPropTypes["Boolean"] = 2] = "Boolean";
AsnPropTypes[AsnPropTypes["OctetString"] = 3] = "OctetString";
AsnPropTypes[AsnPropTypes["BitString"] = 4] = "BitString";
AsnPropTypes[AsnPropTypes["Integer"] = 5] = "Integer";
AsnPropTypes[AsnPropTypes["Enumerated"] = 6] = "Enumerated";
AsnPropTypes[AsnPropTypes["ObjectIdentifier"] = 7] = "ObjectIdentifier";
AsnPropTypes[AsnPropTypes["Utf8String"] = 8] = "Utf8String";
AsnPropTypes[AsnPropTypes["BmpString"] = 9] = "BmpString";
AsnPropTypes[AsnPropTypes["UniversalString"] = 10] = "UniversalString";
AsnPropTypes[AsnPropTypes["NumericString"] = 11] = "NumericString";
AsnPropTypes[AsnPropTypes["PrintableString"] = 12] = "PrintableString";
AsnPropTypes[AsnPropTypes["TeletexString"] = 13] = "TeletexString";
AsnPropTypes[AsnPropTypes["VideotexString"] = 14] = "VideotexString";
AsnPropTypes[AsnPropTypes["IA5String"] = 15] = "IA5String";
AsnPropTypes[AsnPropTypes["GraphicString"] = 16] = "GraphicString";
AsnPropTypes[AsnPropTypes["VisibleString"] = 17] = "VisibleString";
AsnPropTypes[AsnPropTypes["GeneralString"] = 18] = "GeneralString";
AsnPropTypes[AsnPropTypes["CharacterString"] = 19] = "CharacterString";
AsnPropTypes[AsnPropTypes["UTCTime"] = 20] = "UTCTime";
AsnPropTypes[AsnPropTypes["GeneralizedTime"] = 21] = "GeneralizedTime";
AsnPropTypes[AsnPropTypes["DATE"] = 22] = "DATE";
AsnPropTypes[AsnPropTypes["TimeOfDay"] = 23] = "TimeOfDay";
AsnPropTypes[AsnPropTypes["DateTime"] = 24] = "DateTime";
AsnPropTypes[AsnPropTypes["Duration"] = 25] = "Duration";
AsnPropTypes[AsnPropTypes["TIME"] = 26] = "TIME";
AsnPropTypes[AsnPropTypes["Null"] = 27] = "Null";
AsnPropTypes[AsnPropTypes["RelativeObjectIdentifier"] = 28] = "RelativeObjectIdentifier";
})(AsnPropTypes || (AsnPropTypes = {}));
-1
View File
@@ -1 +0,0 @@
export * from "./schema_validation";
@@ -1,3 +0,0 @@
export class AsnSchemaValidationError extends Error {
schemas = [];
}
-39
View File
@@ -1,39 +0,0 @@
export function isConvertible(target) {
if (typeof target === "function" && target.prototype) {
if (target.prototype.toASN && target.prototype.fromASN) {
return true;
}
else {
return isConvertible(target.prototype);
}
}
else {
return !!(target && typeof target === "object" && "toASN" in target && "fromASN" in target);
}
}
export function isTypeOfArray(target) {
if (target) {
const proto = Object.getPrototypeOf(target);
if (proto?.prototype?.constructor === Array) {
return true;
}
return isTypeOfArray(proto);
}
return false;
}
export function isArrayEqual(bytes1, bytes2) {
if (!(bytes1 && bytes2)) {
return false;
}
if (bytes1.byteLength !== bytes2.byteLength) {
return false;
}
const b1 = new Uint8Array(bytes1);
const b2 = new Uint8Array(bytes2);
for (let i = 0; i < bytes1.byteLength; i++) {
if (b1[i] !== b2[i]) {
return false;
}
}
return true;
}
-9
View File
@@ -1,9 +0,0 @@
export * from "./converters";
export * from "./types/index";
export { AsnProp, AsnType, AsnChoiceType, AsnSequenceType, AsnSetType } from "./decorators";
export { AsnTypeTypes, AsnPropTypes } from "./enums";
export { AsnParser } from "./parser";
export { AsnSerializer } from "./serializer";
export * from "./errors";
export * from "./objects";
export * from "./convert";
-13
View File
@@ -1,13 +0,0 @@
export class AsnArray extends Array {
constructor(items = []) {
if (typeof items === "number") {
super(items);
}
else {
super();
for (const item of items) {
this.push(item);
}
}
}
}
-308
View File
@@ -1,308 +0,0 @@
import * as asn1js from "asn1js";
import { toArrayBuffer } from "@peculiar/utils";
import { AsnPropTypes, AsnTypeTypes } from "./enums";
import * as converters from "./converters";
import { AsnSchemaValidationError } from "./errors";
import { isConvertible, isTypeOfArray } from "./helper";
import { schemaStorage } from "./storage";
export class AsnParser {
static parse(data, target, options) {
const asn1Parsed = asn1js.fromBER(toArrayBuffer(data), options?.berOptions);
if (asn1Parsed.result.error) {
throw new Error(asn1Parsed.result.error);
}
const res = this.fromASN(asn1Parsed.result, target, options);
return res;
}
static fromASN(asn1Schema, target, options) {
try {
if (isConvertible(target)) {
const value = new target();
return value.fromASN(asn1Schema);
}
const schema = schemaStorage.get(target);
schemaStorage.cache(target);
let targetSchema = schema.schema;
const choiceResult = this.handleChoiceTypes(asn1Schema, schema, target, targetSchema, options);
if (choiceResult?.result) {
return choiceResult.result;
}
if (choiceResult?.targetSchema) {
targetSchema = choiceResult.targetSchema;
}
const sequenceResult = this.handleSequenceTypes(asn1Schema, schema, target, targetSchema);
const res = new target();
if (isTypeOfArray(target)) {
return this.handleArrayTypes(asn1Schema, schema, target, options);
}
this.processSchemaItems(schema, sequenceResult, res, options);
return res;
}
catch (error) {
if (error instanceof AsnSchemaValidationError) {
error.schemas.push(target.name);
}
throw error;
}
}
static handleChoiceTypes(asn1Schema, schema, target, targetSchema, options) {
if (asn1Schema.constructor === asn1js.Constructed && schema.type === AsnTypeTypes.Choice && asn1Schema.idBlock.tagClass === 3) {
for (const key in schema.items) {
const schemaItem = schema.items[key];
if (schemaItem.context === asn1Schema.idBlock.tagNumber && schemaItem.implicit) {
if (typeof schemaItem.type === "function" && schemaStorage.has(schemaItem.type)) {
const fieldSchema = schemaStorage.get(schemaItem.type);
if (fieldSchema && fieldSchema.type === AsnTypeTypes.Sequence) {
const newSeq = new asn1js.Sequence();
if ("value" in asn1Schema.valueBlock && Array.isArray(asn1Schema.valueBlock.value) && "value" in newSeq.valueBlock) {
newSeq.valueBlock.value = asn1Schema.valueBlock.value;
const fieldValue = this.fromASN(newSeq, schemaItem.type, options);
const res = new target();
res[key] = fieldValue;
return { result: res };
}
}
}
}
}
}
else if (asn1Schema.constructor === asn1js.Constructed && schema.type !== AsnTypeTypes.Choice) {
const newTargetSchema = new asn1js.Constructed({
idBlock: {
tagClass: 3,
tagNumber: asn1Schema.idBlock.tagNumber,
},
value: schema.schema.valueBlock.value,
});
for (const key in schema.items) {
delete asn1Schema[key];
}
return { targetSchema: newTargetSchema };
}
return null;
}
static handleSequenceTypes(asn1Schema, schema, target, targetSchema) {
if (schema.type === AsnTypeTypes.Sequence) {
const asn1ComparedSchema = asn1js.compareSchema({}, asn1Schema, targetSchema);
if (!asn1ComparedSchema.verified) {
throw new AsnSchemaValidationError(`Data does not match to ${target.name} ASN1 schema.${asn1ComparedSchema.result.error ? ` ${asn1ComparedSchema.result.error}` : ""}`);
}
return asn1ComparedSchema;
}
else {
const asn1ComparedSchema = asn1js.compareSchema({}, asn1Schema, targetSchema);
if (!asn1ComparedSchema.verified) {
throw new AsnSchemaValidationError(`Data does not match to ${target.name} ASN1 schema.${asn1ComparedSchema.result.error ? ` ${asn1ComparedSchema.result.error}` : ""}`);
}
return asn1ComparedSchema;
}
}
static processRepeatedField(asn1Elements, asn1Index, schemaItem) {
let elementsToProcess = asn1Elements.slice(asn1Index);
if (elementsToProcess.length === 1 && elementsToProcess[0].constructor.name === "Sequence") {
const seq = elementsToProcess[0];
if (seq.valueBlock && seq.valueBlock.value && Array.isArray(seq.valueBlock.value)) {
elementsToProcess = seq.valueBlock.value;
}
}
if (typeof schemaItem.type === "number") {
const converter = converters.defaultConverter(schemaItem.type);
if (!converter)
throw new Error(`No converter for ASN.1 type ${schemaItem.type}`);
return elementsToProcess
.filter((el) => el && el.valueBlock)
.map((el) => {
try {
return converter.fromASN(el);
}
catch {
return undefined;
}
})
.filter((v) => v !== undefined);
}
else {
return elementsToProcess
.filter((el) => el && el.valueBlock)
.map((el) => {
try {
return this.fromASN(el, schemaItem.type);
}
catch {
return undefined;
}
})
.filter((v) => v !== undefined);
}
}
static processPrimitiveField(asn1Element, schemaItem) {
const converter = converters.defaultConverter(schemaItem.type);
if (!converter)
throw new Error(`No converter for ASN.1 type ${schemaItem.type}`);
return converter.fromASN(asn1Element);
}
static isOptionalChoiceField(schemaItem) {
return (schemaItem.optional &&
typeof schemaItem.type === "function" &&
schemaStorage.has(schemaItem.type) &&
schemaStorage.get(schemaItem.type).type === AsnTypeTypes.Choice);
}
static processOptionalChoiceField(asn1Element, schemaItem) {
try {
const value = this.fromASN(asn1Element, schemaItem.type);
return {
processed: true,
value,
};
}
catch (err) {
if (err instanceof AsnSchemaValidationError && /Wrong values for Choice type/.test(err.message)) {
return { processed: false };
}
throw err;
}
}
static handleArrayTypes(asn1Schema, schema, target, options) {
if (!("value" in asn1Schema.valueBlock && Array.isArray(asn1Schema.valueBlock.value))) {
throw new Error("Cannot get items from the ASN.1 parsed value. ASN.1 object is not constructed.");
}
const itemType = schema.itemType;
if (typeof itemType === "number") {
const converter = converters.defaultConverter(itemType);
if (!converter) {
throw new Error(`Cannot get default converter for array item of ${target.name} ASN1 schema`);
}
return target.from(asn1Schema.valueBlock.value, (element) => converter.fromASN(element));
}
else {
return target.from(asn1Schema.valueBlock.value, (element) => this.fromASN(element, itemType, options));
}
}
static processSchemaItems(schema, asn1ComparedSchema, res, options) {
for (const key in schema.items) {
const asn1SchemaValue = asn1ComparedSchema.result[key];
if (!asn1SchemaValue) {
continue;
}
const schemaItem = schema.items[key];
const schemaItemType = schemaItem.type;
let parsedValue;
if (typeof schemaItemType === "number" || isConvertible(schemaItemType)) {
parsedValue = this.processPrimitiveSchemaItem(asn1SchemaValue, schemaItem, schemaItemType, options);
}
else {
parsedValue = this.processComplexSchemaItem(asn1SchemaValue, schemaItem, schemaItemType, options);
}
if (parsedValue && typeof parsedValue === "object" && "value" in parsedValue && "raw" in parsedValue) {
res[key] = parsedValue.value;
res[`${key}Raw`] = parsedValue.raw;
}
else {
res[key] = parsedValue;
}
}
}
static processPrimitiveSchemaItem(asn1SchemaValue, schemaItem, schemaItemType, options) {
const converter = schemaItem.converter ?? (isConvertible(schemaItemType) ? new schemaItemType() : null);
if (!converter) {
throw new Error("Converter is empty");
}
if (schemaItem.repeated) {
return this.processRepeatedPrimitiveItem(asn1SchemaValue, schemaItem, converter, options);
}
else {
return this.processSinglePrimitiveItem(asn1SchemaValue, schemaItem, schemaItemType, converter, options);
}
}
static processRepeatedPrimitiveItem(asn1SchemaValue, schemaItem, converter, options) {
if (schemaItem.implicit) {
const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
const newItem = new Container();
newItem.valueBlock = asn1SchemaValue.valueBlock;
const newItemAsn = asn1js.fromBER(newItem.toBER(false), options?.berOptions);
if (newItemAsn.offset === -1) {
throw new Error(`Cannot parse the child item. ${newItemAsn.result.error}`);
}
if (!("value" in newItemAsn.result.valueBlock && Array.isArray(newItemAsn.result.valueBlock.value))) {
throw new Error("Cannot get items from the ASN.1 parsed value. ASN.1 object is not constructed.");
}
const value = newItemAsn.result.valueBlock.value;
return Array.from(value, (element) => converter.fromASN(element));
}
else {
return Array.from(asn1SchemaValue, (element) => converter.fromASN(element));
}
}
static processSinglePrimitiveItem(asn1SchemaValue, schemaItem, schemaItemType, converter, options) {
let value = asn1SchemaValue;
if (schemaItem.implicit) {
let newItem;
if (isConvertible(schemaItemType)) {
newItem = new schemaItemType().toSchema("");
}
else {
const Asn1TypeName = AsnPropTypes[schemaItemType];
const Asn1Type = asn1js[Asn1TypeName];
if (!Asn1Type) {
throw new Error(`Cannot get '${Asn1TypeName}' class from asn1js module`);
}
newItem = new Asn1Type();
}
newItem.valueBlock = value.valueBlock;
value = asn1js.fromBER(newItem.toBER(false), options?.berOptions).result;
}
return converter.fromASN(value);
}
static processComplexSchemaItem(asn1SchemaValue, schemaItem, schemaItemType, options) {
if (schemaItem.repeated) {
if (!Array.isArray(asn1SchemaValue)) {
throw new Error("Cannot get list of items from the ASN.1 parsed value. ASN.1 value should be iterable.");
}
return Array.from(asn1SchemaValue, (element) => this.fromASN(element, schemaItemType, options));
}
else {
const valueToProcess = this.handleImplicitTagging(asn1SchemaValue, schemaItem, schemaItemType);
if (this.isOptionalChoiceField(schemaItem)) {
try {
return this.fromASN(valueToProcess, schemaItemType, options);
}
catch (err) {
if (err instanceof AsnSchemaValidationError && /Wrong values for Choice type/.test(err.message)) {
return undefined;
}
throw err;
}
}
else {
const parsedValue = this.fromASN(valueToProcess, schemaItemType, options);
if (schemaItem.raw) {
return {
value: parsedValue,
raw: asn1SchemaValue.valueBeforeDecodeView,
};
}
return parsedValue;
}
}
}
static handleImplicitTagging(asn1SchemaValue, schemaItem, schemaItemType) {
if (schemaItem.implicit && typeof schemaItem.context === "number") {
const schema = schemaStorage.get(schemaItemType);
if (schema.type === AsnTypeTypes.Sequence) {
const newSeq = new asn1js.Sequence();
if ("value" in asn1SchemaValue.valueBlock && Array.isArray(asn1SchemaValue.valueBlock.value) && "value" in newSeq.valueBlock) {
newSeq.valueBlock.value = asn1SchemaValue.valueBlock.value;
return newSeq;
}
}
else if (schema.type === AsnTypeTypes.Set) {
const newSet = new asn1js.Set();
if ("value" in asn1SchemaValue.valueBlock && Array.isArray(asn1SchemaValue.valueBlock.value) && "value" in newSet.valueBlock) {
newSet.valueBlock.value = asn1SchemaValue.valueBlock.value;
return newSet;
}
}
}
return asn1SchemaValue;
}
}
-160
View File
@@ -1,160 +0,0 @@
import * as asn1js from "asn1js";
import { AsnPropTypes, AsnTypeTypes } from "./enums";
import { isConvertible } from "./helper";
export class AsnSchemaStorage {
items = new WeakMap();
has(target) {
return this.items.has(target);
}
get(target, checkSchema = false) {
const schema = this.items.get(target);
if (!schema) {
throw new Error(`Cannot get schema for '${target.prototype.constructor.name}' target`);
}
if (checkSchema && !schema.schema) {
throw new Error(`Schema '${target.prototype.constructor.name}' doesn't contain ASN.1 schema. Call 'AsnSchemaStorage.cache'.`);
}
return schema;
}
cache(target) {
const schema = this.get(target);
if (!schema.schema) {
schema.schema = this.create(target, true);
}
}
createDefault(target) {
const schema = {
type: AsnTypeTypes.Sequence,
items: {},
};
const parentSchema = this.findParentSchema(target);
if (parentSchema) {
Object.assign(schema, parentSchema);
schema.items = Object.assign({}, schema.items, parentSchema.items);
}
return schema;
}
create(target, useNames) {
const schema = this.items.get(target) || this.createDefault(target);
const asn1Value = [];
for (const key in schema.items) {
const item = schema.items[key];
const name = useNames ? key : "";
let asn1Item;
if (typeof item.type === "number") {
const Asn1TypeName = AsnPropTypes[item.type];
const Asn1Type = asn1js[Asn1TypeName];
if (!Asn1Type) {
throw new Error(`Cannot get ASN1 class by name '${Asn1TypeName}'`);
}
asn1Item = new Asn1Type({ name });
}
else if (isConvertible(item.type)) {
const instance = new item.type();
asn1Item = instance.toSchema(name);
}
else if (item.optional) {
const itemSchema = this.get(item.type);
if (itemSchema.type === AsnTypeTypes.Choice) {
asn1Item = new asn1js.Any({ name });
}
else {
asn1Item = this.create(item.type, false);
asn1Item.name = name;
}
}
else {
asn1Item = new asn1js.Any({ name });
}
const optional = !!item.optional || item.defaultValue !== undefined;
if (item.repeated) {
asn1Item.name = "";
const Container = item.repeated === "set" ? asn1js.Set : asn1js.Sequence;
asn1Item = new Container({
name: "",
value: [
new asn1js.Repeated({
name,
value: asn1Item,
}),
],
});
}
if (item.context !== null && item.context !== undefined) {
if (item.implicit) {
if (typeof item.type === "number" || isConvertible(item.type)) {
const Container = item.repeated ? asn1js.Constructed : asn1js.Primitive;
asn1Value.push(new Container({
name,
optional,
idBlock: {
tagClass: 3,
tagNumber: item.context,
},
}));
}
else {
this.cache(item.type);
const isRepeated = !!item.repeated;
let value = !isRepeated ? this.get(item.type, true).schema : asn1Item;
value = "valueBlock" in value ? value.valueBlock.value : value.value;
asn1Value.push(new asn1js.Constructed({
name: !isRepeated ? name : "",
optional,
idBlock: {
tagClass: 3,
tagNumber: item.context,
},
value: value,
}));
}
}
else {
asn1Value.push(new asn1js.Constructed({
optional,
idBlock: {
tagClass: 3,
tagNumber: item.context,
},
value: [asn1Item],
}));
}
}
else {
asn1Item.optional = optional;
asn1Value.push(asn1Item);
}
}
switch (schema.type) {
case AsnTypeTypes.Sequence:
return new asn1js.Sequence({
value: asn1Value,
name: "",
});
case AsnTypeTypes.Set:
return new asn1js.Set({
value: asn1Value,
name: "",
});
case AsnTypeTypes.Choice:
return new asn1js.Choice({
value: asn1Value,
name: "",
});
default:
throw new Error("Unsupported ASN1 type in use");
}
}
set(target, schema) {
this.items.set(target, schema);
return this;
}
findParentSchema(target) {
const parent = Object.getPrototypeOf(target);
if (parent) {
const schema = this.items.get(parent);
return schema || this.findParentSchema(parent);
}
return null;
}
}
-145
View File
@@ -1,145 +0,0 @@
import * as asn1js from "asn1js";
import { toArrayBuffer } from "@peculiar/utils";
import * as converters from "./converters";
import { AsnPropTypes, AsnTypeTypes } from "./enums";
import { isConvertible, isArrayEqual } from "./helper";
import { schemaStorage } from "./storage";
export class AsnSerializer {
static serialize(obj) {
if (obj instanceof asn1js.BaseBlock) {
return obj.toBER(false);
}
return this.toASN(obj).toBER(false);
}
static toASN(obj) {
if (obj && typeof obj === "object" && isConvertible(obj)) {
return obj.toASN();
}
if (!(obj && typeof obj === "object")) {
throw new TypeError("Parameter 1 should be type of Object.");
}
const target = obj.constructor;
const schema = schemaStorage.get(target);
schemaStorage.cache(target);
let asn1Value = [];
if (schema.itemType) {
if (!Array.isArray(obj)) {
throw new TypeError("Parameter 1 should be type of Array.");
}
if (typeof schema.itemType === "number") {
const converter = converters.defaultConverter(schema.itemType);
if (!converter) {
throw new Error(`Cannot get default converter for array item of ${target.name} ASN1 schema`);
}
asn1Value = obj.map((o) => converter.toASN(o));
}
else {
asn1Value = obj.map((o) => this.toAsnItem({ type: schema.itemType }, "[]", target, o));
}
}
else {
for (const key in schema.items) {
const schemaItem = schema.items[key];
const objProp = obj[key];
if (objProp === undefined ||
schemaItem.defaultValue === objProp ||
(typeof schemaItem.defaultValue === "object" && typeof objProp === "object" && isArrayEqual(this.serialize(schemaItem.defaultValue), this.serialize(objProp)))) {
continue;
}
const asn1Item = AsnSerializer.toAsnItem(schemaItem, key, target, objProp);
if (typeof schemaItem.context === "number") {
if (schemaItem.implicit) {
if (!schemaItem.repeated && (typeof schemaItem.type === "number" || isConvertible(schemaItem.type))) {
const value = {};
value.valueHex = asn1Item instanceof asn1js.Null ? toArrayBuffer(asn1Item.valueBeforeDecodeView) : asn1Item.valueBlock.toBER();
asn1Value.push(new asn1js.Primitive({
optional: schemaItem.optional,
idBlock: {
tagClass: 3,
tagNumber: schemaItem.context,
},
...value,
}));
}
else {
asn1Value.push(new asn1js.Constructed({
optional: schemaItem.optional,
idBlock: {
tagClass: 3,
tagNumber: schemaItem.context,
},
value: asn1Item.valueBlock.value,
}));
}
}
else {
asn1Value.push(new asn1js.Constructed({
optional: schemaItem.optional,
idBlock: {
tagClass: 3,
tagNumber: schemaItem.context,
},
value: [asn1Item],
}));
}
}
else if (schemaItem.repeated) {
asn1Value = asn1Value.concat(asn1Item);
}
else {
asn1Value.push(asn1Item);
}
}
}
let asnSchema;
switch (schema.type) {
case AsnTypeTypes.Sequence:
asnSchema = new asn1js.Sequence({ value: asn1Value });
break;
case AsnTypeTypes.Set:
asnSchema = new asn1js.Set({ value: asn1Value });
break;
case AsnTypeTypes.Choice:
if (!asn1Value[0]) {
throw new Error(`Schema '${target.name}' has wrong data. Choice cannot be empty.`);
}
asnSchema = asn1Value[0];
break;
}
return asnSchema;
}
static toAsnItem(schemaItem, key, target, objProp) {
let asn1Item;
if (typeof schemaItem.type === "number") {
const converter = schemaItem.converter;
if (!converter) {
throw new Error(`Property '${key}' doesn't have converter for type ${AsnPropTypes[schemaItem.type]} in schema '${target.name}'`);
}
if (schemaItem.repeated) {
if (!Array.isArray(objProp)) {
throw new TypeError("Parameter 'objProp' should be type of Array.");
}
const items = Array.from(objProp, (element) => converter.toASN(element));
const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
asn1Item = new Container({ value: items });
}
else {
asn1Item = converter.toASN(objProp);
}
}
else {
if (schemaItem.repeated) {
if (!Array.isArray(objProp)) {
throw new TypeError("Parameter 'objProp' should be type of Array.");
}
const items = Array.from(objProp, (element) => this.toASN(element));
const Container = schemaItem.repeated === "sequence" ? asn1js.Sequence : asn1js.Set;
asn1Item = new Container({ value: items });
}
else {
asn1Item = this.toASN(objProp);
}
}
return asn1Item;
}
}
-2
View File
@@ -1,2 +0,0 @@
import { AsnSchemaStorage } from "./schema";
export const schemaStorage = new AsnSchemaStorage();
-1
View File
@@ -1 +0,0 @@
export {};
-66
View File
@@ -1,66 +0,0 @@
import * as asn1js from "asn1js";
import { isBufferSource, toArrayBuffer } from "@peculiar/utils";
export class BitString {
unusedBits = 0;
value = new ArrayBuffer(0);
constructor(params, unusedBits = 0) {
if (params) {
if (typeof params === "number") {
this.fromNumber(params);
}
else if (isBufferSource(params)) {
this.unusedBits = unusedBits;
this.value = toArrayBuffer(params);
}
else {
throw TypeError("Unsupported type of 'params' argument for BitString");
}
}
}
fromASN(asn) {
if (!(asn instanceof asn1js.BitString)) {
throw new TypeError("Argument 'asn' is not instance of ASN.1 BitString");
}
this.unusedBits = asn.valueBlock.unusedBits;
this.value = toArrayBuffer(asn.valueBlock.valueHex);
return this;
}
toASN() {
return new asn1js.BitString({
unusedBits: this.unusedBits,
valueHex: this.value,
});
}
toSchema(name) {
return new asn1js.BitString({ name });
}
toNumber() {
let res = "";
const uintArray = new Uint8Array(this.value);
for (const octet of uintArray) {
res += octet.toString(2).padStart(8, "0");
}
res = res.split("").reverse().join("");
if (this.unusedBits) {
res = res.slice(this.unusedBits).padStart(this.unusedBits, "0");
}
return parseInt(res, 2);
}
fromNumber(value) {
let bits = value.toString(2);
const octetSize = (bits.length + 7) >> 3;
this.unusedBits = (octetSize << 3) - bits.length;
const octets = new Uint8Array(octetSize);
bits = bits
.padStart(octetSize << 3, "0")
.split("")
.reverse()
.join("");
let index = 0;
while (index < octetSize) {
octets[index] = parseInt(bits.slice(index << 3, (index << 3) + 8), 2);
index++;
}
this.value = octets.buffer;
}
}
-2
View File
@@ -1,2 +0,0 @@
export * from "./bit_string";
export * from "./octet_string";
-40
View File
@@ -1,40 +0,0 @@
import * as asn1js from "asn1js";
import { isBufferSource, toArrayBuffer } from "@peculiar/utils";
export class OctetString {
buffer;
get byteLength() {
return this.buffer.byteLength;
}
get byteOffset() {
return 0;
}
constructor(param) {
if (typeof param === "number") {
this.buffer = new ArrayBuffer(param);
}
else {
if (isBufferSource(param)) {
this.buffer = toArrayBuffer(param);
}
else if (Array.isArray(param)) {
this.buffer = new Uint8Array(param).buffer;
}
else {
this.buffer = new ArrayBuffer(0);
}
}
}
fromASN(asn) {
if (!(asn instanceof asn1js.OctetString)) {
throw new TypeError("Argument 'asn' is not instance of ASN.1 OctetString");
}
this.buffer = toArrayBuffer(asn.valueBlock.valueHex);
return this;
}
toASN() {
return new asn1js.OctetString({ valueHex: this.buffer });
}
toSchema(name) {
return new asn1js.OctetString({ name });
}
}
-20
View File
@@ -1,20 +0,0 @@
import { type BufferSourceLike } from "@peculiar/utils";
import { IEmptyConstructor, IAsnParseOptions } from "./types";
export declare class AsnConvert {
static serialize(obj: unknown): ArrayBuffer;
static parse<T>(data: BufferSourceLike, target: IEmptyConstructor<T>, options?: IAsnParseOptions): T;
/**
* Returns a string representation of an ASN.1 encoded data
* @param data ASN.1 encoded buffer source
* @param options Optional parsing options forwarded to `asn1js.fromBER`
* @returns String representation of ASN.1 structure
*/
static toString(data: BufferSourceLike, options?: IAsnParseOptions): string;
/**
* Returns a string representation of an ASN.1 schema
* @param obj Object which can be serialized to ASN.1 schema
* @param options Optional parsing options forwarded to `asn1js.fromBER`
* @returns String representation of ASN.1 structure
*/
static toString(obj: unknown, options?: IAsnParseOptions): string;
}
-117
View File
@@ -1,117 +0,0 @@
import * as asn1js from "asn1js";
import { AnyConverterType, IAsnConverter, IntegerConverterType } from "./types";
import { AsnPropTypes } from "./enums";
import { OctetString } from "./types/index";
/**
* NOTE: Converter MUST have name Asn<Asn1PropType.name>Converter.
* Asn1Prop decorator link custom converters by name of the Asn1PropType
*/
/**
* ASN.1 ANY converter
*/
export declare const AsnAnyConverter: IAsnConverter<AnyConverterType>;
/**
* ASN.1 INTEGER to Number/String converter
*/
export declare const AsnIntegerConverter: IAsnConverter<IntegerConverterType, asn1js.Integer>;
/**
* ASN.1 ENUMERATED converter
*/
export declare const AsnEnumeratedConverter: IAsnConverter<number, asn1js.Enumerated>;
/**
* ASN.1 INTEGER to ArrayBuffer converter
*/
export declare const AsnIntegerArrayBufferConverter: IAsnConverter<ArrayBuffer, asn1js.Integer>;
/**
* ASN.1 INTEGER to BigInt converter
*/
export declare const AsnIntegerBigIntConverter: IAsnConverter<bigint, asn1js.Integer>;
/**
* ASN.1 BIT STRING converter
*/
export declare const AsnBitStringConverter: IAsnConverter<ArrayBuffer, asn1js.BitString>;
/**
* ASN.1 OBJECT IDENTIFIER converter
*/
export declare const AsnObjectIdentifierConverter: IAsnConverter<string, asn1js.ObjectIdentifier>;
/**
* ASN.1 RELATIVE OBJECT IDENTIFIER converter
*/
export declare const AsnRelativeObjectIdentifierConverter: IAsnConverter<string, asn1js.RelativeObjectIdentifier>;
/**
* ASN.1 BOOLEAN converter
*/
export declare const AsnBooleanConverter: IAsnConverter<boolean, asn1js.Boolean>;
/**
* ASN.1 OCTET_STRING converter
*/
export declare const AsnOctetStringConverter: IAsnConverter<ArrayBuffer, asn1js.OctetString>;
/**
* ASN.1 OCTET_STRING converter to OctetString class
*/
export declare const AsnConstructedOctetStringConverter: IAsnConverter<OctetString, asn1js.OctetString>;
/**
* ASN.1 UTF8_STRING converter
*/
export declare const AsnUtf8StringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 BPM STRING converter
*/
export declare const AsnBmpStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 UNIVERSAL STRING converter
*/
export declare const AsnUniversalStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 NUMERIC STRING converter
*/
export declare const AsnNumericStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 PRINTABLE STRING converter
*/
export declare const AsnPrintableStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 TELETEX STRING converter
*/
export declare const AsnTeletexStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 VIDEOTEX STRING converter
*/
export declare const AsnVideotexStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 IA5 STRING converter
*/
export declare const AsnIA5StringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 GRAPHIC STRING converter
*/
export declare const AsnGraphicStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 VISIBLE STRING converter
*/
export declare const AsnVisibleStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 GENERAL STRING converter
*/
export declare const AsnGeneralStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 CHARACTER STRING converter
*/
export declare const AsnCharacterStringConverter: IAsnConverter<string, asn1js.AsnType>;
/**
* ASN.1 UTCTime converter
*/
export declare const AsnUTCTimeConverter: IAsnConverter<Date, asn1js.UTCTime>;
/**
* ASN.1 GeneralizedTime converter
*/
export declare const AsnGeneralizedTimeConverter: IAsnConverter<Date, asn1js.GeneralizedTime>;
/**
* ASN.1 ANY converter
*/
export declare const AsnNullConverter: IAsnConverter<null, asn1js.Null>;
/**
* Returns default converter for specified type
* @param type
*/
export declare function defaultConverter(type: AsnPropTypes): IAsnConverter | null;
-36
View File
@@ -1,36 +0,0 @@
import { AsnPropTypes, AsnTypeTypes } from "./enums";
import { IAsnConverter, IEmptyConstructor } from "./types";
export type AsnItemType<T = unknown> = AsnPropTypes | IEmptyConstructor<T>;
export interface IAsn1TypeOptions {
type: AsnTypeTypes;
itemType?: AsnItemType;
}
export type AsnRepeatTypeString = "sequence" | "set";
export type AsnRepeatType = AsnRepeatTypeString;
export interface IAsn1PropOptions {
type: AsnItemType;
optional?: boolean;
defaultValue?: unknown;
context?: number;
implicit?: boolean;
converter?: IAsnConverter;
repeated?: AsnRepeatType;
/**
* If true, the raw ASN.1 encoded bytes of the property will be stored in a separate property
* with 'Raw' suffix.
*/
raw?: boolean;
}
export type AsnTypeDecorator = (target: IEmptyConstructor) => void;
export declare const AsnType: (options: IAsn1TypeOptions) => AsnTypeDecorator;
export declare const AsnChoiceType: () => AsnTypeDecorator;
export interface IAsn1SetOptions {
itemType: AsnItemType;
}
export declare const AsnSetType: (options: IAsn1SetOptions) => AsnTypeDecorator;
export interface IAsn1SequenceOptions {
itemType?: AsnItemType;
}
export declare const AsnSequenceType: (options: IAsn1SequenceOptions) => AsnTypeDecorator;
export type AsnPropDecorator = (target: object, propertyKey: string) => void;
export declare const AsnProp: (options: IAsn1PropOptions) => AsnPropDecorator;
-41
View File
@@ -1,41 +0,0 @@
/**
* ASN.1 types for classes
*/
export declare enum AsnTypeTypes {
Sequence = 0,
Set = 1,
Choice = 2
}
/**
* ASN.1 types for properties
*/
export declare enum AsnPropTypes {
Any = 1,
Boolean = 2,
OctetString = 3,
BitString = 4,
Integer = 5,
Enumerated = 6,
ObjectIdentifier = 7,
Utf8String = 8,
BmpString = 9,
UniversalString = 10,
NumericString = 11,
PrintableString = 12,
TeletexString = 13,
VideotexString = 14,
IA5String = 15,
GraphicString = 16,
VisibleString = 17,
GeneralString = 18,
CharacterString = 19,
UTCTime = 20,
GeneralizedTime = 21,
DATE = 22,
TimeOfDay = 23,
DateTime = 24,
Duration = 25,
TIME = 26,
Null = 27,
RelativeObjectIdentifier = 28
}
-1
View File
@@ -1 +0,0 @@
export * from "./schema_validation";
@@ -1,3 +0,0 @@
export declare class AsnSchemaValidationError extends Error {
schemas: string[];
}
-5
View File
@@ -1,5 +0,0 @@
import { IAsnConvertible, IEmptyConstructor } from "./types";
export declare function isConvertible(target: IEmptyConstructor): target is new () => IAsnConvertible;
export declare function isConvertible(target: unknown): target is IAsnConvertible;
export declare function isTypeOfArray(target: unknown): target is typeof Array;
export declare function isArrayEqual(bytes1: ArrayBuffer, bytes2: ArrayBuffer): boolean;
-10
View File
@@ -1,10 +0,0 @@
export * from "./converters";
export * from "./types/index";
export { AsnProp, AsnType, AsnChoiceType, AsnSequenceType, AsnSetType } from "./decorators";
export { AsnTypeTypes, AsnPropTypes } from "./enums";
export { AsnParser } from "./parser";
export { AsnSerializer } from "./serializer";
export { IAsnConverter, IAsnConvertible, IAsnParseOptions } from "./types";
export * from "./errors";
export * from "./objects";
export * from "./convert";
-3
View File
@@ -1,3 +0,0 @@
export declare abstract class AsnArray<T> extends Array<T> {
constructor(items?: T[]);
}
-72
View File
@@ -1,72 +0,0 @@
import * as asn1js from "asn1js";
import { type BufferSourceLike } from "@peculiar/utils";
import { IEmptyConstructor, IAsnParseOptions } from "./types";
/**
* Deserializes objects from ASN.1 encoded data
*/
export declare class AsnParser {
/**
* Deserializes an object from the ASN.1 encoded buffer
* @param data ASN.1 encoded buffer
* @param target Target schema for object deserialization
*/
static parse<T>(data: BufferSourceLike, target: IEmptyConstructor<T>, options?: IAsnParseOptions): T;
/**
* Deserializes an object from the asn1js object
* @param asn1Schema asn1js object
* @param target Target schema for object deserialization
*/
static fromASN<T>(asn1Schema: asn1js.AsnType, target: IEmptyConstructor<T>, options?: IAsnParseOptions): T;
/**
* Handles Choice types with context tags (IMPLICIT) and IMPLICIT tagging
*/
private static handleChoiceTypes;
/**
* Handles SEQUENCE types with optional CHOICE fields and schema comparison
*/
private static handleSequenceTypes;
/**
* Processes repeated fields in manual mapping
*/
private static processRepeatedField;
/**
* Processes primitive fields in manual mapping
*/
private static processPrimitiveField;
/**
* Checks if a schema item is an optional CHOICE field
*/
private static isOptionalChoiceField;
/**
* Processes optional CHOICE fields in manual mapping
*/
private static processOptionalChoiceField;
/**
* Handles array types
*/
private static handleArrayTypes;
/**
* Processes all schema items
*/
private static processSchemaItems;
/**
* Processes primitive schema items
*/
private static processPrimitiveSchemaItem;
/**
* Processes repeated primitive items
*/
private static processRepeatedPrimitiveItem;
/**
* Processes single primitive items
*/
private static processSinglePrimitiveItem;
/**
* Processes complex schema items (SEQUENCE, SET, CHOICE)
*/
private static processComplexSchemaItem;
/**
* Handles IMPLICIT tagging for complex types
*/
private static handleImplicitTagging;
}
-32
View File
@@ -1,32 +0,0 @@
import * as asn1js from "asn1js";
import { AsnRepeatType } from "./decorators";
import { AsnPropTypes, AsnTypeTypes } from "./enums";
import { IAsnConverter, IEmptyConstructor } from "./types";
export interface IAsnSchemaItem {
type: AsnPropTypes | IEmptyConstructor;
optional?: boolean;
defaultValue?: unknown;
context?: number;
implicit?: boolean;
converter?: IAsnConverter;
repeated?: AsnRepeatType;
raw?: boolean;
}
export interface IAsnSchema {
type: AsnTypeTypes;
itemType: AsnPropTypes | IEmptyConstructor;
items: Record<string, IAsnSchemaItem>;
schema?: AsnSchemaType;
}
export type AsnSchemaType = asn1js.Sequence | asn1js.Set | asn1js.Choice;
export declare class AsnSchemaStorage {
protected items: WeakMap<object, IAsnSchema>;
has(target: object): boolean;
get(target: IEmptyConstructor, checkSchema: true): IAsnSchema & Required<Pick<IAsnSchema, "schema">>;
get(target: IEmptyConstructor, checkSchema?: false): IAsnSchema;
cache(target: IEmptyConstructor): void;
createDefault(target: object): IAsnSchema;
create(target: object, useNames: boolean): AsnSchemaType;
set(target: object, schema: IAsnSchema): this;
protected findParentSchema(target: object): IAsnSchema | null;
}
-17
View File
@@ -1,17 +0,0 @@
import * as asn1js from "asn1js";
/**
* Serializes objects into ASN.1 encoded data
*/
export declare class AsnSerializer {
/**
* Serializes an object to the ASN.1 encoded buffer
* @param obj The object to serialize
*/
static serialize(obj: unknown): ArrayBuffer;
/**
* Serialize an object to the asn1js object
* @param obj The object to serialize
*/
static toASN(obj: unknown): asn1js.AsnType;
private static toAsnItem;
}
-2
View File
@@ -1,2 +0,0 @@
import { AsnSchemaStorage } from "./schema";
export declare const schemaStorage: AsnSchemaStorage;
-42
View File
@@ -1,42 +0,0 @@
/**
* ASN1 type
*/
import * as asn1js from "asn1js";
export type IEmptyConstructor<T = unknown> = new () => T;
/**
* Options for parsing ASN.1 encoded data
*/
export interface IAsnParseOptions {
/**
* Resource limits forwarded to `asn1js.fromBER` for untrusted input
* (`maxDepth`, `maxNodes`, `maxContentLength`). When omitted, the asn1js
* defaults apply (maxDepth: 100, maxNodes: 10000, maxContentLength: 16MB).
*/
berOptions?: asn1js.FromBerOptions;
}
/**
* Allows to convert ASN.1 object to JS value and back
*/
export interface IAsnConverter<T = unknown, AsnType = asn1js.AsnType> {
/**
* Returns JS value from ASN.1 object
* @param value ASN.1 object from asn1js module
*/
fromASN(value: AsnType): T;
/**
* Returns ASN.1 object from JS value
* @param value JS value
*/
toASN(value: T): AsnType;
}
export type IntegerConverterType = string | number;
export type AnyConverterType = ArrayBuffer | null;
/**
* Allows an object to control its own ASN.1 serialization and deserialization
*/
export interface IAsnConvertible<T = asn1js.AsnType> {
fromASN(asn: T): this;
toASN(): T;
toSchema(name: string): asn1js.BaseBlock;
}
export type IAsnConvertibleConstructor = new () => IAsnConvertible;
-15
View File
@@ -1,15 +0,0 @@
import * as asn1js from "asn1js";
import { type BufferSourceLike } from "@peculiar/utils";
import { IAsnConvertible } from "../types";
export declare class BitString<T extends number = number> implements IAsnConvertible {
unusedBits: number;
value: ArrayBuffer;
constructor();
constructor(value: T);
constructor(value: BufferSourceLike, unusedBits?: number);
fromASN(asn: asn1js.BitString): this;
toASN(): asn1js.BitString;
toSchema(name: string): asn1js.BitString;
toNumber(): T;
fromNumber(value: T): void;
}
-2
View File
@@ -1,2 +0,0 @@
export * from "./bit_string";
export * from "./octet_string";
-15
View File
@@ -1,15 +0,0 @@
import * as asn1js from "asn1js";
import { BufferSourceLike } from "@peculiar/utils";
import { IAsnConvertible } from "../types";
export declare class OctetString implements IAsnConvertible, ArrayBufferView {
buffer: ArrayBuffer;
get byteLength(): number;
get byteOffset(): number;
constructor();
constructor(byteLength: number);
constructor(bytes: number[]);
constructor(bytes: BufferSourceLike);
fromASN(asn: asn1js.OctetString): this;
toASN(): asn1js.OctetString;
toSchema(name: string): asn1js.OctetString;
}
-64
View File
@@ -1,64 +0,0 @@
{
"name": "@peculiar/asn1-schema",
"version": "2.11.0",
"description": "Decorators and helper APIs for declaring ASN.1 schemas in TypeScript.",
"keywords": [
"asn",
"asn1",
"schema",
"serialize",
"parse",
"decorator",
"der"
],
"author": "PeculiarVentures, LLC",
"license": "MIT",
"engines": {
"node": ">=14"
},
"files": [
"build/**/*.{js,d.ts}",
"LICENSE",
"README.md"
],
"main": "build/cjs/index.js",
"module": "build/es2015/index.js",
"types": "build/types/index.d.ts",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/PeculiarVentures/asn1-schema",
"directory": "packages/schema"
},
"bugs": {
"url": "https://github.com/PeculiarVentures/asn1-schema/issues"
},
"homepage": "https://github.com/PeculiarVentures/asn1-schema/tree/master/packages/schema#readme",
"scripts": {
"clear": "rimraf build",
"build": "npm run build:module && npm run build:types",
"build:module": "npm run build:cjs && npm run build:es2015",
"build:cjs": "tsc -p tsconfig.compile.json --removeComments --module commonjs --outDir build/cjs",
"build:es2015": "tsc -p tsconfig.compile.json --removeComments --module ES2015 --outDir build/es2015",
"prebuild:types": "rimraf build/types",
"build:types": "tsc -p tsconfig.compile.json --outDir build/types --declaration --emitDeclarationOnly",
"rebuild": "npm run clear && npm run build"
},
"dependencies": {
"@peculiar/utils": "^2.0.2",
"asn1js": "^3.0.10",
"tslib": "^2.8.1"
},
"contributors": [
{
"email": "rmh@unmitigatedrisk.com",
"name": "Ryan Hurst"
},
{
"email": "microshine@mail.ru",
"name": "Miroshin Stepan"
}
]
}
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2018
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.
-158
View File
@@ -1,158 +0,0 @@
# JSON-SCHEMA
[![License](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/PeculiarVentures/json-schema/master/LICENSE.md)
[![CircleCI](https://circleci.com/gh/PeculiarVentures/json-schema.svg?style=svg)](https://circleci.com/gh/PeculiarVentures/json-schema)
[![Coverage Status](https://coveralls.io/repos/github/PeculiarVentures/json-schema/badge.svg?branch=master&t=ddJivl)](https://coveralls.io/github/PeculiarVentures/json-schema?branch=master)
[![npm version](https://badge.fury.io/js/%40peculiar%2Fjson-schema.svg)](https://badge.fury.io/js/%40peculiar%2Fjson-schema)
[![NPM](https://nodei.co/npm/@peculiar/json-schema.png)](https://nodei.co/npm/@peculiar/json-schema/)
This package uses ES2015 [decorators](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841) to simplify JSON [schema creation and use](https://json-schema.org/understanding-json-schema/index.html).
## Introduction
JSON (JavaScript Object Notation) is a lightweight data-interchange format that was designed to be easy for humans to read and write but in practice, it is [minefield](http://seriot.ch/parsing_json.html) when it machines need to parse it.
While the use of schemas can help with this problem their use can be complicated. When using `json-schema` this is addressed by using decorators to make both serialization and parsing of XML possible via a simple class that handles the schemas for you.
This is important because validating input data before its use is important to do because all input data is evil. Using a schema helps you handle this data [safely](https://www.whitehatsec.com/blog/handling-untrusted-json-safely/).
## Installation
Installation is handled via `npm`:
```
$ npm install @peculiar/json-schema
```
## Examples
### Node.js
Creating a schema:
```js
import { JsonParser, JsonSerializer, JsonProp, JsonPropTypes, IJsonConverter } from "@peculiar/json-schema";
// custom data converter
const JsonBase64UrlConverter: IJsonConverter<Uint8Array, string> = {
fromJSON: (value: string) => base64UrlToBuffer(value),
toJSON: (value: Uint8Array) => bufferToBase64Url(value),
};
class EcPublicKey {
@JsonProp({ name: "kty" })
keyType = "EC";
@JsonProp({ name: "crv" })
namedCurve = "";
@JsonProp({ converter: JsonBase64UrlConverter })
x = new Uint8Array(0);
@JsonProp({ converter: JsonBase64UrlConverter })
y = new Uint8Array(0);
@JsonProp({ name: "ext", type: JsonPropTypes.Boolean, optional: true })
extractable = false;
@JsonProp({ name: "key_ops", type: JsonPropTypes.String, repeated: true, optional: true })
usages: string[] = [];
}
const json = `{
"kty": "EC",
"crv": "P-256",
"x": "zCQ5BPHPCLZYgdpo1n-x_90P2Ij52d53YVwTh3ZdiMo",
"y": "pDfQTUx0-OiZc5ZuKMcA7v2Q7ZPKsQwzB58bft0JTko",
"ext": true
}`;
const ecPubKey = JsonParser.parse(json, { targetSchema: EcPublicKey });
console.log(ecPubKey);
ecPubKey.usages.push("verify");
const jsonText = JsonSerializer.serialize(ecPubKey, undefined, undefined, 2);
console.log(jsonText);
// Output
//
// EcPublicKey {keyType: "EC", namedCurve: "P-256", x: Uint8Array(32), y: Uint8Array(32), extractable: true, …}
//
// {
// "kty": "EC",
// "crv": "P-256",
// "x": "zCQ5BPHPCLZYgdpo1n+x/90P2Ij52d53YVwTh3ZdiMo=",
// "y": "pDfQTUx0+OiZc5ZuKMcA7v2Q7ZPKsQwzB58bft0JTko=",
// "ext": true,
// "key_ops": [
// "verify"
// ]
// }
```
Extending a Schema:
```js
class BaseObject {
@JsonProp({ name: "i" })
public id = 0;
}
class Word extends BaseObject {
@JsonProp({ name: "t" })
public text = "";
}
class Person extends BaseObject {
@JsonProp({ name: "n" })
public name = 0;
@JsonProp({ name: "w", repeated: true, type: Word })
public words = [];
}
const json = `{
"i":1,
"n":"Bob",
"w":[
{"i":2,"t":"hello"},
{"i":3,"t":"world"}
]
}`;
const person = JsonParser.parse(json, { targetSchema: Person });
console.log(person);
const word = new Word();
word.id = 4;
word.text = "!!!";
const jsonText = JsonSerializer.serialize(person, undefined, undefined, 2);
console.log(jsonText);
// Output
//
// Person {id: 1, name: "Bob", words: [Word {id: 2, text: "hello"}, Word {id: 3, text: "world"}]}
// {
// "i": 1,
// "n": "Bob",
// "w": [
// {
// "i": 2,
// "t": "hello"
// },
// {
// "i": 3,
// "t": "world"
// },
// {
// "i": 4,
// "t": "!!!"
// }
// ]
// }
```
## API
Use [index.d.ts](index.d.ts) file
-484
View File
@@ -1,484 +0,0 @@
/**
* Copyright (c) 2020, Peculiar Ventures, All rights reserved.
*/
class JsonError extends Error {
constructor(message, innerError) {
super(innerError
? `${message}. See the inner exception for more details.`
: message);
this.message = message;
this.innerError = innerError;
}
}
class TransformError extends JsonError {
constructor(schema, message, innerError) {
super(message, innerError);
this.schema = schema;
}
}
class ParserError extends TransformError {
constructor(schema, message, innerError) {
super(schema, `JSON doesn't match to '${schema.target.name}' schema. ${message}`, innerError);
}
}
class ValidationError extends JsonError {
}
class SerializerError extends JsonError {
constructor(schemaName, message, innerError) {
super(`Cannot serialize by '${schemaName}' schema. ${message}`, innerError);
this.schemaName = schemaName;
}
}
class KeyError extends ParserError {
constructor(schema, keys, errors = {}) {
super(schema, "Some keys doesn't match to schema");
this.keys = keys;
this.errors = errors;
}
}
var JsonPropTypes;
(function (JsonPropTypes) {
JsonPropTypes[JsonPropTypes["Any"] = 0] = "Any";
JsonPropTypes[JsonPropTypes["Boolean"] = 1] = "Boolean";
JsonPropTypes[JsonPropTypes["Number"] = 2] = "Number";
JsonPropTypes[JsonPropTypes["String"] = 3] = "String";
})(JsonPropTypes || (JsonPropTypes = {}));
function checkType(value, type) {
switch (type) {
case JsonPropTypes.Boolean:
return typeof value === "boolean";
case JsonPropTypes.Number:
return typeof value === "number";
case JsonPropTypes.String:
return typeof value === "string";
}
return true;
}
function throwIfTypeIsWrong(value, type) {
if (!checkType(value, type)) {
throw new TypeError(`Value must be ${JsonPropTypes[type]}`);
}
}
function isConvertible(target) {
if (target && target.prototype) {
if (target.prototype.toJSON && target.prototype.fromJSON) {
return true;
}
else {
return isConvertible(target.prototype);
}
}
else {
return !!(target && target.toJSON && target.fromJSON);
}
}
class JsonSchemaStorage {
constructor() {
this.items = new Map();
}
has(target) {
return this.items.has(target) || !!this.findParentSchema(target);
}
get(target) {
const schema = this.items.get(target) || this.findParentSchema(target);
if (!schema) {
throw new Error("Cannot get schema for current target");
}
return schema;
}
create(target) {
const schema = { names: {} };
const parentSchema = this.findParentSchema(target);
if (parentSchema) {
Object.assign(schema, parentSchema);
schema.names = {};
for (const name in parentSchema.names) {
schema.names[name] = Object.assign({}, parentSchema.names[name]);
}
}
schema.target = target;
return schema;
}
set(target, schema) {
this.items.set(target, schema);
return this;
}
findParentSchema(target) {
const parent = target.__proto__;
if (parent) {
const schema = this.items.get(parent);
return schema || this.findParentSchema(parent);
}
return null;
}
}
const DEFAULT_SCHEMA = "default";
const schemaStorage = new JsonSchemaStorage();
class PatternValidation {
constructor(pattern) {
this.pattern = new RegExp(pattern);
}
validate(value) {
const pattern = new RegExp(this.pattern.source, this.pattern.flags);
if (typeof value !== "string") {
throw new ValidationError("Incoming value must be string");
}
if (!pattern.exec(value)) {
throw new ValidationError(`Value doesn't match to pattern '${pattern.toString()}'`);
}
}
}
class InclusiveValidation {
constructor(min = Number.MIN_VALUE, max = Number.MAX_VALUE) {
this.min = min;
this.max = max;
}
validate(value) {
throwIfTypeIsWrong(value, JsonPropTypes.Number);
if (!(this.min <= value && value <= this.max)) {
const min = this.min === Number.MIN_VALUE ? "MIN" : this.min;
const max = this.max === Number.MAX_VALUE ? "MAX" : this.max;
throw new ValidationError(`Value doesn't match to diapason [${min},${max}]`);
}
}
}
class ExclusiveValidation {
constructor(min = Number.MIN_VALUE, max = Number.MAX_VALUE) {
this.min = min;
this.max = max;
}
validate(value) {
throwIfTypeIsWrong(value, JsonPropTypes.Number);
if (!(this.min < value && value < this.max)) {
const min = this.min === Number.MIN_VALUE ? "MIN" : this.min;
const max = this.max === Number.MAX_VALUE ? "MAX" : this.max;
throw new ValidationError(`Value doesn't match to diapason (${min},${max})`);
}
}
}
class LengthValidation {
constructor(length, minLength, maxLength) {
this.length = length;
this.minLength = minLength;
this.maxLength = maxLength;
}
validate(value) {
if (this.length !== undefined) {
if (value.length !== this.length) {
throw new ValidationError(`Value length must be exactly ${this.length}.`);
}
return;
}
if (this.minLength !== undefined) {
if (value.length < this.minLength) {
throw new ValidationError(`Value length must be more than ${this.minLength}.`);
}
}
if (this.maxLength !== undefined) {
if (value.length > this.maxLength) {
throw new ValidationError(`Value length must be less than ${this.maxLength}.`);
}
}
}
}
class EnumerationValidation {
constructor(enumeration) {
this.enumeration = enumeration;
}
validate(value) {
throwIfTypeIsWrong(value, JsonPropTypes.String);
if (!this.enumeration.includes(value)) {
throw new ValidationError(`Value must be one of ${this.enumeration.map((v) => `'${v}'`).join(", ")}`);
}
}
}
class JsonTransform {
static checkValues(data, schemaItem) {
const values = Array.isArray(data) ? data : [data];
for (const value of values) {
for (const validation of schemaItem.validations) {
if (validation instanceof LengthValidation && schemaItem.repeated) {
validation.validate(data);
}
else {
validation.validate(value);
}
}
}
}
static checkTypes(value, schemaItem) {
if (schemaItem.repeated && !Array.isArray(value)) {
throw new TypeError("Value must be Array");
}
if (typeof schemaItem.type === "number") {
const values = Array.isArray(value) ? value : [value];
for (const v of values) {
throwIfTypeIsWrong(v, schemaItem.type);
}
}
}
static getSchemaByName(schema, name = DEFAULT_SCHEMA) {
return { ...schema.names[DEFAULT_SCHEMA], ...schema.names[name] };
}
}
class JsonSerializer extends JsonTransform {
static serialize(obj, options, replacer, space) {
const json = this.toJSON(obj, options);
return JSON.stringify(json, replacer, space);
}
static toJSON(obj, options = {}) {
let res;
let targetSchema = options.targetSchema;
const schemaName = options.schemaName || DEFAULT_SCHEMA;
if (isConvertible(obj)) {
return obj.toJSON();
}
if (Array.isArray(obj)) {
res = [];
for (const item of obj) {
res.push(this.toJSON(item, options));
}
}
else if (typeof obj === "object") {
if (targetSchema && !schemaStorage.has(targetSchema)) {
throw new JsonError("Cannot get schema for `targetSchema` param");
}
targetSchema = (targetSchema || obj.constructor);
if (schemaStorage.has(targetSchema)) {
const schema = schemaStorage.get(targetSchema);
res = {};
const namedSchema = this.getSchemaByName(schema, schemaName);
for (const key in namedSchema) {
try {
const item = namedSchema[key];
const objItem = obj[key];
let value;
if ((item.optional && objItem === undefined)
|| (item.defaultValue !== undefined && objItem === item.defaultValue)) {
continue;
}
if (!item.optional && objItem === undefined) {
throw new SerializerError(targetSchema.name, `Property '${key}' is required.`);
}
if (typeof item.type === "number") {
if (item.converter) {
if (item.repeated) {
value = objItem.map((el) => item.converter.toJSON(el, obj));
}
else {
value = item.converter.toJSON(objItem, obj);
}
}
else {
value = objItem;
}
}
else {
if (item.repeated) {
value = objItem.map((el) => this.toJSON(el, { schemaName }));
}
else {
value = this.toJSON(objItem, { schemaName });
}
}
this.checkTypes(value, item);
this.checkValues(value, item);
res[item.name || key] = value;
}
catch (e) {
if (e instanceof SerializerError) {
throw e;
}
else {
throw new SerializerError(schema.target.name, `Property '${key}' is wrong. ${e.message}`, e);
}
}
}
}
else {
res = {};
for (const key in obj) {
res[key] = this.toJSON(obj[key], { schemaName });
}
}
}
else {
res = obj;
}
return res;
}
}
class JsonParser extends JsonTransform {
static parse(data, options) {
const obj = JSON.parse(data);
return this.fromJSON(obj, options);
}
static fromJSON(target, options) {
const targetSchema = options.targetSchema;
const schemaName = options.schemaName || DEFAULT_SCHEMA;
const obj = new targetSchema();
if (isConvertible(obj)) {
return obj.fromJSON(target);
}
const schema = schemaStorage.get(targetSchema);
const namedSchema = this.getSchemaByName(schema, schemaName);
const keyErrors = {};
if (options.strictProperty && !Array.isArray(target)) {
JsonParser.checkStrictProperty(target, namedSchema, schema);
}
for (const key in namedSchema) {
try {
const item = namedSchema[key];
const name = item.name || key;
const value = target[name];
if (value === undefined && (item.optional || item.defaultValue !== undefined)) {
continue;
}
if (!item.optional && value === undefined) {
throw new ParserError(schema, `Property '${name}' is required.`);
}
this.checkTypes(value, item);
this.checkValues(value, item);
if (typeof (item.type) === "number") {
if (item.converter) {
if (item.repeated) {
obj[key] = value.map((el) => item.converter.fromJSON(el, obj));
}
else {
obj[key] = item.converter.fromJSON(value, obj);
}
}
else {
obj[key] = value;
}
}
else {
const newOptions = {
...options,
targetSchema: item.type,
schemaName,
};
if (item.repeated) {
obj[key] = value.map((el) => this.fromJSON(el, newOptions));
}
else {
obj[key] = this.fromJSON(value, newOptions);
}
}
}
catch (e) {
if (!(e instanceof ParserError)) {
e = new ParserError(schema, `Property '${key}' is wrong. ${e.message}`, e);
}
if (options.strictAllKeys) {
keyErrors[key] = e;
}
else {
throw e;
}
}
}
const keys = Object.keys(keyErrors);
if (keys.length) {
throw new KeyError(schema, keys, keyErrors);
}
return obj;
}
static checkStrictProperty(target, namedSchema, schema) {
const jsonProps = Object.keys(target);
const schemaProps = Object.keys(namedSchema);
const keys = [];
for (const key of jsonProps) {
if (schemaProps.indexOf(key) === -1) {
keys.push(key);
}
}
if (keys.length) {
throw new KeyError(schema, keys);
}
}
}
function getValidations(item) {
const validations = [];
if (item.pattern) {
validations.push(new PatternValidation(item.pattern));
}
if (item.type === JsonPropTypes.Number || item.type === JsonPropTypes.Any) {
if (item.minInclusive !== undefined || item.maxInclusive !== undefined) {
validations.push(new InclusiveValidation(item.minInclusive, item.maxInclusive));
}
if (item.minExclusive !== undefined || item.maxExclusive !== undefined) {
validations.push(new ExclusiveValidation(item.minExclusive, item.maxExclusive));
}
if (item.enumeration !== undefined) {
validations.push(new EnumerationValidation(item.enumeration));
}
}
if (item.type === JsonPropTypes.String || item.repeated || item.type === JsonPropTypes.Any) {
if (item.length !== undefined || item.minLength !== undefined || item.maxLength !== undefined) {
validations.push(new LengthValidation(item.length, item.minLength, item.maxLength));
}
}
return validations;
}
const JsonProp = (options = {}) => (target, propertyKey) => {
const errorMessage = `Cannot set type for ${propertyKey} property of ${target.constructor.name} schema`;
let schema;
if (!schemaStorage.has(target.constructor)) {
schema = schemaStorage.create(target.constructor);
schemaStorage.set(target.constructor, schema);
}
else {
schema = schemaStorage.get(target.constructor);
if (schema.target !== target.constructor) {
schema = schemaStorage.create(target.constructor);
schemaStorage.set(target.constructor, schema);
}
}
const defaultSchema = {
type: JsonPropTypes.Any,
validations: [],
};
const copyOptions = Object.assign(defaultSchema, options);
copyOptions.validations = getValidations(copyOptions);
if (typeof copyOptions.type !== "number") {
if (!schemaStorage.has(copyOptions.type) && !isConvertible(copyOptions.type)) {
throw new Error(`${errorMessage}. Assigning type doesn't have schema.`);
}
}
let schemaNames;
if (Array.isArray(options.schema)) {
schemaNames = options.schema;
}
else {
schemaNames = [options.schema || DEFAULT_SCHEMA];
}
for (const schemaName of schemaNames) {
if (!schema.names[schemaName]) {
schema.names[schemaName] = {};
}
const namedSchema = schema.names[schemaName];
namedSchema[propertyKey] = copyOptions;
}
};
export { JsonError, JsonParser, JsonProp, JsonPropTypes, JsonSerializer, KeyError, ParserError, SerializerError, TransformError, ValidationError };
-495
View File
@@ -1,495 +0,0 @@
/**
* Copyright (c) 2020, Peculiar Ventures, All rights reserved.
*/
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
class JsonError extends Error {
constructor(message, innerError) {
super(innerError
? `${message}. See the inner exception for more details.`
: message);
this.message = message;
this.innerError = innerError;
}
}
class TransformError extends JsonError {
constructor(schema, message, innerError) {
super(message, innerError);
this.schema = schema;
}
}
class ParserError extends TransformError {
constructor(schema, message, innerError) {
super(schema, `JSON doesn't match to '${schema.target.name}' schema. ${message}`, innerError);
}
}
class ValidationError extends JsonError {
}
class SerializerError extends JsonError {
constructor(schemaName, message, innerError) {
super(`Cannot serialize by '${schemaName}' schema. ${message}`, innerError);
this.schemaName = schemaName;
}
}
class KeyError extends ParserError {
constructor(schema, keys, errors = {}) {
super(schema, "Some keys doesn't match to schema");
this.keys = keys;
this.errors = errors;
}
}
(function (JsonPropTypes) {
JsonPropTypes[JsonPropTypes["Any"] = 0] = "Any";
JsonPropTypes[JsonPropTypes["Boolean"] = 1] = "Boolean";
JsonPropTypes[JsonPropTypes["Number"] = 2] = "Number";
JsonPropTypes[JsonPropTypes["String"] = 3] = "String";
})(exports.JsonPropTypes || (exports.JsonPropTypes = {}));
function checkType(value, type) {
switch (type) {
case exports.JsonPropTypes.Boolean:
return typeof value === "boolean";
case exports.JsonPropTypes.Number:
return typeof value === "number";
case exports.JsonPropTypes.String:
return typeof value === "string";
}
return true;
}
function throwIfTypeIsWrong(value, type) {
if (!checkType(value, type)) {
throw new TypeError(`Value must be ${exports.JsonPropTypes[type]}`);
}
}
function isConvertible(target) {
if (target && target.prototype) {
if (target.prototype.toJSON && target.prototype.fromJSON) {
return true;
}
else {
return isConvertible(target.prototype);
}
}
else {
return !!(target && target.toJSON && target.fromJSON);
}
}
class JsonSchemaStorage {
constructor() {
this.items = new Map();
}
has(target) {
return this.items.has(target) || !!this.findParentSchema(target);
}
get(target) {
const schema = this.items.get(target) || this.findParentSchema(target);
if (!schema) {
throw new Error("Cannot get schema for current target");
}
return schema;
}
create(target) {
const schema = { names: {} };
const parentSchema = this.findParentSchema(target);
if (parentSchema) {
Object.assign(schema, parentSchema);
schema.names = {};
for (const name in parentSchema.names) {
schema.names[name] = Object.assign({}, parentSchema.names[name]);
}
}
schema.target = target;
return schema;
}
set(target, schema) {
this.items.set(target, schema);
return this;
}
findParentSchema(target) {
const parent = target.__proto__;
if (parent) {
const schema = this.items.get(parent);
return schema || this.findParentSchema(parent);
}
return null;
}
}
const DEFAULT_SCHEMA = "default";
const schemaStorage = new JsonSchemaStorage();
class PatternValidation {
constructor(pattern) {
this.pattern = new RegExp(pattern);
}
validate(value) {
const pattern = new RegExp(this.pattern.source, this.pattern.flags);
if (typeof value !== "string") {
throw new ValidationError("Incoming value must be string");
}
if (!pattern.exec(value)) {
throw new ValidationError(`Value doesn't match to pattern '${pattern.toString()}'`);
}
}
}
class InclusiveValidation {
constructor(min = Number.MIN_VALUE, max = Number.MAX_VALUE) {
this.min = min;
this.max = max;
}
validate(value) {
throwIfTypeIsWrong(value, exports.JsonPropTypes.Number);
if (!(this.min <= value && value <= this.max)) {
const min = this.min === Number.MIN_VALUE ? "MIN" : this.min;
const max = this.max === Number.MAX_VALUE ? "MAX" : this.max;
throw new ValidationError(`Value doesn't match to diapason [${min},${max}]`);
}
}
}
class ExclusiveValidation {
constructor(min = Number.MIN_VALUE, max = Number.MAX_VALUE) {
this.min = min;
this.max = max;
}
validate(value) {
throwIfTypeIsWrong(value, exports.JsonPropTypes.Number);
if (!(this.min < value && value < this.max)) {
const min = this.min === Number.MIN_VALUE ? "MIN" : this.min;
const max = this.max === Number.MAX_VALUE ? "MAX" : this.max;
throw new ValidationError(`Value doesn't match to diapason (${min},${max})`);
}
}
}
class LengthValidation {
constructor(length, minLength, maxLength) {
this.length = length;
this.minLength = minLength;
this.maxLength = maxLength;
}
validate(value) {
if (this.length !== undefined) {
if (value.length !== this.length) {
throw new ValidationError(`Value length must be exactly ${this.length}.`);
}
return;
}
if (this.minLength !== undefined) {
if (value.length < this.minLength) {
throw new ValidationError(`Value length must be more than ${this.minLength}.`);
}
}
if (this.maxLength !== undefined) {
if (value.length > this.maxLength) {
throw new ValidationError(`Value length must be less than ${this.maxLength}.`);
}
}
}
}
class EnumerationValidation {
constructor(enumeration) {
this.enumeration = enumeration;
}
validate(value) {
throwIfTypeIsWrong(value, exports.JsonPropTypes.String);
if (!this.enumeration.includes(value)) {
throw new ValidationError(`Value must be one of ${this.enumeration.map((v) => `'${v}'`).join(", ")}`);
}
}
}
class JsonTransform {
static checkValues(data, schemaItem) {
const values = Array.isArray(data) ? data : [data];
for (const value of values) {
for (const validation of schemaItem.validations) {
if (validation instanceof LengthValidation && schemaItem.repeated) {
validation.validate(data);
}
else {
validation.validate(value);
}
}
}
}
static checkTypes(value, schemaItem) {
if (schemaItem.repeated && !Array.isArray(value)) {
throw new TypeError("Value must be Array");
}
if (typeof schemaItem.type === "number") {
const values = Array.isArray(value) ? value : [value];
for (const v of values) {
throwIfTypeIsWrong(v, schemaItem.type);
}
}
}
static getSchemaByName(schema, name = DEFAULT_SCHEMA) {
return { ...schema.names[DEFAULT_SCHEMA], ...schema.names[name] };
}
}
class JsonSerializer extends JsonTransform {
static serialize(obj, options, replacer, space) {
const json = this.toJSON(obj, options);
return JSON.stringify(json, replacer, space);
}
static toJSON(obj, options = {}) {
let res;
let targetSchema = options.targetSchema;
const schemaName = options.schemaName || DEFAULT_SCHEMA;
if (isConvertible(obj)) {
return obj.toJSON();
}
if (Array.isArray(obj)) {
res = [];
for (const item of obj) {
res.push(this.toJSON(item, options));
}
}
else if (typeof obj === "object") {
if (targetSchema && !schemaStorage.has(targetSchema)) {
throw new JsonError("Cannot get schema for `targetSchema` param");
}
targetSchema = (targetSchema || obj.constructor);
if (schemaStorage.has(targetSchema)) {
const schema = schemaStorage.get(targetSchema);
res = {};
const namedSchema = this.getSchemaByName(schema, schemaName);
for (const key in namedSchema) {
try {
const item = namedSchema[key];
const objItem = obj[key];
let value;
if ((item.optional && objItem === undefined)
|| (item.defaultValue !== undefined && objItem === item.defaultValue)) {
continue;
}
if (!item.optional && objItem === undefined) {
throw new SerializerError(targetSchema.name, `Property '${key}' is required.`);
}
if (typeof item.type === "number") {
if (item.converter) {
if (item.repeated) {
value = objItem.map((el) => item.converter.toJSON(el, obj));
}
else {
value = item.converter.toJSON(objItem, obj);
}
}
else {
value = objItem;
}
}
else {
if (item.repeated) {
value = objItem.map((el) => this.toJSON(el, { schemaName }));
}
else {
value = this.toJSON(objItem, { schemaName });
}
}
this.checkTypes(value, item);
this.checkValues(value, item);
res[item.name || key] = value;
}
catch (e) {
if (e instanceof SerializerError) {
throw e;
}
else {
throw new SerializerError(schema.target.name, `Property '${key}' is wrong. ${e.message}`, e);
}
}
}
}
else {
res = {};
for (const key in obj) {
res[key] = this.toJSON(obj[key], { schemaName });
}
}
}
else {
res = obj;
}
return res;
}
}
class JsonParser extends JsonTransform {
static parse(data, options) {
const obj = JSON.parse(data);
return this.fromJSON(obj, options);
}
static fromJSON(target, options) {
const targetSchema = options.targetSchema;
const schemaName = options.schemaName || DEFAULT_SCHEMA;
const obj = new targetSchema();
if (isConvertible(obj)) {
return obj.fromJSON(target);
}
const schema = schemaStorage.get(targetSchema);
const namedSchema = this.getSchemaByName(schema, schemaName);
const keyErrors = {};
if (options.strictProperty && !Array.isArray(target)) {
JsonParser.checkStrictProperty(target, namedSchema, schema);
}
for (const key in namedSchema) {
try {
const item = namedSchema[key];
const name = item.name || key;
const value = target[name];
if (value === undefined && (item.optional || item.defaultValue !== undefined)) {
continue;
}
if (!item.optional && value === undefined) {
throw new ParserError(schema, `Property '${name}' is required.`);
}
this.checkTypes(value, item);
this.checkValues(value, item);
if (typeof (item.type) === "number") {
if (item.converter) {
if (item.repeated) {
obj[key] = value.map((el) => item.converter.fromJSON(el, obj));
}
else {
obj[key] = item.converter.fromJSON(value, obj);
}
}
else {
obj[key] = value;
}
}
else {
const newOptions = {
...options,
targetSchema: item.type,
schemaName,
};
if (item.repeated) {
obj[key] = value.map((el) => this.fromJSON(el, newOptions));
}
else {
obj[key] = this.fromJSON(value, newOptions);
}
}
}
catch (e) {
if (!(e instanceof ParserError)) {
e = new ParserError(schema, `Property '${key}' is wrong. ${e.message}`, e);
}
if (options.strictAllKeys) {
keyErrors[key] = e;
}
else {
throw e;
}
}
}
const keys = Object.keys(keyErrors);
if (keys.length) {
throw new KeyError(schema, keys, keyErrors);
}
return obj;
}
static checkStrictProperty(target, namedSchema, schema) {
const jsonProps = Object.keys(target);
const schemaProps = Object.keys(namedSchema);
const keys = [];
for (const key of jsonProps) {
if (schemaProps.indexOf(key) === -1) {
keys.push(key);
}
}
if (keys.length) {
throw new KeyError(schema, keys);
}
}
}
function getValidations(item) {
const validations = [];
if (item.pattern) {
validations.push(new PatternValidation(item.pattern));
}
if (item.type === exports.JsonPropTypes.Number || item.type === exports.JsonPropTypes.Any) {
if (item.minInclusive !== undefined || item.maxInclusive !== undefined) {
validations.push(new InclusiveValidation(item.minInclusive, item.maxInclusive));
}
if (item.minExclusive !== undefined || item.maxExclusive !== undefined) {
validations.push(new ExclusiveValidation(item.minExclusive, item.maxExclusive));
}
if (item.enumeration !== undefined) {
validations.push(new EnumerationValidation(item.enumeration));
}
}
if (item.type === exports.JsonPropTypes.String || item.repeated || item.type === exports.JsonPropTypes.Any) {
if (item.length !== undefined || item.minLength !== undefined || item.maxLength !== undefined) {
validations.push(new LengthValidation(item.length, item.minLength, item.maxLength));
}
}
return validations;
}
const JsonProp = (options = {}) => (target, propertyKey) => {
const errorMessage = `Cannot set type for ${propertyKey} property of ${target.constructor.name} schema`;
let schema;
if (!schemaStorage.has(target.constructor)) {
schema = schemaStorage.create(target.constructor);
schemaStorage.set(target.constructor, schema);
}
else {
schema = schemaStorage.get(target.constructor);
if (schema.target !== target.constructor) {
schema = schemaStorage.create(target.constructor);
schemaStorage.set(target.constructor, schema);
}
}
const defaultSchema = {
type: exports.JsonPropTypes.Any,
validations: [],
};
const copyOptions = Object.assign(defaultSchema, options);
copyOptions.validations = getValidations(copyOptions);
if (typeof copyOptions.type !== "number") {
if (!schemaStorage.has(copyOptions.type) && !isConvertible(copyOptions.type)) {
throw new Error(`${errorMessage}. Assigning type doesn't have schema.`);
}
}
let schemaNames;
if (Array.isArray(options.schema)) {
schemaNames = options.schema;
}
else {
schemaNames = [options.schema || DEFAULT_SCHEMA];
}
for (const schemaName of schemaNames) {
if (!schema.names[schemaName]) {
schema.names[schemaName] = {};
}
const namedSchema = schema.names[schemaName];
namedSchema[propertyKey] = copyOptions;
}
};
exports.JsonError = JsonError;
exports.JsonParser = JsonParser;
exports.JsonProp = JsonProp;
exports.JsonSerializer = JsonSerializer;
exports.KeyError = KeyError;
exports.ParserError = ParserError;
exports.SerializerError = SerializerError;
exports.TransformError = TransformError;
exports.ValidationError = ValidationError;
-51
View File
@@ -1,51 +0,0 @@
import { JsonPropTypes } from "./prop_types";
import { IEmptyConstructor, IJsonConverter } from "./types";
export interface IJsonPropOptions {
type?: JsonPropTypes | IEmptyConstructor<any>;
optional?: boolean;
defaultValue?: any;
converter?: IJsonConverter<any, any>;
repeated?: boolean;
name?: string;
/**
* Defines name of schema
*/
schema?: string | string[];
/**
* Defines regular expression for text
*/
pattern?: string | RegExp;
/**
* Defines a list of acceptable values
*/
enumeration?: string[];
/**
* Specifies the exact number of characters or list items allowed. Must be equal to or greater than zero
*/
length?: number;
/**
* Specifies the minimum number of characters or list items allowed. Must be equal to or greater than zero
*/
minLength?: number;
/**
* Specifies the maximum number of characters or list items allowed. Must be equal to or greater than zero
*/
maxLength?: number;
/**
* Specifies the lower bounds for numeric values (the value must be greater than or equal to this value)
*/
minInclusive?: number;
/**
* Specifies the upper bounds for numeric values (the value must be less than or equal to this value)
*/
maxInclusive?: number;
/**
* Specifies the lower bounds for numeric values (the value must be greater than this value)
*/
minExclusive?: number;
/**
* Specifies the upper bounds for numeric values (the value must be less than this value)
*/
maxExclusive?: number;
}
export declare const JsonProp: (options?: IJsonPropOptions) => (target: object, propertyKey: string) => void;
-6
View File
@@ -1,6 +0,0 @@
export * from "./json_error";
export * from "./parser_error";
export * from "./validation_error";
export * from "./serializer_error";
export * from "./transform_error";
export * from "./key_error";
@@ -1,5 +0,0 @@
export declare class JsonError extends Error {
message: string;
innerError?: Error | undefined;
constructor(message: string, innerError?: Error | undefined);
}
-10
View File
@@ -1,10 +0,0 @@
import { IJsonSchema } from "../schema";
import { ParserError } from "./parser_error";
export interface IKeyErrors {
[key: string]: Error;
}
export declare class KeyError extends ParserError {
keys: string[];
errors: IKeyErrors;
constructor(schema: IJsonSchema, keys: string[], errors?: IKeyErrors);
}
@@ -1,5 +0,0 @@
import { IJsonSchema } from "../schema";
import { TransformError } from "./transform_error";
export declare class ParserError extends TransformError {
constructor(schema: IJsonSchema, message: string, innerError?: Error);
}
@@ -1,5 +0,0 @@
import { JsonError } from "./json_error";
export declare class SerializerError extends JsonError {
schemaName: string;
constructor(schemaName: string, message: string, innerError?: Error);
}
@@ -1,6 +0,0 @@
import { IJsonSchema } from "../schema";
import { JsonError } from "./json_error";
export declare class TransformError extends JsonError {
schema: IJsonSchema;
constructor(schema: IJsonSchema, message: string, innerError?: Error);
}
@@ -1,3 +0,0 @@
import { JsonError } from "./json_error";
export declare class ValidationError extends JsonError {
}
-5
View File
@@ -1,5 +0,0 @@
import { JsonPropTypes } from "./prop_types";
import { IJsonConvertible } from "./types";
export declare function checkType(value: any, type: JsonPropTypes): boolean;
export declare function throwIfTypeIsWrong(value: any, type: JsonPropTypes): void;
export declare function isConvertible(target: any): target is IJsonConvertible;
-6
View File
@@ -1,6 +0,0 @@
export * from "./serializer";
export * from "./parser";
export * from "./decorators";
export { JsonPropTypes } from "./prop_types";
export * from "./types";
export * from "./errors";
-26
View File
@@ -1,26 +0,0 @@
import { JsonTransform } from "./transform";
import { IEmptyConstructor } from "./types";
export interface IJsonParserOptions<T> {
targetSchema: IEmptyConstructor<T>;
schemaName?: string;
/**
* Enable strict checking of properties. Throw exception if incoming JSON has odd fields
*/
strictProperty?: boolean;
/**
* Checks all properties for object and throws KeyError with list of wrong keys
*/
strictAllKeys?: boolean;
}
export declare class JsonParser extends JsonTransform {
static parse<T>(data: string, options: IJsonParserOptions<T>): T;
static fromJSON<T>(target: any, options: IJsonParserOptions<T>): T;
/**
* Checks for odd properties in target object.
* @param target Target object
* @param namedSchema Named schema with a list of properties
* @param schema
* @throws Throws ParserError exception whenever target object has odd property
*/
private static checkStrictProperty;
}
-6
View File
@@ -1,6 +0,0 @@
export declare enum JsonPropTypes {
Any = 0,
Boolean = 1,
Number = 2,
String = 3
}
-35
View File
@@ -1,35 +0,0 @@
import { JsonPropTypes } from "./prop_types";
import { IEmptyConstructor, IJsonConverter, IValidation } from "./types";
export interface IValidationEvent {
propName: string;
value: any;
}
export interface IJsonSchemaItem {
type: JsonPropTypes | IEmptyConstructor<any>;
optional?: boolean;
defaultValue?: any;
converter?: IJsonConverter<any, any>;
repeated?: boolean;
name?: string;
validations: IValidation[];
}
export interface IJsonSchema {
target: IEmptyConstructor<any>;
names: {
[key: string]: {
[key: string]: IJsonSchemaItem;
};
};
}
export declare class JsonSchemaStorage {
protected items: Map<object, IJsonSchema>;
has(target: object): boolean;
get(target: object): IJsonSchema;
/**
* Creates new schema
* @param target Target object
*/
create(target: object): IJsonSchema;
set(target: object, schema: IJsonSchema): this;
protected findParentSchema(target: object): IJsonSchema | null;
}
-10
View File
@@ -1,10 +0,0 @@
import { JsonTransform } from "./transform";
import { IEmptyConstructor } from "./types";
export interface IJsonSerializerOptions {
targetSchema?: IEmptyConstructor<any>;
schemaName?: string;
}
export declare class JsonSerializer extends JsonTransform {
static serialize(obj: any, options?: IJsonSerializerOptions, replacer?: (key: string, value: any) => any, space?: string | number): string;
static toJSON(obj: any, options?: IJsonSerializerOptions): any;
}
-6
View File
@@ -1,6 +0,0 @@
import { JsonSchemaStorage } from "./schema";
/**
* Name of default schema
*/
export declare const DEFAULT_SCHEMA = "default";
export declare const schemaStorage: JsonSchemaStorage;
-9
View File
@@ -1,9 +0,0 @@
import { IJsonSchema, IJsonSchemaItem } from "./schema";
export interface IJsonNamedSchema {
[key: string]: IJsonSchemaItem;
}
export declare class JsonTransform {
protected static checkValues(data: any, schemaItem: IJsonSchemaItem): void;
protected static checkTypes(value: any, schemaItem: IJsonSchemaItem): void;
protected static getSchemaByName(schema: IJsonSchema, name?: string): IJsonNamedSchema;
}
-12
View File
@@ -1,12 +0,0 @@
export declare type IEmptyConstructor<T> = new () => T;
export interface IJsonConverter<T, S> {
fromJSON(value: S, target: any): T;
toJSON(value: T, target: any): S;
}
export interface IJsonConvertible<T = any> {
fromJSON(json: T): this;
toJSON(): T;
}
export interface IValidation {
validate(value: any): void;
}
@@ -1,6 +0,0 @@
import { IValidation } from "../types";
export declare class EnumerationValidation implements IValidation {
private enumeration;
constructor(enumeration: string[]);
validate(value: any): void;
}
@@ -1,7 +0,0 @@
import { IValidation } from "../types";
export declare class ExclusiveValidation implements IValidation {
private min;
private max;
constructor(min?: number, max?: number);
validate(value: any): void;
}
@@ -1,7 +0,0 @@
import { IValidation } from "../types";
export declare class InclusiveValidation implements IValidation {
private min;
private max;
constructor(min?: number, max?: number);
validate(value: any): void;
}
@@ -1,5 +0,0 @@
export * from "./pattern";
export * from "./inclusive";
export * from "./exclusive";
export * from "./length";
export * from "./enumeration";
@@ -1,8 +0,0 @@
import { IValidation } from "../types";
export declare class LengthValidation implements IValidation {
private length?;
private minLength?;
private maxLength?;
constructor(length?: number | undefined, minLength?: number | undefined, maxLength?: number | undefined);
validate(value: any): void;
}
@@ -1,6 +0,0 @@
import { IValidation } from "../types";
export declare class PatternValidation implements IValidation {
private pattern;
constructor(pattern: string | RegExp);
validate(value: any): void;
}
-95
View File
@@ -1,95 +0,0 @@
{
"name": "@peculiar/json-schema",
"version": "1.1.12",
"description": "This package uses ES2015 decorators to simplify JSON schema creation and use",
"main": "build/index.js",
"module": "build/index.es.js",
"types": "build/types/index.d.ts",
"scripts": {
"test": "mocha",
"prepare": "npm run build",
"lint": "tslint -p .",
"lint:fix": "tslint --fix -p .",
"build": "npm run build:module && npm run build:types",
"build:module": "rollup -c",
"build:types": "tsc -p tsconfig.types.json",
"clear": "rimraf build/*",
"rebuild": "npm run clear && npm run build",
"prepub": "npm run lint && npm run build",
"pub": "npm version patch && npm publish",
"postpub": "git push && git push --tags origin master",
"prepub:next": "npm run lint && npm run build",
"pub:next": "npm version prerelease --preid=next && npm publish --tag next",
"postpub:next": "git push",
"coverage": "nyc npm test",
"coveralls": "nyc report --reporter=text-lcov | coveralls"
},
"repository": {
"type": "git",
"url": "git+https://github.com/PeculiarVentures/json-schema.git"
},
"keywords": [
"json",
"stringify",
"serialize",
"parse",
"convert",
"decorator"
],
"author": "PeculiarVentures, Inc",
"contributors": [
{
"email": "rmh@unmitigatedrisk.com",
"name": "Ryan Hurst"
},
{
"email": "microshine@mail.ru",
"name": "Miroshin Stepan"
}
],
"license": "MIT",
"bugs": {
"url": "https://github.com/PeculiarVentures/json-schema/issues"
},
"homepage": "https://github.com/PeculiarVentures/json-schema#readme",
"engines": {
"node": ">=8.0.0"
},
"devDependencies": {
"@types/mocha": "^8.0.0",
"@types/node": "^12.12.51",
"mocha": "^8.0.1",
"rimraf": "^3.0.2",
"rollup": "^2.22.2",
"rollup-plugin-typescript2": "^0.27.1",
"ts-node": "^8.10.2",
"tslint": "^6.1.2",
"typescript": "^3.9.7"
},
"dependencies": {
"tslib": "^2.0.0"
},
"nyc": {
"extension": [
".ts",
".tsx"
],
"include": [
"src/**/*.ts"
],
"exclude": [
"**/*.d.ts"
],
"reporter": [
"text-summary",
"html"
]
},
"mocha": {
"require": "ts-node/register",
"extension": [
"ts"
],
"watch-files": "test/**/*.ts"
}
}
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2017-2026 Peculiar Ventures, LLC
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.
-299
View File
@@ -1,299 +0,0 @@
# @peculiar/utils
[![npm version](https://img.shields.io/npm/v/%40peculiar%2Futils.svg)](https://www.npmjs.com/package/@peculiar/utils)
[![Test](https://github.com/PeculiarVentures/pvtsutils/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/PeculiarVentures/pvtsutils/actions/workflows/test.yml)
[![Coverage Status](https://coveralls.io/repos/github/PeculiarVentures/pvtsutils/badge.svg?branch=master)](https://coveralls.io/github/PeculiarVentures/pvtsutils?branch=master)
[![License: MIT](https://img.shields.io/github/license/PeculiarVentures/pvtsutils.svg)](https://github.com/PeculiarVentures/pvtsutils/blob/master/LICENSE)
Modern byte, text, converter registry, and PEM utilities for TypeScript projects.
The package is designed around a modular v2 API:
- multi-entry exports for tree-shake-friendly imports;
- `encode` and `decode` terminology;
- an extensible runtime converter registry;
- generic PEM helpers without PKI-specific parsing;
- a legacy compatibility layer for historical `pvtsutils` consumers.
## Install
```bash
npm install @peculiar/utils
```
## Entry Points
```ts
import { bytes } from "@peculiar/utils";
import { hex, base64, base64url } from "@peculiar/utils/encoding";
import { pem } from "@peculiar/utils/pem";
import { convert, createConverterRegistry, defaultConverters } from "@peculiar/utils/converters";
import { Convert } from "@peculiar/utils/legacy";
```
## Bytes Helpers
`@peculiar/utils/bytes` stays focused on stateless byte sequence utilities. It does not include stateful readers, writers, ASN.1 parsing, PDF parsing, or other structured binary readers. For structured binary parsing, use a dedicated binary reader package.
```ts
import { bytes } from "@peculiar/utils";
const offset = bytes.indexOf(new Uint8Array([0x25, 0x25, 0x45, 0x4f, 0x46]), "%%EOF", {
encoding: "ascii",
});
const suffix = bytes.endsWith(new Uint8Array([0x25, 0x25, 0x45, 0x4f, 0x46]), "%%EOF", {
encoding: "ascii",
});
```
### Find `startxref` In A PDF Tail
```ts
import { lastIndexOf } from "@peculiar/utils/bytes";
const tailStart = Math.max(0, pdf.byteLength - 4096);
const offset = lastIndexOf(pdf, "startxref", {
encoding: "ascii",
start: pdf.byteLength,
end: tailStart,
});
if (offset === -1) {
throw new Error("PDF startxref marker not found");
}
```
### Find `startxref` Via `tail`
```ts
import { lastIndexOf, tail } from "@peculiar/utils/bytes";
const pdfTail = tail(pdf, 4096);
const localOffset = lastIndexOf(pdfTail, "startxref", {
encoding: "ascii",
});
const offset =
localOffset === -1
? -1
: pdf.byteLength - pdfTail.byteLength + localOffset;
```
### Check Prefixes And Suffixes
```ts
import { bytes } from "@peculiar/utils";
bytes.startsWith(data, "-----BEGIN", { encoding: "ascii" });
bytes.endsWith(data, "%%EOF", { encoding: "ascii" });
```
### Compare Byte Sequences
```ts
import { bytes } from "@peculiar/utils";
const result = bytes.compare(a, b);
if (result === 0) {
console.log("equal");
}
```
## Convert API
The default `convert` facade is a convenience singleton backed by the built-in registry.
```ts
import { convert } from "@peculiar/utils/converters";
const bytes = convert.decode("base64", "AQID");
const text = convert.encode("hex", bytes, { case: "upper" });
```
Deprecated `convert.to(...)` and `convert.from(...)` aliases are still available for temporary migration, but the primary v2 API is `encode` and `decode`.
## Transcode
Direct text-to-text transcoding goes through the registry without a manual intermediate step.
```ts
import { convert } from "@peculiar/utils/converters";
import { hex } from "@peculiar/utils/encoding";
const pemText = convert.transcode("AQID", {
from: "base64",
to: "pem",
toOptions: {
label: "CERTIFICATE",
},
});
const hexText = convert.transcode(pemText, {
from: "pem",
fromOptions: { label: "CERTIFICATE" },
to: "hex",
toOptions: hex.formats.colonUpper,
});
```
There is intentionally no chain API.
## Hex Formatting
The `hex` codec accepts common input styles and can format output explicitly.
```ts
import { hex } from "@peculiar/utils/encoding";
hex.decode("0102030405060708090a0b0c");
hex.decode("01020304 05060708 090a0b0c");
hex.decode("01:02:03:04:05:06:07:08:09:0A:0B:0C");
hex.decode("0x0102030405060708090a0b0c");
hex.encode(new Uint8Array([1, 2, 3, 4]), hex.formats.colonUpper);
hex.encode(new Uint8Array([1, 2, 3, 4]), {
prefix: "0x",
group: {
size: 2,
separator: " ",
},
});
```
Available presets:
- `hex.formats.compact`
- `hex.formats.upper`
- `hex.formats.colon`
- `hex.formats.colonUpper`
- `hex.formats.groupsOf4`
- `hex.formats.prefixed`
## Preserve Formatting
Use `parse` and `format` when you want to keep the original visual style of a hex string.
```ts
import { hex } from "@peculiar/utils/encoding";
const parsed = hex.parse("01:02:03:04:05:06");
parsed.bytes;
parsed.format;
parsed.normalized;
const updated = hex.format(new Uint8Array([0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]), parsed.format);
```
The same capabilities are available through the registry facade:
```ts
import { convert } from "@peculiar/utils/converters";
const parsed = convert.parse("hex", "01:02:03:04");
const formatted = convert.format("hex", new Uint8Array([0xaa, 0xbb, 0xcc, 0xdd]), parsed.format);
```
## PEM Helpers
PEM support stays generic. The package does not parse ASN.1, validate PKI semantics, or handle encrypted PEM containers.
```ts
import { pem } from "@peculiar/utils/pem";
const text = pem.encode("CERTIFICATE", new Uint8Array([1, 2, 3]));
const blocks = pem.decode(text);
const block = pem.find(text, "CERTIFICATE");
const matches = pem.findAll(text, "CERTIFICATE");
const bundle = pem.encodeMany([
{ label: "CERTIFICATE", data: new Uint8Array([1, 2, 3]) },
{ label: "PRIVATE KEY", data: new Uint8Array([4, 5, 6]) },
]);
```
## Safe Decode And Detection
```ts
import { convert } from "@peculiar/utils/converters";
const result = convert.tryDecode("hex", "01:02:03");
if (result.ok) {
console.log(result.bytes);
} else {
console.error(result.error);
}
const candidates = convert.detect("-----BEGIN DATA-----\nAQID\n-----END DATA-----\n", {
formats: ["pem", "base64", "hex"],
});
```
## Custom Registries
Applications can create isolated registries instead of mutating global state.
```ts
import { createConverterRegistry, defaultConverters } from "@peculiar/utils/converters";
const registry = createConverterRegistry(defaultConverters);
registry.register({
name: "base58btc",
aliases: ["b58"],
encode(data) {
return base58btcEncode(data);
},
decode(text) {
return base58btcDecode(text);
},
});
```
Name and alias conflicts throw by default. Use `{ override: true }` only when replacement is intentional.
## Typed Converter Options
Built-in converters expose typed options through the registry facade.
```ts
import { convert } from "@peculiar/utils/converters";
convert.encode("hex", new Uint8Array([1, 2, 3]), {
case: "upper",
});
```
Wrong options are rejected by TypeScript:
```ts
convert.encode("hex", new Uint8Array([1, 2, 3]), {
label: "CERTIFICATE",
});
```
Custom converters can extend the options map via module augmentation:
```ts
declare module "@peculiar/utils/converters" {
interface ConverterOptionsMap {
base58btc: {
encode: Base58EncodeOptions;
decode: Base58DecodeOptions;
};
}
}
```
## Legacy Compatibility
The old `pvtsutils`-style surface is preserved under the legacy entry point.
```ts
import { BufferSourceConverter, Convert, assign, combine, isEqual } from "@peculiar/utils/legacy";
```
-115
View File
@@ -1,115 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isArrayBuffer = isArrayBuffer;
exports.isSharedArrayBuffer = isSharedArrayBuffer;
exports.isArrayBufferLike = isArrayBufferLike;
exports.isArrayBufferView = isArrayBufferView;
exports.isBufferSource = isBufferSource;
exports.assertBufferSource = assertBufferSource;
exports.toUint8Array = toUint8Array;
exports.toUint8ArrayCopy = toUint8ArrayCopy;
exports.toArrayBuffer = toArrayBuffer;
exports.toArrayBufferLike = toArrayBufferLike;
exports.toView = toView;
exports.toViewCopy = toViewCopy;
const ARRAY_BUFFER_TAG = "[object ArrayBuffer]";
const SHARED_ARRAY_BUFFER_TAG = "[object SharedArrayBuffer]";
function tagOf(value) {
return Object.prototype.toString.call(value);
}
function isDataViewConstructor(type) {
return type === DataView || type.prototype instanceof DataView;
}
function bytesPerElement(type) {
if (isDataViewConstructor(type)) {
return 1;
}
const value = type.BYTES_PER_ELEMENT;
return value ?? 1;
}
function isArrayBufferViewLike(value) {
if (ArrayBuffer.isView(value)) {
return true;
}
if (!value || typeof value !== "object") {
return false;
}
const view = value;
return typeof view.byteOffset === "number"
&& typeof view.byteLength === "number"
&& isArrayBufferLike(view.buffer);
}
function copyBytes(data) {
const view = toUint8Array(data);
const copy = new Uint8Array(view.byteLength);
copy.set(view);
return copy;
}
function isArrayBuffer(value) {
return tagOf(value) === ARRAY_BUFFER_TAG;
}
function isSharedArrayBuffer(value) {
return typeof SharedArrayBuffer !== "undefined" && tagOf(value) === SHARED_ARRAY_BUFFER_TAG;
}
function isArrayBufferLike(value) {
return isArrayBuffer(value) || isSharedArrayBuffer(value);
}
function isArrayBufferView(value) {
return isArrayBufferViewLike(value);
}
function isBufferSource(value) {
return isArrayBufferLike(value) || isArrayBufferView(value);
}
function assertBufferSource(value) {
if (!isBufferSource(value)) {
throw new TypeError("Expected ArrayBuffer, SharedArrayBuffer, or ArrayBufferView");
}
}
function toUint8Array(data) {
assertBufferSource(data);
if (isArrayBufferLike(data)) {
return new Uint8Array(data);
}
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
}
function toUint8ArrayCopy(data) {
return copyBytes(data);
}
function toArrayBuffer(data) {
assertBufferSource(data);
if (isArrayBuffer(data)) {
return data;
}
const buffer = new ArrayBuffer(data.byteLength);
new Uint8Array(buffer).set(toUint8Array(data));
return buffer;
}
function toArrayBufferLike(data) {
assertBufferSource(data);
if (isArrayBufferLike(data)) {
return data;
}
if (data.byteOffset === 0 && data.byteLength === data.buffer.byteLength) {
return data.buffer;
}
return copyBytes(data).buffer;
}
function toView(data, type) {
assertBufferSource(data);
if (ArrayBuffer.isView(data) && data.constructor === type) {
return data;
}
const view = toUint8Array(data);
const elementSize = bytesPerElement(type);
if (view.byteOffset % elementSize !== 0 || view.byteLength % elementSize !== 0) {
throw new RangeError(`Cannot create ${type.name} over unaligned byte range`);
}
if (isDataViewConstructor(type)) {
return new type(view.buffer, view.byteOffset, view.byteLength);
}
return new type(view.buffer, view.byteOffset, view.byteLength / elementSize);
}
function toViewCopy(data, type) {
const copy = toUint8ArrayCopy(data);
return toView(copy, type);
}
-41
View File
@@ -1,41 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.concatToUint8Array = concatToUint8Array;
exports.concat = concat;
const buffer_source_js_1 = require("./buffer-source.js");
function concatToUint8Array(buffers) {
const views = [];
let length = 0;
for (const buffer of buffers) {
const view = (0, buffer_source_js_1.toUint8Array)(buffer);
views.push(view);
length += view.byteLength;
}
const result = new Uint8Array(length);
let offset = 0;
for (const view of views) {
result.set(view, offset);
offset += view.byteLength;
}
return result;
}
function concat(first, second, ...rest) {
let buffers;
let type;
if (typeof second === "function") {
buffers = Array.from(first);
type = second;
}
else if ((0, buffer_source_js_1.isBufferSource)(first)) {
buffers = [first, second, ...rest].filter(buffer_source_js_1.isBufferSource);
}
else {
buffers = Array.from(first);
if (second) {
buffers.push(second);
}
buffers.push(...rest);
}
const bytes = concatToUint8Array(buffers);
return type ? (0, buffer_source_js_1.toView)(bytes, type) : bytes.buffer;
}
-17
View File
@@ -1,17 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.equal = equal;
const buffer_source_js_1 = require("./buffer-source.js");
function equal(a, b, options = {}) {
const left = (0, buffer_source_js_1.toUint8Array)(a);
const right = (0, buffer_source_js_1.toUint8Array)(b);
if (!options.constantTime && left.byteLength !== right.byteLength) {
return false;
}
const length = Math.max(left.byteLength, right.byteLength);
let diff = left.byteLength ^ right.byteLength;
for (let i = 0; i < length; i++) {
diff |= (left[i] ?? 0) ^ (right[i] ?? 0);
}
return diff === 0;
}
-31
View File
@@ -1,31 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.tail = exports.startsWith = exports.slice = exports.lastIndexOf = exports.indexOf = exports.includes = exports.endsWith = exports.copy = exports.compare = exports.equal = exports.concatToUint8Array = exports.concat = exports.toViewCopy = exports.toView = exports.toUint8ArrayCopy = exports.toUint8Array = exports.toArrayBufferLike = exports.toArrayBuffer = exports.isSharedArrayBuffer = exports.isBufferSource = exports.isArrayBufferView = exports.isArrayBufferLike = exports.isArrayBuffer = exports.assertBufferSource = void 0;
var buffer_source_js_1 = require("./buffer-source.js");
Object.defineProperty(exports, "assertBufferSource", { enumerable: true, get: function () { return buffer_source_js_1.assertBufferSource; } });
Object.defineProperty(exports, "isArrayBuffer", { enumerable: true, get: function () { return buffer_source_js_1.isArrayBuffer; } });
Object.defineProperty(exports, "isArrayBufferLike", { enumerable: true, get: function () { return buffer_source_js_1.isArrayBufferLike; } });
Object.defineProperty(exports, "isArrayBufferView", { enumerable: true, get: function () { return buffer_source_js_1.isArrayBufferView; } });
Object.defineProperty(exports, "isBufferSource", { enumerable: true, get: function () { return buffer_source_js_1.isBufferSource; } });
Object.defineProperty(exports, "isSharedArrayBuffer", { enumerable: true, get: function () { return buffer_source_js_1.isSharedArrayBuffer; } });
Object.defineProperty(exports, "toArrayBuffer", { enumerable: true, get: function () { return buffer_source_js_1.toArrayBuffer; } });
Object.defineProperty(exports, "toArrayBufferLike", { enumerable: true, get: function () { return buffer_source_js_1.toArrayBufferLike; } });
Object.defineProperty(exports, "toUint8Array", { enumerable: true, get: function () { return buffer_source_js_1.toUint8Array; } });
Object.defineProperty(exports, "toUint8ArrayCopy", { enumerable: true, get: function () { return buffer_source_js_1.toUint8ArrayCopy; } });
Object.defineProperty(exports, "toView", { enumerable: true, get: function () { return buffer_source_js_1.toView; } });
Object.defineProperty(exports, "toViewCopy", { enumerable: true, get: function () { return buffer_source_js_1.toViewCopy; } });
var concat_js_1 = require("./concat.js");
Object.defineProperty(exports, "concat", { enumerable: true, get: function () { return concat_js_1.concat; } });
Object.defineProperty(exports, "concatToUint8Array", { enumerable: true, get: function () { return concat_js_1.concatToUint8Array; } });
var equal_js_1 = require("./equal.js");
Object.defineProperty(exports, "equal", { enumerable: true, get: function () { return equal_js_1.equal; } });
var sequence_js_1 = require("./sequence.js");
Object.defineProperty(exports, "compare", { enumerable: true, get: function () { return sequence_js_1.compare; } });
Object.defineProperty(exports, "copy", { enumerable: true, get: function () { return sequence_js_1.copy; } });
Object.defineProperty(exports, "endsWith", { enumerable: true, get: function () { return sequence_js_1.endsWith; } });
Object.defineProperty(exports, "includes", { enumerable: true, get: function () { return sequence_js_1.includes; } });
Object.defineProperty(exports, "indexOf", { enumerable: true, get: function () { return sequence_js_1.indexOf; } });
Object.defineProperty(exports, "lastIndexOf", { enumerable: true, get: function () { return sequence_js_1.lastIndexOf; } });
Object.defineProperty(exports, "slice", { enumerable: true, get: function () { return sequence_js_1.slice; } });
Object.defineProperty(exports, "startsWith", { enumerable: true, get: function () { return sequence_js_1.startsWith; } });
Object.defineProperty(exports, "tail", { enumerable: true, get: function () { return sequence_js_1.tail; } });
-161
View File
@@ -1,161 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.indexOf = indexOf;
exports.lastIndexOf = lastIndexOf;
exports.includes = includes;
exports.startsWith = startsWith;
exports.endsWith = endsWith;
exports.slice = slice;
exports.tail = tail;
exports.copy = copy;
exports.compare = compare;
const buffer_source_js_1 = require("./buffer-source.js");
function clampIndex(value, fallback, length) {
const normalized = Number.isFinite(value) ? Math.trunc(value) : fallback;
if (normalized <= 0) {
return 0;
}
if (normalized >= length) {
return length;
}
return normalized;
}
function normalizeForwardRange(length, options) {
const start = clampIndex(options?.start, 0, length);
const end = clampIndex(options?.end, length, length);
return end >= start ? [start, end] : [start, start];
}
function normalizeReverseRange(length, options) {
const start = clampIndex(options?.start, length, length);
const end = clampIndex(options?.end, 0, length);
return start >= end ? [end, start] : [start, start];
}
function normalizeSliceIndex(value, fallback, length) {
const normalized = Number.isFinite(value) ? Math.trunc(value) : fallback;
if (normalized < 0) {
return Math.max(length + normalized, 0);
}
if (normalized > length) {
return length;
}
return normalized;
}
function encodeAscii(text) {
const bytes = new Uint8Array(text.length);
for (let i = 0; i < text.length; i++) {
bytes[i] = text.charCodeAt(i) & 0xff;
}
return bytes;
}
function encodeUtf8(text) {
return new TextEncoder().encode(text);
}
function toPatternBytes(pattern, options) {
if (typeof pattern === "string") {
return options?.encoding === "utf8" ? encodeUtf8(pattern) : encodeAscii(pattern);
}
return (0, buffer_source_js_1.toUint8Array)(pattern);
}
function bytesEqualAt(data, pattern, offset) {
for (let index = 0; index < pattern.byteLength; index++) {
if (data[offset + index] !== pattern[index]) {
return false;
}
}
return true;
}
function indexOf(data, pattern, options) {
const bytes = (0, buffer_source_js_1.toUint8Array)(data);
const needle = toPatternBytes(pattern, options);
const [start, end] = normalizeForwardRange(bytes.byteLength, options);
if (needle.byteLength === 0) {
return start;
}
const lastOffset = end - needle.byteLength;
if (lastOffset < start) {
return -1;
}
for (let offset = start; offset <= lastOffset; offset++) {
if (bytesEqualAt(bytes, needle, offset)) {
return offset;
}
}
return -1;
}
function lastIndexOf(data, pattern, options) {
const bytes = (0, buffer_source_js_1.toUint8Array)(data);
const needle = toPatternBytes(pattern, options);
const [end, start] = normalizeReverseRange(bytes.byteLength, options);
if (needle.byteLength === 0) {
return start;
}
const firstOffset = start - needle.byteLength;
if (firstOffset < end) {
return -1;
}
for (let offset = firstOffset; offset >= end; offset--) {
if (bytesEqualAt(bytes, needle, offset)) {
return offset;
}
}
return -1;
}
function includes(data, pattern, options) {
return indexOf(data, pattern, options) !== -1;
}
function startsWith(data, pattern, options) {
const bytes = (0, buffer_source_js_1.toUint8Array)(data);
const needle = toPatternBytes(pattern, options);
if (needle.byteLength > bytes.byteLength) {
return false;
}
return bytesEqualAt(bytes, needle, 0);
}
function endsWith(data, pattern, options) {
const bytes = (0, buffer_source_js_1.toUint8Array)(data);
const needle = toPatternBytes(pattern, options);
if (needle.byteLength > bytes.byteLength) {
return false;
}
return bytesEqualAt(bytes, needle, bytes.byteLength - needle.byteLength);
}
function slice(data, start, end) {
const bytes = (0, buffer_source_js_1.toUint8Array)(data);
const normalizedStart = normalizeSliceIndex(start, 0, bytes.byteLength);
const normalizedEnd = normalizeSliceIndex(end, bytes.byteLength, bytes.byteLength);
if (normalizedEnd <= normalizedStart) {
return bytes.subarray(normalizedStart, normalizedStart);
}
return bytes.subarray(normalizedStart, normalizedEnd);
}
function tail(data, length) {
const bytes = (0, buffer_source_js_1.toUint8Array)(data);
const normalizedLength = Number.isFinite(length) ? Math.max(0, Math.trunc(length)) : 0;
if (normalizedLength >= bytes.byteLength) {
return bytes;
}
return bytes.subarray(bytes.byteLength - normalizedLength);
}
function copy(data) {
return (0, buffer_source_js_1.toUint8ArrayCopy)(data);
}
function compare(a, b) {
const left = (0, buffer_source_js_1.toUint8Array)(a);
const right = (0, buffer_source_js_1.toUint8Array)(b);
const limit = Math.min(left.byteLength, right.byteLength);
for (let index = 0; index < limit; index++) {
if (left[index] < right[index]) {
return -1;
}
if (left[index] > right[index]) {
return 1;
}
}
if (left.byteLength < right.byteLength) {
return -1;
}
if (left.byteLength > right.byteLength) {
return 1;
}
return 0;
}