import { Buffer } from '@stacks/common' ; import { validateMnemonic , mnemonicToEntropy , entropyToMnemonic } from 'bip39' ; import { randomBytes , GetRandomBytes } from './cryptoRandom' ; import { createSha2Hash } from './sha2Hash' ; import { createHmacSha256 } from './hmacSha256' ; import { createCipher } from './aesCipher' ; import { createPbkdf2 } from './pbkdf2' ; import { TriplesecDecryptSignature } from './cryptoUtils' ; param { string } phrase param { string } password return { Promise < Buffer > } private ignore export async function encryptMnemonic ( phrase , password , opts ? ) { let mnemonicEntropy ; try { mnemonicEntropy = mnemonicToEntropy ( phrase ) ; } catch ( error ) { console . error ( 'Invalid mnemonic phrase provided' ) ; console . error ( error ) ; throw new Error ( 'Not a valid bip39 mnemonic' ) ; } const plaintextNormalized = Buffer . from ( mnemonicEntropy , 'hex' ) ; const pbkdf2 = await createPbkdf2 ( ) ; let salt ; if ( opts && opts . getRandomBytes ) { salt = opts . getRandomBytes ( 16 ) ; } else { salt = randomBytes ( 16 ) ; } const keysAndIV = await pbkdf2 . derive ( password , salt , 100000 , 48 , 'sha512' ) ; const encKey = keysAndIV . slice ( 0 , 16 ) ; const macKey = keysAndIV . slice ( 16 , 32 ) ; const iv = keysAndIV . slice ( 32 , 48 ) ; const cipher = await createCipher ( ) ; const cipherText = await cipher . encrypt ( 'aes-128-cbc' , encKey , iv , plaintextNormalized ) ; const hmacPayload = Buffer . concat ( [ salt , cipherText ] ) ; const hmacSha256 = await createHmacSha256 ( ) ; const hmacDigest = await hmacSha256 . digest ( macKey , hmacPayload ) ; const payload = Buffer . concat ( [ salt , hmacDigest , cipherText ] ) ; return payload ; } class PasswordError extends Error { } ignore async function decryptMnemonicBuffer ( dataBuffer , password ) { const salt = dataBuffer . slice ( 0 , 16 ) ; const hmacSig = dataBuffer . slice ( 16 , 48 ) ; const cipherText = dataBuffer . slice ( 48 ) ; const hmacPayload = Buffer . concat ( [ salt , cipherText ] ) ; const pbkdf2 = await createPbkdf2 ( ) ; const keysAndIV = await pbkdf2 . derive ( password , salt , 100000 , 48 , 'sha512' ) ; const encKey = keysAndIV . slice ( 0 , 16 ) ; const macKey = keysAndIV . slice ( 16 , 32 ) ; const iv = keysAndIV . slice ( 32 , 48 ) ; const decipher = await createCipher ( ) ; const decryptedResult = await decipher . decrypt ( 'aes-128-cbc' , encKey , iv , cipherText ) ; const hmacSha256 = await createHmacSha256 ( ) ; const hmacDigest = await hmacSha256 . digest ( macKey , hmacPayload ) ; const sha2Hash = await createSha2Hash ( ) ; const hmacSigHash = await sha2Hash . digest ( hmacSig ) ; const hmacDigestHash = await sha2Hash . digest ( hmacDigest ) ; if ( ! hmacSigHash . equals ( hmacDigestHash ) ) { throw new PasswordError ( 'Wrong password (HMAC mismatch)' ) ; } let mnemonic ; try { mnemonic = entropyToMnemonic ( decryptedResult ) ; } catch ( error ) { console . error ( 'Error thrown by `entropyToMnemonic`' ) ; console . error ( error ) ; throw new PasswordError ( 'Wrong password (invalid plaintext)' ) ; } if ( ! validateMnemonic ( mnemonic ) ) { throw new PasswordError ( 'Wrong password (invalid plaintext)' ) ; } return mnemonic ; } param { Buffer } dataBuffer param { String } password return { Promise < Buffer > } private ignore function decryptLegacy ( dataBuffer , password , triplesecDecrypt ? ) { return new Promise < Buffer > ( ( resolve , reject ) => { if ( ! triplesecDecrypt ) { reject ( new Error ( 'The `triplesec.decrypt` function must be provided' ) ) ; } triplesecDecrypt ! ( { key : Buffer . from ( password ) , data : dataBuffer , } , ( err , plaintextBuffer ) => { if ( ! err ) { resolve ( plaintextBuffer ! ) ; } else { reject ( err ) ; } } ) ; } ) ; } param data param password return private ignore export async function decryptMnemonic ( data , password , triplesecDecrypt ? ) { const dataBuffer = Buffer . isBuffer ( data ) ? data : Buffer . from ( data , 'hex' ) ; try { return await decryptMnemonicBuffer ( dataBuffer , password ) ; } catch ( err ) { if ( err instanceof PasswordError ) { throw err ; } const data = await decryptLegacy ( dataBuffer , password , triplesecDecrypt ) ; return data . toString ( ) ; } }