commit 20b26fc38297ada6c630a5aa9dc826cd618af9a8
parent e383c41f4efcf58bdffc8635750955d8f2963f6c
Author: Florian Dold <dold@taler.net>
Date: Mon, 20 Jul 2026 20:48:32 +0200
util: test that the browser PRNG is never a constant source
Diffstat:
1 file changed, 60 insertions(+), 0 deletions(-)
diff --git a/packages/taler-util/src/prng-browser.test.ts b/packages/taler-util/src/prng-browser.test.ts
@@ -0,0 +1,60 @@
+/*
+ This file is part of GNU Taler
+ (C) 2026 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { test } from "node:test";
+import assert from "node:assert";
+import { loadBrowserPrng } from "./prng-browser.js";
+import { randomBytes, setPRNG } from "./nacl-fast.js";
+
+/**
+ * The browser entry point installs this PRNG unconditionally. Where no
+ * CSPRNG exists it must leave nacl's throwing default in place: a constant
+ * source yields publicly known private keys.
+ */
+test("browser PRNG never silently produces constant bytes", (t) => {
+ // Reinstate nacl's default "no PRNG" behaviour, so this test is independent
+ // of whatever another test file may have installed.
+ setPRNG(() => {
+ throw new Error("no PRNG");
+ });
+
+ assert.strictEqual(
+ typeof (globalThis as any).self,
+ "undefined",
+ "precondition: this test must run without a `self` global",
+ );
+
+ loadBrowserPrng();
+
+ let first: Uint8Array | undefined;
+ try {
+ first = randomBytes(32);
+ } catch (e) {
+ // Correct behaviour: no CSPRNG available, so randomBytes refuses.
+ return;
+ }
+
+ // If a PRNG was installed at all, it must not be a constant source.
+ assert.ok(
+ first.some((b) => b !== 0),
+ "randomBytes returned all zero bytes",
+ );
+ assert.notDeepStrictEqual(
+ Array.from(first),
+ Array.from(randomBytes(32)),
+ "two consecutive randomBytes calls returned identical output",
+ );
+});