get-random-values-polypony
Version:
Synchronous randombytes function that works in node, the browser & react-native!
1 lines • 5.09 kB
JavaScript
const test=require("fresh-tape"),getRandomValues=require(".."),SAMPLES=1024;function uintstr(uint8Array){const arr=[];for(let i=0;i<uint8Array.length;i++)arr[i]=uint8Array[i];const str=arr.join(",");return str}module.exports=function(name){function testInputs(suffix,override){testInput("Uint8ClampedArray"+suffix,new Uint8ClampedArray(SAMPLES),override),testInput("Uint8Array"+suffix,new Uint8Array(SAMPLES),override),testInput("Int8Array"+suffix,new Int8Array(SAMPLES),override),testInput("Uint16Array"+suffix,new Uint16Array(SAMPLES/2),override),testInput("Int16Array"+suffix,new Int16Array(SAMPLES/2),override),testInput("Uint32Array"+suffix,new Uint32Array(SAMPLES/4),override),testInput("Int32Array"+suffix,new Int32Array(SAMPLES/4),override)}function testInput(type,input,override){test(name+" - "+type,function(t){testRandomOutput(t,input,override||getRandomValues),t.end()})}function testOffset(name,original,offset,length){const total=original.length;test(name+" - offset: "+offset+"/"+length+" of "+total,function(t){const input=new Uint8Array(original.buffer,offset,length);let after,afterStr,before,beforeStr;0<offset&&(before=getRandomValues(new Uint8Array(original.buffer,0,offset)),beforeStr=uintstr(before),t.ok(0!==stats(before).avgDiff,"before needs to be filled")),(0===length||0<length)&&(after=getRandomValues(new Uint8Array(original.buffer,offset+length)),afterStr=uintstr(after),t.ok(0!==stats(after).avgDiff,"after needs to be filled")),getRandomValues(input),before&&t.equals(uintstr(before),beforeStr,"before "+offset+" is untouched"),after&&t.equals(uintstr(after),afterStr,"after "+(offset+length)+" is untouched"),t.end()})}getRandomValues.polyfill();const base="undefined"==typeof window?global:window,crypto=base.crypto;test(name+" - polyfill",function(t){t.ok("function"==typeof crypto.getRandomValues,"crypto.getRandomValues should exist"),"getRandomValuesBrowser"!==name&&t.equals(crypto.getRandomValues.name,name+"Limited","the polyfill support should be limited"),testRandomOutput(t,new Uint8Array(1e3),function(input){return crypto.getRandomValues(input)}),crypto.getRandomValues(new Uint8Array(65536));try{crypto.getRandomValues(new Uint8Array(65537)),t.fail("too large uint8array")}catch(_){}try{crypto.getRandomValues(new Float32Array(10)),t.fail("Float32Array is supposed to throw")}catch(_){}try{crypto.getRandomValues(new Float64Array(10)),t.fail("Float64Array is supposed to throw")}catch(_){}try{crypto.getRandomValues(new DataView(new ArrayBuffer(10))),t.fail("DataView is supposed to throw")}catch(_){}if(base.BigInt64Array){try{crypto.getRandomValues(new base.BigInt64Array(10)),t.fail("BigInt64Array is supposed to throw")}catch(_){}try{crypto.getRandomValues(new base.BigUint64Array(10)),t.fail("BigUint64Array is supposed to throw")}catch(_){}}t.end()}),testInputs(" - polyfilled",function(input){return crypto.getRandomValues(input)}),test(name+" - performance",function(t){for(let size=1e3;;){const buffer=new Uint8Array(size),start=Date.now();getRandomValues(buffer);const end=Date.now(),duration=end-start;if(50<duration){t.ok(!0,size+" random values "+duration+"ms");break}size*=10}t.end()}),test(name+" - name matches",function(t){t.equals(getRandomValues.name,name),t.end()}),testOffset("small data",new Uint8Array(48),16,16),testOffset("zero offset",new Uint8Array(48),0,16),testOffset("full length",new Uint8Array(48),16,32),testOffset("big offset",new Uint8Array(163840),32768,78643),testOffset("int32 array",new Int32Array(12),16,16),testInputs(""),testInput("Float32Array",new Float32Array(SAMPLES/4)),testInput("Float64Array",new Float64Array(SAMPLES/8)),testInput("DataView",new DataView(new ArrayBuffer(SAMPLES))),testInput("large Uint8Array",new Uint8Array(163840)),base.BigInt64Array&&(testInput("BigInt64Array",new base.BigInt64Array(SAMPLES)),testInput("BigUint64Array",new base.BigUint64Array(SAMPLES)))};function stats(parts){var _Mathabs=Math.abs;let prev,max=Number.NEGATIVE_INFINITY,min=Number.POSITIVE_INFINITY,totalDiff=0;for(let i=0;i<parts.length;i++){const value=parts[i];value>max&&(max=value),value<min&&(min=value),prev!==void 0&&(totalDiff+=_Mathabs(prev-value)),prev=value}return{min:min,max:max,avgDiff:totalDiff/parts.length,diff:_Mathabs(max-min)}}function testRandomOutput(t,input,getRandomValues){const result=getRandomValues(input);t.same(result,input,"the input is returned as output");const valueMin=0,lowerThreshold=1077952576,upperThreshold=3233857728,diffByValMin=.25,runStats=stats(new Uint32Array(input.buffer)),min=runStats.min,max=runStats.max,diff=runStats.diff,diffByVal=runStats.avgDiff/4294967295;t.ok(diffByVal>=diffByValMin,"average difference by value: "+diffByVal+" >= "+diffByValMin),t.ok(min>=valueMin,"lower bounds: "+min+" >= "+valueMin),t.ok(min<lowerThreshold,"lower threshold: "+min+" > "+lowerThreshold),t.ok(max<=4294967295,"upper bounds: "+max+" <= "+4294967295),t.ok(max>upperThreshold,"upper threshold: "+max+" > "+upperThreshold);const spectrum=(0|diff*(1e3/4294967295))/10;t.ok(75<spectrum,"spectrum used: "+spectrum+"% or "+diff+" of "+4294967295+" (>75% required)")}