import { streamXOR , stream } from "@stablelib/xsalsa20" ; import { oneTimeAuth , equal as authEqual } from "@stablelib/poly1305" ; import { wipe } from "@stablelib/wipe" ; import { randomBytes , RandomSource } from "@stablelib/random" ; export function secretBox ( key , nonce , data ) { if ( nonce . length !== 24 ) { throw new Error ( "secretBox nonce must be 24 bytes" ) ; } const firstBlock = new Uint8Array ( 64 ) ; const nonceCounter = new Uint8Array ( 24 + 8 ) ; nonceCounter . set ( nonce ) ; stream ( key , nonceCounter , firstBlock , 8 ) ; const result = new Uint8Array ( 16 + data . length ) ; for ( let i = 0 ; i < 32 && i < data . length ; i ++ ) { result [ 16 + i ] = data [ i ] ^ firstBlock [ 32 + i ] ; } if ( data . length > 32 ) { streamXOR ( key , nonceCounter , data . subarray ( 32 ) , result . subarray ( 16 + 32 ) , 8 ) ; } const auth = oneTimeAuth ( firstBlock . subarray ( 0 , 32 ) , result . subarray ( 16 ) ) ; for ( let i = 0 ; i < auth . length ; i ++ ) { result [ i ] = auth [ i ] ; } wipe ( auth ) ; wipe ( firstBlock ) ; wipe ( nonceCounter ) ; return result ; } export function openSecretBox ( key , nonce , box ) { if ( nonce . length !== 24 ) { throw new Error ( "secretBox nonce must be 24 bytes" ) ; } if ( box . length < 16 ) { throw new Error ( "secretBox data must be at least 16 bytes" ) ; } const firstBlock = new Uint8Array ( 64 ) ; const nonceCounter = new Uint8Array ( 24 + 8 ) ; nonceCounter . set ( nonce ) ; stream ( key , nonceCounter , firstBlock , 8 ) ; const auth = oneTimeAuth ( firstBlock . subarray ( 0 , 32 ) , box . subarray ( 16 ) ) ; if ( ! authEqual ( auth , box . subarray ( 0 , 16 ) ) ) { return null ; } const ciphertext = box . subarray ( 16 ) ; const result = new Uint8Array ( ciphertext . length ) ; for ( let i = 0 ; i < 32 && i < ciphertext . length ; i ++ ) { result [ i ] = ciphertext [ i ] ^ firstBlock [ 32 + i ] ; } if ( ciphertext . length > 32 ) { streamXOR ( key , nonceCounter , ciphertext . subarray ( 32 ) , result . subarray ( 32 ) , 8 ) ; } wipe ( auth ) ; wipe ( firstBlock ) ; wipe ( nonceCounter ) ; return result ; } /** Generates a 32-byte random secret key.  */ export function generateKey ( prng ? ) { return randomBytes ( 32 , prng ) ; }