@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.
- package/README.md +27 -0
- package/lib/esm/asn1.js +504 -0
- package/lib/esm/bigint-math.js +34 -0
- package/lib/esm/cipher.js +1272 -0
- package/lib/esm/constants.js +15 -0
- package/lib/esm/crypto-utils.js +47 -0
- package/lib/esm/dh.js +411 -0
- package/lib/esm/ecdh.js +356 -0
- package/lib/esm/ecdsa.js +125 -0
- package/lib/esm/hash.js +100 -0
- package/lib/esm/hkdf.js +58 -0
- package/lib/esm/hmac.js +93 -0
- package/lib/esm/index.js +158 -0
- package/lib/esm/key-object.js +330 -0
- package/lib/esm/mgf1.js +27 -0
- package/lib/esm/pbkdf2.js +68 -0
- package/lib/esm/public-encrypt.js +175 -0
- package/lib/esm/random.js +138 -0
- package/lib/esm/rsa-oaep.js +95 -0
- package/lib/esm/rsa-pss.js +100 -0
- package/lib/esm/scrypt.js +134 -0
- package/lib/esm/sign.js +248 -0
- package/lib/esm/timing-safe-equal.js +13 -0
- package/lib/esm/x509.js +214 -0
- package/lib/types/asn1.d.ts +87 -0
- package/lib/types/bigint-math.d.ts +13 -0
- package/lib/types/cipher.d.ts +84 -0
- package/lib/types/constants.d.ts +10 -0
- package/lib/types/crypto-utils.d.ts +22 -0
- package/lib/types/dh.d.ts +79 -0
- package/lib/types/ecdh.d.ts +96 -0
- package/lib/types/ecdsa.d.ts +21 -0
- package/lib/types/hash.d.ts +25 -0
- package/lib/types/hkdf.d.ts +9 -0
- package/lib/types/hmac.d.ts +20 -0
- package/lib/types/index.d.ts +105 -0
- package/lib/types/key-object.d.ts +36 -0
- package/lib/types/mgf1.d.ts +5 -0
- package/lib/types/pbkdf2.d.ts +9 -0
- package/lib/types/public-encrypt.d.ts +42 -0
- package/lib/types/random.d.ts +22 -0
- package/lib/types/rsa-oaep.d.ts +8 -0
- package/lib/types/rsa-pss.d.ts +8 -0
- package/lib/types/scrypt.d.ts +11 -0
- package/lib/types/sign.d.ts +61 -0
- package/lib/types/timing-safe-equal.d.ts +6 -0
- package/lib/types/x509.d.ts +72 -0
- package/package.json +45 -0
- package/src/asn1.ts +797 -0
- package/src/bigint-math.ts +45 -0
- package/src/cipher.spec.ts +332 -0
- package/src/cipher.ts +952 -0
- package/src/constants.ts +16 -0
- package/src/crypto-utils.ts +64 -0
- package/src/dh.spec.ts +111 -0
- package/src/dh.ts +761 -0
- package/src/ecdh.spec.ts +116 -0
- package/src/ecdh.ts +624 -0
- package/src/ecdsa.ts +243 -0
- package/src/extended.spec.ts +444 -0
- package/src/gcm.spec.ts +141 -0
- package/src/hash.spec.ts +86 -0
- package/src/hash.ts +119 -0
- package/src/hkdf.ts +99 -0
- package/src/hmac.spec.ts +64 -0
- package/src/hmac.ts +123 -0
- package/src/index.ts +93 -0
- package/src/key-object.spec.ts +202 -0
- package/src/key-object.ts +401 -0
- package/src/mgf1.ts +37 -0
- package/src/pbkdf2.spec.ts +76 -0
- package/src/pbkdf2.ts +106 -0
- package/src/public-encrypt.ts +288 -0
- package/src/random.spec.ts +133 -0
- package/src/random.ts +183 -0
- package/src/rsa-oaep.ts +167 -0
- package/src/rsa-pss.ts +190 -0
- package/src/scrypt.spec.ts +90 -0
- package/src/scrypt.ts +191 -0
- package/src/sign.spec.ts +160 -0
- package/src/sign.ts +319 -0
- package/src/test.mts +19 -0
- package/src/timing-safe-equal.ts +21 -0
- package/src/x509.spec.ts +210 -0
- package/src/x509.ts +262 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
package/lib/esm/sign.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { Hash } from "./hash.js";
|
|
3
|
+
import { parsePemKey, rsaKeySize } from "./asn1.js";
|
|
4
|
+
import { modPow, bigIntToBytes, bytesToBigInt } from "./bigint-math.js";
|
|
5
|
+
const DIGEST_INFO_PREFIX = {
|
|
6
|
+
sha1: new Uint8Array([
|
|
7
|
+
48,
|
|
8
|
+
33,
|
|
9
|
+
48,
|
|
10
|
+
9,
|
|
11
|
+
6,
|
|
12
|
+
5,
|
|
13
|
+
43,
|
|
14
|
+
14,
|
|
15
|
+
3,
|
|
16
|
+
2,
|
|
17
|
+
26,
|
|
18
|
+
5,
|
|
19
|
+
0,
|
|
20
|
+
4,
|
|
21
|
+
20
|
|
22
|
+
]),
|
|
23
|
+
sha256: new Uint8Array([
|
|
24
|
+
48,
|
|
25
|
+
49,
|
|
26
|
+
48,
|
|
27
|
+
13,
|
|
28
|
+
6,
|
|
29
|
+
9,
|
|
30
|
+
96,
|
|
31
|
+
134,
|
|
32
|
+
72,
|
|
33
|
+
1,
|
|
34
|
+
101,
|
|
35
|
+
3,
|
|
36
|
+
4,
|
|
37
|
+
2,
|
|
38
|
+
1,
|
|
39
|
+
5,
|
|
40
|
+
0,
|
|
41
|
+
4,
|
|
42
|
+
32
|
|
43
|
+
]),
|
|
44
|
+
sha512: new Uint8Array([
|
|
45
|
+
48,
|
|
46
|
+
81,
|
|
47
|
+
48,
|
|
48
|
+
13,
|
|
49
|
+
6,
|
|
50
|
+
9,
|
|
51
|
+
96,
|
|
52
|
+
134,
|
|
53
|
+
72,
|
|
54
|
+
1,
|
|
55
|
+
101,
|
|
56
|
+
3,
|
|
57
|
+
4,
|
|
58
|
+
2,
|
|
59
|
+
3,
|
|
60
|
+
5,
|
|
61
|
+
0,
|
|
62
|
+
4,
|
|
63
|
+
64
|
|
64
|
+
])
|
|
65
|
+
};
|
|
66
|
+
function normalizeSignAlgorithm(algorithm) {
|
|
67
|
+
let alg = algorithm.toLowerCase().replace(/-/g, "");
|
|
68
|
+
if (alg.startsWith("rsa")) {
|
|
69
|
+
alg = alg.slice(3);
|
|
70
|
+
}
|
|
71
|
+
if (!DIGEST_INFO_PREFIX[alg]) {
|
|
72
|
+
throw new Error(`Unsupported algorithm: ${algorithm}. Supported: RSA-SHA1, RSA-SHA256, RSA-SHA512`);
|
|
73
|
+
}
|
|
74
|
+
return alg;
|
|
75
|
+
}
|
|
76
|
+
function extractPem(key) {
|
|
77
|
+
if (typeof key === "string") {
|
|
78
|
+
return key;
|
|
79
|
+
}
|
|
80
|
+
if (Buffer.isBuffer(key) || key instanceof Uint8Array) {
|
|
81
|
+
return Buffer.from(key).toString("utf8");
|
|
82
|
+
}
|
|
83
|
+
if (key && typeof key === "object" && "key" in key) {
|
|
84
|
+
const k = key.key;
|
|
85
|
+
if (typeof k === "string") return k;
|
|
86
|
+
if (Buffer.isBuffer(k) || k instanceof Uint8Array) return Buffer.from(k).toString("utf8");
|
|
87
|
+
}
|
|
88
|
+
throw new TypeError("Invalid key argument");
|
|
89
|
+
}
|
|
90
|
+
class Sign {
|
|
91
|
+
_algorithm;
|
|
92
|
+
_hash;
|
|
93
|
+
_finalized = false;
|
|
94
|
+
constructor(algorithm) {
|
|
95
|
+
this._algorithm = normalizeSignAlgorithm(algorithm);
|
|
96
|
+
this._hash = new Hash(this._algorithm);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Update the Sign object with the given data.
|
|
100
|
+
*/
|
|
101
|
+
update(data, inputEncoding) {
|
|
102
|
+
if (this._finalized) {
|
|
103
|
+
throw new Error("Sign was already finalized");
|
|
104
|
+
}
|
|
105
|
+
this._hash.update(data, inputEncoding);
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Compute the signature using the private key.
|
|
110
|
+
* Returns the signature as a Buffer (or string if outputEncoding is given).
|
|
111
|
+
*/
|
|
112
|
+
sign(privateKey, outputEncoding) {
|
|
113
|
+
if (this._finalized) {
|
|
114
|
+
throw new Error("Sign was already finalized");
|
|
115
|
+
}
|
|
116
|
+
this._finalized = true;
|
|
117
|
+
const digest = this._hash.digest();
|
|
118
|
+
const pem = extractPem(privateKey);
|
|
119
|
+
const parsed = parsePemKey(pem);
|
|
120
|
+
if (parsed.type !== "rsa-private") {
|
|
121
|
+
throw new Error("privateKey must be an RSA private key");
|
|
122
|
+
}
|
|
123
|
+
const { n, e, d } = parsed.components;
|
|
124
|
+
const keyLen = rsaKeySize(n);
|
|
125
|
+
const prefix = DIGEST_INFO_PREFIX[this._algorithm];
|
|
126
|
+
const digestInfo = new Uint8Array(prefix.length + digest.length);
|
|
127
|
+
digestInfo.set(prefix, 0);
|
|
128
|
+
digestInfo.set(digest, prefix.length);
|
|
129
|
+
const padLen = keyLen - digestInfo.length - 3;
|
|
130
|
+
if (padLen < 8) {
|
|
131
|
+
throw new Error("Key is too short for the specified hash algorithm");
|
|
132
|
+
}
|
|
133
|
+
const em = new Uint8Array(keyLen);
|
|
134
|
+
em[0] = 0;
|
|
135
|
+
em[1] = 1;
|
|
136
|
+
for (let i = 2; i < 2 + padLen; i++) {
|
|
137
|
+
em[i] = 255;
|
|
138
|
+
}
|
|
139
|
+
em[2 + padLen] = 0;
|
|
140
|
+
em.set(digestInfo, 3 + padLen);
|
|
141
|
+
const m = bytesToBigInt(em);
|
|
142
|
+
const s = modPow(m, d, n);
|
|
143
|
+
const sigBytes = bigIntToBytes(s, keyLen);
|
|
144
|
+
const sigBuf = Buffer.from(sigBytes);
|
|
145
|
+
if (outputEncoding) {
|
|
146
|
+
return sigBuf.toString(outputEncoding);
|
|
147
|
+
}
|
|
148
|
+
return sigBuf;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
class Verify {
|
|
152
|
+
_algorithm;
|
|
153
|
+
_hash;
|
|
154
|
+
_finalized = false;
|
|
155
|
+
constructor(algorithm) {
|
|
156
|
+
this._algorithm = normalizeSignAlgorithm(algorithm);
|
|
157
|
+
this._hash = new Hash(this._algorithm);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Update the Verify object with the given data.
|
|
161
|
+
*/
|
|
162
|
+
update(data, inputEncoding) {
|
|
163
|
+
if (this._finalized) {
|
|
164
|
+
throw new Error("Verify was already finalized");
|
|
165
|
+
}
|
|
166
|
+
this._hash.update(data, inputEncoding);
|
|
167
|
+
return this;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Verify the signature against the public key.
|
|
171
|
+
* Returns true if the signature is valid, false otherwise.
|
|
172
|
+
*/
|
|
173
|
+
verify(publicKey, signature, signatureEncoding) {
|
|
174
|
+
if (this._finalized) {
|
|
175
|
+
throw new Error("Verify was already finalized");
|
|
176
|
+
}
|
|
177
|
+
this._finalized = true;
|
|
178
|
+
const digest = this._hash.digest();
|
|
179
|
+
const pem = extractPem(publicKey);
|
|
180
|
+
const parsed = parsePemKey(pem);
|
|
181
|
+
let n;
|
|
182
|
+
let e;
|
|
183
|
+
if (parsed.type === "rsa-public") {
|
|
184
|
+
n = parsed.components.n;
|
|
185
|
+
e = parsed.components.e;
|
|
186
|
+
} else if (parsed.type === "rsa-private") {
|
|
187
|
+
n = parsed.components.n;
|
|
188
|
+
e = parsed.components.e;
|
|
189
|
+
} else {
|
|
190
|
+
throw new Error("publicKey must be an RSA public or private key");
|
|
191
|
+
}
|
|
192
|
+
const keyLen = rsaKeySize(n);
|
|
193
|
+
let sigBytes;
|
|
194
|
+
if (typeof signature === "string") {
|
|
195
|
+
sigBytes = Buffer.from(signature, signatureEncoding || "base64");
|
|
196
|
+
} else {
|
|
197
|
+
sigBytes = signature instanceof Uint8Array ? signature : Buffer.from(signature);
|
|
198
|
+
}
|
|
199
|
+
if (sigBytes.length !== keyLen) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
const s = bytesToBigInt(sigBytes);
|
|
203
|
+
if (s >= n) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const m = modPow(s, e, n);
|
|
207
|
+
const em = bigIntToBytes(m, keyLen);
|
|
208
|
+
if (em[0] !== 0 || em[1] !== 1) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
let sepIdx = 2;
|
|
212
|
+
while (sepIdx < em.length && em[sepIdx] === 255) {
|
|
213
|
+
sepIdx++;
|
|
214
|
+
}
|
|
215
|
+
if (sepIdx >= em.length || em[sepIdx] !== 0) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
if (sepIdx - 2 < 8) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
sepIdx++;
|
|
222
|
+
const recoveredDigestInfo = em.slice(sepIdx);
|
|
223
|
+
const prefix = DIGEST_INFO_PREFIX[this._algorithm];
|
|
224
|
+
const expectedDigestInfo = new Uint8Array(prefix.length + digest.length);
|
|
225
|
+
expectedDigestInfo.set(prefix, 0);
|
|
226
|
+
expectedDigestInfo.set(digest, prefix.length);
|
|
227
|
+
if (recoveredDigestInfo.length !== expectedDigestInfo.length) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
let diff = 0;
|
|
231
|
+
for (let i = 0; i < recoveredDigestInfo.length; i++) {
|
|
232
|
+
diff |= recoveredDigestInfo[i] ^ expectedDigestInfo[i];
|
|
233
|
+
}
|
|
234
|
+
return diff === 0;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function createSign(algorithm) {
|
|
238
|
+
return new Sign(algorithm);
|
|
239
|
+
}
|
|
240
|
+
function createVerify(algorithm) {
|
|
241
|
+
return new Verify(algorithm);
|
|
242
|
+
}
|
|
243
|
+
export {
|
|
244
|
+
Sign,
|
|
245
|
+
Verify,
|
|
246
|
+
createSign,
|
|
247
|
+
createVerify
|
|
248
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
function timingSafeEqual(a, b) {
|
|
2
|
+
if (a.length !== b.length) {
|
|
3
|
+
throw new RangeError("Input buffers must have the same byte length");
|
|
4
|
+
}
|
|
5
|
+
let result = 0;
|
|
6
|
+
for (let i = 0; i < a.length; i++) {
|
|
7
|
+
result |= a[i] ^ b[i];
|
|
8
|
+
}
|
|
9
|
+
return result === 0;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
timingSafeEqual
|
|
13
|
+
};
|
package/lib/esm/x509.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { parseX509, parseX509Der, encodeSubjectPublicKeyInfo, derToPem } from "./asn1.js";
|
|
3
|
+
import { KeyObject } from "./key-object.js";
|
|
4
|
+
import { createHash } from "./index.js";
|
|
5
|
+
class X509Certificate {
|
|
6
|
+
_components;
|
|
7
|
+
_pem;
|
|
8
|
+
constructor(buf) {
|
|
9
|
+
if (typeof buf === "string") {
|
|
10
|
+
this._pem = buf;
|
|
11
|
+
this._components = parseX509(buf);
|
|
12
|
+
} else {
|
|
13
|
+
const bufData = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
|
|
14
|
+
const str = bufData.toString("utf8");
|
|
15
|
+
if (str.includes("-----BEGIN CERTIFICATE-----")) {
|
|
16
|
+
this._pem = str;
|
|
17
|
+
this._components = parseX509(str);
|
|
18
|
+
} else {
|
|
19
|
+
this._components = parseX509Der(new Uint8Array(bufData.buffer, bufData.byteOffset, bufData.byteLength));
|
|
20
|
+
this._pem = derToPem(this._components.raw, "CERTIFICATE");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** The DER encoded certificate data. */
|
|
25
|
+
get raw() {
|
|
26
|
+
return Buffer.from(this._components.raw);
|
|
27
|
+
}
|
|
28
|
+
/** The serial number of the certificate as a hex string. */
|
|
29
|
+
get serialNumber() {
|
|
30
|
+
return this._components.serialNumber.toString(16).toUpperCase();
|
|
31
|
+
}
|
|
32
|
+
/** The subject of the certificate. */
|
|
33
|
+
get subject() {
|
|
34
|
+
return this._components.subject;
|
|
35
|
+
}
|
|
36
|
+
/** The issuer of the certificate. */
|
|
37
|
+
get issuer() {
|
|
38
|
+
return this._components.issuer;
|
|
39
|
+
}
|
|
40
|
+
/** The start date of the certificate validity period. */
|
|
41
|
+
get validFrom() {
|
|
42
|
+
return this._components.validFrom.toUTCString();
|
|
43
|
+
}
|
|
44
|
+
/** The end date of the certificate validity period. */
|
|
45
|
+
get validTo() {
|
|
46
|
+
return this._components.validTo.toUTCString();
|
|
47
|
+
}
|
|
48
|
+
/** SHA-1 fingerprint of the certificate. */
|
|
49
|
+
get fingerprint() {
|
|
50
|
+
const hash = createHash("sha1").update(this._components.raw).digest();
|
|
51
|
+
return formatFingerprint(hash);
|
|
52
|
+
}
|
|
53
|
+
/** SHA-256 fingerprint of the certificate. */
|
|
54
|
+
get fingerprint256() {
|
|
55
|
+
const hash = createHash("sha256").update(this._components.raw).digest();
|
|
56
|
+
return formatFingerprint(hash);
|
|
57
|
+
}
|
|
58
|
+
/** SHA-512 fingerprint of the certificate. */
|
|
59
|
+
get fingerprint512() {
|
|
60
|
+
const hash = createHash("sha512").update(this._components.raw).digest();
|
|
61
|
+
return formatFingerprint(hash);
|
|
62
|
+
}
|
|
63
|
+
/** The public key of the certificate as a KeyObject. */
|
|
64
|
+
get publicKey() {
|
|
65
|
+
if (!this._components.publicKey) {
|
|
66
|
+
throw new Error("Certificate does not contain a supported public key type");
|
|
67
|
+
}
|
|
68
|
+
const der = encodeSubjectPublicKeyInfo(this._components.publicKey);
|
|
69
|
+
const pem = derToPem(der, "PUBLIC KEY");
|
|
70
|
+
return new KeyObject("public", {
|
|
71
|
+
parsed: { type: "rsa-public", components: this._components.publicKey },
|
|
72
|
+
pem
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** The Subject Alternative Name extension, if present. */
|
|
76
|
+
get subjectAltName() {
|
|
77
|
+
if (!this._components.subjectAltName || this._components.subjectAltName.length === 0) {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
return this._components.subjectAltName.join(", ");
|
|
81
|
+
}
|
|
82
|
+
/** The key usage extension (stub — returns undefined). */
|
|
83
|
+
get keyUsage() {
|
|
84
|
+
return void 0;
|
|
85
|
+
}
|
|
86
|
+
/** Information access extension (stub — returns undefined). */
|
|
87
|
+
get infoAccess() {
|
|
88
|
+
return void 0;
|
|
89
|
+
}
|
|
90
|
+
/** Whether this certificate is a CA certificate. */
|
|
91
|
+
get ca() {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Check whether the certificate matches the given hostname.
|
|
96
|
+
*/
|
|
97
|
+
checkHost(name) {
|
|
98
|
+
const cnMatch = this._components.subject.match(/CN=([^,]+)/);
|
|
99
|
+
if (cnMatch) {
|
|
100
|
+
const cn = cnMatch[1].trim();
|
|
101
|
+
if (matchHostname(cn, name)) return cn;
|
|
102
|
+
}
|
|
103
|
+
if (this._components.subjectAltName) {
|
|
104
|
+
for (const san of this._components.subjectAltName) {
|
|
105
|
+
if (san.startsWith("DNS:")) {
|
|
106
|
+
const dnsName = san.substring(4);
|
|
107
|
+
if (matchHostname(dnsName, name)) return dnsName;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check whether the certificate matches the given email address.
|
|
115
|
+
*/
|
|
116
|
+
checkEmail(email) {
|
|
117
|
+
const emailLower = email.toLowerCase();
|
|
118
|
+
const emailMatch = this._components.subject.match(/emailAddress=([^,]+)/);
|
|
119
|
+
if (emailMatch && emailMatch[1].toLowerCase() === emailLower) {
|
|
120
|
+
return emailMatch[1];
|
|
121
|
+
}
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Check whether the certificate matches the given IP address.
|
|
126
|
+
*/
|
|
127
|
+
checkIP(ip) {
|
|
128
|
+
if (this._components.subjectAltName) {
|
|
129
|
+
for (const san of this._components.subjectAltName) {
|
|
130
|
+
if (san.startsWith("IP Address:") && san.substring(11) === ip) {
|
|
131
|
+
return ip;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return void 0;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Verify the certificate signature using the given public key.
|
|
139
|
+
*/
|
|
140
|
+
verify(_publicKey) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Check whether this certificate was issued by the given issuer certificate.
|
|
145
|
+
*/
|
|
146
|
+
checkIssued(otherCert) {
|
|
147
|
+
return this.issuer === otherCert.subject;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Returns a legacy certificate object for compatibility.
|
|
151
|
+
*/
|
|
152
|
+
toLegacyObject() {
|
|
153
|
+
return {
|
|
154
|
+
subject: parseDNToObject(this._components.subject),
|
|
155
|
+
issuer: parseDNToObject(this._components.issuer),
|
|
156
|
+
valid_from: this.validFrom,
|
|
157
|
+
valid_to: this.validTo,
|
|
158
|
+
serialNumber: this.serialNumber,
|
|
159
|
+
fingerprint: this.fingerprint,
|
|
160
|
+
fingerprint256: this.fingerprint256
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Returns the PEM-encoded certificate.
|
|
165
|
+
*/
|
|
166
|
+
toString() {
|
|
167
|
+
return this._pem;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Returns the PEM-encoded certificate in JSON context.
|
|
171
|
+
*/
|
|
172
|
+
toJSON() {
|
|
173
|
+
return this._pem;
|
|
174
|
+
}
|
|
175
|
+
get [Symbol.toStringTag]() {
|
|
176
|
+
return "X509Certificate";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function formatFingerprint(hash) {
|
|
180
|
+
const hex = hash.toString("hex").toUpperCase();
|
|
181
|
+
const parts = [];
|
|
182
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
183
|
+
parts.push(hex.substring(i, i + 2));
|
|
184
|
+
}
|
|
185
|
+
return parts.join(":");
|
|
186
|
+
}
|
|
187
|
+
function matchHostname(pattern, hostname) {
|
|
188
|
+
const patternLower = pattern.toLowerCase();
|
|
189
|
+
const hostLower = hostname.toLowerCase();
|
|
190
|
+
if (patternLower === hostLower) return true;
|
|
191
|
+
if (patternLower.startsWith("*.")) {
|
|
192
|
+
const suffix = patternLower.substring(2);
|
|
193
|
+
const hostParts = hostLower.split(".");
|
|
194
|
+
if (hostParts.length >= 2) {
|
|
195
|
+
const hostSuffix = hostParts.slice(1).join(".");
|
|
196
|
+
return hostSuffix === suffix;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
function parseDNToObject(dn) {
|
|
202
|
+
const result = {};
|
|
203
|
+
const parts = dn.split(", ");
|
|
204
|
+
for (const part of parts) {
|
|
205
|
+
const eqIdx = part.indexOf("=");
|
|
206
|
+
if (eqIdx > 0) {
|
|
207
|
+
result[part.substring(0, eqIdx)] = part.substring(eqIdx + 1);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
212
|
+
export {
|
|
213
|
+
X509Certificate
|
|
214
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
interface DerValue {
|
|
2
|
+
tag: number;
|
|
3
|
+
data: Uint8Array;
|
|
4
|
+
children?: DerValue[];
|
|
5
|
+
}
|
|
6
|
+
export interface RsaPublicComponents {
|
|
7
|
+
n: bigint;
|
|
8
|
+
e: bigint;
|
|
9
|
+
}
|
|
10
|
+
export interface RsaPrivateComponents {
|
|
11
|
+
n: bigint;
|
|
12
|
+
e: bigint;
|
|
13
|
+
d: bigint;
|
|
14
|
+
p: bigint;
|
|
15
|
+
q: bigint;
|
|
16
|
+
}
|
|
17
|
+
export type ParsedKey = {
|
|
18
|
+
type: 'rsa-public';
|
|
19
|
+
components: RsaPublicComponents;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'rsa-private';
|
|
22
|
+
components: RsaPrivateComponents;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Encode a BigInt as an ASN.1 INTEGER.
|
|
26
|
+
*/
|
|
27
|
+
export declare function bigintToAsn1Integer(value: bigint): Uint8Array;
|
|
28
|
+
/**
|
|
29
|
+
* Encode an ASN.1 SEQUENCE from its children.
|
|
30
|
+
*/
|
|
31
|
+
export declare function encodeSequence(children: Uint8Array[]): Uint8Array;
|
|
32
|
+
/**
|
|
33
|
+
* Encode RSA public key components as PKCS#1 RSAPublicKey DER.
|
|
34
|
+
*/
|
|
35
|
+
export declare function encodeRsaPublicKeyPkcs1(components: RsaPublicComponents): Uint8Array;
|
|
36
|
+
/**
|
|
37
|
+
* Encode RSA public key as PKCS#8 SubjectPublicKeyInfo DER.
|
|
38
|
+
*/
|
|
39
|
+
export declare function encodeSubjectPublicKeyInfo(components: RsaPublicComponents): Uint8Array;
|
|
40
|
+
/**
|
|
41
|
+
* Encode RSA private key components as PKCS#1 RSAPrivateKey DER.
|
|
42
|
+
*/
|
|
43
|
+
export declare function encodeRsaPrivateKeyPkcs1(components: RsaPrivateComponents): Uint8Array;
|
|
44
|
+
/**
|
|
45
|
+
* Encode RSA private key as PKCS#8 PrivateKeyInfo DER.
|
|
46
|
+
*/
|
|
47
|
+
export declare function encodePrivateKeyInfo(components: RsaPrivateComponents): Uint8Array;
|
|
48
|
+
/**
|
|
49
|
+
* Convert DER bytes to PEM string.
|
|
50
|
+
*/
|
|
51
|
+
export declare function derToPem(der: Uint8Array, type: string): string;
|
|
52
|
+
export interface X509Components {
|
|
53
|
+
raw: Uint8Array;
|
|
54
|
+
tbsCertificate: Uint8Array;
|
|
55
|
+
serialNumber: bigint;
|
|
56
|
+
issuer: string;
|
|
57
|
+
subject: string;
|
|
58
|
+
validFrom: Date;
|
|
59
|
+
validTo: Date;
|
|
60
|
+
publicKey: RsaPublicComponents | null;
|
|
61
|
+
publicKeyAlgorithm: string;
|
|
62
|
+
signatureAlgorithm: string;
|
|
63
|
+
signature: Uint8Array;
|
|
64
|
+
subjectAltName?: string[];
|
|
65
|
+
extensions?: DerValue[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse an X.509 certificate from PEM or DER.
|
|
69
|
+
*/
|
|
70
|
+
export declare function parseX509(pem: string): X509Components;
|
|
71
|
+
/**
|
|
72
|
+
* Parse an X.509 certificate from DER bytes.
|
|
73
|
+
*/
|
|
74
|
+
export declare function parseX509Der(der: Uint8Array): X509Components;
|
|
75
|
+
/**
|
|
76
|
+
* Parse a PEM-encoded RSA key. Supports:
|
|
77
|
+
* - RSA PUBLIC KEY (PKCS#1)
|
|
78
|
+
* - PUBLIC KEY (PKCS#8 SubjectPublicKeyInfo)
|
|
79
|
+
* - RSA PRIVATE KEY (PKCS#1)
|
|
80
|
+
* - PRIVATE KEY (PKCS#8 PrivateKeyInfo)
|
|
81
|
+
*/
|
|
82
|
+
export declare function parsePemKey(pem: string): ParsedKey;
|
|
83
|
+
/**
|
|
84
|
+
* Get the key size in bytes (byte length of modulus n).
|
|
85
|
+
*/
|
|
86
|
+
export declare function rsaKeySize(n: bigint): number;
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modular exponentiation using square-and-multiply (BigInt).
|
|
3
|
+
* Returns (base ** exp) % mod.
|
|
4
|
+
*/
|
|
5
|
+
export declare function modPow(base: bigint, exp: bigint, mod: bigint): bigint;
|
|
6
|
+
/**
|
|
7
|
+
* Convert a BigInt to a big-endian Uint8Array of exactly `length` bytes.
|
|
8
|
+
*/
|
|
9
|
+
export declare function bigIntToBytes(value: bigint, length: number): Uint8Array;
|
|
10
|
+
/**
|
|
11
|
+
* Convert a Uint8Array (big-endian) to a non-negative BigInt.
|
|
12
|
+
*/
|
|
13
|
+
export declare function bytesToBigInt(bytes: Uint8Array): bigint;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
interface AlgorithmInfo {
|
|
3
|
+
keySize: number;
|
|
4
|
+
ivSize: number;
|
|
5
|
+
mode: 'cbc' | 'ctr' | 'ecb' | 'cfb' | 'ofb' | 'gcm';
|
|
6
|
+
}
|
|
7
|
+
declare class CipherBase {
|
|
8
|
+
protected _roundKeys: Uint8Array[];
|
|
9
|
+
protected _iv: Uint8Array;
|
|
10
|
+
protected _mode: AlgorithmInfo['mode'];
|
|
11
|
+
protected _buffer: Uint8Array;
|
|
12
|
+
protected _autoPadding: boolean;
|
|
13
|
+
protected _finalized: boolean;
|
|
14
|
+
constructor(algorithm: string, key: Uint8Array, iv: Uint8Array | null);
|
|
15
|
+
setAutoPadding(autoPadding: boolean): this;
|
|
16
|
+
}
|
|
17
|
+
declare class Cipher extends CipherBase {
|
|
18
|
+
private _prevBlock;
|
|
19
|
+
private _counter;
|
|
20
|
+
private _gcmH;
|
|
21
|
+
private _gcmJ0;
|
|
22
|
+
private _gcmAAD;
|
|
23
|
+
private _gcmCiphertext;
|
|
24
|
+
private _gcmCiphertextLen;
|
|
25
|
+
private _gcmAuthTag;
|
|
26
|
+
private _gcmAADSet;
|
|
27
|
+
constructor(algorithm: string, key: Uint8Array, iv: Uint8Array | null);
|
|
28
|
+
/**
|
|
29
|
+
* Set Additional Authenticated Data for GCM mode.
|
|
30
|
+
* Must be called before any update() calls.
|
|
31
|
+
*/
|
|
32
|
+
setAAD(data: Buffer | Uint8Array): this;
|
|
33
|
+
/**
|
|
34
|
+
* Get the authentication tag after final() has been called.
|
|
35
|
+
* Only valid for GCM mode.
|
|
36
|
+
*/
|
|
37
|
+
getAuthTag(): Buffer;
|
|
38
|
+
update(data: string | Buffer | Uint8Array, inputEncoding?: string, outputEncoding?: string): string | Buffer;
|
|
39
|
+
final(outputEncoding?: string): string | Buffer;
|
|
40
|
+
private _encryptBlock;
|
|
41
|
+
private _processStream;
|
|
42
|
+
/**
|
|
43
|
+
* GCM encryption: CTR mode encryption, also accumulates ciphertext for GHASH.
|
|
44
|
+
*/
|
|
45
|
+
private _processGcmEncrypt;
|
|
46
|
+
}
|
|
47
|
+
declare class Decipher extends CipherBase {
|
|
48
|
+
private _prevBlock;
|
|
49
|
+
private _counter;
|
|
50
|
+
private _pendingUtf8;
|
|
51
|
+
private _gcmH;
|
|
52
|
+
private _gcmJ0;
|
|
53
|
+
private _gcmAAD;
|
|
54
|
+
private _gcmCiphertext;
|
|
55
|
+
private _gcmCiphertextLen;
|
|
56
|
+
private _gcmExpectedTag;
|
|
57
|
+
private _gcmAADSet;
|
|
58
|
+
constructor(algorithm: string, key: Uint8Array, iv: Uint8Array | null);
|
|
59
|
+
/**
|
|
60
|
+
* Set Additional Authenticated Data for GCM mode.
|
|
61
|
+
* Must be called before any update() calls.
|
|
62
|
+
*/
|
|
63
|
+
setAAD(data: Buffer | Uint8Array): this;
|
|
64
|
+
/**
|
|
65
|
+
* Set the expected authentication tag for GCM decryption.
|
|
66
|
+
* Must be called before final().
|
|
67
|
+
*/
|
|
68
|
+
setAuthTag(tag: Buffer | Uint8Array): this;
|
|
69
|
+
private _encodeWithUtf8Handling;
|
|
70
|
+
update(data: string | Buffer | Uint8Array, inputEncoding?: string, outputEncoding?: string): string | Buffer;
|
|
71
|
+
final(outputEncoding?: string): string | Buffer;
|
|
72
|
+
private _decryptBlock;
|
|
73
|
+
private _processStream;
|
|
74
|
+
/**
|
|
75
|
+
* GCM decryption: CTR mode decryption (same as encryption, since CTR is symmetric).
|
|
76
|
+
*/
|
|
77
|
+
private _processGcmDecrypt;
|
|
78
|
+
}
|
|
79
|
+
export declare function createCipher(_algorithm: string, _password: string | Buffer | Uint8Array): never;
|
|
80
|
+
export declare function createCipheriv(algorithm: string, key: string | Buffer | Uint8Array, iv: string | Buffer | Uint8Array | null): Cipher;
|
|
81
|
+
export declare function createDecipher(_algorithm: string, _password: string | Buffer | Uint8Array): never;
|
|
82
|
+
export declare function createDecipheriv(algorithm: string, key: string | Buffer | Uint8Array, iv: string | Buffer | Uint8Array | null): Decipher;
|
|
83
|
+
export declare function getCiphers(): string[];
|
|
84
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const constants: {
|
|
2
|
+
readonly RSA_PKCS1_PADDING: 1;
|
|
3
|
+
readonly RSA_NO_PADDING: 3;
|
|
4
|
+
readonly RSA_PKCS1_OAEP_PADDING: 4;
|
|
5
|
+
readonly RSA_PKCS1_PSS_PADDING: 6;
|
|
6
|
+
readonly DH_CHECK_P_NOT_SAFE_PRIME: 2;
|
|
7
|
+
readonly DH_CHECK_P_NOT_PRIME: 1;
|
|
8
|
+
readonly DH_UNABLE_TO_CHECK_GENERATOR: 4;
|
|
9
|
+
readonly DH_NOT_SUITABLE_GENERATOR: 8;
|
|
10
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a hash algorithm name to lowercase without hyphens.
|
|
4
|
+
* e.g. "SHA-256" → "sha256", "MD5" → "md5"
|
|
5
|
+
*/
|
|
6
|
+
export declare function normalizeAlgorithm(algorithm: string): string;
|
|
7
|
+
/** Hash digest output sizes in bytes. */
|
|
8
|
+
export declare const DIGEST_SIZES: Record<string, number>;
|
|
9
|
+
/** Hash block sizes in bytes (used for HMAC key padding). */
|
|
10
|
+
export declare const BLOCK_SIZES: Record<string, number>;
|
|
11
|
+
/** Set of supported hash algorithm names (normalized). */
|
|
12
|
+
export declare const SUPPORTED_ALGORITHMS: Set<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Get hash digest size in bytes for a given algorithm.
|
|
15
|
+
* Accepts unnormalized names (e.g. "SHA-256").
|
|
16
|
+
*/
|
|
17
|
+
export declare function hashSize(algo: string): number;
|
|
18
|
+
/**
|
|
19
|
+
* Convert various input types to Buffer.
|
|
20
|
+
* Handles string, Buffer, Uint8Array, DataView, and ArrayBuffer.
|
|
21
|
+
*/
|
|
22
|
+
export declare function toBuffer(input: string | Buffer | Uint8Array | DataView | ArrayBuffer, encoding?: string): Buffer;
|