Delete 100 files via Git Manager GUI
This commit is contained in:
-10
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because one or more lines are too long
-21
@@ -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
@@ -1,45 +0,0 @@
|
|||||||
# `@peculiar/asn1-schema`
|
|
||||||
|
|
||||||
[](https://github.com/PeculiarVentures/asn1-schema/blob/master/packages/schema/LICENSE)
|
|
||||||
[](https://badge.fury.io/js/%40peculiar%2Fasn1-schema)
|
|
||||||
|
|
||||||
[](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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,4 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
const tslib_1 = require("tslib");
|
|
||||||
tslib_1.__exportStar(require("./schema_validation"), exports);
|
|
||||||
-7
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,2 +0,0 @@
|
|||||||
"use strict";
|
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
|
||||||
-71
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1 +0,0 @@
|
|||||||
export * from "./schema_validation";
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
export class AsnSchemaValidationError extends Error {
|
|
||||||
schemas = [];
|
|
||||||
}
|
|
||||||
-39
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,2 +0,0 @@
|
|||||||
import { AsnSchemaStorage } from "./schema";
|
|
||||||
export const schemaStorage = new AsnSchemaStorage();
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export {};
|
|
||||||
-66
@@ -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
@@ -1,2 +0,0 @@
|
|||||||
export * from "./bit_string";
|
|
||||||
export * from "./octet_string";
|
|
||||||
-40
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1 +0,0 @@
|
|||||||
export * from "./schema_validation";
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
export declare class AsnSchemaValidationError extends Error {
|
|
||||||
schemas: string[];
|
|
||||||
}
|
|
||||||
-5
@@ -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
@@ -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
@@ -1,3 +0,0 @@
|
|||||||
export declare abstract class AsnArray<T> extends Array<T> {
|
|
||||||
constructor(items?: T[]);
|
|
||||||
}
|
|
||||||
-72
@@ -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
@@ -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
@@ -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
@@ -1,2 +0,0 @@
|
|||||||
import { AsnSchemaStorage } from "./schema";
|
|
||||||
export declare const schemaStorage: AsnSchemaStorage;
|
|
||||||
-42
@@ -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
@@ -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
@@ -1,2 +0,0 @@
|
|||||||
export * from "./bit_string";
|
|
||||||
export * from "./octet_string";
|
|
||||||
-15
@@ -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
@@ -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
@@ -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
@@ -1,158 +0,0 @@
|
|||||||
# JSON-SCHEMA
|
|
||||||
|
|
||||||
[](https://raw.githubusercontent.com/PeculiarVentures/json-schema/master/LICENSE.md)
|
|
||||||
[](https://circleci.com/gh/PeculiarVentures/json-schema)
|
|
||||||
[](https://coveralls.io/github/PeculiarVentures/json-schema?branch=master)
|
|
||||||
[](https://badge.fury.io/js/%40peculiar%2Fjson-schema)
|
|
||||||
|
|
||||||
[](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
@@ -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
@@ -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
@@ -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
@@ -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";
|
|
||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
export declare class JsonError extends Error {
|
|
||||||
message: string;
|
|
||||||
innerError?: Error | undefined;
|
|
||||||
constructor(message: string, innerError?: Error | undefined);
|
|
||||||
}
|
|
||||||
-10
@@ -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);
|
|
||||||
}
|
|
||||||
-5
@@ -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);
|
|
||||||
}
|
|
||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
import { JsonError } from "./json_error";
|
|
||||||
export declare class SerializerError extends JsonError {
|
|
||||||
schemaName: string;
|
|
||||||
constructor(schemaName: string, message: string, innerError?: Error);
|
|
||||||
}
|
|
||||||
-6
@@ -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);
|
|
||||||
}
|
|
||||||
-3
@@ -1,3 +0,0 @@
|
|||||||
import { JsonError } from "./json_error";
|
|
||||||
export declare class ValidationError extends JsonError {
|
|
||||||
}
|
|
||||||
-5
@@ -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
@@ -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
@@ -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
@@ -1,6 +0,0 @@
|
|||||||
export declare enum JsonPropTypes {
|
|
||||||
Any = 0,
|
|
||||||
Boolean = 1,
|
|
||||||
Number = 2,
|
|
||||||
String = 3
|
|
||||||
}
|
|
||||||
-35
@@ -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
@@ -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
@@ -1,6 +0,0 @@
|
|||||||
import { JsonSchemaStorage } from "./schema";
|
|
||||||
/**
|
|
||||||
* Name of default schema
|
|
||||||
*/
|
|
||||||
export declare const DEFAULT_SCHEMA = "default";
|
|
||||||
export declare const schemaStorage: JsonSchemaStorage;
|
|
||||||
-9
@@ -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
@@ -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;
|
|
||||||
}
|
|
||||||
-6
@@ -1,6 +0,0 @@
|
|||||||
import { IValidation } from "../types";
|
|
||||||
export declare class EnumerationValidation implements IValidation {
|
|
||||||
private enumeration;
|
|
||||||
constructor(enumeration: string[]);
|
|
||||||
validate(value: any): void;
|
|
||||||
}
|
|
||||||
-7
@@ -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;
|
|
||||||
}
|
|
||||||
-7
@@ -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;
|
|
||||||
}
|
|
||||||
-5
@@ -1,5 +0,0 @@
|
|||||||
export * from "./pattern";
|
|
||||||
export * from "./inclusive";
|
|
||||||
export * from "./exclusive";
|
|
||||||
export * from "./length";
|
|
||||||
export * from "./enumeration";
|
|
||||||
-8
@@ -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;
|
|
||||||
}
|
|
||||||
-6
@@ -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
@@ -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
@@ -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
@@ -1,299 +0,0 @@
|
|||||||
# @peculiar/utils
|
|
||||||
|
|
||||||
[](https://www.npmjs.com/package/@peculiar/utils)
|
|
||||||
[](https://github.com/PeculiarVentures/pvtsutils/actions/workflows/test.yml)
|
|
||||||
[](https://coveralls.io/github/PeculiarVentures/pvtsutils?branch=master)
|
|
||||||
[](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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user