import { randomStringForEntropy } from '@stablelib/random'; import { keccak_256 } from '@noble/hashes/sha3'; import { bytesToHex } from '@noble/hashes/utils'; const ISO8601 = /^(?[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(.[0-9]+)?(([Zz])|([+|-]([01][0-9]|2[0-3]):[0-5][0-9]))$/; /** * This method leverages a native CSPRNG with support for both browser and Node.js * environments in order generate a cryptographically secure nonce for use in the * SiweMessage in order to prevent replay attacks. * * 96 bits has been chosen as a number to sufficiently balance size and security considerations * relative to the lifespan of it's usage. * * @returns cryptographically generated random nonce with 96 bits of entropy encoded with * an alphanumeric character set. */ export const generateNonce = (): string => { const nonce = randomStringForEntropy(96); if (!nonce || nonce.length < 8) { throw new Error('Error during nonce creation.'); } return nonce; }; /** * This method matches the given date string against the ISO-8601 regex and also * performs checks if it's a valid date. * @param inputDate any string to be validated against ISO-8601 * @returns boolean indicating if the providade date is valid and conformant to ISO-8601 */ export const isValidISO8601Date = (inputDate: string): boolean => { /* Split groups and make sure inputDate is in ISO8601 format */ const inputMatch = ISO8601.exec(inputDate); /* if inputMatch is null the date is not ISO-8601 */ if (!inputDate) { return false; } /* Creates a date object with input date to parse for invalid days e.g. Feb, 30 -> Mar, 01 */ const inputDateParsed = new Date(inputMatch.groups.date).toISOString(); /* Get groups from new parsed date to compare with the original input */ const parsedInputMatch = ISO8601.exec(inputDateParsed); /* Compare remaining fields */ return inputMatch.groups.date === parsedInputMatch.groups.date; }; export const checkInvalidKeys = ( obj: T, keys: Array, ): Array => { const invalidKeys: Array = []; Object.keys(obj).forEach((key) => { if (!keys.includes(key as keyof T)) { invalidKeys.push(key as keyof T); } }); return invalidKeys; }; /** * This method is supposed to check if an address is conforming to EIP-55. * @param address Address to be checked if conforms with EIP-55. * @returns Either the return is or not in the EIP-55 format. */ export const isEIP55Address = (address: string) => { if (address.length != 42) { return false; } const lowerAddress = `${address}`.toLowerCase().replace('0x', ''); var hash = bytesToHex(keccak_256(lowerAddress)); var ret = '0x'; for (var i = 0; i < lowerAddress.length; i++) { if (parseInt(hash[i], 16) >= 8) { ret += lowerAddress[i].toUpperCase(); } else { ret += lowerAddress[i]; } } return address === ret; }; export const parseIntegerNumber = (number: string): number => { const parsed = parseInt(number); // TODO: Fix this // if (parsed === NaN) throw new Error('Invalid number.'); if (parsed === Infinity) throw new Error('Invalid number.'); return parsed; };