UNPKG

@aladas-org/cryptocalc

Version:
270 lines (223 loc) 8.74 kB
// ==================================================================================== // ================================== hex_utils.js ================================= // ==================================================================================== "use strict"; const HEX_ALPHABET = "0123456789abcdefABCDEF"; // https://gist.github.com/bugventure/36cb8923ec212e47b47602e3821d1005 const HEX_LOOKUP_TABLE = { '0': '0000', '1': '0001', '2': '0010', '3': '0011', '4': '0100', '5': '0101', '6': '0110', '7': '0111', '8': '1000', '9': '1001', 'a': '1010', 'b': '1011', 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111', 'A': '1010', 'B': '1011', 'C': '1100', 'D': '1101', 'E': '1110', 'F': '1111' }; // HEX_LOOKUP_TABLE // https://stackoverflow.com/questions/9907419/how-to-get-a-key-in-a-javascript-object-by-its-value const getKeyByValue = (object, value) => { return Object.keys(object).find(key => object[key] === value); }; // getKeyByValue() const isBinaryString = ( in_str ) => { if (!in_str || typeof in_str !== 'string') return false; return /^[01]+$/.test(in_str); }; // isBinaryString const binaryToHex = ( in_binary_str ) => { let hex_str = ''; let nibble = ''; let hex_digit = ''; // console.log("in_binary_str: '" + in_binary_str + "'"); for ( let i=0; i < in_binary_str.length; i+=4 ) { nibble = in_binary_str.substring(i, i+4); // [i..i+3] // console.log("nibble(" + i + "): '" + nibble + "'"); hex_digit = getKeyByValue(HEX_LOOKUP_TABLE, nibble); if ( hex_digit == undefined ) { throw new Error("**ERROR** in hex_utils.binaryToHex"); } //console.log("hex_digit: " + hex_digit); hex_str += hex_digit; } return hex_str; }; // binaryToHex() //const hexToBinary = (in_hex_str, trace) => { const hexToBinary = (in_hex_str, trace) => { let hex_str = hexWithoutPrefix(in_hex_str); //if (trace != undefined && trace == true) { // console.log(" hex_str: '" + hex_str + "'"); //} let binary_str = ''; let nibble = ''; let hex_digit = ''; for ( let i=0; i < hex_str.length; i++ ) { hex_digit = hex_str[i].toLowerCase(); nibble = HEX_LOOKUP_TABLE[hex_digit]; //if (trace != undefined && trace == true) { // console.log(" hex_digit[" + i + "]: " + hex_digit); // console.log(" nibble[" + i + "]: " + nibble); //} // console.log("hex_digit: " + hex_digit + " nibble: " + nibble); binary_str += nibble; } return binary_str; }; // hexToBinary() const hexWithoutPrefix = (hex_str) => { //console.log("hexWithoutPrefix: '" + hex_str + "'"); if (hex_str.startsWith('0x')) { hex_str = hex_str.substring(2); } return hex_str; }; // hexWithoutPrefix const hexWithPrefix = ( hex_str ) => { if (! hex_str.startsWith('0x')) { hex_str = '0x' + hex_str; } return hex_str; }; // hexWithPrefix const isHexString = ( in_str ) => { in_str = hexWithoutPrefix(in_str.toLowerCase()); let is_hex = true; for ( let i=0; i < in_str.length; i++ ) { let c = in_str[i]; if ( HEX_ALPHABET.indexOf(c) == -1 ) { return false; } } return is_hex; }; // isHexString const hexToUint8Array = (hex_str) => { //console.log("hexToUint8Array: " + hex_str); //console.log("hexToUint8Array length: " + hex_str.length); hex_str = hexWithoutPrefix(hex_str); if ( hex_str.length % 2 !== 0 ) { throw "**ERROR 1** Invalid hex_str: " + hex_str + " length is not pair: " + hex_str.length; } /* from www.java2s.com */ let array_buffer = new Uint8Array(hex_str.length / 2); for (let i=0; i < hex_str.length; i+=2) { let hex_digit = hex_str.substr(i, 2); //console.log("hexToUint8Array: i=" + i + " hex_digit= '" + hex_digit + "'"); let byte_value = parseInt(hex_digit, 16); if (isNaN(byte_value)){ throw "**ERROR 2** Invalid hex_str"; } array_buffer[i/2] = byte_value; } return array_buffer; }; // hexToUint8Array const uint8ArrayToHex = (unint_array) => { let hex_str = ""; for (let i=0; i < unint_array.length; i++) { let current_uint = unint_array[i]; //console.log(">> current_uint[" + i + "]: " + current_uint); let hex_value = current_uint.toString(16).padStart(2, '0'); //console.log(">> hex_value: [" + i + "]: " + hex_value); hex_str += hex_value; } return hex_str; }; // uint8ArrayToHex const hexToBytes = (hex_str) => { if (hex_str.length % 2 !== 0) { throw "Must have an even number of hex digits to convert to bytes"; } let byte_count = hex_str.length / 2; let hex_bytes = []; for (var i=0; i < byte_count; i++) { hex_bytes.push(parseInt(hex_str.substr(i*2, 2), 16)); } return hex_bytes; }; // hexToBytes() const hexToB64 = (hex_str) => { if (hex_str.startsWith("0x")) hex_str = hex_str.substring(2); return btoa(hex_str.match(/\w{2}/g).map(function(a) { return String.fromCharCode(parseInt(a, 16)); }).join("")); }; // hexToB64 // https://stackoverflow.com/questions/39460182/decode-base64-to-hexadecimal-string-with-javascript // checked with: // OK: https://base64.guru/converter/decode/hex // OK: https://8gwifi.org/base64Hex.jsp // OK: https://tomeko.net/online_tools/base64.php?lang=en // OK: https://conv.darkbyte.ru/ const b64ToHex = ( b64_str ) => { const buffer = Buffer.from( b64_str, 'base64' ); //const buffer = new ArrayBuffer(b64_str, 'base64'); return buffer.toString('hex'); }; // b64ToHex /** * Cryptographically secure random number generator * Works in both Node.js and browser environments * * @param {number} min - Minimum value (inclusive) * @param {number} max - Maximum value (exclusive) * @returns {number} Random number between min (inclusive) and max (exclusive) */ const secureRandom = (min, max) => { // Validate input if (min >= max) { throw new Error('Max must be greater than min'); } // Calculate the range const range = max - min; // Create a typed array for crypto random values const byteArray = new Uint32Array(1); // Get crypto module - works in both Node and browser let crypto = undefined; if (typeof exports === 'object') { crypto = require('crypto'); } else crypto = window.crypto || window.msCrypto; // console.log("secureRandom crypto: " + crypto); // Generate random values crypto.getRandomValues(byteArray); // Convert to float between 0 (inclusive) and 1 (exclusive) const randomFloat = byteArray[0] / (0xFFFFFFFF + 1); // Scale to desired range and return return Math.floor(randomFloat * range) + min; }; // secureRandom() function getRandomInt(max) { return secureRandom(0, max+1); }; // getRandomInt() function getRandomByte() { return getRandomInt(255); }; // getRandomByte() const getRandomHexValue = ( byte_count ) => { let hex_str = ""; for (let i=0; i < byte_count; i++) { let current_uint = getRandomInt(255); //console.log("---------------"); //console.log("current_uint[" + i + "] = " + current_uint); let hex_value = current_uint.toString(16).padStart(2, '0'); hex_str += hex_value; //console.log("hex_str bytes: " + hex_str.length/2 + "\n " + hex_str); } return hex_str; }; // getRandomHexValue() const testRandomInt = () => { let max = 255; let min_1 = max; let min_2 = max; let max_1 = 0; let max_2 = 0; for (let i=0; i<1000; i++) { let random_1 = getRandomInt(max); min_1 = random_1 < min_1 ? random_1 : min_1; max_1 = random_1 > max_1 ? random_1 : max_1; let random_2 = Math.floor(Math.random() * max); min_2 = random_2 < min_2 ? random_2 : min_2; max_2 = random_2 > max_2 ? random_2 : max_2; console.log("secureRandom R1: " + random_1 + " (min:" + min_1 + " max:" + max_1 + ") " + "R2: " + random_2 + " (min:" + min_2 + " max:" + max_2 + ") "); } }; // testRandomInt() // testRandomInt(); if (typeof exports === 'object') { exports.hexToBytes = hexToBytes exports.hexToUint8Array = hexToUint8Array exports.uint8ArrayToHex = uint8ArrayToHex exports.hexWithoutPrefix = hexWithoutPrefix exports.hexWithPrefix = hexWithPrefix exports.isHexString = isHexString exports.isBinaryString = isBinaryString exports.hexToBinary = hexToBinary exports.binaryToHex = binaryToHex exports.hexToB64 = hexToB64 exports.b64ToHex = b64ToHex exports.getRandomInt = getRandomInt exports.getRandomByte = getRandomByte exports.getRandomHexValue = getRandomHexValue } // exports of 'hex_utils.js'