import * as asmCrypto from 'asmcrypto.js' ; import suite from 'chuhai' ; import * as hashJs from 'hash.js' ; import { HashFunction } from '../bin/bin' ; import { instantiateRipemd160 , instantiateSha1 , instantiateSha256 , instantiateSha512 , } from './crypto' ; declare const benchError ; declare const benchComplete ; const isUint8Array = ( array ) : array is Uint8Array => array ?. constructor ?. name === 'Uint8Array' ; const compare = ( a ? , b ? ) => { if ( ! isUint8Array ( a ) || ! isUint8Array ( b ) || a . toString ( ) !== b . toString ( ) ) { benchError ( `\nInvalid result: ${ a } is not equal to ${ b }\n` ) ; } } ; const randomBytes = ( bytes ) => crypto . getRandomValues ( new Uint8Array ( bytes ) ) ; const singlePassBrowserBenchmark = async ( { hashFunction , hashFunctionName , inputLength , subtleCryptoAlgorithmName , } ) => suite ( `browser: ${ hashFunctionName }: hash a ${ inputLength }-byte input` , ( s ) => { let message = randomBytes ( inputLength ) ; let hash ; s . cycle ( ( ) => { if ( hash === null ) { benchError ( `asmcrypto.js produced a null result given message: ${ message . toString ( ) }` ) ; } else { compare ( hash , hashFunction . hash ( message ) ) ; } message = randomBytes ( inputLength ) ; } ) ; s . bench ( 'libauth' , ( ) => { hash = hashFunction . hash ( message ) ; } ) ; s . bench ( 'hash.js' , ( ) => { hash = new Uint8Array ( hashJs [ hashFunctionName ] ( ) . update ( message ) . digest ( ) ) ; } ) ; if ( typeof subtleCryptoAlgorithmName === 'string' ) { s . bench ( 'crypto.subtle' , ( deferred ) => { window . crypto . subtle . digest ( subtleCryptoAlgorithmName , message ) . then ( ( buffer ) => { hash = new Uint8Array ( buffer ) ; deferred . resolve ( ) ; } ) ; } , { defer : true , } ) ; const Algorithm = subtleCryptoAlgorithmName === 'SHA-1' ? asmCrypto . Sha1 : subtleCryptoAlgorithmName === 'SHA-256' ? asmCrypto . Sha256 : asmCrypto . Sha512 ; s . bench ( 'asmcrypto.js' , ( ) => { const instance = new Algorithm ( ) ; hash = instance . process ( message ) . finish ( ) . result ; } ) ; } } ) ; const mb = 1000000 ; const incrementalBrowserBenchmark = async ( { chunkSize , hashFunction , hashFunctionName , totalInput , } ) => suite ( `browser: ${ hashFunctionName }: incrementally hash a ${ totalInput / mb }MB input in ${ chunkSize / mb }MB chunks` , ( s ) => { let message ; let messageChunks ; let hash ; const nextCycle = ( ) => { /**
         * We can't get this much entropy, so we just use 0s here.
         */ message = new Uint8Array ( totalInput ) . fill ( 0 ) ; const chunkCount = Math . ceil ( message . length / chunkSize ) ; messageChunks = Array . from ( { length : chunkCount } ) . map ( ( _ , index ) => message . slice ( index * chunkSize , index * chunkSize + chunkSize ) ) ; } ; nextCycle ( ) ; s . cycle ( ( ) => { if ( hash === null ) { benchError ( `asmcrypto.js produced a null result given message: ${ message . toString ( ) }` ) ; } else { compare ( new Uint8Array ( hash ) , hashFunction . hash ( message ) ) ; } nextCycle ( ) ; } ) ; s . bench ( 'libauth' , ( ) => { hash = hashFunction . final ( messageChunks . reduce ( ( state , chunk ) => hashFunction . update ( state , chunk ) , hashFunction . init ( ) ) ) ; } ) ; s . bench ( 'hash.js' , ( ) => { hash = messageChunks . reduce ( ( state , chunk ) => state . update ( chunk ) , hashJs [ hashFunctionName ] ( ) ) . digest ( ) ; } ) ; if ( hashFunctionName !== 'ripemd160' ) { const Algorithm = hashFunctionName === 'sha1' ? asmCrypto . Sha1 : hashFunctionName === 'sha256' ? asmCrypto . Sha256 : asmCrypto . Sha512 ; s . bench ( 'asmcrypto.js' , ( ) => { const instance = new Algorithm ( ) ; hash = instance . process ( message ) . finish ( ) . result ; } ) ; } } ) ; const browserBenchmarks = async ( { hashFunction , hashFunctionName , subtleCryptoAlgorithmName , } ) => { await singlePassBrowserBenchmark ( { hashFunction , hashFunctionName , inputLength : 32 , subtleCryptoAlgorithmName , } ) ; await singlePassBrowserBenchmark ( { hashFunction , hashFunctionName , inputLength : 100 , subtleCryptoAlgorithmName , } ) ; await singlePassBrowserBenchmark ( { hashFunction , hashFunctionName , inputLength : 1000 , subtleCryptoAlgorithmName , } ) ; await singlePassBrowserBenchmark ( { hashFunction , hashFunctionName , inputLength : 10000 , subtleCryptoAlgorithmName , } ) ; await incrementalBrowserBenchmark ( { chunkSize : mb , hashFunction , hashFunctionName , totalInput : mb * 32 , } ) ; } ; ( async ( ) => { const sha1 = await instantiateSha1 ( ) ; const sha256 = await instantiateSha256 ( ) ; const sha512 = await instantiateSha512 ( ) ; const ripemd160 = await instantiateRipemd160 ( ) ; await browserBenchmarks ( { hashFunction : sha1 , hashFunctionName : 'sha1' , subtleCryptoAlgorithmName : 'SHA-1' , } ) ; await browserBenchmarks ( { hashFunction : sha256 , hashFunctionName : 'sha256' , subtleCryptoAlgorithmName : 'SHA-256' , } ) ; await browserBenchmarks ( { hashFunction : sha512 , hashFunctionName : 'sha512' , subtleCryptoAlgorithmName : 'SHA-512' , } ) ; await browserBenchmarks ( { hashFunction : ripemd160 , hashFunctionName : 'ripemd160' , } ) ; benchComplete ( ) ; } ) ( ) . catch ( ( err ) => { console . error ( err ) ; } ) ;