@gjsify/crypto 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +27 -0
  2. package/lib/esm/asn1.js +504 -0
  3. package/lib/esm/bigint-math.js +34 -0
  4. package/lib/esm/cipher.js +1272 -0
  5. package/lib/esm/constants.js +15 -0
  6. package/lib/esm/crypto-utils.js +47 -0
  7. package/lib/esm/dh.js +411 -0
  8. package/lib/esm/ecdh.js +356 -0
  9. package/lib/esm/ecdsa.js +125 -0
  10. package/lib/esm/hash.js +100 -0
  11. package/lib/esm/hkdf.js +58 -0
  12. package/lib/esm/hmac.js +93 -0
  13. package/lib/esm/index.js +158 -0
  14. package/lib/esm/key-object.js +330 -0
  15. package/lib/esm/mgf1.js +27 -0
  16. package/lib/esm/pbkdf2.js +68 -0
  17. package/lib/esm/public-encrypt.js +175 -0
  18. package/lib/esm/random.js +138 -0
  19. package/lib/esm/rsa-oaep.js +95 -0
  20. package/lib/esm/rsa-pss.js +100 -0
  21. package/lib/esm/scrypt.js +134 -0
  22. package/lib/esm/sign.js +248 -0
  23. package/lib/esm/timing-safe-equal.js +13 -0
  24. package/lib/esm/x509.js +214 -0
  25. package/lib/types/asn1.d.ts +87 -0
  26. package/lib/types/bigint-math.d.ts +13 -0
  27. package/lib/types/cipher.d.ts +84 -0
  28. package/lib/types/constants.d.ts +10 -0
  29. package/lib/types/crypto-utils.d.ts +22 -0
  30. package/lib/types/dh.d.ts +79 -0
  31. package/lib/types/ecdh.d.ts +96 -0
  32. package/lib/types/ecdsa.d.ts +21 -0
  33. package/lib/types/hash.d.ts +25 -0
  34. package/lib/types/hkdf.d.ts +9 -0
  35. package/lib/types/hmac.d.ts +20 -0
  36. package/lib/types/index.d.ts +105 -0
  37. package/lib/types/key-object.d.ts +36 -0
  38. package/lib/types/mgf1.d.ts +5 -0
  39. package/lib/types/pbkdf2.d.ts +9 -0
  40. package/lib/types/public-encrypt.d.ts +42 -0
  41. package/lib/types/random.d.ts +22 -0
  42. package/lib/types/rsa-oaep.d.ts +8 -0
  43. package/lib/types/rsa-pss.d.ts +8 -0
  44. package/lib/types/scrypt.d.ts +11 -0
  45. package/lib/types/sign.d.ts +61 -0
  46. package/lib/types/timing-safe-equal.d.ts +6 -0
  47. package/lib/types/x509.d.ts +72 -0
  48. package/package.json +45 -0
  49. package/src/asn1.ts +797 -0
  50. package/src/bigint-math.ts +45 -0
  51. package/src/cipher.spec.ts +332 -0
  52. package/src/cipher.ts +952 -0
  53. package/src/constants.ts +16 -0
  54. package/src/crypto-utils.ts +64 -0
  55. package/src/dh.spec.ts +111 -0
  56. package/src/dh.ts +761 -0
  57. package/src/ecdh.spec.ts +116 -0
  58. package/src/ecdh.ts +624 -0
  59. package/src/ecdsa.ts +243 -0
  60. package/src/extended.spec.ts +444 -0
  61. package/src/gcm.spec.ts +141 -0
  62. package/src/hash.spec.ts +86 -0
  63. package/src/hash.ts +119 -0
  64. package/src/hkdf.ts +99 -0
  65. package/src/hmac.spec.ts +64 -0
  66. package/src/hmac.ts +123 -0
  67. package/src/index.ts +93 -0
  68. package/src/key-object.spec.ts +202 -0
  69. package/src/key-object.ts +401 -0
  70. package/src/mgf1.ts +37 -0
  71. package/src/pbkdf2.spec.ts +76 -0
  72. package/src/pbkdf2.ts +106 -0
  73. package/src/public-encrypt.ts +288 -0
  74. package/src/random.spec.ts +133 -0
  75. package/src/random.ts +183 -0
  76. package/src/rsa-oaep.ts +167 -0
  77. package/src/rsa-pss.ts +190 -0
  78. package/src/scrypt.spec.ts +90 -0
  79. package/src/scrypt.ts +191 -0
  80. package/src/sign.spec.ts +160 -0
  81. package/src/sign.ts +319 -0
  82. package/src/test.mts +19 -0
  83. package/src/timing-safe-equal.ts +21 -0
  84. package/src/x509.spec.ts +210 -0
  85. package/src/x509.ts +262 -0
  86. package/tsconfig.json +31 -0
  87. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,175 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { randomBytes } from "./random.js";
