@robinpath/cli 1.75.0 → 1.77.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 +111 -111
- package/dist/cli.mjs +347 -458
- package/modules/_helpers.js +33 -33
- package/modules/assert.js +270 -270
- package/modules/buffer.js +245 -245
- package/modules/child.js +176 -176
- package/modules/crypto.js +352 -352
- package/modules/dns.js +146 -146
- package/modules/events.js +174 -174
- package/modules/file.js +361 -361
- package/modules/http.js +268 -268
- package/modules/index.js +76 -76
- package/modules/net.js +189 -189
- package/modules/os.js +219 -219
- package/modules/path.js +162 -162
- package/modules/process.js +214 -214
- package/modules/stream.js +322 -322
- package/modules/string_decoder.js +106 -106
- package/modules/timer.js +167 -167
- package/modules/tls.js +264 -264
- package/modules/tty.js +169 -169
- package/modules/url.js +189 -189
- package/modules/util.js +275 -275
- package/modules/zlib.js +126 -126
- package/package.json +1 -1
package/modules/crypto.js
CHANGED
|
@@ -1,352 +1,352 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Native crypto module for RobinPath.
|
|
3
|
-
* Full cryptographic operations powered by Node.js crypto.
|
|
4
|
-
*/
|
|
5
|
-
import {
|
|
6
|
-
createHash, createHmac, createCipheriv, createDecipheriv,
|
|
7
|
-
randomBytes, randomUUID, randomInt, pbkdf2 as _pbkdf2, scrypt as _scrypt,
|
|
8
|
-
getCiphers, getHashes
|
|
9
|
-
} from 'node:crypto';
|
|
10
|
-
import { toStr, toNum, requireArgs } from './_helpers.js';
|
|
11
|
-
|
|
12
|
-
export const CryptoNativeFunctions = {
|
|
13
|
-
|
|
14
|
-
// --- Hashing ---
|
|
15
|
-
|
|
16
|
-
hash: async (args) => {
|
|
17
|
-
requireArgs('crypto.hash', args, 2);
|
|
18
|
-
const algo = toStr(args[0]);
|
|
19
|
-
const data = toStr(args[1]);
|
|
20
|
-
const encoding = toStr(args[2], 'hex');
|
|
21
|
-
return createHash(algo).update(data).digest(encoding);
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
md5: async (args) => {
|
|
25
|
-
requireArgs('crypto.md5', args, 1);
|
|
26
|
-
return createHash('md5').update(toStr(args[0])).digest('hex');
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
sha1: async (args) => {
|
|
30
|
-
requireArgs('crypto.sha1', args, 1);
|
|
31
|
-
return createHash('sha1').update(toStr(args[0])).digest('hex');
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
sha256: async (args) => {
|
|
35
|
-
requireArgs('crypto.sha256', args, 1);
|
|
36
|
-
return createHash('sha256').update(toStr(args[0])).digest('hex');
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
sha512: async (args) => {
|
|
40
|
-
requireArgs('crypto.sha512', args, 1);
|
|
41
|
-
return createHash('sha512').update(toStr(args[0])).digest('hex');
|
|
42
|
-
},
|
|
43
|
-
|
|
44
|
-
// --- HMAC ---
|
|
45
|
-
|
|
46
|
-
hmac: async (args) => {
|
|
47
|
-
requireArgs('crypto.hmac', args, 3);
|
|
48
|
-
const algo = toStr(args[0]);
|
|
49
|
-
const key = toStr(args[1]);
|
|
50
|
-
const data = toStr(args[2]);
|
|
51
|
-
const encoding = toStr(args[3], 'hex');
|
|
52
|
-
return createHmac(algo, key).update(data).digest(encoding);
|
|
53
|
-
},
|
|
54
|
-
|
|
55
|
-
hmacSha256: async (args) => {
|
|
56
|
-
requireArgs('crypto.hmacSha256', args, 2);
|
|
57
|
-
return createHmac('sha256', toStr(args[0])).update(toStr(args[1])).digest('hex');
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
hmacSha512: async (args) => {
|
|
61
|
-
requireArgs('crypto.hmacSha512', args, 2);
|
|
62
|
-
return createHmac('sha512', toStr(args[0])).update(toStr(args[1])).digest('hex');
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
// --- Encryption ---
|
|
66
|
-
|
|
67
|
-
encrypt: async (args) => {
|
|
68
|
-
requireArgs('crypto.encrypt', args, 3);
|
|
69
|
-
const algo = toStr(args[0], 'aes-256-cbc');
|
|
70
|
-
const key = toStr(args[1]);
|
|
71
|
-
const data = toStr(args[2]);
|
|
72
|
-
// Derive a 32-byte key from the input key
|
|
73
|
-
const keyBuf = createHash('sha256').update(key).digest();
|
|
74
|
-
// Generate random IV
|
|
75
|
-
const iv = randomBytes(16);
|
|
76
|
-
const cipher = createCipheriv(algo, keyBuf, iv);
|
|
77
|
-
let encrypted = cipher.update(data, 'utf-8', 'hex');
|
|
78
|
-
encrypted += cipher.final('hex');
|
|
79
|
-
// Return IV + encrypted data separated by ':'
|
|
80
|
-
return iv.toString('hex') + ':' + encrypted;
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
decrypt: async (args) => {
|
|
84
|
-
requireArgs('crypto.decrypt', args, 3);
|
|
85
|
-
const algo = toStr(args[0], 'aes-256-cbc');
|
|
86
|
-
const key = toStr(args[1]);
|
|
87
|
-
const encryptedStr = toStr(args[2]);
|
|
88
|
-
const keyBuf = createHash('sha256').update(key).digest();
|
|
89
|
-
const parts = encryptedStr.split(':');
|
|
90
|
-
if (parts.length !== 2) throw new Error('crypto.decrypt: invalid encrypted data format (expected iv:data)');
|
|
91
|
-
const iv = Buffer.from(parts[0], 'hex');
|
|
92
|
-
const encrypted = parts[1];
|
|
93
|
-
const decipher = createDecipheriv(algo, keyBuf, iv);
|
|
94
|
-
let decrypted = decipher.update(encrypted, 'hex', 'utf-8');
|
|
95
|
-
decrypted += decipher.final('utf-8');
|
|
96
|
-
return decrypted;
|
|
97
|
-
},
|
|
98
|
-
|
|
99
|
-
// --- Random ---
|
|
100
|
-
|
|
101
|
-
randomBytes: async (args) => {
|
|
102
|
-
const size = toNum(args[0], 32);
|
|
103
|
-
const encoding = toStr(args[1], 'hex');
|
|
104
|
-
return randomBytes(size).toString(encoding);
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
randomUUID: () => randomUUID(),
|
|
108
|
-
|
|
109
|
-
randomInt: (args) => {
|
|
110
|
-
const min = args.length >= 2 ? toNum(args[0], 0) : 0;
|
|
111
|
-
const max = args.length >= 2 ? toNum(args[1], 100) : toNum(args[0], 100);
|
|
112
|
-
return randomInt(min, max);
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
// --- Key Derivation ---
|
|
116
|
-
|
|
117
|
-
pbkdf2: (args) => {
|
|
118
|
-
requireArgs('crypto.pbkdf2', args, 2);
|
|
119
|
-
const password = toStr(args[0]);
|
|
120
|
-
const salt = toStr(args[1], 'salt');
|
|
121
|
-
const iterations = toNum(args[2], 100000);
|
|
122
|
-
const keylen = toNum(args[3], 64);
|
|
123
|
-
const digest = toStr(args[4], 'sha512');
|
|
124
|
-
return new Promise((resolve, reject) => {
|
|
125
|
-
_pbkdf2(password, salt, iterations, keylen, digest, (err, key) => {
|
|
126
|
-
if (err) reject(err);
|
|
127
|
-
else resolve(key.toString('hex'));
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
scrypt: (args) => {
|
|
133
|
-
requireArgs('crypto.scrypt', args, 2);
|
|
134
|
-
const password = toStr(args[0]);
|
|
135
|
-
const salt = toStr(args[1]);
|
|
136
|
-
const keylen = toNum(args[2], 64);
|
|
137
|
-
return new Promise((resolve, reject) => {
|
|
138
|
-
_scrypt(password, salt, keylen, (err, key) => {
|
|
139
|
-
if (err) reject(err);
|
|
140
|
-
else resolve(key.toString('hex'));
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
},
|
|
144
|
-
|
|
145
|
-
// --- Encoding ---
|
|
146
|
-
|
|
147
|
-
base64Encode: (args) => {
|
|
148
|
-
requireArgs('crypto.base64Encode', args, 1);
|
|
149
|
-
return Buffer.from(toStr(args[0])).toString('base64');
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
base64Decode: (args) => {
|
|
153
|
-
requireArgs('crypto.base64Decode', args, 1);
|
|
154
|
-
return Buffer.from(toStr(args[0]), 'base64').toString('utf-8');
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
base64UrlEncode: (args) => {
|
|
158
|
-
requireArgs('crypto.base64UrlEncode', args, 1);
|
|
159
|
-
return Buffer.from(toStr(args[0])).toString('base64url');
|
|
160
|
-
},
|
|
161
|
-
|
|
162
|
-
base64UrlDecode: (args) => {
|
|
163
|
-
requireArgs('crypto.base64UrlDecode', args, 1);
|
|
164
|
-
return Buffer.from(toStr(args[0]), 'base64url').toString('utf-8');
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
hexEncode: (args) => {
|
|
168
|
-
requireArgs('crypto.hexEncode', args, 1);
|
|
169
|
-
return Buffer.from(toStr(args[0])).toString('hex');
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
hexDecode: (args) => {
|
|
173
|
-
requireArgs('crypto.hexDecode', args, 1);
|
|
174
|
-
return Buffer.from(toStr(args[0]), 'hex').toString('utf-8');
|
|
175
|
-
},
|
|
176
|
-
|
|
177
|
-
// --- Info ---
|
|
178
|
-
|
|
179
|
-
ciphers: () => getCiphers(),
|
|
180
|
-
|
|
181
|
-
hashes: () => getHashes()
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
export const CryptoNativeFunctionMetadata = {
|
|
185
|
-
hash: {
|
|
186
|
-
description: 'Hash data with any supported algorithm',
|
|
187
|
-
parameters: [
|
|
188
|
-
{ name: 'algorithm', dataType: 'string', description: 'Hash algorithm (md5, sha256, sha512, etc.)', formInputType: 'text', required: true },
|
|
189
|
-
{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true },
|
|
190
|
-
{ name: 'encoding', dataType: 'string', description: 'Output encoding (hex, base64)', formInputType: 'text', required: false, defaultValue: 'hex' }
|
|
191
|
-
],
|
|
192
|
-
returnType: 'string', returnDescription: 'Hash digest', example: 'crypto.hash "sha256" "hello"'
|
|
193
|
-
},
|
|
194
|
-
md5: {
|
|
195
|
-
description: 'MD5 hash',
|
|
196
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true }],
|
|
197
|
-
returnType: 'string', returnDescription: 'MD5 hex digest', example: 'crypto.md5 "hello"'
|
|
198
|
-
},
|
|
199
|
-
sha1: {
|
|
200
|
-
description: 'SHA-1 hash',
|
|
201
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true }],
|
|
202
|
-
returnType: 'string', returnDescription: 'SHA-1 hex digest', example: 'crypto.sha1 "hello"'
|
|
203
|
-
},
|
|
204
|
-
sha256: {
|
|
205
|
-
description: 'SHA-256 hash',
|
|
206
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true }],
|
|
207
|
-
returnType: 'string', returnDescription: 'SHA-256 hex digest', example: 'crypto.sha256 "hello"'
|
|
208
|
-
},
|
|
209
|
-
sha512: {
|
|
210
|
-
description: 'SHA-512 hash',
|
|
211
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true }],
|
|
212
|
-
returnType: 'string', returnDescription: 'SHA-512 hex digest', example: 'crypto.sha512 "hello"'
|
|
213
|
-
},
|
|
214
|
-
hmac: {
|
|
215
|
-
description: 'HMAC with any algorithm',
|
|
216
|
-
parameters: [
|
|
217
|
-
{ name: 'algorithm', dataType: 'string', description: 'Hash algorithm', formInputType: 'text', required: true },
|
|
218
|
-
{ name: 'key', dataType: 'string', description: 'Secret key', formInputType: 'text', required: true },
|
|
219
|
-
{ name: 'data', dataType: 'string', description: 'Data to sign', formInputType: 'text', required: true },
|
|
220
|
-
{ name: 'encoding', dataType: 'string', description: 'Output encoding', formInputType: 'text', required: false, defaultValue: 'hex' }
|
|
221
|
-
],
|
|
222
|
-
returnType: 'string', returnDescription: 'HMAC digest', example: 'crypto.hmac "sha256" "secret" "data"'
|
|
223
|
-
},
|
|
224
|
-
hmacSha256: {
|
|
225
|
-
description: 'HMAC-SHA256',
|
|
226
|
-
parameters: [
|
|
227
|
-
{ name: 'key', dataType: 'string', description: 'Secret key', formInputType: 'text', required: true },
|
|
228
|
-
{ name: 'data', dataType: 'string', description: 'Data to sign', formInputType: 'text', required: true }
|
|
229
|
-
],
|
|
230
|
-
returnType: 'string', returnDescription: 'HMAC-SHA256 hex digest', example: 'crypto.hmacSha256 "secret" "data"'
|
|
231
|
-
},
|
|
232
|
-
hmacSha512: {
|
|
233
|
-
description: 'HMAC-SHA512',
|
|
234
|
-
parameters: [
|
|
235
|
-
{ name: 'key', dataType: 'string', description: 'Secret key', formInputType: 'text', required: true },
|
|
236
|
-
{ name: 'data', dataType: 'string', description: 'Data to sign', formInputType: 'text', required: true }
|
|
237
|
-
],
|
|
238
|
-
returnType: 'string', returnDescription: 'HMAC-SHA512 hex digest', example: 'crypto.hmacSha512 "secret" "data"'
|
|
239
|
-
},
|
|
240
|
-
encrypt: {
|
|
241
|
-
description: 'Encrypt data with AES (returns iv:ciphertext)',
|
|
242
|
-
parameters: [
|
|
243
|
-
{ name: 'algorithm', dataType: 'string', description: 'Cipher algorithm (default: aes-256-cbc)', formInputType: 'text', required: false, defaultValue: 'aes-256-cbc' },
|
|
244
|
-
{ name: 'key', dataType: 'string', description: 'Encryption key (hashed to 32 bytes)', formInputType: 'text', required: true },
|
|
245
|
-
{ name: 'data', dataType: 'string', description: 'Data to encrypt', formInputType: 'text', required: true }
|
|
246
|
-
],
|
|
247
|
-
returnType: 'string', returnDescription: 'iv:encryptedHex string', example: 'crypto.encrypt "aes-256-cbc" "mykey" "secret data"'
|
|
248
|
-
},
|
|
249
|
-
decrypt: {
|
|
250
|
-
description: 'Decrypt data from encrypt() output',
|
|
251
|
-
parameters: [
|
|
252
|
-
{ name: 'algorithm', dataType: 'string', description: 'Cipher algorithm', formInputType: 'text', required: false, defaultValue: 'aes-256-cbc' },
|
|
253
|
-
{ name: 'key', dataType: 'string', description: 'Encryption key', formInputType: 'text', required: true },
|
|
254
|
-
{ name: 'encryptedData', dataType: 'string', description: 'iv:ciphertext from encrypt()', formInputType: 'text', required: true }
|
|
255
|
-
],
|
|
256
|
-
returnType: 'string', returnDescription: 'Decrypted string', example: 'crypto.decrypt "aes-256-cbc" "mykey" $encrypted'
|
|
257
|
-
},
|
|
258
|
-
randomBytes: {
|
|
259
|
-
description: 'Generate random bytes',
|
|
260
|
-
parameters: [
|
|
261
|
-
{ name: 'size', dataType: 'number', description: 'Number of bytes (default: 32)', formInputType: 'number', required: false, defaultValue: 32 },
|
|
262
|
-
{ name: 'encoding', dataType: 'string', description: 'Output encoding (hex, base64)', formInputType: 'text', required: false, defaultValue: 'hex' }
|
|
263
|
-
],
|
|
264
|
-
returnType: 'string', returnDescription: 'Random bytes as encoded string', example: 'crypto.randomBytes 16'
|
|
265
|
-
},
|
|
266
|
-
randomUUID: {
|
|
267
|
-
description: 'Generate a random UUID v4',
|
|
268
|
-
parameters: [],
|
|
269
|
-
returnType: 'string', returnDescription: 'UUID v4 string', example: 'crypto.randomUUID'
|
|
270
|
-
},
|
|
271
|
-
randomInt: {
|
|
272
|
-
description: 'Generate a random integer',
|
|
273
|
-
parameters: [
|
|
274
|
-
{ name: 'min', dataType: 'number', description: 'Minimum (or max if single arg)', formInputType: 'number', required: false, defaultValue: 0 },
|
|
275
|
-
{ name: 'max', dataType: 'number', description: 'Maximum (exclusive)', formInputType: 'number', required: false, defaultValue: 100 }
|
|
276
|
-
],
|
|
277
|
-
returnType: 'number', returnDescription: 'Random integer', example: 'crypto.randomInt 1 100'
|
|
278
|
-
},
|
|
279
|
-
pbkdf2: {
|
|
280
|
-
description: 'Derive key using PBKDF2',
|
|
281
|
-
parameters: [
|
|
282
|
-
{ name: 'password', dataType: 'string', description: 'Password', formInputType: 'text', required: true },
|
|
283
|
-
{ name: 'salt', dataType: 'string', description: 'Salt', formInputType: 'text', required: true },
|
|
284
|
-
{ name: 'iterations', dataType: 'number', description: 'Iterations (default: 100000)', formInputType: 'number', required: false, defaultValue: 100000 },
|
|
285
|
-
{ name: 'keylen', dataType: 'number', description: 'Key length (default: 64)', formInputType: 'number', required: false, defaultValue: 64 },
|
|
286
|
-
{ name: 'digest', dataType: 'string', description: 'Digest algorithm (default: sha512)', formInputType: 'text', required: false, defaultValue: 'sha512' }
|
|
287
|
-
],
|
|
288
|
-
returnType: 'string', returnDescription: 'Derived key as hex', example: 'crypto.pbkdf2 "password" "salt"'
|
|
289
|
-
},
|
|
290
|
-
scrypt: {
|
|
291
|
-
description: 'Derive key using scrypt',
|
|
292
|
-
parameters: [
|
|
293
|
-
{ name: 'password', dataType: 'string', description: 'Password', formInputType: 'text', required: true },
|
|
294
|
-
{ name: 'salt', dataType: 'string', description: 'Salt', formInputType: 'text', required: true },
|
|
295
|
-
{ name: 'keylen', dataType: 'number', description: 'Key length (default: 64)', formInputType: 'number', required: false, defaultValue: 64 }
|
|
296
|
-
],
|
|
297
|
-
returnType: 'string', returnDescription: 'Derived key as hex', example: 'crypto.scrypt "password" "salt"'
|
|
298
|
-
},
|
|
299
|
-
base64Encode: {
|
|
300
|
-
description: 'Encode string to Base64',
|
|
301
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Data to encode', formInputType: 'text', required: true }],
|
|
302
|
-
returnType: 'string', returnDescription: 'Base64 encoded string', example: 'crypto.base64Encode "hello"'
|
|
303
|
-
},
|
|
304
|
-
base64Decode: {
|
|
305
|
-
description: 'Decode Base64 to string',
|
|
306
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Base64 data', formInputType: 'text', required: true }],
|
|
307
|
-
returnType: 'string', returnDescription: 'Decoded string', example: 'crypto.base64Decode "aGVsbG8="'
|
|
308
|
-
},
|
|
309
|
-
base64UrlEncode: {
|
|
310
|
-
description: 'Encode string to URL-safe Base64',
|
|
311
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Data to encode', formInputType: 'text', required: true }],
|
|
312
|
-
returnType: 'string', returnDescription: 'Base64url encoded string', example: 'crypto.base64UrlEncode "hello"'
|
|
313
|
-
},
|
|
314
|
-
base64UrlDecode: {
|
|
315
|
-
description: 'Decode URL-safe Base64 to string',
|
|
316
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Base64url data', formInputType: 'text', required: true }],
|
|
317
|
-
returnType: 'string', returnDescription: 'Decoded string', example: 'crypto.base64UrlDecode "aGVsbG8"'
|
|
318
|
-
},
|
|
319
|
-
hexEncode: {
|
|
320
|
-
description: 'Encode string to hex',
|
|
321
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Data to encode', formInputType: 'text', required: true }],
|
|
322
|
-
returnType: 'string', returnDescription: 'Hex encoded string', example: 'crypto.hexEncode "hello"'
|
|
323
|
-
},
|
|
324
|
-
hexDecode: {
|
|
325
|
-
description: 'Decode hex to string',
|
|
326
|
-
parameters: [{ name: 'data', dataType: 'string', description: 'Hex data', formInputType: 'text', required: true }],
|
|
327
|
-
returnType: 'string', returnDescription: 'Decoded string', example: 'crypto.hexDecode "68656c6c6f"'
|
|
328
|
-
},
|
|
329
|
-
ciphers: {
|
|
330
|
-
description: 'List all supported cipher algorithms',
|
|
331
|
-
parameters: [],
|
|
332
|
-
returnType: 'array', returnDescription: 'Array of cipher names', example: 'crypto.ciphers'
|
|
333
|
-
},
|
|
334
|
-
hashes: {
|
|
335
|
-
description: 'List all supported hash algorithms',
|
|
336
|
-
parameters: [],
|
|
337
|
-
returnType: 'array', returnDescription: 'Array of hash names', example: 'crypto.hashes'
|
|
338
|
-
}
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
export const CryptoNativeModuleMetadata = {
|
|
342
|
-
description: 'Cryptographic operations: hashing, HMAC, encryption, key derivation, random generation, and encoding',
|
|
343
|
-
methods: Object.keys(CryptoNativeFunctions)
|
|
344
|
-
};
|
|
345
|
-
|
|
346
|
-
export default {
|
|
347
|
-
name: 'crypto',
|
|
348
|
-
functions: CryptoNativeFunctions,
|
|
349
|
-
functionMetadata: CryptoNativeFunctionMetadata,
|
|
350
|
-
moduleMetadata: CryptoNativeModuleMetadata,
|
|
351
|
-
global: false
|
|
352
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Native crypto module for RobinPath.
|
|
3
|
+
* Full cryptographic operations powered by Node.js crypto.
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
createHash, createHmac, createCipheriv, createDecipheriv,
|
|
7
|
+
randomBytes, randomUUID, randomInt, pbkdf2 as _pbkdf2, scrypt as _scrypt,
|
|
8
|
+
getCiphers, getHashes
|
|
9
|
+
} from 'node:crypto';
|
|
10
|
+
import { toStr, toNum, requireArgs } from './_helpers.js';
|
|
11
|
+
|
|
12
|
+
export const CryptoNativeFunctions = {
|
|
13
|
+
|
|
14
|
+
// --- Hashing ---
|
|
15
|
+
|
|
16
|
+
hash: async (args) => {
|
|
17
|
+
requireArgs('crypto.hash', args, 2);
|
|
18
|
+
const algo = toStr(args[0]);
|
|
19
|
+
const data = toStr(args[1]);
|
|
20
|
+
const encoding = toStr(args[2], 'hex');
|
|
21
|
+
return createHash(algo).update(data).digest(encoding);
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
md5: async (args) => {
|
|
25
|
+
requireArgs('crypto.md5', args, 1);
|
|
26
|
+
return createHash('md5').update(toStr(args[0])).digest('hex');
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
sha1: async (args) => {
|
|
30
|
+
requireArgs('crypto.sha1', args, 1);
|
|
31
|
+
return createHash('sha1').update(toStr(args[0])).digest('hex');
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
sha256: async (args) => {
|
|
35
|
+
requireArgs('crypto.sha256', args, 1);
|
|
36
|
+
return createHash('sha256').update(toStr(args[0])).digest('hex');
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
sha512: async (args) => {
|
|
40
|
+
requireArgs('crypto.sha512', args, 1);
|
|
41
|
+
return createHash('sha512').update(toStr(args[0])).digest('hex');
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// --- HMAC ---
|
|
45
|
+
|
|
46
|
+
hmac: async (args) => {
|
|
47
|
+
requireArgs('crypto.hmac', args, 3);
|
|
48
|
+
const algo = toStr(args[0]);
|
|
49
|
+
const key = toStr(args[1]);
|
|
50
|
+
const data = toStr(args[2]);
|
|
51
|
+
const encoding = toStr(args[3], 'hex');
|
|
52
|
+
return createHmac(algo, key).update(data).digest(encoding);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
hmacSha256: async (args) => {
|
|
56
|
+
requireArgs('crypto.hmacSha256', args, 2);
|
|
57
|
+
return createHmac('sha256', toStr(args[0])).update(toStr(args[1])).digest('hex');
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
hmacSha512: async (args) => {
|
|
61
|
+
requireArgs('crypto.hmacSha512', args, 2);
|
|
62
|
+
return createHmac('sha512', toStr(args[0])).update(toStr(args[1])).digest('hex');
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
// --- Encryption ---
|
|
66
|
+
|
|
67
|
+
encrypt: async (args) => {
|
|
68
|
+
requireArgs('crypto.encrypt', args, 3);
|
|
69
|
+
const algo = toStr(args[0], 'aes-256-cbc');
|
|
70
|
+
const key = toStr(args[1]);
|
|
71
|
+
const data = toStr(args[2]);
|
|
72
|
+
// Derive a 32-byte key from the input key
|
|
73
|
+
const keyBuf = createHash('sha256').update(key).digest();
|
|
74
|
+
// Generate random IV
|
|
75
|
+
const iv = randomBytes(16);
|
|
76
|
+
const cipher = createCipheriv(algo, keyBuf, iv);
|
|
77
|
+
let encrypted = cipher.update(data, 'utf-8', 'hex');
|
|
78
|
+
encrypted += cipher.final('hex');
|
|
79
|
+
// Return IV + encrypted data separated by ':'
|
|
80
|
+
return iv.toString('hex') + ':' + encrypted;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
decrypt: async (args) => {
|
|
84
|
+
requireArgs('crypto.decrypt', args, 3);
|
|
85
|
+
const algo = toStr(args[0], 'aes-256-cbc');
|
|
86
|
+
const key = toStr(args[1]);
|
|
87
|
+
const encryptedStr = toStr(args[2]);
|
|
88
|
+
const keyBuf = createHash('sha256').update(key).digest();
|
|
89
|
+
const parts = encryptedStr.split(':');
|
|
90
|
+
if (parts.length !== 2) throw new Error('crypto.decrypt: invalid encrypted data format (expected iv:data)');
|
|
91
|
+
const iv = Buffer.from(parts[0], 'hex');
|
|
92
|
+
const encrypted = parts[1];
|
|
93
|
+
const decipher = createDecipheriv(algo, keyBuf, iv);
|
|
94
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf-8');
|
|
95
|
+
decrypted += decipher.final('utf-8');
|
|
96
|
+
return decrypted;
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
// --- Random ---
|
|
100
|
+
|
|
101
|
+
randomBytes: async (args) => {
|
|
102
|
+
const size = toNum(args[0], 32);
|
|
103
|
+
const encoding = toStr(args[1], 'hex');
|
|
104
|
+
return randomBytes(size).toString(encoding);
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
randomUUID: () => randomUUID(),
|
|
108
|
+
|
|
109
|
+
randomInt: (args) => {
|
|
110
|
+
const min = args.length >= 2 ? toNum(args[0], 0) : 0;
|
|
111
|
+
const max = args.length >= 2 ? toNum(args[1], 100) : toNum(args[0], 100);
|
|
112
|
+
return randomInt(min, max);
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// --- Key Derivation ---
|
|
116
|
+
|
|
117
|
+
pbkdf2: (args) => {
|
|
118
|
+
requireArgs('crypto.pbkdf2', args, 2);
|
|
119
|
+
const password = toStr(args[0]);
|
|
120
|
+
const salt = toStr(args[1], 'salt');
|
|
121
|
+
const iterations = toNum(args[2], 100000);
|
|
122
|
+
const keylen = toNum(args[3], 64);
|
|
123
|
+
const digest = toStr(args[4], 'sha512');
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
_pbkdf2(password, salt, iterations, keylen, digest, (err, key) => {
|
|
126
|
+
if (err) reject(err);
|
|
127
|
+
else resolve(key.toString('hex'));
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
scrypt: (args) => {
|
|
133
|
+
requireArgs('crypto.scrypt', args, 2);
|
|
134
|
+
const password = toStr(args[0]);
|
|
135
|
+
const salt = toStr(args[1]);
|
|
136
|
+
const keylen = toNum(args[2], 64);
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
_scrypt(password, salt, keylen, (err, key) => {
|
|
139
|
+
if (err) reject(err);
|
|
140
|
+
else resolve(key.toString('hex'));
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// --- Encoding ---
|
|
146
|
+
|
|
147
|
+
base64Encode: (args) => {
|
|
148
|
+
requireArgs('crypto.base64Encode', args, 1);
|
|
149
|
+
return Buffer.from(toStr(args[0])).toString('base64');
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
base64Decode: (args) => {
|
|
153
|
+
requireArgs('crypto.base64Decode', args, 1);
|
|
154
|
+
return Buffer.from(toStr(args[0]), 'base64').toString('utf-8');
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
base64UrlEncode: (args) => {
|
|
158
|
+
requireArgs('crypto.base64UrlEncode', args, 1);
|
|
159
|
+
return Buffer.from(toStr(args[0])).toString('base64url');
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
base64UrlDecode: (args) => {
|
|
163
|
+
requireArgs('crypto.base64UrlDecode', args, 1);
|
|
164
|
+
return Buffer.from(toStr(args[0]), 'base64url').toString('utf-8');
|
|
165
|
+
},
|
|
166
|
+
|
|
167
|
+
hexEncode: (args) => {
|
|
168
|
+
requireArgs('crypto.hexEncode', args, 1);
|
|
169
|
+
return Buffer.from(toStr(args[0])).toString('hex');
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
hexDecode: (args) => {
|
|
173
|
+
requireArgs('crypto.hexDecode', args, 1);
|
|
174
|
+
return Buffer.from(toStr(args[0]), 'hex').toString('utf-8');
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// --- Info ---
|
|
178
|
+
|
|
179
|
+
ciphers: () => getCiphers(),
|
|
180
|
+
|
|
181
|
+
hashes: () => getHashes()
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export const CryptoNativeFunctionMetadata = {
|
|
185
|
+
hash: {
|
|
186
|
+
description: 'Hash data with any supported algorithm',
|
|
187
|
+
parameters: [
|
|
188
|
+
{ name: 'algorithm', dataType: 'string', description: 'Hash algorithm (md5, sha256, sha512, etc.)', formInputType: 'text', required: true },
|
|
189
|
+
{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true },
|
|
190
|
+
{ name: 'encoding', dataType: 'string', description: 'Output encoding (hex, base64)', formInputType: 'text', required: false, defaultValue: 'hex' }
|
|
191
|
+
],
|
|
192
|
+
returnType: 'string', returnDescription: 'Hash digest', example: 'crypto.hash "sha256" "hello"'
|
|
193
|
+
},
|
|
194
|
+
md5: {
|
|
195
|
+
description: 'MD5 hash',
|
|
196
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true }],
|
|
197
|
+
returnType: 'string', returnDescription: 'MD5 hex digest', example: 'crypto.md5 "hello"'
|
|
198
|
+
},
|
|
199
|
+
sha1: {
|
|
200
|
+
description: 'SHA-1 hash',
|
|
201
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true }],
|
|
202
|
+
returnType: 'string', returnDescription: 'SHA-1 hex digest', example: 'crypto.sha1 "hello"'
|
|
203
|
+
},
|
|
204
|
+
sha256: {
|
|
205
|
+
description: 'SHA-256 hash',
|
|
206
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true }],
|
|
207
|
+
returnType: 'string', returnDescription: 'SHA-256 hex digest', example: 'crypto.sha256 "hello"'
|
|
208
|
+
},
|
|
209
|
+
sha512: {
|
|
210
|
+
description: 'SHA-512 hash',
|
|
211
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Data to hash', formInputType: 'text', required: true }],
|
|
212
|
+
returnType: 'string', returnDescription: 'SHA-512 hex digest', example: 'crypto.sha512 "hello"'
|
|
213
|
+
},
|
|
214
|
+
hmac: {
|
|
215
|
+
description: 'HMAC with any algorithm',
|
|
216
|
+
parameters: [
|
|
217
|
+
{ name: 'algorithm', dataType: 'string', description: 'Hash algorithm', formInputType: 'text', required: true },
|
|
218
|
+
{ name: 'key', dataType: 'string', description: 'Secret key', formInputType: 'text', required: true },
|
|
219
|
+
{ name: 'data', dataType: 'string', description: 'Data to sign', formInputType: 'text', required: true },
|
|
220
|
+
{ name: 'encoding', dataType: 'string', description: 'Output encoding', formInputType: 'text', required: false, defaultValue: 'hex' }
|
|
221
|
+
],
|
|
222
|
+
returnType: 'string', returnDescription: 'HMAC digest', example: 'crypto.hmac "sha256" "secret" "data"'
|
|
223
|
+
},
|
|
224
|
+
hmacSha256: {
|
|
225
|
+
description: 'HMAC-SHA256',
|
|
226
|
+
parameters: [
|
|
227
|
+
{ name: 'key', dataType: 'string', description: 'Secret key', formInputType: 'text', required: true },
|
|
228
|
+
{ name: 'data', dataType: 'string', description: 'Data to sign', formInputType: 'text', required: true }
|
|
229
|
+
],
|
|
230
|
+
returnType: 'string', returnDescription: 'HMAC-SHA256 hex digest', example: 'crypto.hmacSha256 "secret" "data"'
|
|
231
|
+
},
|
|
232
|
+
hmacSha512: {
|
|
233
|
+
description: 'HMAC-SHA512',
|
|
234
|
+
parameters: [
|
|
235
|
+
{ name: 'key', dataType: 'string', description: 'Secret key', formInputType: 'text', required: true },
|
|
236
|
+
{ name: 'data', dataType: 'string', description: 'Data to sign', formInputType: 'text', required: true }
|
|
237
|
+
],
|
|
238
|
+
returnType: 'string', returnDescription: 'HMAC-SHA512 hex digest', example: 'crypto.hmacSha512 "secret" "data"'
|
|
239
|
+
},
|
|
240
|
+
encrypt: {
|
|
241
|
+
description: 'Encrypt data with AES (returns iv:ciphertext)',
|
|
242
|
+
parameters: [
|
|
243
|
+
{ name: 'algorithm', dataType: 'string', description: 'Cipher algorithm (default: aes-256-cbc)', formInputType: 'text', required: false, defaultValue: 'aes-256-cbc' },
|
|
244
|
+
{ name: 'key', dataType: 'string', description: 'Encryption key (hashed to 32 bytes)', formInputType: 'text', required: true },
|
|
245
|
+
{ name: 'data', dataType: 'string', description: 'Data to encrypt', formInputType: 'text', required: true }
|
|
246
|
+
],
|
|
247
|
+
returnType: 'string', returnDescription: 'iv:encryptedHex string', example: 'crypto.encrypt "aes-256-cbc" "mykey" "secret data"'
|
|
248
|
+
},
|
|
249
|
+
decrypt: {
|
|
250
|
+
description: 'Decrypt data from encrypt() output',
|
|
251
|
+
parameters: [
|
|
252
|
+
{ name: 'algorithm', dataType: 'string', description: 'Cipher algorithm', formInputType: 'text', required: false, defaultValue: 'aes-256-cbc' },
|
|
253
|
+
{ name: 'key', dataType: 'string', description: 'Encryption key', formInputType: 'text', required: true },
|
|
254
|
+
{ name: 'encryptedData', dataType: 'string', description: 'iv:ciphertext from encrypt()', formInputType: 'text', required: true }
|
|
255
|
+
],
|
|
256
|
+
returnType: 'string', returnDescription: 'Decrypted string', example: 'crypto.decrypt "aes-256-cbc" "mykey" $encrypted'
|
|
257
|
+
},
|
|
258
|
+
randomBytes: {
|
|
259
|
+
description: 'Generate random bytes',
|
|
260
|
+
parameters: [
|
|
261
|
+
{ name: 'size', dataType: 'number', description: 'Number of bytes (default: 32)', formInputType: 'number', required: false, defaultValue: 32 },
|
|
262
|
+
{ name: 'encoding', dataType: 'string', description: 'Output encoding (hex, base64)', formInputType: 'text', required: false, defaultValue: 'hex' }
|
|
263
|
+
],
|
|
264
|
+
returnType: 'string', returnDescription: 'Random bytes as encoded string', example: 'crypto.randomBytes 16'
|
|
265
|
+
},
|
|
266
|
+
randomUUID: {
|
|
267
|
+
description: 'Generate a random UUID v4',
|
|
268
|
+
parameters: [],
|
|
269
|
+
returnType: 'string', returnDescription: 'UUID v4 string', example: 'crypto.randomUUID'
|
|
270
|
+
},
|
|
271
|
+
randomInt: {
|
|
272
|
+
description: 'Generate a random integer',
|
|
273
|
+
parameters: [
|
|
274
|
+
{ name: 'min', dataType: 'number', description: 'Minimum (or max if single arg)', formInputType: 'number', required: false, defaultValue: 0 },
|
|
275
|
+
{ name: 'max', dataType: 'number', description: 'Maximum (exclusive)', formInputType: 'number', required: false, defaultValue: 100 }
|
|
276
|
+
],
|
|
277
|
+
returnType: 'number', returnDescription: 'Random integer', example: 'crypto.randomInt 1 100'
|
|
278
|
+
},
|
|
279
|
+
pbkdf2: {
|
|
280
|
+
description: 'Derive key using PBKDF2',
|
|
281
|
+
parameters: [
|
|
282
|
+
{ name: 'password', dataType: 'string', description: 'Password', formInputType: 'text', required: true },
|
|
283
|
+
{ name: 'salt', dataType: 'string', description: 'Salt', formInputType: 'text', required: true },
|
|
284
|
+
{ name: 'iterations', dataType: 'number', description: 'Iterations (default: 100000)', formInputType: 'number', required: false, defaultValue: 100000 },
|
|
285
|
+
{ name: 'keylen', dataType: 'number', description: 'Key length (default: 64)', formInputType: 'number', required: false, defaultValue: 64 },
|
|
286
|
+
{ name: 'digest', dataType: 'string', description: 'Digest algorithm (default: sha512)', formInputType: 'text', required: false, defaultValue: 'sha512' }
|
|
287
|
+
],
|
|
288
|
+
returnType: 'string', returnDescription: 'Derived key as hex', example: 'crypto.pbkdf2 "password" "salt"'
|
|
289
|
+
},
|
|
290
|
+
scrypt: {
|
|
291
|
+
description: 'Derive key using scrypt',
|
|
292
|
+
parameters: [
|
|
293
|
+
{ name: 'password', dataType: 'string', description: 'Password', formInputType: 'text', required: true },
|
|
294
|
+
{ name: 'salt', dataType: 'string', description: 'Salt', formInputType: 'text', required: true },
|
|
295
|
+
{ name: 'keylen', dataType: 'number', description: 'Key length (default: 64)', formInputType: 'number', required: false, defaultValue: 64 }
|
|
296
|
+
],
|
|
297
|
+
returnType: 'string', returnDescription: 'Derived key as hex', example: 'crypto.scrypt "password" "salt"'
|
|
298
|
+
},
|
|
299
|
+
base64Encode: {
|
|
300
|
+
description: 'Encode string to Base64',
|
|
301
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Data to encode', formInputType: 'text', required: true }],
|
|
302
|
+
returnType: 'string', returnDescription: 'Base64 encoded string', example: 'crypto.base64Encode "hello"'
|
|
303
|
+
},
|
|
304
|
+
base64Decode: {
|
|
305
|
+
description: 'Decode Base64 to string',
|
|
306
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Base64 data', formInputType: 'text', required: true }],
|
|
307
|
+
returnType: 'string', returnDescription: 'Decoded string', example: 'crypto.base64Decode "aGVsbG8="'
|
|
308
|
+
},
|
|
309
|
+
base64UrlEncode: {
|
|
310
|
+
description: 'Encode string to URL-safe Base64',
|
|
311
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Data to encode', formInputType: 'text', required: true }],
|
|
312
|
+
returnType: 'string', returnDescription: 'Base64url encoded string', example: 'crypto.base64UrlEncode "hello"'
|
|
313
|
+
},
|
|
314
|
+
base64UrlDecode: {
|
|
315
|
+
description: 'Decode URL-safe Base64 to string',
|
|
316
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Base64url data', formInputType: 'text', required: true }],
|
|
317
|
+
returnType: 'string', returnDescription: 'Decoded string', example: 'crypto.base64UrlDecode "aGVsbG8"'
|
|
318
|
+
},
|
|
319
|
+
hexEncode: {
|
|
320
|
+
description: 'Encode string to hex',
|
|
321
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Data to encode', formInputType: 'text', required: true }],
|
|
322
|
+
returnType: 'string', returnDescription: 'Hex encoded string', example: 'crypto.hexEncode "hello"'
|
|
323
|
+
},
|
|
324
|
+
hexDecode: {
|
|
325
|
+
description: 'Decode hex to string',
|
|
326
|
+
parameters: [{ name: 'data', dataType: 'string', description: 'Hex data', formInputType: 'text', required: true }],
|
|
327
|
+
returnType: 'string', returnDescription: 'Decoded string', example: 'crypto.hexDecode "68656c6c6f"'
|
|
328
|
+
},
|
|
329
|
+
ciphers: {
|
|
330
|
+
description: 'List all supported cipher algorithms',
|
|
331
|
+
parameters: [],
|
|
332
|
+
returnType: 'array', returnDescription: 'Array of cipher names', example: 'crypto.ciphers'
|
|
333
|
+
},
|
|
334
|
+
hashes: {
|
|
335
|
+
description: 'List all supported hash algorithms',
|
|
336
|
+
parameters: [],
|
|
337
|
+
returnType: 'array', returnDescription: 'Array of hash names', example: 'crypto.hashes'
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
export const CryptoNativeModuleMetadata = {
|
|
342
|
+
description: 'Cryptographic operations: hashing, HMAC, encryption, key derivation, random generation, and encoding',
|
|
343
|
+
methods: Object.keys(CryptoNativeFunctions)
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export default {
|
|
347
|
+
name: 'crypto',
|
|
348
|
+
functions: CryptoNativeFunctions,
|
|
349
|
+
functionMetadata: CryptoNativeFunctionMetadata,
|
|
350
|
+
moduleMetadata: CryptoNativeModuleMetadata,
|
|
351
|
+
global: false
|
|
352
|
+
};
|