license import { ContextSecurity } from "./structures/contextSecurity" ; import { DefaultEncryptionParameters } from "./parameters/default" ; import { validateSalt , validateIterations } from "./parameters/validate" ; import { InvalidCipherText } from "./errors/types" ; import { Blob } from "./structures/blob" ; export class SecureStorageContext { contextSecurity : ContextSecurity ; constructor ( passphrase , params ? ) { this . contextSecurity = new ContextSecurity ( passphrase , params ) ; } private getVersionBuffer ( ) { let paddedVersion = DefaultEncryptionParameters . Version . toString ( ) . padStart ( 12 , '0' ) ; var versionBuffer = new ArrayBuffer ( paddedVersion . length ) ; var versionUint8Array = new Uint8Array ( versionBuffer ) ; for ( var i = 0 ; i < paddedVersion . length ; i ++ ) { versionUint8Array [ i ] = paddedVersion . charCodeAt ( i ) ; } return versionBuffer ; } private generateRandomValues ( byteLength ) { return window . crypto . getRandomValues ( new Uint8Array ( byteLength ) ) ; } private async createPasskey ( passphrase ) { return await window . crypto . subtle . importKey ( "raw" , passphrase , "PBKDF2" , false , [ "deriveBits" , "deriveKey" ] ) . then ( ( cryptoKey ) => { return cryptoKey ; } ) ; } private async deriveBitsFromRawKey ( rawKey , salt , iterations ) { return await window . crypto . subtle . deriveBits ( { name : "PBKDF2" , hash : "SHA-256" , salt : salt , iterations : iterations , } , rawKey as CryptoKey , 256 ) . then ( ( masterBits ) => { return masterBits ; } ) ; } private async deriveMasterKey ( salt , iterations ) { let rawKey = await this . createPasskey ( this . contextSecurity . passphrase ) ; const masterKeyBits = await this . deriveBitsFromRawKey ( rawKey , salt , iterations ) ; return await window . crypto . subtle . importKey ( "raw" , masterKeyBits , { name : "HKDF" , hash : "SHA-256" , } , false , [ "deriveBits" , "deriveKey" ] ) . then ( masterKey => { return masterKey ; } ) ; } private async deriveAESKeyFromMasterKey ( masterKey , nonce ) { const hkdfSalt = Uint8Array . from ( atob ( "wJW4IFKeBayDZpNJTmMyb1nkGWX4LXdZ3XOJwz3reAY=" ) , c => c . charCodeAt ( 0 ) ) ; return await window . crypto . subtle . deriveKey ( { name : "HKDF" , salt : hkdfSalt , info : nonce , hash : "SHA-256" } , masterKey , { name : "AES-GCM" , length : 256 } , false , [ "encrypt" , "decrypt" ] ) . then ( aesKey => { return aesKey ; } ) ; } public async asyncForEach ( array , callback ) { for ( let index = 0 ; index < array . length ; index ++ ) { await callback ( array [ index ] , index , array ) ; } } public async lock ( records , compatibleWithBlob ? ) { if ( ! ( records instanceof ArrayBuffer || Array . isArray ( records ) && records . every ( value => value instanceof ArrayBuffer ) ) ) { throw new SyntaxError ( "The provided records parameter was not an instance or list of ArrayBuffer." ) ; } let saltOverride ; let iterationsOverride ; if ( compatibleWithBlob ) { if ( compatibleWithBlob . salt ) { saltOverride = compatibleWithBlob . salt ; validateSalt ( saltOverride , compatibleWithBlob . version ) ; } if ( compatibleWithBlob . nIterations ) { iterationsOverride = compatibleWithBlob . nIterations ; validateIterations ( iterationsOverride , compatibleWithBlob . version ) ; } } let salt = saltOverride || this . generateRandomValues ( DefaultEncryptionParameters . SaltSize ) ; let nIterations = iterationsOverride || this . contextSecurity . nIterations ; let masterKey = await this . deriveMasterKey ( salt , nIterations ) ; var blobs = [ ] ; let recordsArray = [ ] ; recordsArray = recordsArray . concat ( records ) ; await this . asyncForEach ( recordsArray , async ( record ) => { let nonce = this . generateRandomValues ( DefaultEncryptionParameters . NonceSize ) ; let aesKey = await this . deriveAESKeyFromMasterKey ( masterKey , nonce ) ; await window . crypto . subtle . encrypt ( { name : "AES-GCM" , iv : this . getVersionBuffer ( ) } , aesKey , record ) . then ( encryptedObject => { blobs . push ( new Blob ( encryptedObject , salt , nonce , nIterations , this . contextSecurity . version ) ) ; } ) ; } ) ; return Array . isArray ( records ) ? blobs : blobs [ 0 ] ; } public async unlock ( blobs ) { var records = [ ] ; let blobsArray = [ ] ; blobsArray = blobsArray . concat ( blobs ) ; await this . asyncForEach ( blobsArray , async ( blob ) => { let masterKey = await this . deriveMasterKey ( blob . salt , blob . nIterations ) ; let aesKey = await this . deriveAESKeyFromMasterKey ( masterKey , blob . nonce ) ; try { await window . crypto . subtle . decrypt ( { name : "AES-GCM" , iv : this . getVersionBuffer ( ) } , aesKey , blob . encryptedObject ) . then ( decryptedValue => { records . push ( decryptedValue ) ; } ) ; } catch { throw new InvalidCipherText ( ) ; } } ) ; return Array . isArray ( blobs ) ? records : records [ 0 ] ; } } export { Blob , DefaultEncryptionParameters } ;