3
+ import { parsePemKey, rsaKeySize } from "./asn1.js";
4
+ import { modPow, bigIntToBytes, bytesToBigInt } from "./bigint-math.js";
5
+ function extractPem(key) {
6
+ if (typeof key === "string") {
7
+ return key;
8
+ }
9
+ if (Buffer.isBuffer(key) || key instanceof Uint8Array) {
10
+ return Buffer.from(key).toString("utf8");
11
+ }
12
+ if (key && typeof key === "object" && "key" in key) {
13
+ const k = key.key;
14
+ if (typeof k === "string") return k;
15
+ if (Buffer.isBuffer(k) || k instanceof Uint8Array) return Buffer.from(k).toString("utf8");
16
+ }
17
+ throw new TypeError("Invalid key argument");
18
+ }
19
+ function pkcs1v15Type2Pad(data, keyLen) {
20
+ const maxDataLen = keyLen - 11;
21
+ if (data.length > maxDataLen) {
22
+ throw new Error(`Data too long for key size. Max ${maxDataLen} bytes, got ${data.length}`);
23
+ }
24
+ const padLen = keyLen - data.length - 3;
25
+ const em = new Uint8Array(keyLen);
26
+ em[0] = 0;
27
+ em[1] = 2;
28
+ const padding = randomBytes(padLen);
29
+ for (let i = 0; i < padLen; i++) {
30
+ while (padding[i] === 0) {
31
+ const replacement = randomBytes(1);
32
+ padding[i] = replacement[0];
33
+ }
34
+ em[2 + i] = padding[i];
35
+ }
36
+ em[2 + padLen] = 0;
37
+ em.set(data, 3 + padLen);
38
+ return em;
39
+ }
40
+ function pkcs1v15Type1Pad(data, keyLen) {
41
+ const maxDataLen = keyLen - 11;
42
+ if (data.length > maxDataLen) {
43
+ throw new Error(`Data too long for key size. Max ${maxDataLen} bytes, got ${data.length}`);
44
+ }
45
+ const padLen = keyLen - data.length - 3;
46
+ const em = new Uint8Array(keyLen);
47
+ em[0] = 0;
48
+ em[1] = 1;
49
+ for (let i = 2; i < 2 + padLen; i++) {
50
+ em[i] = 255;
51
+ }
52
+ em[2 + padLen] = 0;
53
+ em.set(data, 3 + padLen);
54
+ return em;
55
+ }
56
+ function pkcs1v15Type2Unpad(em) {
57
+ if (em.length < 11) {
58
+ throw new Error("Decryption error: message too short");
59
+ }
60
+ if (em[0] !== 0 || em[1] !== 2) {
61
+ throw new Error("Decryption error: invalid padding");
62
+ }
63
+ let sepIdx = 2;
64
+ while (sepIdx < em.length && em[sepIdx] !== 0) {
65
+ sepIdx++;
66
+ }
67
+ if (sepIdx >= em.length || sepIdx < 10) {
68
+ throw new Error("Decryption error: invalid padding");
69
+ }
70
+ return em.slice(sepIdx + 1);
71
+ }
72
+ function pkcs1v15Type1Unpad(em) {
73
+ if (em.length < 11) {
74
+ throw new Error("Decryption error: message too short");
75
+ }
76
+ if (em[0] !== 0 || em[1] !== 1) {
77
+ throw new Error("Decryption error: invalid padding");
78
+ }
79
+ let sepIdx = 2;
80
+ while (sepIdx < em.length && em[sepIdx] === 255) {
81
+ sepIdx++;
82
+ }
83
+ if (sepIdx >= em.length || em[sepIdx] !== 0 || sepIdx < 10) {
84
+ throw new Error("Decryption error: invalid padding");
85
+ }
86
+ return em.slice(sepIdx + 1);
87
+ }
88
+ function publicEncrypt(key, buffer) {
89
+ const pem = extractPem(key);
90
+ const parsed = parsePemKey(pem);
91
+ let n;
92
+ let e;
93
+ if (parsed.type === "rsa-public") {
94
+ n = parsed.components.n;
95
+ e = parsed.components.e;
96
+ } else if (parsed.type === "rsa-private") {
97
+ n = parsed.components.n;
98
+ e = parsed.components.e;
99
+ } else {
100
+ throw new Error("Key must be an RSA public or private key");
101
+ }
102
+ const keyLen = rsaKeySize(n);
103
+ const data = buffer instanceof Uint8Array ? buffer : Buffer.from(buffer);
104
+ const em = pkcs1v15Type2Pad(data, keyLen);
105
+ const m = bytesToBigInt(em);
106
+ const c = modPow(m, e, n);
107
+ return Buffer.from(bigIntToBytes(c, keyLen));
108
+ }
109
+ function privateDecrypt(key, buffer) {
110
+ const pem = extractPem(key);
111
+ const parsed = parsePemKey(pem);
112
+ if (parsed.type !== "rsa-private") {
113
+ throw new Error("Key must be an RSA private key");
114
+ }
115
+ const { n, d } = parsed.components;
116
+ const keyLen = rsaKeySize(n);
117
+ const data = buffer instanceof Uint8Array ? buffer : Buffer.from(buffer);
118
+ if (data.length !== keyLen) {
119
+ throw new Error(`Data length (${data.length}) does not match key length (${keyLen})`);
120
+ }
121
+ const c = bytesToBigInt(data);
122
+ if (c >= n) {
123
+ throw new Error("Decryption error: cipher value out of range");
124
+ }
125
+ const m = modPow(c, d, n);
126
+ const em = bigIntToBytes(m, keyLen);
127
+ return Buffer.from(pkcs1v15Type2Unpad(em));
128
+ }
129
+ function privateEncrypt(key, buffer) {
130
+ const pem = extractPem(key);
131
+ const parsed = parsePemKey(pem);
132
+ if (parsed.type !== "rsa-private") {
133
+ throw new Error("Key must be an RSA private key");
134
+ }
135
+ const { n, d } = parsed.components;
136
+ const keyLen = rsaKeySize(n);
137
+ const data = buffer instanceof Uint8Array ? buffer : Buffer.from(buffer);
138
+ const em = pkcs1v15Type1Pad(data, keyLen);
139
+ const m = bytesToBigInt(em);
140
+ const c = modPow(m, d, n);
141
+ return Buffer.from(bigIntToBytes(c, keyLen));
142
+ }
143
+ function publicDecrypt(key, buffer) {
144
+ const pem = extractPem(key);
145
+ const parsed = parsePemKey(pem);
146
+ let n;
147
+ let e;
148
+ if (parsed.type === "rsa-public") {
149
+ n = parsed.components.n;
150
+ e = parsed.components.e;
151
+ } else if (parsed.type === "rsa-private") {
152
+ n = parsed.components.n;
153
+ e = parsed.components.e;
154
+ } else {
155
+ throw new Error("Key must be an RSA public or private key");
156
+ }
157
+ const keyLen = rsaKeySize(n);
158
+ const data = buffer instanceof Uint8Array ? buffer : Buffer.from(buffer);
159
+ if (data.length !== keyLen) {
160
+ throw new Error(`Data length (${data.length}) does not match key length (${keyLen})`);
161
+ }
162
+ const c = bytesToBigInt(data);
163
+ if (c >= n) {
164
+ throw new Error("Decryption error: cipher value out of range");
165
+ }
166
+ const m = modPow(c, e, n);
167
+ const em = bigIntToBytes(m, keyLen);
168
+ return Buffer.from(pkcs1v15Type1Unpad(em));
169
+ }
170
+ export {
171
+ privateDecrypt,
172
+ privateEncrypt,
173
+ publicDecrypt,
174
+ publicEncrypt
175
+ };
@@ -0,0 +1,138 @@
1
+ import { Buffer } from "node:buffer";
2
+ const hasWebCrypto = typeof globalThis.crypto !== "undefined" && typeof globalThis.crypto.getRandomValues === "function";
3
+ function fillRandom(view) {
4
+ if (hasWebCrypto) {
5
+ for (let offset = 0; offset < view.length; offset += 65536) {
6
+ const length = Math.min(view.length - offset, 65536);
7
+ const slice = new Uint8Array(view.buffer, view.byteOffset + offset, length);
8
+ globalThis.crypto.getRandomValues(slice);
9
+ }
10
+ } else {
11
+ try {
12
+ const GLib = imports.gi.GLib;
13
+ for (let i = 0; i < view.length; i++) {
14
+ view[i] = GLib.random_int_range(0, 256);
15
+ }
16
+ } catch {
17
+ for (let i = 0; i < view.length; i++) {
18
+ view[i] = Math.floor(Math.random() * 256);
19
+ }
20
+ }
21
+ }
22
+ }
23
+ function randomBytes(size, callback) {
24
+ if (typeof size !== "number" || size < 0 || !Number.isInteger(size)) {
25
+ throw new TypeError(`The "size" argument must be a non-negative integer. Received ${size}`);
26
+ }
27
+ try {
28
+ const buf = Buffer.alloc(size);
29
+ if (size > 0) {
30
+ fillRandom(new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
31
+ }
32
+ if (callback) {
33
+ callback(null, buf);
34
+ } else {
35
+ return buf;
36
+ }
37
+ } catch (err) {
38
+ if (callback) {
39
+ callback(err, Buffer.alloc(0));
40
+ } else {
41
+ throw err;
42
+ }
43
+ }
44
+ }
45
+ function randomFillSync(buffer, offset = 0, size) {
46
+ const length = size ?? buffer.length - offset;
47
+ if (offset < 0 || offset > buffer.length) {
48
+ throw new RangeError(`The value of "offset" is out of range. Received ${offset}`);
49
+ }
50
+ if (length < 0 || offset + length > buffer.length) {
51
+ throw new RangeError(`The value of "size" is out of range. Received ${length}`);
52
+ }
53
+ if (length > 0) {
54
+ const byteOffset = buffer instanceof Buffer ? buffer.byteOffset : buffer.byteOffset;
55
+ const view = new Uint8Array(buffer.buffer, byteOffset + offset, length);
56
+ fillRandom(view);
57
+ }
58
+ return buffer;
59
+ }
60
+ function randomFill(buffer, offset, size, callback) {
61
+ let _offset;
62
+ let _size;
63
+ let _callback;
64
+ if (typeof offset === "function") {
65
+ _callback = offset;
66
+ _offset = 0;
67
+ _size = buffer.length;
68
+ } else if (typeof size === "function") {
69
+ _callback = size;
70
+ _offset = offset;
71
+ _size = buffer.length - offset;
72
+ } else {
73
+ _callback = callback;
74
+ _offset = offset;
75
+ _size = size;
76
+ }
77
+ try {
78
+ randomFillSync(buffer, _offset, _size);
79
+ _callback(null, buffer);
80
+ } catch (err) {
81
+ _callback(err, buffer);
82
+ }
83
+ }
84
+ function randomUUID() {
85
+ if (hasWebCrypto && typeof globalThis.crypto.randomUUID === "function") {
86
+ return globalThis.crypto.randomUUID();
87
+ }
88
+ const bytes = new Uint8Array(16);
89
+ fillRandom(bytes);
90
+ bytes[6] = bytes[6] & 15 | 64;
91
+ bytes[8] = bytes[8] & 63 | 128;
92
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
93
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
94
+ }
95
+ function randomInt(min, max, callback) {
96
+ let _min;
97
+ let _max;
98
+ let _callback;
99
+ if (typeof max === "function") {
100
+ _callback = max;
101
+ _max = min;
102
+ _min = 0;
103
+ } else if (typeof max === "number") {
104
+ _min = min;
105
+ _max = max;
106
+ _callback = callback;
107
+ } else {
108
+ _max = min;
109
+ _min = 0;
110
+ _callback = callback;
111
+ }
112
+ if (!Number.isInteger(_min)) {
113
+ throw new TypeError(`The "min" argument must be a safe integer. Received ${_min}`);
114
+ }
115
+ if (!Number.isInteger(_max)) {
116
+ throw new TypeError(`The "max" argument must be a safe integer. Received ${_max}`);
117
+ }
118
+ if (_min >= _max) {
119
+ throw new RangeError(`The value of "min" must be less than "max". Received min: ${_min}, max: ${_max}`);
120
+ }
121
+ const range = _max - _min;
122
+ const bytes = new Uint32Array(1);
123
+ const view = new Uint8Array(bytes.buffer);
124
+ fillRandom(view);
125
+ const value = _min + bytes[0] % range;
126
+ if (_callback) {
127
+ _callback(null, value);
128
+ } else {
129
+ return value;
130
+ }
131
+ }
132
+ export {
133
+ randomBytes,
134
+ randomFill,
135
+ randomFillSync,
136
+ randomInt,
137
+ randomUUID
138
+ };
@@ -0,0 +1,95 @@
1
+ import { Hash } from "./hash.js";
2
+ import { randomBytes } from "./random.js";
3
+ import { mgf1 } from "./mgf1.js";
4
+ import { parsePemKey, rsaKeySize } from "./asn1.js";
5
+ import { hashSize } from "./crypto-utils.js";
6
+ import { modPow, bigIntToBytes, bytesToBigInt } from "./bigint-math.js";
7
+ function hashDigest(algo, data) {
8
+ const h = new Hash(algo);
9
+ h.update(data);
10
+ return new Uint8Array(h.digest());
11
+ }
12
+ function rsaOaepEncrypt(hashAlgo, pubKeyPem, plaintext, label) {
13
+ const parsed = parsePemKey(pubKeyPem);
14
+ let n, e;
15
+ if (parsed.type === "rsa-public") {
16
+ ({ n, e } = parsed.components);
17
+ } else if (parsed.type === "rsa-private") {
18
+ ({ n, e } = parsed.components);
19
+ } else {
20
+ throw new Error("RSA-OAEP: expected RSA key");
21
+ }
22
+ const keyBytes = Math.ceil(rsaKeySize(n) / 8);
23
+ const hLen = hashSize(hashAlgo);
24
+ if (plaintext.length > keyBytes - 2 * hLen - 2) {
25
+ throw new Error("RSA-OAEP: message too long");
26
+ }
27
+ const lHash = hashDigest(hashAlgo, label || new Uint8Array(0));
28
+ const dbLen = keyBytes - hLen - 1;
29
+ const DB = new Uint8Array(dbLen);
30
+ DB.set(lHash, 0);
31
+ DB[dbLen - plaintext.length - 1] = 1;
32
+ DB.set(plaintext, dbLen - plaintext.length);
33
+ const seed = new Uint8Array(randomBytes(hLen));
34
+ const dbMask = mgf1(hashAlgo, seed, dbLen);
35
+ const maskedDB = new Uint8Array(dbLen);
36
+ for (let i = 0; i < dbLen; i++) maskedDB[i] = DB[i] ^ dbMask[i];
37
+ const seedMask = mgf1(hashAlgo, maskedDB, hLen);
38
+ const maskedSeed = new Uint8Array(hLen);
39
+ for (let i = 0; i < hLen; i++) maskedSeed[i] = seed[i] ^ seedMask[i];
40
+ const EM = new Uint8Array(keyBytes);
41
+ EM[0] = 0;
42
+ EM.set(maskedSeed, 1);
43
+ EM.set(maskedDB, 1 + hLen);
44
+ const m = bytesToBigInt(EM);
45
+ const c = modPow(m, e, n);
46
+ return bigIntToBytes(c, keyBytes);
47
+ }
48
+ function rsaOaepDecrypt(hashAlgo, privKeyPem, ciphertext, label) {
49
+ const parsed = parsePemKey(privKeyPem);
50
+ if (parsed.type !== "rsa-private") throw new Error("RSA-OAEP: expected RSA private key");
51
+ const { n, d } = parsed.components;
52
+ const keyBytes = Math.ceil(rsaKeySize(n) / 8);
53
+ const hLen = hashSize(hashAlgo);
54
+ if (ciphertext.length !== keyBytes || keyBytes < 2 * hLen + 2) {
55
+ throw new Error("RSA-OAEP: decryption error");
56
+ }
57
+ const c = bytesToBigInt(ciphertext);
58
+ const m = modPow(c, d, n);
59
+ const EM = bigIntToBytes(m, keyBytes);
60
+ const Y = EM[0];
61
+ const maskedSeed = EM.slice(1, 1 + hLen);
62
+ const maskedDB = EM.slice(1 + hLen);
63
+ const dbLen = keyBytes - hLen - 1;
64
+ const seedMask = mgf1(hashAlgo, maskedDB, hLen);
65
+ const seed = new Uint8Array(hLen);
66
+ for (let i = 0; i < hLen; i++) seed[i] = maskedSeed[i] ^ seedMask[i];
67
+ const dbMask = mgf1(hashAlgo, seed, dbLen);
68
+ const DB = new Uint8Array(dbLen);
69
+ for (let i = 0; i < dbLen; i++) DB[i] = maskedDB[i] ^ dbMask[i];
70
+ const lHash = hashDigest(hashAlgo, label || new Uint8Array(0));
71
+ const lHashPrime = DB.slice(0, hLen);
72
+ let valid = Y === 0 ? 1 : 0;
73
+ for (let i = 0; i < hLen; i++) {
74
+ valid &= lHash[i] === lHashPrime[i] ? 1 : 0;
75
+ }
76
+ let sepIdx = -1;
77
+ for (let i = hLen; i < dbLen; i++) {
78
+ if (DB[i] === 1) {
79
+ sepIdx = i;
80
+ break;
81
+ } else if (DB[i] !== 0) {
82
+ valid = 0;
83
+ break;
84
+ }
85
+ }
86
+ if (sepIdx === -1) valid = 0;
87
+ if (!valid) {
88
+ throw new Error("RSA-OAEP: decryption error");
89
+ }
90
+ return DB.slice(sepIdx + 1);
91
+ }
92
+ export {
93
+ rsaOaepDecrypt,
94
+ rsaOaepEncrypt
95
+ };
@@ -0,0 +1,100 @@
1
+ import { Hash } from "./hash.js";
2
+ import { randomBytes } from "./random.js";
3
+ import { mgf1 } from "./mgf1.js";
4
+ import { parsePemKey, rsaKeySize } from "./asn1.js";
5
+ import { hashSize } from "./crypto-utils.js";
6
+ import { modPow, bigIntToBytes, bytesToBigInt } from "./bigint-math.js";
7
+ function hashDigest(algo, data) {
8
+ const h = new Hash(algo);
9
+ h.update(data);
10
+ return new Uint8Array(h.digest());
11
+ }
12
+ function emsaPssEncode(mHash, emBits, hashAlgo, saltLength) {
13
+ const hLen = hashSize(hashAlgo);
14
+ const emLen = Math.ceil(emBits / 8);
15
+ if (emLen < hLen + saltLength + 2) {
16
+ throw new Error("RSA-PSS: encoding error \u2014 key too short");
17
+ }
18
+ const salt = saltLength > 0 ? new Uint8Array(randomBytes(saltLength)) : new Uint8Array(0);
19
+ const mPrime = new Uint8Array(8 + hLen + saltLength);
20
+ mPrime.set(mHash, 8);
21
+ mPrime.set(salt, 8 + hLen);
22
+ const H = hashDigest(hashAlgo, mPrime);
23
+ const dbLen = emLen - hLen - 1;
24
+ const DB = new Uint8Array(dbLen);
25
+ DB[dbLen - saltLength - 1] = 1;
26
+ DB.set(salt, dbLen - saltLength);
27
+ const dbMask = mgf1(hashAlgo, H, dbLen);
28
+ const maskedDB = new Uint8Array(dbLen);
29
+ for (let i = 0; i < dbLen; i++) maskedDB[i] = DB[i] ^ dbMask[i];
30
+ const zeroBits = 8 * emLen - emBits;
31
+ if (zeroBits > 0) maskedDB[0] &= 255 >>> zeroBits;
32
+ const EM = new Uint8Array(emLen);
33
+ EM.set(maskedDB, 0);
34
+ EM.set(H, dbLen);
35
+ EM[emLen - 1] = 188;
36
+ return EM;
37
+ }
38
+ function emsaPssVerify(mHash, EM, emBits, hashAlgo, saltLength) {
39
+ const hLen = hashSize(hashAlgo);
40
+ const emLen = Math.ceil(emBits / 8);
41
+ if (emLen < hLen + saltLength + 2) return false;
42
+ if (EM[emLen - 1] !== 188) return false;
43
+ const dbLen = emLen - hLen - 1;
44
+ const maskedDB = EM.slice(0, dbLen);
45
+ const H = EM.slice(dbLen, dbLen + hLen);
46
+ const zeroBits = 8 * emLen - emBits;
47
+ if (zeroBits > 0 && (maskedDB[0] & 255 << 8 - zeroBits) !== 0) return false;
48
+ const dbMask = mgf1(hashAlgo, H, dbLen);
49
+ const DB = new Uint8Array(dbLen);
50
+ for (let i = 0; i < dbLen; i++) DB[i] = maskedDB[i] ^ dbMask[i];
51
+ if (zeroBits > 0) DB[0] &= 255 >>> zeroBits;
52
+ for (let i = 0; i < dbLen - saltLength - 1; i++) {
53
+ if (DB[i] !== 0) return false;
54
+ }
55
+ if (DB[dbLen - saltLength - 1] !== 1) return false;
56
+ const salt = DB.slice(dbLen - saltLength);
57
+ const mPrime = new Uint8Array(8 + hLen + saltLength);
58
+ mPrime.set(mHash, 8);
59
+ mPrime.set(salt, 8 + hLen);
60
+ const HPrime = hashDigest(hashAlgo, mPrime);
61
+ if (H.length !== HPrime.length) return false;
62
+ let diff = 0;
63
+ for (let i = 0; i < H.length; i++) diff |= H[i] ^ HPrime[i];
64
+ return diff === 0;
65
+ }
66
+ function rsaPssSign(hashAlgo, privKeyPem, data, saltLength) {
67
+ const parsed = parsePemKey(privKeyPem);
68
+ if (parsed.type !== "rsa-private") throw new Error("RSA-PSS: expected RSA private key");
69
+ const { n, d } = parsed.components;
70
+ const keyBits = rsaKeySize(n);
71
+ const keyBytes = Math.ceil(keyBits / 8);
72
+ const mHash = hashDigest(hashAlgo, data);
73
+ const EM = emsaPssEncode(mHash, keyBits - 1, hashAlgo, saltLength);
74
+ const m = bytesToBigInt(EM);
75
+ const s = modPow(m, d, n);
76
+ return bigIntToBytes(s, keyBytes);
77
+ }
78
+ function rsaPssVerify(hashAlgo, pubKeyPem, signature, data, saltLength) {
79
+ const parsed = parsePemKey(pubKeyPem);
80
+ let n, e;
81
+ if (parsed.type === "rsa-public") {
82
+ ({ n, e } = parsed.components);
83
+ } else if (parsed.type === "rsa-private") {
84
+ ({ n, e } = parsed.components);
85
+ } else {
86
+ throw new Error("RSA-PSS: expected RSA key");
87
+ }
88
+ const keyBits = rsaKeySize(n);
89
+ const keyBytes = Math.ceil(keyBits / 8);
90
+ if (signature.length !== keyBytes) return false;
91
+ const s = bytesToBigInt(signature);
92
+ const m = modPow(s, e, n);
93
+ const EM = bigIntToBytes(m, Math.ceil((keyBits - 1) / 8));
94
+ const mHash = hashDigest(hashAlgo, data);
95
+ return emsaPssVerify(mHash, EM, keyBits - 1, hashAlgo, saltLength);
96
+ }
97
+ export {
98
+ rsaPssSign,
99
+ rsaPssVerify
100
+ };
@@ -0,0 +1,134 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { pbkdf2Sync } from "./pbkdf2.js";
3
+ function R(a, b) {
4
+ return (a << b | a >>> 32 - b) >>> 0;
5
+ }
6
+ function salsa20_8(B) {
7
+ const x = new Uint32Array(16);
8
+ for (let i = 0; i < 16; i++) x[i] = B[i];
9
+ for (let i = 0; i < 4; i++) {
10
+ x[4] ^= R(x[0] + x[12], 7);
11
+ x[8] ^= R(x[4] + x[0], 9);
12
+ x[12] ^= R(x[8] + x[4], 13);
13
+ x[0] ^= R(x[12] + x[8], 18);
14
+ x[9] ^= R(x[5] + x[1], 7);
15
+ x[13] ^= R(x[9] + x[5], 9);
16
+ x[1] ^= R(x[13] + x[9], 13);
17
+ x[5] ^= R(x[1] + x[13], 18);
18
+ x[14] ^= R(x[10] + x[6], 7);
19
+ x[2] ^= R(x[14] + x[10], 9);
20
+ x[6] ^= R(x[2] + x[14], 13);
21
+ x[10] ^= R(x[6] + x[2], 18);
22
+ x[3] ^= R(x[15] + x[11], 7);
23
+ x[7] ^= R(x[3] + x[15], 9);
24
+ x[11] ^= R(x[7] + x[3], 13);
25
+ x[15] ^= R(x[11] + x[7], 18);
26
+ x[1] ^= R(x[0] + x[3], 7);
27
+ x[2] ^= R(x[1] + x[0], 9);
28
+ x[3] ^= R(x[2] + x[1], 13);
29
+ x[0] ^= R(x[3] + x[2], 18);
30
+ x[6] ^= R(x[5] + x[4], 7);
31
+ x[7] ^= R(x[6] + x[5], 9);
32
+ x[4] ^= R(x[7] + x[6], 13);
33
+ x[5] ^= R(x[4] + x[7], 18);
34
+ x[11] ^= R(x[10] + x[9], 7);
35
+ x[8] ^= R(x[11] + x[10], 9);
36
+ x[9] ^= R(x[8] + x[11], 13);
37
+ x[10] ^= R(x[9] + x[8], 18);
38
+ x[12] ^= R(x[15] + x[14], 7);
39
+ x[13] ^= R(x[12] + x[15], 9);
40
+ x[14] ^= R(x[13] + x[12], 13);
41
+ x[15] ^= R(x[14] + x[13], 18);
42
+ }
43
+ for (let i = 0; i < 16; i++) B[i] = B[i] + x[i] >>> 0;
44
+ }
45
+ function blockMix(B, r) {
46
+ const blockWords = 2 * r * 16;
47
+ const X = new Uint32Array(16);
48
+ for (let i = 0; i < 16; i++) X[i] = B[blockWords - 16 + i];
49
+ const Y = new Uint32Array(blockWords);
50
+ for (let i = 0; i < 2 * r; i++) {
51
+ for (let j = 0; j < 16; j++) X[j] ^= B[i * 16 + j];
52
+ salsa20_8(X);
53
+ for (let j = 0; j < 16; j++) Y[i * 16 + j] = X[j];
54
+ }
55
+ for (let i = 0; i < r; i++) {
56
+ for (let j = 0; j < 16; j++) B[i * 16 + j] = Y[2 * i * 16 + j];
57
+ }
58
+ for (let i = 0; i < r; i++) {
59
+ for (let j = 0; j < 16; j++) B[(r + i) * 16 + j] = Y[(2 * i + 1) * 16 + j];
60
+ }
61
+ }
62
+ function roMix(B, N, r) {
63
+ const blockWords = 2 * r * 16;
64
+ const V = new Array(N);
65
+ for (let i = 0; i < N; i++) {
66
+ V[i] = new Uint32Array(B);
67
+ blockMix(B, r);
68
+ }
69
+ for (let i = 0; i < N; i++) {
70
+ const j = B[blockWords - 16] & N - 1;
71
+ for (let k = 0; k < blockWords; k++) B[k] ^= V[j][k];
72
+ blockMix(B, r);
73
+ }
74
+ }
75
+ function bytesToWords(bytes) {
76
+ const words = new Uint32Array(bytes.length / 4);
77
+ for (let i = 0; i < words.length; i++) {
78
+ words[i] = bytes[i * 4] | bytes[i * 4 + 1] << 8 | bytes[i * 4 + 2] << 16 | bytes[i * 4 + 3] << 24;
79
+ }
80
+ return words;
81
+ }
82
+ function wordsToBytes(words) {
83
+ const bytes = new Uint8Array(words.length * 4);
84
+ for (let i = 0; i < words.length; i++) {
85
+ bytes[i * 4] = words[i] & 255;
86
+ bytes[i * 4 + 1] = words[i] >> 8 & 255;
87
+ bytes[i * 4 + 2] = words[i] >> 16 & 255;
88
+ bytes[i * 4 + 3] = words[i] >> 24 & 255;
89
+ }
90
+ return bytes;
91
+ }
92
+ function scryptCore(password, salt, N, r, p, keyLen) {
93
+ const blockSize = 128 * r;
94
+ const B = pbkdf2Sync(password, salt, 1, p * blockSize, "sha256");
95
+ for (let i = 0; i < p; i++) {
96
+ const blockBytes = new Uint8Array(B.buffer, B.byteOffset + i * blockSize, blockSize);
97
+ const blockWords = bytesToWords(blockBytes);
98
+ roMix(blockWords, N, r);
99
+ const result = wordsToBytes(blockWords);
100
+ blockBytes.set(result);
101
+ }
102
+ return pbkdf2Sync(password, B, 1, keyLen, "sha256");
103
+ }
104
+ function scryptSync(password, salt, keylen, options) {
105
+ const pwd = typeof password === "string" ? Buffer.from(password) : Buffer.from(password);
106
+ const slt = typeof salt === "string" ? Buffer.from(salt) : Buffer.from(salt);
107
+ const N = options?.N ?? 16384;
108
+ const r = options?.r ?? 8;
109
+ const p = options?.p ?? 1;
110
+ if (N <= 0 || (N & N - 1) !== 0) {
111
+ throw new Error("N must be a positive power of 2");
112
+ }
113
+ return scryptCore(pwd, slt, N, r, p, keylen);
114
+ }
115
+ function scrypt(password, salt, keylen, optionsOrCallback, callback) {
116
+ let options = {};
117
+ let cb;
118
+ if (typeof optionsOrCallback === "function") {
119
+ cb = optionsOrCallback;
120
+ } else {
121
+ options = optionsOrCallback;
122
+ cb = callback;
123
+ }
124
+ try {
125
+ const result = scryptSync(password, salt, keylen, options);
126
+ setTimeout(() => cb(null, result), 0);
127
+ } catch (err) {
128
+ setTimeout(() => cb(err instanceof Error ? err : new Error(String(err)), Buffer.alloc(0)), 0);
129
+ }
130
+ }
131
+ export {
132
+ scrypt,
133
+ scryptSync
134
+ };