fortify2-js
Version:
MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.
573 lines (567 loc) • 22.3 kB
JavaScript
;
var crypto = require('crypto');
function _interopNamespaceDefault(e) {
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto);
/**
* @author iDevo
* Enhanced RSA Key Size Calculator
* Calculates appropriate RSA key size based on data size with improved security and performance
*/
// Enhanced constants for better maintainability and security
const HASH_SIZES = {
sha1: 20, // Deprecated - included for legacy support only
sha224: 28,
sha256: 32, // Recommended default
sha384: 48, // High security
sha512: 64, // Maximum security
sha3_256: 32,
sha3_384: 48,
sha3_512: 64,
};
// Security level mappings for key strength assessment
const SECURITY_LEVELS = {
minimal: {
bits: 80,
description: "Legacy compatibility only",
minKeySize: 1024,
},
standard: {
bits: 112,
description: "Current standard security",
minKeySize: 2048,
},
high: {
bits: 128,
description: "High security applications",
minKeySize: 3072,
},
maximum: { bits: 256, description: "Maximum security", minKeySize: 15360 },
};
const STANDARD_RSA_KEY_SIZES = [2048, 3072, 4096, 7680, 8192, 15360];
const MIN_SECURE_KEY_SIZE = 2048; // NIST recommendation
const DEFAULT_HASH_ALGORITHM = "sha256";
// Performance thresholds
const PERFORMANCE_THRESHOLDS = {
fast: 2048, // Fast operations
balanced: 3072, // Balance of security and performance
secure: 4096, // High security with acceptable performance
maximum: 8192, // Maximum security, slower operations
};
/**
* Calculate OAEP padding overhead for given hash algorithm
*/
function calculateOAEPOverhead(hashAlgorithm) {
const hashSize = HASH_SIZES[hashAlgorithm];
return 2 * hashSize + 2;
}
/**
* Validate input parameters
*/
function validateInputs(dataSize, rsaKeySize, hashAlgorithm) {
if (!Number.isInteger(dataSize) || dataSize < 0) {
throw new Error("Data size must be a non-negative integer");
}
if (dataSize > 1024 * 1024) {
// 1MB limit for RSA
console.warn(`Large data size (${dataSize} bytes) - consider hybrid encryption instead`);
}
if (rsaKeySize !== undefined) {
if (!Number.isInteger(rsaKeySize) || rsaKeySize < MIN_SECURE_KEY_SIZE) {
throw new Error(`RSA key size must be at least ${MIN_SECURE_KEY_SIZE} bits for security`);
}
if (rsaKeySize % 8 !== 0) {
throw new Error("RSA key size must be divisible by 8");
}
}
if (hashAlgorithm && !(hashAlgorithm in HASH_SIZES)) {
throw new Error(`Unsupported hash algorithm: ${hashAlgorithm}. Supported: ${Object.keys(HASH_SIZES).join(", ")}`);
}
}
/**
* Get security level based on key size
*/
function getSecurityLevel(keySize) {
if (keySize >= 8192)
return "maximum";
if (keySize >= 4096)
return "high";
if (keySize >= 3072)
return "standard";
return "minimal";
}
/**
* Calculate the minimum RSA key size needed for the given data size
* @param dataSize - Size of data to encrypt in bytes
* @param hashAlgorithm - Hash algorithm for OAEP padding (default: sha256)
* @param allowCustomSize - Allow non-standard key sizes (default: false)
* @returns Recommended RSA key size in bits
*/
function calculateRSAKeySize(dataSize, hashAlgorithm = DEFAULT_HASH_ALGORITHM, allowCustomSize = false) {
validateInputs(dataSize, undefined, hashAlgorithm);
const oaepOverhead = calculateOAEPOverhead(hashAlgorithm);
const requiredKeyBytes = dataSize + oaepOverhead;
const requiredKeyBits = requiredKeyBytes * 8;
// Find the smallest standard key size that can accommodate the data
for (const keySize of STANDARD_RSA_KEY_SIZES) {
const maxDataSize = Math.floor(keySize / 8) - oaepOverhead;
if (dataSize <= maxDataSize) {
console.info(`Data size: ${dataSize} bytes, selected RSA key size: ${keySize} bits (max data: ${maxDataSize} bytes)`);
return keySize;
}
}
// Handle cases where data is too large for standard sizes
if (!allowCustomSize) {
const maxStandardSize = Math.max(...STANDARD_RSA_KEY_SIZES);
const maxDataForLargest = Math.floor(maxStandardSize / 8) - oaepOverhead;
throw new Error(`Data size ${dataSize} bytes exceeds maximum for standard RSA keys (max: ${maxDataForLargest} bytes). ` +
`Consider using hybrid encryption (RSA + AES) or set allowCustomSize=true.`);
}
// Calculate custom size rounded up to nearest 1024 bits
const customKeySize = Math.ceil(requiredKeyBits / 1024) * 1024;
console.warn(`Data size ${dataSize} bytes requires custom RSA key size: ${customKeySize} bits`);
return customKeySize;
}
/**
* Generate RSA key pair with appropriate size for the given data
* @param dataSize - Size of data to encrypt in bytes
* @param hashAlgorithm - Hash algorithm for OAEP padding
* @param allowCustomSize - Allow non-standard key sizes
* @returns RSA key pair with metadata
*/
function generateRSAKeyPairForData(dataSize, hashAlgorithm = DEFAULT_HASH_ALGORITHM, allowCustomSize = false) {
validateInputs(dataSize, undefined, hashAlgorithm);
const keySize = calculateRSAKeySize(dataSize, hashAlgorithm, allowCustomSize);
const maxDataSize = getMaxDataSizeForRSAKey(keySize, hashAlgorithm);
console.info(`Generating RSA key pair with ${keySize} bits for data size ${dataSize} bytes`);
try {
const keyPair = crypto__namespace.generateKeyPairSync("rsa", {
modulusLength: keySize,
publicKeyEncoding: {
type: "spki",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem",
cipher: undefined, // No password protection by default
passphrase: undefined,
},
});
return {
publicKey: keyPair.publicKey,
privateKey: keyPair.privateKey,
keySize,
maxDataSize,
hashAlgorithm,
};
}
catch (error) {
console.error(`Failed to generate RSA key pair: ${error.message}`);
throw new Error(`RSA key generation failed: ${error.message}`);
}
}
/**
* Generate password-protected RSA key pair
* @param dataSize - Size of data to encrypt in bytes
* @param passphrase - Password to protect the private key
* @param hashAlgorithm - Hash algorithm for OAEP padding
* @returns Protected RSA key pair
*/
function generateProtectedRSAKeyPairForData(dataSize, passphrase, hashAlgorithm = DEFAULT_HASH_ALGORITHM) {
validateInputs(dataSize, undefined, hashAlgorithm);
if (!passphrase || passphrase.length < 8) {
throw new Error("Passphrase must be at least 8 characters long");
}
const keySize = calculateRSAKeySize(dataSize, hashAlgorithm);
const maxDataSize = getMaxDataSizeForRSAKey(keySize, hashAlgorithm);
try {
const keyPair = crypto__namespace.generateKeyPairSync("rsa", {
modulusLength: keySize,
publicKeyEncoding: {
type: "spki",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem",
cipher: "aes-256-cbc",
passphrase: passphrase,
},
});
return {
publicKey: keyPair.publicKey,
privateKey: keyPair.privateKey,
keySize,
maxDataSize,
hashAlgorithm,
};
}
catch (error) {
console.error(`Failed to generate protected RSA key pair: ${error.message}`);
throw new Error(`Protected RSA key generation failed: ${error.message}`);
}
}
/**
* Get maximum data size that can be encrypted with a given RSA key size
* @param rsaKeySize - RSA key size in bits
* @param hashAlgorithm - Hash algorithm used for OAEP
* @returns Maximum data size in bytes
*/
function getMaxDataSizeForRSAKey(rsaKeySize, hashAlgorithm = DEFAULT_HASH_ALGORITHM) {
validateInputs(0, rsaKeySize, hashAlgorithm);
const oaepOverhead = calculateOAEPOverhead(hashAlgorithm);
const keyBytes = Math.floor(rsaKeySize / 8);
const maxDataSize = keyBytes - oaepOverhead;
if (maxDataSize <= 0) {
throw new Error(`RSA key size ${rsaKeySize} is too small for ${hashAlgorithm} OAEP padding`);
}
return maxDataSize;
}
/**
* Validate if data can be encrypted with the given RSA key
* @param dataSize - Size of data in bytes
* @param rsaKeySize - RSA key size in bits
* @param hashAlgorithm - Hash algorithm used for OAEP
* @returns Validation result with details
*/
function validateDataSizeForRSAKey(dataSize, rsaKeySize, hashAlgorithm = DEFAULT_HASH_ALGORITHM) {
validateInputs(dataSize, rsaKeySize, hashAlgorithm);
const maxDataSize = getMaxDataSizeForRSAKey(rsaKeySize, hashAlgorithm);
const valid = dataSize <= maxDataSize;
let recommendation;
if (!valid) {
const requiredKeySize = calculateRSAKeySize(dataSize, hashAlgorithm, true);
recommendation = `Data size ${dataSize} bytes requires at least ${requiredKeySize} bits RSA key`;
}
else if (dataSize > 245) {
// Typical AES key size
recommendation =
"Consider using hybrid encryption (RSA + AES) for better performance with large data";
}
return { valid, maxDataSize, recommendation };
}
/**
* Get RSA key size recommendations for different security levels
* @param dataSize - Size of data to encrypt in bytes
* @param hashAlgorithm - Hash algorithm for OAEP padding
* @returns Array of recommendations
*/
function getRSARecommendations(dataSize, hashAlgorithm = DEFAULT_HASH_ALGORITHM) {
validateInputs(dataSize, undefined, hashAlgorithm);
const recommendations = [];
for (const keySize of STANDARD_RSA_KEY_SIZES) {
const maxDataSize = getMaxDataSizeForRSAKey(keySize, hashAlgorithm);
if (dataSize <= maxDataSize) {
const securityLevel = getSecurityLevel(keySize);
let recommendation = `${keySize}-bit RSA provides ${securityLevel} security`;
if (keySize === 2048) {
recommendation += " (minimum recommended for new applications)";
}
else if (keySize >= 4096) {
recommendation +=
" (recommended for high-security applications)";
}
recommendations.push({
keySize,
maxDataSize,
securityLevel,
recommendation,
});
}
}
return recommendations;
}
/**
* Test RSA encryption/decryption with performance monitoring
* @param dataSize - Size of test data in bytes
* @param rsaKeySize - RSA key size in bits
* @param hashAlgorithm - Hash algorithm for OAEP padding
* @param iterations - Number of test iterations for performance measurement
* @returns Comprehensive test result
*/
async function testRSAWithDataSize(dataSize, rsaKeySize, hashAlgorithm = DEFAULT_HASH_ALGORITHM, iterations = 1) {
try {
validateInputs(dataSize, rsaKeySize, hashAlgorithm);
if (iterations < 1 || iterations > 1000) {
throw new Error("Iterations must be between 1 and 1000");
}
// Generate test data
const testData = crypto__namespace.randomBytes(dataSize);
// Generate RSA key pair
const keyPair = crypto__namespace.generateKeyPairSync("rsa", {
modulusLength: rsaKeySize,
publicKeyEncoding: { type: "spki", format: "pem" },
privateKeyEncoding: { type: "pkcs8", format: "pem" },
});
const startTime = process.hrtime.bigint();
let encryptedSize = 0;
let allDecryptedMatch = true;
// Run multiple iterations for performance testing
for (let i = 0; i < iterations; i++) {
// Test encryption
const encrypted = crypto__namespace.publicEncrypt({
key: keyPair.publicKey,
padding: crypto__namespace.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: hashAlgorithm,
}, testData);
encryptedSize = encrypted.length;
// Test decryption
const decrypted = crypto__namespace.privateDecrypt({
key: keyPair.privateKey,
padding: crypto__namespace.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: hashAlgorithm,
}, encrypted);
if (!testData.equals(decrypted)) {
allDecryptedMatch = false;
break;
}
}
const endTime = process.hrtime.bigint();
const performanceMs = Number(endTime - startTime) / 1000000 / iterations; // Average per iteration
return {
success: true,
encryptedSize,
decryptedMatches: allDecryptedMatch,
performanceMs: Math.round(performanceMs * 100) / 100, // Round to 2 decimal places
};
}
catch (error) {
console.error(`RSA test failed: ${error.message}`);
return {
success: false,
error: error.message,
};
}
}
/**
* Benchmark RSA performance across different key sizes
* @param dataSize - Size of test data in bytes
* @param keySizes - Array of key sizes to test
* @param iterations - Number of iterations per key size
* @returns Performance comparison results
*/
async function benchmarkRSAPerformance(dataSize, keySizes = [...STANDARD_RSA_KEY_SIZES], iterations = 10) {
const results = [];
for (const keySize of keySizes) {
try {
const validation = validateDataSizeForRSAKey(dataSize, keySize);
if (!validation.valid) {
results.push({
keySize,
avgTimeMs: 0,
success: false,
error: `Data too large for ${keySize}-bit key`,
});
continue;
}
const testResult = await testRSAWithDataSize(dataSize, keySize, DEFAULT_HASH_ALGORITHM, iterations);
results.push({
keySize,
avgTimeMs: testResult.performanceMs || 0,
success: testResult.success,
error: testResult.error,
});
}
catch (error) {
results.push({
keySize,
avgTimeMs: 0,
success: false,
error: error.message,
});
}
}
return results;
}
/**
* Utility to suggest hybrid encryption when RSA alone is inefficient
* @param dataSize - Size of data to encrypt in bytes
* @returns Suggestion for encryption approach
*/
function getEncryptionSuggestion(dataSize) {
validateInputs(dataSize);
// Threshold where hybrid encryption becomes more efficient
const hybridThreshold = 245; // Roughly AES-256 key size
if (dataSize <= hybridThreshold) {
return {
approach: "rsa",
reason: "Data size is small enough for direct RSA encryption",
};
}
return {
approach: "hybrid",
reason: "Large data size - hybrid encryption (RSA + AES) recommended for better performance",
details: {
aesKeySize: 256, // AES-256 recommended
rsaKeySize: 2048, // Minimum secure RSA size for key exchange
estimatedPerformanceGain: "10-1000x faster encryption/decryption",
},
};
}
/**
* Enhanced key validation with security assessment
* @param publicKey - RSA public key in PEM format
* @param privateKey - RSA private key in PEM format (optional)
* @returns Comprehensive validation result
*/
function validateRSAKeyPair(publicKey, privateKey) {
const result = {
isValid: true,
errors: [],
warnings: [],
securityScore: 100,
recommendations: [],
};
try {
// Validate public key format
if (!publicKey.includes("-----BEGIN PUBLIC KEY-----")) {
result.errors.push("Invalid public key format - must be PEM encoded");
result.isValid = false;
result.securityScore -= 50;
}
// Extract key size from public key
const keyObject = crypto__namespace.createPublicKey(publicKey);
// Get key size by checking the modulus length for RSA keys
const keyDetails = keyObject.asymmetricKeyDetails;
const keySize = keyDetails?.modulusLength || 0;
// Security assessment based on key size
if (keySize < 2048) {
result.errors.push(`Key size ${keySize} bits is below minimum secure threshold (2048 bits)`);
result.isValid = false;
result.securityScore = 0;
}
else if (keySize < 3072) {
result.warnings.push(`Key size ${keySize} bits provides minimal security - consider upgrading to 3072+ bits`);
result.securityScore = Math.max(result.securityScore - 30, 0);
result.recommendations.push("Upgrade to 3072-bit or 4096-bit keys for better security");
}
else if (keySize >= 4096) {
result.recommendations.push("Excellent key size for high-security applications");
}
// Validate private key if provided
if (privateKey) {
if (!privateKey.includes("-----BEGIN PRIVATE KEY-----") &&
!privateKey.includes("-----BEGIN RSA PRIVATE KEY-----") &&
!privateKey.includes("-----BEGIN ENCRYPTED PRIVATE KEY-----")) {
result.errors.push("Invalid private key format - must be PEM encoded");
result.isValid = false;
result.securityScore -= 25;
}
// Check if private key is encrypted
if (privateKey.includes("-----BEGIN ENCRYPTED PRIVATE KEY-----")) {
result.recommendations.push("Private key is properly encrypted");
}
else {
result.warnings.push("Private key is not encrypted - consider adding passphrase protection");
result.securityScore = Math.max(result.securityScore - 20, 0);
result.recommendations.push("Encrypt private key with a strong passphrase");
}
}
// Performance level assessment
const performanceLevel = getPerformanceLevel(keySize);
result.recommendations.push(`Performance level: ${performanceLevel}`);
}
catch (error) {
result.errors.push(`Key validation failed: ${error.message}`);
result.isValid = false;
result.securityScore = 0;
}
return result;
}
/**
* Get performance level based on key size
*/
function getPerformanceLevel(keySize) {
if (keySize <= PERFORMANCE_THRESHOLDS.fast)
return "fast";
if (keySize <= PERFORMANCE_THRESHOLDS.balanced)
return "balanced";
if (keySize <= PERFORMANCE_THRESHOLDS.secure)
return "secure";
return "maximum";
}
/**
* Enhanced security assessment for RSA configuration
* @param keySize - RSA key size in bits
* @param hashAlgorithm - Hash algorithm used
* @param dataSize - Size of data to encrypt
* @returns Security assessment with recommendations
*/
function assessRSASecurity(keySize, hashAlgorithm, dataSize) {
const vulnerabilities = [];
const recommendations = [];
let score = 100;
// Assess key size security
let level = "minimal";
if (keySize >= SECURITY_LEVELS.maximum.minKeySize) {
level = "maximum";
}
else if (keySize >= SECURITY_LEVELS.high.minKeySize) {
level = "high";
}
else if (keySize >= SECURITY_LEVELS.standard.minKeySize) {
level = "standard";
}
// Check for deprecated algorithms
if (hashAlgorithm === "sha1") {
vulnerabilities.push("SHA-1 is cryptographically broken and should not be used");
score -= 50;
recommendations.push("Upgrade to SHA-256 or higher");
}
// Check key size adequacy
if (keySize < 2048) {
vulnerabilities.push("Key size below 2048 bits is vulnerable to factorization attacks");
score = 0;
}
else if (keySize < 3072) {
vulnerabilities.push("Key size may be vulnerable to future quantum attacks");
score -= 20;
recommendations.push("Consider upgrading to 3072+ bits for quantum resistance");
}
// Data size assessment
if (dataSize > 245) {
recommendations.push("Consider hybrid encryption for better performance and security");
}
// Compliance assessment
const compliance = {
nist: keySize >= 2048 && hashAlgorithm !== "sha1",
fips: keySize >= 2048 &&
["sha256", "sha384", "sha512"].includes(hashAlgorithm),
commonCriteria: keySize >= 3072 &&
["sha256", "sha384", "sha512"].includes(hashAlgorithm),
};
return {
level,
score: Math.max(score, 0),
vulnerabilities,
recommendations,
compliance,
};
}
exports.assessRSASecurity = assessRSASecurity;
exports.benchmarkRSAPerformance = benchmarkRSAPerformance;
exports.calculateRSAKeySize = calculateRSAKeySize;
exports.generateProtectedRSAKeyPairForData = generateProtectedRSAKeyPairForData;
exports.generateRSAKeyPairForData = generateRSAKeyPairForData;
exports.getEncryptionSuggestion = getEncryptionSuggestion;
exports.getMaxDataSizeForRSAKey = getMaxDataSizeForRSAKey;
exports.getRSARecommendations = getRSARecommendations;
exports.testRSAWithDataSize = testRSAWithDataSize;
exports.validateDataSizeForRSAKey = validateDataSizeForRSAKey;
exports.validateRSAKeyPair = validateRSAKeyPair;
//# sourceMappingURL=rsaKeyCalculator.js.map