194 lines
6.2 KiB
TypeScript
194 lines
6.2 KiB
TypeScript
import chai, { assert, expect } from 'chai';
|
|
import chaiBytes from 'chai-bytes';
|
|
import { bufferByteSize, LiteHashDRBG } from '../ts/util/LiteHashDRBG';
|
|
import { RandUtil } from '../ts/util/RandUtil';
|
|
import { convertBytesLikeToArrayBuffer } from '../ts/util/convertBytesLikeToArrayBuffer';
|
|
import { convertBytesLikeToUint8Array as toBytes } from '../ts/util/convertBytesLikeToUint8Array';
|
|
import _ from 'lodash';
|
|
|
|
chai.use(chaiBytes);
|
|
|
|
type Bytes = ArrayBuffer | DataView | Uint8Array;
|
|
type MaybeBytes = Bytes | string;
|
|
|
|
function fillBlock(body: MaybeBytes, filler: MaybeBytes = '\0', length = bufferByteSize): Uint8Array {
|
|
const u8Body = toBytes(body);
|
|
const u8Filler = toBytes(filler, false);
|
|
|
|
if (u8Filler.byteLength < 1) {
|
|
throw new Error('filler must have length');
|
|
}
|
|
|
|
const buffer = new Uint8Array(length);
|
|
buffer.set(u8Body, 0);
|
|
let bufferIdx = u8Body.byteLength;
|
|
|
|
while (bufferIdx + u8Filler.byteLength < length) {
|
|
buffer.set(u8Filler, bufferIdx);
|
|
bufferIdx += u8Filler.byteLength;
|
|
}
|
|
|
|
if (bufferIdx < length) {
|
|
const slice = new Uint8Array(u8Filler.buffer, u8Filler.byteOffset, length - bufferIdx);
|
|
buffer.set(slice, bufferIdx);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
class DummyBlockRNG extends LiteHashDRBG {
|
|
private repeatBlockCnt: number;
|
|
private repeatBlock: ArrayBuffer[];
|
|
|
|
public constructor(repeatBlock: MaybeBytes[], stateIdx = 0) {
|
|
super('x');
|
|
|
|
this.repeatBlock = [];
|
|
for (const rawBlock of repeatBlock) {
|
|
const block = convertBytesLikeToArrayBuffer(rawBlock);
|
|
if (block.byteLength !== bufferByteSize) {
|
|
throw new Error;
|
|
}
|
|
this.repeatBlock.push(block);
|
|
}
|
|
this.repeatBlockCnt = this.repeatBlock.length;
|
|
this.stateIdx = stateIdx;
|
|
this.bufferIdx = 0;
|
|
this.genNextBlock();
|
|
}
|
|
|
|
protected genNextBlock() {
|
|
if (!this.repeatBlock) {
|
|
return;
|
|
}
|
|
this.buffer = this.repeatBlock[this.stateIdx];
|
|
this.bufferIdx = 0;
|
|
this.stateIdx = (this.stateIdx + 1) % this.repeatBlockCnt;
|
|
}
|
|
}
|
|
|
|
const fixedKey = 'HelloWorld';
|
|
|
|
|
|
describe('RNGtestDummy', () => {
|
|
const rng = new DummyBlockRNG([
|
|
fillBlock('', "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff")
|
|
]);
|
|
|
|
it('BasicConvert', () => {
|
|
assert.equal(toBytes("\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff", false).length, 16);
|
|
});
|
|
it('SimpleByte', () => {
|
|
assert.equalBytes(toBytes("\x00", false), rng.nextBytes(1), 'b1');
|
|
assert.equalBytes(toBytes("\x11\x22", false), rng.nextBytes(2), 'b2');
|
|
assert.equalBytes(toBytes("\x33\x44\x55", false), rng.nextBytes(3), 'b3');
|
|
assert.equalBytes(toBytes("\x66\x77\x88\x99", false), rng.nextBytes(4), 'b4');
|
|
});
|
|
|
|
it('OverflowBlock', () => {
|
|
for (const idx in _.range(16)) {
|
|
assert.equalBytes(
|
|
toBytes("\xaa\xbb\xcc\xdd\xee\xff\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99", false),
|
|
rng.nextBytes(16)
|
|
);
|
|
}
|
|
});
|
|
|
|
it('MultiBlock', () => {
|
|
assert.equalBytes(
|
|
fillBlock('', "\xaa\xbb\xcc\xdd\xee\xff\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99", bufferByteSize * 2),
|
|
rng.nextBytes(bufferByteSize * 2)
|
|
);
|
|
});
|
|
|
|
it('bitTest', () => {
|
|
assert.equalBytes(toBytes("\x00", false), rng.nextBits(1)); //aa
|
|
assert.equalBytes(toBytes("\x01", false), rng.nextBits(1)); //bb
|
|
assert.equalBytes(toBytes("\xcc", false), rng.nextBits(8)); //cc
|
|
assert.equalBytes(toBytes("\xdd\x02", false), rng.nextBits(10)); //ddee
|
|
assert.equalBytes(toBytes("\x7f", false), rng.nextBits(7)); //ff
|
|
assert.equalBytes(toBytes("\x00\x11\x22\x33\x44\x55\x06", false), rng.nextBits(53));
|
|
});
|
|
|
|
it('int', () => {
|
|
assert.equal(0x77, rng.nextInt(0xff));
|
|
assert.equal(0x9988, rng.nextInt((1 << 16) - 1));
|
|
assert.equal(0xddccbbaa, rng.nextInt(0xffffffff));
|
|
assert.equal(0x0433221100ffee, rng.nextInt());
|
|
assert.equal(0x05, rng.nextInt(0x0f)); //55
|
|
assert.equal(0x06, rng.nextInt(0x12)); //66
|
|
assert.equal(0x08, rng.nextInt(99)); //77(119 -> 7bit) -> 88(136 -> 8bit -> 8)
|
|
assert.equal(0x99, rng.nextInt(0x99)); //99
|
|
assert.equal(0xaa, rng.nextInt(0xaa)); //aa (fit Max)
|
|
});
|
|
|
|
it('float', () => {
|
|
const floatMax = 2 ** 53;
|
|
const fa = rng.nextFloat1();
|
|
assert.equal(0x1100ffeeddccbb / floatMax, fa);
|
|
assert.isTrue(0.5313720384 > fa);
|
|
assert.isTrue(0.5313720383 < fa);
|
|
const fb = rng.nextFloat1();
|
|
assert.equal(0x08776655443322 / floatMax, fb);
|
|
});
|
|
});
|
|
|
|
describe('RNGexpectedError', ()=>{
|
|
const rng = new LiteHashDRBG(fixedKey);
|
|
it('nextBits0', ()=>{
|
|
assert.throw(()=>rng.nextBits(0));
|
|
});
|
|
it('nextBits-1', ()=>{
|
|
assert.throw(()=>rng.nextBits(-1));
|
|
});
|
|
|
|
it('nextBytes0', ()=>{
|
|
assert.throw(()=>rng.nextBytes(0));
|
|
});
|
|
it('nextBytes-1', ()=>{
|
|
assert.throw(()=>rng.nextBytes(-1));
|
|
});
|
|
|
|
const randUtil = new RandUtil(rng);
|
|
it('utilEmptyChoice', ()=>{
|
|
assert.throw(()=>randUtil.choice([]));
|
|
});
|
|
it('utilEmptyChoiceUsingWeight', ()=>{
|
|
assert.throw(()=>randUtil.choiceUsingWeight({}));
|
|
});
|
|
it('utilEmptyChoiceUsingWeightPair', ()=>{
|
|
assert.throw(()=>randUtil.choiceUsingWeightPair([]));
|
|
});
|
|
});
|
|
|
|
describe('RNGAcceptable', ()=>{
|
|
const rng = new LiteHashDRBG(fixedKey);
|
|
it('RNG', ()=>{
|
|
rng.nextInt(0);
|
|
rng.nextInt(2 ** 53 - 1);
|
|
rng.nextBytes(65);
|
|
rng.nextBits(512);
|
|
});
|
|
|
|
const randUtil = new RandUtil(rng);
|
|
it('RandUtil', ()=>{
|
|
randUtil.choice([0, 0, 0]);
|
|
randUtil.choiceUsingWeight({
|
|
0: 0,
|
|
1: -1
|
|
});
|
|
randUtil.choiceUsingWeightPair([
|
|
[0, 0],
|
|
[1, 0],
|
|
[2, -2]
|
|
]);
|
|
randUtil.nextBool(1.1);
|
|
randUtil.nextBool(-0.1);
|
|
randUtil.shuffle([]);
|
|
randUtil.shuffle([1]);
|
|
randUtil.nextRange(0, 0);
|
|
randUtil.nextRangeInt(0, 0);
|
|
randUtil.nextRange(1, -1);
|
|
randUtil.nextRangeInt(1, -1);
|
|
})
|
|
}); |