浏览代码

feat: US-008 - Crypto Node - Random Generation

fszontagh 6 月之前
父节点
当前提交
c489700392
共有 1 个文件被更改,包括 139 次插入32 次删除
  1. 139 32
      nodes/crypto/crypto.js

+ 139 - 32
nodes/crypto/crypto.js

@@ -42,21 +42,51 @@ const configSchema = {
             description: 'Secret key for HMAC generation. Supports {{variable}} interpolation.',
             showWhen: { field: 'operation', value: 'hmac' }
         },
+        randomType: {
+            type: 'string',
+            title: 'Random Type',
+            enum: ['uuid', 'hex', 'alphanumeric', 'password'],
+            enumLabels: ['UUID v4', 'Hex Token', 'Alphanumeric String', 'Secure Password'],
+            default: 'uuid',
+            description: 'Type of random value to generate',
+            showWhen: { field: 'operation', value: 'random' }
+        },
         randomLength: {
             type: 'number',
-            title: 'Random Bytes Length',
+            title: 'Length',
             default: 32,
-            description: 'Number of random bytes to generate (1-1024)',
+            minimum: 1,
+            maximum: 1024,
+            description: 'Length of the generated value (1-1024). For hex, this is number of bytes (output will be 2x characters).',
             showWhen: { field: 'operation', value: 'random' }
         },
-        randomFormat: {
-            type: 'string',
-            title: 'Random Output Format',
-            enum: ['hex', 'base64', 'uuid'],
-            enumLabels: ['Hexadecimal', 'Base64', 'UUID v4'],
-            default: 'hex',
-            description: 'Format of the random output',
-            showWhen: { field: 'operation', value: 'random' }
+        passwordUppercase: {
+            type: 'boolean',
+            title: 'Include Uppercase',
+            default: true,
+            description: 'Include uppercase letters (A-Z)',
+            showWhen: { field: 'randomType', value: 'password' }
+        },
+        passwordLowercase: {
+            type: 'boolean',
+            title: 'Include Lowercase',
+            default: true,
+            description: 'Include lowercase letters (a-z)',
+            showWhen: { field: 'randomType', value: 'password' }
+        },
+        passwordNumbers: {
+            type: 'boolean',
+            title: 'Include Numbers',
+            default: true,
+            description: 'Include numbers (0-9)',
+            showWhen: { field: 'randomType', value: 'password' }
+        },
+        passwordSymbols: {
+            type: 'boolean',
+            title: 'Include Symbols',
+            default: true,
+            description: 'Include symbols (!@#$%^&*()_+-=[]{}|;:,.<>?)',
+            showWhen: { field: 'randomType', value: 'password' }
         }
     },
     required: ['operation']
@@ -95,6 +125,14 @@ const outputSchema = {
             type: 'string',
             description: 'Output format (hex or base64)'
         },
+        randomType: {
+            type: 'string',
+            description: 'Type of random value generated (uuid, hex, alphanumeric, password)'
+        },
+        length: {
+            type: 'number',
+            description: 'Length of the generated random value'
+        },
         inputLength: {
             type: 'number',
             description: 'Length of the input data in bytes'
@@ -118,6 +156,43 @@ function interpolate(template, data) {
     });
 }
 
