diff --git a/src/interpreter/lib/math.ts b/src/interpreter/lib/math.ts index 023303c7..691753b0 100644 --- a/src/interpreter/lib/math.ts +++ b/src/interpreter/lib/math.ts @@ -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); diff --git a/src/utils/random/chacha20.ts b/src/utils/random/chacha20.ts index bea6df14..ab33ce18 100644 --- a/src/utils/random/chacha20.ts +++ b/src/utils/random/chacha20.ts @@ -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); diff --git a/src/utils/random/genrng.ts b/src/utils/random/genrng.ts index 799308fe..fff2ee9e 100644 --- a/src/utils/random/genrng.ts +++ b/src/utils/random/genrng.ts @@ -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') { @@ -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') { @@ -28,13 +26,12 @@ export function GenerateRC4Random(seed: Value | undefined) : VNativeFn | VNull { }); } -export async function GenerateChaCha20Random(seed: Value | undefined) : Promise { - 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 { + 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); diff --git a/test/std.ts b/test/std.ts index 3018a11f..22598d13 100644 --- a/test/std.ts +++ b/test/std.ts @@ -1,6 +1,7 @@ import * as assert from 'assert'; -import { describe, test } from 'vitest'; +import { describe, expect, test } from 'vitest'; import { utils } from '../src'; +import { AiScriptRuntimeError } from '../src/error'; import { NUM, STR, NULL, ARR, OBJ, BOOL, TRUE, FALSE, ERROR ,FN_NATIVE } from '../src/interpreter/value'; import { exe, eq } from './testutils'; @@ -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', () => { diff --git a/unreleased/math-gen-rng-disallow-null-seed.md b/unreleased/math-gen-rng-disallow-null-seed.md new file mode 100644 index 00000000..c224e27a --- /dev/null +++ b/unreleased/math-gen-rng-disallow-null-seed.md @@ -0,0 +1 @@ +- Fix: `Math:gen_rng`の`seed`に`null`を与えてもエラーが発生しない問題を修正