UNPKG

@sschepis/resolang

Version:

ResoLang - Core quantum resonance computation library for browser and Node.js

465 lines (396 loc) 13 kB
/** * Unified Cryptographic Module for the Prime Resonance Network * Combines and optimizes crypto utilities from crypto-utils.ts and utils/crypto.ts * * This module provides: * - SHA-256 hashing (for strings and byte arrays) * - HMAC-SHA256 * - PBKDF2 key derivation * - Random number/byte generation * - Utility functions (XOR, hex conversion, byte comparison) */ import { modPow } from './math'; // SHA-256 constants (first 32 bits of fractional parts of cube roots of first 64 primes) const K: u32[] = [ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 ]; // PRNG state for random number generation let prngState: u64 = 0x123456789ABCDEF0; /** * Right rotate helper for SHA-256 */ function rightRotate(value: u32, amount: u32): u32 { return (value >>> amount) | (value << (32 - amount)); } /** * SHA-256 hash function for byte arrays * Based on FIPS 180-4 specification */ export function sha256(data: Uint8Array): Uint8Array { // Initial hash values (first 32 bits of fractional parts of square roots of first 8 primes) let h0: u32 = 0x6a09e667; let h1: u32 = 0xbb67ae85; let h2: u32 = 0x3c6ef372; let h3: u32 = 0xa54ff53a; let h4: u32 = 0x510e527f; let h5: u32 = 0x9b05688c; let h6: u32 = 0x1f83d9ab; let h7: u32 = 0x5be0cd19; // Pre-processing const msgLen = data.length; const bitLen = msgLen * 8; // Padding: append 1 bit followed by zeros const paddingLen = (64 - ((msgLen + 9) % 64)) % 64; const totalLen = msgLen + 1 + paddingLen + 8; const padded = new Uint8Array(totalLen); for (let i = 0; i < msgLen; i++) { padded[i] = data[i]; } padded[msgLen] = 0x80; // Append 1 bit (and 7 zero bits) // Append length as 64-bit big-endian const bitLenHi = u32(bitLen / 0x100000000); const bitLenLo = u32(bitLen & 0xFFFFFFFF); padded[totalLen - 8] = u8(bitLenHi >> 24); padded[totalLen - 7] = u8(bitLenHi >> 16); padded[totalLen - 6] = u8(bitLenHi >> 8); padded[totalLen - 5] = u8(bitLenHi); padded[totalLen - 4] = u8(bitLenLo >> 24); padded[totalLen - 3] = u8(bitLenLo >> 16); padded[totalLen - 2] = u8(bitLenLo >> 8); padded[totalLen - 1] = u8(bitLenLo); // Process message in 512-bit chunks for (let chunk = 0; chunk < totalLen; chunk += 64) { // Break chunk into sixteen 32-bit words const w = new Array<u32>(64); for (let i = 0; i < 16; i++) { const offset = chunk + i * 4; w[i] = (u32(padded[offset]) << 24) | (u32(padded[offset + 1]) << 16) | (u32(padded[offset + 2]) << 8) | u32(padded[offset + 3]); } // Extend the sixteen 32-bit words into sixty-four 32-bit words for (let i = 16; i < 64; i++) { const s0 = rightRotate(w[i - 15], 7) ^ rightRotate(w[i - 15], 18) ^ (w[i - 15] >>> 3); const s1 = rightRotate(w[i - 2], 17) ^ rightRotate(w[i - 2], 19) ^ (w[i - 2] >>> 10); w[i] = w[i - 16] + s0 + w[i - 7] + s1; } // Initialize working variables let a = h0, b = h1, c = h2, d = h3; let e = h4, f = h5, g = h6, h = h7; // Main loop for (let i = 0; i < 64; i++) { const s1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25); const ch = (e & f) ^ (~e & g); const temp1 = h + s1 + ch + K[i] + w[i]; const s0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22); const maj = (a & b) ^ (a & c) ^ (b & c); const temp2 = s0 + maj; h = g; g = f; f = e; e = d + temp1; d = c; c = b; b = a; a = temp1 + temp2; } // Add compressed chunk to current hash value h0 += a; h1 += b; h2 += c; h3 += d; h4 += e; h5 += f; h6 += g; h7 += h; } // Produce final hash value const hash = new Uint8Array(32); hash[0] = u8(h0 >> 24); hash[1] = u8(h0 >> 16); hash[2] = u8(h0 >> 8); hash[3] = u8(h0); hash[4] = u8(h1 >> 24); hash[5] = u8(h1 >> 16); hash[6] = u8(h1 >> 8); hash[7] = u8(h1); hash[8] = u8(h2 >> 24); hash[9] = u8(h2 >> 16); hash[10] = u8(h2 >> 8); hash[11] = u8(h2); hash[12] = u8(h3 >> 24); hash[13] = u8(h3 >> 16); hash[14] = u8(h3 >> 8); hash[15] = u8(h3); hash[16] = u8(h4 >> 24); hash[17] = u8(h4 >> 16); hash[18] = u8(h4 >> 8); hash[19] = u8(h4); hash[20] = u8(h5 >> 24); hash[21] = u8(h5 >> 16); hash[22] = u8(h5 >> 8); hash[23] = u8(h5); hash[24] = u8(h6 >> 24); hash[25] = u8(h6 >> 16); hash[26] = u8(h6 >> 8); hash[27] = u8(h6); hash[28] = u8(h7 >> 24); hash[29] = u8(h7 >> 16); hash[30] = u8(h7 >> 8); hash[31] = u8(h7); return hash; } /** * SHA-256 hash function for strings * Converts string to UTF-8 bytes and hashes */ export function sha256String(input: string): Uint8Array { const bytes = new Uint8Array(input.length); for (let i = 0; i < input.length; i++) { bytes[i] = input.charCodeAt(i) as u8; } return sha256(bytes); } /** * HMAC-SHA256 implementation * RFC 2104 compliant */ export function hmacSha256(key: Uint8Array, message: Uint8Array): Uint8Array { const blockSize = 64; // If key is longer than block size, hash it let actualKey = key; if (key.length > blockSize) { actualKey = sha256(key); } // Pad key to block size const paddedKey = new Uint8Array(blockSize); for (let i = 0; i < actualKey.length; i++) { paddedKey[i] = actualKey[i]; } // Create inner and outer padded keys const innerPad = new Uint8Array(blockSize); const outerPad = new Uint8Array(blockSize); for (let i = 0; i < blockSize; i++) { innerPad[i] = paddedKey[i] ^ 0x36; outerPad[i] = paddedKey[i] ^ 0x5c; } // Inner hash: SHA256(innerPad || message) const innerMessage = new Uint8Array(blockSize + message.length); for (let i = 0; i < blockSize; i++) { innerMessage[i] = innerPad[i]; } for (let i = 0; i < message.length; i++) { innerMessage[blockSize + i] = message[i]; } const innerHash = sha256(innerMessage); // Outer hash: SHA256(outerPad || innerHash) const outerMessage = new Uint8Array(blockSize + 32); for (let i = 0; i < blockSize; i++) { outerMessage[i] = outerPad[i]; } for (let i = 0; i < 32; i++) { outerMessage[blockSize + i] = innerHash[i]; } return sha256(outerMessage); } /** * PBKDF2 key derivation function with HMAC-SHA256 * RFC 2898 compliant */ export function pbkdf2( password: Uint8Array, salt: Uint8Array, iterations: i32, keyLength: i32 ): Uint8Array { const hashLength = 32; // SHA256 output length const numBlocks = Math.ceil(keyLength as f64 / hashLength) as i32; const derivedKey = new Uint8Array(keyLength); for (let block = 1; block <= numBlocks; block++) { // U1 = HMAC(password, salt || INT_32_BE(block)) const blockBytes = new Uint8Array(4); blockBytes[0] = (block >>> 24) as u8; blockBytes[1] = (block >>> 16) as u8; blockBytes[2] = (block >>> 8) as u8; blockBytes[3] = block as u8; const saltWithBlock = new Uint8Array(salt.length + 4); for (let i = 0; i < salt.length; i++) { saltWithBlock[i] = salt[i]; } for (let i = 0; i < 4; i++) { saltWithBlock[salt.length + i] = blockBytes[i]; } let U = hmacSha256(password, saltWithBlock); let T = new Uint8Array(hashLength); // Copy U to T for (let i = 0; i < hashLength; i++) { T[i] = U[i]; } // Iterate for (let iter = 1; iter < iterations; iter++) { U = hmacSha256(password, U); // XOR U into T for (let i = 0; i < hashLength; i++) { T[i] ^= U[i]; } } // Copy block to derived key const offset = (block - 1) * hashLength; const copyLength = Math.min(hashLength, keyLength - offset); for (let i = 0; i < copyLength; i++) { derivedKey[offset + i] = T[i]; } } return derivedKey; } /** * Generate random float between 0 and 1 * Uses linear congruential generator (not cryptographically secure) */ export function random(): f64 { prngState = (prngState * 6364136223846793005 + 1442695040888963407) & 0xFFFFFFFFFFFFFFFF; return f64(prngState) / f64(0xFFFFFFFFFFFFFFFF); } /** * Generate random bytes * Uses linear congruential generator (not cryptographically secure) * For production use, this should be replaced with a secure random source */ export function randomBytes(length: i32): Uint8Array { const bytes = new Uint8Array(length); for (let i = 0; i < length; i++) { prngState = (prngState * 6364136223846793005 + 1442695040888963407) & 0xFFFFFFFFFFFFFFFF; bytes[i] = u8(prngState & 0xFF); } return bytes; } /** * XOR two byte arrays * Returns array with length of the shorter input */ export function xor(a: Uint8Array, b: Uint8Array): Uint8Array { const length = a.length < b.length ? a.length : b.length; const result = new Uint8Array(length); for (let i = 0; i < length; i++) { result[i] = a[i] ^ b[i]; } return result; } /** * Compare two byte arrays for equality * Not constant-time, use constantTimeCompare for security-sensitive comparisons */ export function bytesEqual(a: Uint8Array, b: Uint8Array): bool { if (a.length != b.length) return false; for (let i = 0; i < a.length; i++) { if (a[i] != b[i]) return false; } return true; } /** * Constant-time comparison to prevent timing attacks */ export function constantTimeCompare(a: Uint8Array, b: Uint8Array): boolean { if (a.length !== b.length) { return false; } let result: u8 = 0; for (let i = 0; i < a.length; i++) { result |= a[i] ^ b[i]; } return result === 0; } /** * Convert bytes to hexadecimal string */ export function bytesToHex(bytes: Uint8Array): string { let hex = ""; for (let i = 0; i < bytes.length; i++) { const byte = bytes[i]; const hi = byte >> 4; const lo = byte & 0x0F; hex += String.fromCharCode(hi < 10 ? 48 + hi : 87 + hi); hex += String.fromCharCode(lo < 10 ? 48 + lo : 87 + lo); } return hex; } /** * Convert hexadecimal string to bytes */ export function hexToBytes(hex: string): Uint8Array { const length = hex.length / 2; const bytes = new Uint8Array(length); for (let i = 0; i < length; i++) { const hi = hex.charCodeAt(i * 2); const lo = hex.charCodeAt(i * 2 + 1); const hiVal = hi >= 97 ? hi - 87 : hi >= 65 ? hi - 55 : hi - 48; const loVal = lo >= 97 ? lo - 87 : lo >= 65 ? lo - 55 : lo - 48; bytes[i] = u8((hiVal << 4) | loVal); } return bytes; } /** * Generate a cryptographically secure random prime * Note: This is a simplified implementation. For production use, * implement proper Miller-Rabin primality testing */ export function generateSecurePrime(bits: i32): i64 { const bytes = Math.ceil(bits as f64 / 8) as i32; while (true) { const randomData = randomBytes(bytes); // Convert to number and ensure it's odd let candidate: i64 = 0; for (let i = 0; i < bytes && i < 8; i++) { candidate |= (randomData[i] as i64) << (i * 8); } // Ensure it's odd and has the right bit length candidate |= 1; // Make odd candidate |= (1 as i64) << (bits - 1); // Set high bit // Simple primality test (should be replaced with Miller-Rabin for production) if (isProbablePrime(candidate, 20)) { return candidate; } } } /** * Miller-Rabin primality test * Tests if a number is probably prime */ function isProbablePrime(n: i64, k: i32): boolean { if (n === 2 || n === 3) return true; if (n < 2 || n % 2 === 0) return false; // Write n-1 as 2^r * d let d = n - 1; let r = 0; while (d % 2 === 0) { d /= 2; r++; } // Witness loop for (let i = 0; i < k; i++) { const a = (2 as i64) + ((random() * ((n - 4) as f64)) as i64); let x = modPow(a, d, n); if (x === 1 || x === n - 1) continue; let continueWitnessLoop = false; for (let j = 0; j < r - 1; j++) { x = modPow(x, 2, n); if (x === n - 1) { continueWitnessLoop = true; break; } } if (!continueWitnessLoop) return false; } return true; } // modPow is now imported from math-optimized module // Backwards compatibility exports export { sha256 as sha256Bytes };