import { base32 , base64url } from "rfc4648" ; import { bufferToArrayBuffer , hexStringToTypedArray } from "./misc" ; import * as origLog from "loglevel" ; const log = origLog . getLogger ( "rs-default" ) ; const DEFAULT_ITER = 20000 ; export const MAGIC_ENCRYPTED_PREFIX_BASE32 = "KNQWY5DFMRPV" ; export const MAGIC_ENCRYPTED_PREFIX_BASE64URL = "U2FsdGVkX" ; const getKeyIVFromPassword = async ( salt , password , rounds = DEFAULT_ITER ) => { const k1 = await window . crypto . subtle . importKey ( "raw" , new TextEncoder ( ) . encode ( password ) , { name : "PBKDF2" } , false , [ "deriveKey" , "deriveBits" ] ) ; const k2 = await window . crypto . subtle . deriveBits ( { name : "PBKDF2" , salt : salt , iterations : rounds , hash : "SHA-256" , } , k1 , 256 + 128 ) ; return k2 ; } ; export const encryptArrayBuffer = async ( arrBuf , password , rounds = DEFAULT_ITER , saltHex = "" ) => { let salt ; if ( saltHex !== "" ) { salt = hexStringToTypedArray ( saltHex ) ; } else { salt = window . crypto . getRandomValues ( new Uint8Array ( 8 ) ) ; } const derivedKey = await getKeyIVFromPassword ( salt , password , rounds ) ; const key = derivedKey . slice ( 0 , 32 ) ; const iv = derivedKey . slice ( 32 , 32 + 16 ) ; const keyCrypt = await window . crypto . subtle . importKey ( "raw" , key , { name : "AES-CBC" } , false , [ "encrypt" , "decrypt" ] ) ; const enc = ( await window . crypto . subtle . encrypt ( { name : "AES-CBC" , iv } , keyCrypt , arrBuf ) ) as ArrayBuffer ; const prefix = new TextEncoder ( ) . encode ( "Salted__" ) ; const res = new Uint8Array ( [ ... prefix , ... salt , ... new Uint8Array ( enc ) ] ) ; return bufferToArrayBuffer ( res ) ; } ; export const decryptArrayBuffer = async ( arrBuf , password , rounds = DEFAULT_ITER ) => { const prefix = arrBuf . slice ( 0 , 8 ) ; const salt = arrBuf . slice ( 8 , 16 ) ; const derivedKey = await getKeyIVFromPassword ( new Uint8Array ( salt ) , password , rounds ) ; const key = derivedKey . slice ( 0 , 32 ) ; const iv = derivedKey . slice ( 32 , 32 + 16 ) ; const keyCrypt = await window . crypto . subtle . importKey ( "raw" , key , { name : "AES-CBC" } , false , [ "encrypt" , "decrypt" ] ) ; const dec = ( await window . crypto . subtle . decrypt ( { name : "AES-CBC" , iv } , keyCrypt , arrBuf . slice ( 16 ) ) ) as ArrayBuffer ; return dec ; } ; export const encryptStringToBase32 = async ( text , password , rounds = DEFAULT_ITER , saltHex = "" ) => { const enc = await encryptArrayBuffer ( bufferToArrayBuffer ( new TextEncoder ( ) . encode ( text ) ) , password , rounds , saltHex ) ; return base32 . stringify ( new Uint8Array ( enc ) , { pad : false } ) ; } ; export const decryptBase32ToString = async ( text , password , rounds = DEFAULT_ITER ) => { return new TextDecoder ( ) . decode ( await decryptArrayBuffer ( bufferToArrayBuffer ( base32 . parse ( text , { loose : true } ) ) , password , rounds ) ) ; } ; export const encryptStringToBase64url = async ( text , password , rounds = DEFAULT_ITER , saltHex = "" ) => { const enc = await encryptArrayBuffer ( bufferToArrayBuffer ( new TextEncoder ( ) . encode ( text ) ) , password , rounds , saltHex ) ; return base64url . stringify ( new Uint8Array ( enc ) , { pad : false } ) ; } ; export const decryptBase64urlToString = async ( text , password , rounds = DEFAULT_ITER ) => { return new TextDecoder ( ) . decode ( await decryptArrayBuffer ( bufferToArrayBuffer ( base64url . parse ( text , { loose : true } ) ) , password , rounds ) ) ; } ;