+function generateRandomString(length, charset) {
+    const charsetLength = charset.length;
+    let result = '';
+
+    // Generate enough random bytes to select characters
+    // We need to handle modulo bias by rejecting values that would cause bias
+    const bytesNeeded = length;
+    const randomHex = smartbotic.crypto.randomBytes(bytesNeeded * 2);
+
+    let pos = 0;
+    while (result.length < length) {
+        if (pos >= randomHex.length) {
+            // Need more random bytes
+            const moreBytes = smartbotic.crypto.randomBytes((length - result.length) * 2);
+            pos = 0;
+            for (let i = 0; i < moreBytes.length && result.length < length; i += 2) {
+                const byte = parseInt(moreBytes.substr(i, 2), 16);
+                // Use rejection sampling to avoid modulo bias
+                const maxUnbiased = Math.floor(256 / charsetLength) * charsetLength;
+                if (byte < maxUnbiased) {
+                    result += charset[byte % charsetLength];
+                }
+            }
+        } else {
+            const byte = parseInt(randomHex.substr(pos, 2), 16);
+            pos += 2;
+            // Use rejection sampling to avoid modulo bias
+            const maxUnbiased = Math.floor(256 / charsetLength) * charsetLength;
+            if (byte < maxUnbiased) {
+                result += charset[byte % charsetLength];
+            }
+        }
+    }
+
+    return result;
+}
+
 async function execute(config, input, context) {
     const operation = config.operation || 'hash';
 
@@ -126,42 +201,74 @@ async function execute(config, input, context) {
     }
 
     if (operation === 'random') {
-        const format = config.randomFormat || 'hex';
+        const randomType = config.randomType || 'uuid';
+        const length = config.randomLength || 32;
+
+        if (randomType !== 'uuid' && (length < 1 || length > 1024)) {
+            throw new Error('Length must be between 1 and 1024');
+        }
 
-        if (format === 'uuid') {
+        if (randomType === 'uuid') {
             const uuid = smartbotic.crypto.randomUUID();
-            smartbotic.log.info('Generated random UUID');
+            smartbotic.log.info('Generated random UUID v4');
             return {
                 result: uuid,
                 operation: 'random',
-                format: 'uuid'
+                randomType: 'uuid',
+                length: 36
             };
         }
 
-        const length = config.randomLength || 32;
-        if (length < 1 || length > 1024) {
-            throw new Error('Random length must be between 1 and 1024');
+        if (randomType === 'hex') {
+            const randomHex = smartbotic.crypto.randomBytes(length);
+            smartbotic.log.info('Generated ' + length + '-byte hex token (' + randomHex.length + ' characters)');
+            return {
+                result: randomHex,
+                operation: 'random',
+                randomType: 'hex',
+                length: randomHex.length
+            };
         }
 
-        const randomHex = smartbotic.crypto.randomBytes(length);
-        let result = randomHex;
+        if (randomType === 'alphanumeric') {
+            const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+            const result = generateRandomString(length, charset);
+            smartbotic.log.info('Generated ' + length + '-character alphanumeric string');
+            return {
+                result: result,
+                operation: 'random',
+                randomType: 'alphanumeric',
+                length: length
+            };
+        }
 
-        if (format === 'base64') {
-            let binary = '';
-            for (let i = 0; i < randomHex.length; i += 2) {
-                binary += String.fromCharCode(parseInt(randomHex.substr(i, 2), 16));
+        if (randomType === 'password') {
+            const includeUppercase = config.passwordUppercase !== false;
+            const includeLowercase = config.passwordLowercase !== false;
+            const includeNumbers = config.passwordNumbers !== false;
+            const includeSymbols = config.passwordSymbols !== false;
+
+            let charset = '';
+            if (includeUppercase) charset += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+            if (includeLowercase) charset += 'abcdefghijklmnopqrstuvwxyz';
+            if (includeNumbers) charset += '0123456789';
+            if (includeSymbols) charset += '!@#$%^&*()_+-=[]{}|;:,.<>?';
+
+            if (charset.length === 0) {
+                throw new Error('At least one character set must be enabled for password generation');
             }
-            result = smartbotic.utils.base64Encode(binary);
-        }
 
-        smartbotic.log.info('Generated ' + length + ' random bytes in ' + format + ' format');
+            const result = generateRandomString(length, charset);
+            smartbotic.log.info('Generated ' + length + '-character secure password');
+            return {
+                result: result,
+                operation: 'random',
+                randomType: 'password',
+                length: length
+            };
+        }
 
-        return {
-            result: result,
-            operation: 'random',
-            format: format,
-            inputLength: length
-        };
+        throw new Error('Unknown random type: ' + randomType);
     }
 
     const inputData = input.data !== undefined ? input.data : input;