commit 0aa8254d84d35ed8945724153340c036ceaf6e76
parent afc4cd9ed32e75d309daa68013694c9c0c65996b
Author: Florian Dold <dold@taler.net>
Date: Wed, 22 Jul 2026 20:44:08 +0200
util: test the donation unit codec against the donau's format
Covers a unit exactly as the donau packs it, the cases that have to be
rejected, and that the key hash follows from the key alone.
Diffstat:
1 file changed, 106 insertions(+), 0 deletions(-)
diff --git a/packages/taler-util/src/types-donau.test.ts b/packages/taler-util/src/types-donau.test.ts
@@ -0,0 +1,106 @@
+/*
+ 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 Affero 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 Affero General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+
+ SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import assert from "node:assert";
+import { test } from "node:test";
+import { encodeCrock, hash, stringToBytes } from "./taler-crypto.js";
+import {
+ codecForDonationUnitKeyGroup,
+ hashDonationUnitPub,
+} from "./types-donau.js";
+
+/**
+ * One donation unit exactly as the donau sends it, i.e. the cipher and the key
+ * and nothing else. Taken from the packing in
+ * donau/src/donau/donau-httpd_get-keys.c.
+ */
+const wireGroup = {
+ donation_unit_pub: {
+ cipher: "RSA",
+ rsa_public_key: "040000W1",
+ },
+ year: 2026,
+ lost: false,
+ value: "TESTKUDOS:0.01",
+};
+
+test("a donation unit as the donau sends it decodes", (t) => {
+ const res = codecForDonationUnitKeyGroup().decode(wireGroup);
+ assert.deepStrictEqual(res, {
+ donation_unit_pub: {
+ cipher: "RSA",
+ rsa_public_key: "040000W1",
+ },
+ value: "TESTKUDOS:0.01",
+ year: 2026,
+ lost: false,
+ });
+});
+
+test("a donation unit missing its key is rejected", (t) => {
+ const cases: [string, unknown][] = [
+ ["no donation_unit_pub", { year: 2026, value: "TESTKUDOS:1" }],
+ [
+ "unknown cipher",
+ { ...wireGroup, donation_unit_pub: { cipher: "XYZ", key: "x" } },
+ ],
+ [
+ "RSA without the key",
+ { ...wireGroup, donation_unit_pub: { cipher: "RSA" } },
+ ],
+ [
+ "CS without the key",
+ { ...wireGroup, donation_unit_pub: { cipher: "CS" } },
+ ],
+ ["year is not a number", { ...wireGroup, year: "2026" }],
+ ["value is not an amount", { ...wireGroup, value: "nope" }],
+ ];
+ for (const [name, v] of cases) {
+ assert.throws(
+ () => codecForDonationUnitKeyGroup().decode(v),
+ `${name} must be rejected`,
+ );
+ }
+});
+
+test("the CS variant carries the key under the name the donau uses", (t) => {
+ const res = codecForDonationUnitKeyGroup().decode({
+ ...wireGroup,
+ donation_unit_pub: { cipher: "CS", cs_public_key: "0123456789ABCDEG" },
+ });
+ assert.deepStrictEqual(res.donation_unit_pub, {
+ cipher: "CS",
+ cs_public_key: "0123456789ABCDEG",
+ });
+});
+
+test("the donation unit hash is taken over the encoded key", (t) => {
+ // The donau hashes the encoded key, which is what the wire carries, so the
+ // hash has to come out of the key alone.
+ const key = encodeCrock(stringToBytes("donation-unit-key"));
+ assert.strictEqual(
+ hashDonationUnitPub({ cipher: "RSA", rsa_public_key: key }),
+ encodeCrock(hash(stringToBytes("donation-unit-key"))),
+ );
+ // A different key gives a different hash.
+ const other = encodeCrock(stringToBytes("another-key"));
+ assert.notStrictEqual(
+ hashDonationUnitPub({ cipher: "RSA", rsa_public_key: other }),
+ hashDonationUnitPub({ cipher: "RSA", rsa_public_key: key }),
+ );
+});