From 8ba2680510233be00f8e44baf3cfe8a61413b01b Mon Sep 17 00:00:00 2001 From: KOSASIH Date: Fri, 9 Aug 2024 09:10:36 +0700 Subject: [PATCH] Create crypto.ts --- projects/pi-nexus-iam/utils/crypto.ts | 90 +++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 projects/pi-nexus-iam/utils/crypto.ts diff --git a/projects/pi-nexus-iam/utils/crypto.ts b/projects/pi-nexus-iam/utils/crypto.ts new file mode 100644 index 000000000..906f5daff --- /dev/null +++ b/projects/pi-nexus-iam/utils/crypto.ts @@ -0,0 +1,90 @@ +import * as bcrypt from 'bcrypt'; +import * as crypto from 'crypto'; +import * as elliptic from 'elliptic'; +import * as scrypt from 'scrypt-js'; +import { randomBytes } from 'crypto'; +import { promisify } from 'util'; + +const ec = new elliptic.ec('secp256k1'); + +interface HashOptions { + saltRounds?: number; + pepper?: string; +} + +interface EncryptOptions { + algorithm?: string; + password?: string; + salt?: string; + iv?: string; +} + +interface DecryptOptions { + algorithm?: string; + password?: string; + salt?: string; + iv?: string; +} + +class Crypto { + private static async generateSalt(): Promise { + return promisify(crypto.randomBytes)(16).then((buf) => buf.toString('hex')); + } + + private static async generatePepper(): Promise { + return promisify(crypto.randomBytes)(16).then((buf) => buf.toString('hex')); + } + + static async hash(password: string, options: HashOptions = {}): Promise { + const saltRounds = options.saltRounds || 10; + const pepper = options.pepper || await this.generatePepper(); + const salt = await this.generateSalt(); + const hash = await bcrypt.hash(password + pepper, saltRounds); + return `${salt}:${hash}`; + } + + static async verify(password: string, hash: string): Promise { + const [salt, storedHash] = hash.split(':'); + const pepper = await this.generatePepper(); + const hashedPassword = await bcrypt.hash(password + pepper, parseInt(salt, 10)); + return hashedPassword === storedHash; + } + + static async encrypt(data: string, options: EncryptOptions = {}): Promise { + const algorithm = options.algorithm || 'aes-256-cbc'; + const password = options.password || randomBytes(32).toString('hex'); + const salt = options.salt || await this.generateSalt(); + const iv = options.iv || randomBytes(16).toString('hex'); + const cipher = crypto.createCipheriv(algorithm, password, iv); + let encrypted = cipher.update(data, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + return `${salt}:${iv}:${encrypted}`; + } + + static async decrypt(encrypted: string, options: DecryptOptions = {}): Promise { + const [salt, iv, encryptedData] = encrypted.split(':'); + const algorithm = options.algorithm || 'aes-256-cbc'; + const password = options.password || randomBytes(32).toString('hex'); + const decipher = crypto.createDecipheriv(algorithm, password, iv); + let decrypted = decipher.update(encryptedData, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + return decrypted; + } + + static async sign(data: string, privateKey: string): Promise { + const key = ec.keyFromPrivate(privateKey, 'hex'); + const signature = key.sign(data); + return signature.toDER('hex'); + } + + static async verifySignature(data: string, signature: string, publicKey: string): Promise { + const key = ec.keyFromPublic(publicKey, 'hex'); + return key.verify(data, signature); + } + + static async scrypt(password: string, salt: string, N: number, r: number, p: number): Promise { + return scrypt.scrypt(password, salt, N, r, p); + } +} + +export default Crypto;