Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/interpreter/lib/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ export const stdMath: Record<`Math:${string}`, Value> = {
else if (options?.type !== undefined) {
throw new AiScriptRuntimeError('`options` must be an object if specified.');
}
if (seed.type !== 'num' && seed.type !== 'str' && seed.type !== 'null') throw new AiScriptRuntimeError('`seed` must be either number or string if specified.');
if (seed.type !== 'num' && seed.type !== 'str') throw new AiScriptRuntimeError('`seed` must be either number or string.');
switch (algo) {
case 'rc4_legacy':
return GenerateLegacyRandom(seed);
Expand Down
19 changes: 7 additions & 12 deletions src/utils/random/chacha20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,15 @@ export class ChaCha20 extends RandomBase {
private buffer: Uint8Array;
private filledBuffer: Uint8Array;
private counter: bigint;
constructor(seed?: Uint8Array | undefined) {
constructor(seed: Uint8Array) {
const keyNonceBytes = CHACHA_IVSIZE + CHACHA_KEYSIZE;
super();
let keynonce: Uint8Array;
if (typeof seed === 'undefined') {
keynonce = crypto.getRandomValues(new Uint8Array(keyNonceBytes));
} else {
keynonce = seed;
if (keynonce.byteLength > keyNonceBytes) keynonce = seed.subarray(0, keyNonceBytes);
if (keynonce.byteLength < keyNonceBytes) {
const y = new Uint8Array(keyNonceBytes);
y.set(keynonce);
keynonce = y;
}
let keynonce = seed;
if (keynonce.byteLength > keyNonceBytes) keynonce = seed.subarray(0, keyNonceBytes);
if (keynonce.byteLength < keyNonceBytes) {
const y = new Uint8Array(keyNonceBytes);
y.set(keynonce);
keynonce = y;
}
const key = keynonce.subarray(0, CHACHA_KEYSIZE);
const nonce = keynonce.subarray(CHACHA_KEYSIZE, CHACHA_KEYSIZE + CHACHA_IVSIZE);
Expand Down
15 changes: 6 additions & 9 deletions src/utils/random/genrng.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { FN_NATIVE, NULL, NUM } from '../../interpreter/value.js';
import { textEncoder } from '../../const.js';
import { SeedRandomWrapper } from './seedrandom.js';
import { ChaCha20 } from './chacha20.js';
import type { VNativeFn, VNull, Value } from '../../interpreter/value.js';
import type { VNativeFn, VNum, VStr } from '../../interpreter/value.js';

export function GenerateLegacyRandom(seed: Value | undefined) : VNativeFn | VNull {
if (!seed || seed.type !== 'num' && seed.type !== 'str') return NULL;
export function GenerateLegacyRandom(seed: VNum | VStr): VNativeFn {
const rng = seedrandom(seed.value.toString());
return FN_NATIVE(([min, max]) => {
if (min && min.type === 'num' && max && max.type === 'num') {
Expand All @@ -16,8 +15,7 @@ export function GenerateLegacyRandom(seed: Value | undefined) : VNativeFn | VNul
});
}

export function GenerateRC4Random(seed: Value | undefined) : VNativeFn | VNull {
if (!seed || seed.type !== 'num' && seed.type !== 'str') return NULL;
export function GenerateRC4Random(seed: VNum | VStr): VNativeFn {
const rng = new SeedRandomWrapper(seed.value);
return FN_NATIVE(([min, max]) => {
if (min && min.type === 'num' && max && max.type === 'num') {
Expand All @@ -28,13 +26,12 @@ export function GenerateRC4Random(seed: Value | undefined) : VNativeFn | VNull {
});
}

export async function GenerateChaCha20Random(seed: Value | undefined) : Promise<VNativeFn | VNull> {
if (!seed || seed.type !== 'num' && seed.type !== 'str' && seed.type !== 'null') return NULL;
let actualSeed : Uint8Array | undefined = undefined;
export async function GenerateChaCha20Random(seed: VNum | VStr): Promise<VNativeFn> {
let actualSeed: Uint8Array;
if (seed.type === 'num')
{
actualSeed = new Uint8Array(await crypto.subtle.digest('SHA-384', new Uint8Array(new Float64Array([seed.value]))));
} else if (seed.type === 'str') {
} else {
actualSeed = new Uint8Array(await crypto.subtle.digest('SHA-384', new Uint8Array(textEncoder.encode(seed.value))));
}
const rng = new ChaCha20(actualSeed);
Expand Down
7 changes: 6 additions & 1 deletion test/std.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import * as assert from 'assert';
import { describe, test } from 'vitest';
import { describe, expect, test } from 'vitest';
import { utils } from '../src';
import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value';
import { exe, eq } from './testutils';
import { AiScriptRuntimeError } from '../src/error';


describe('Core', () => {
Expand Down Expand Up @@ -156,6 +157,10 @@ describe('Math', () => {
`)
eq(res, ARR([BOOL(true), BOOL(true)]));
});

test.concurrent('gen_rng should reject when null is provided as a seed', async () => {
await expect(() => exe('Math:gen_rng(null)')).rejects.toThrow(AiScriptRuntimeError);
});
});

describe('Obj', () => {
Expand Down
1 change: 1 addition & 0 deletions unreleased/math-gen-rng-disallow-null-seed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Fix: `Math:gen_rng`の`seed`に`null`を与えてもエラーが発生しない問題を修正