From 959eec951f412a1093a050706e41aaf66430dfb7 Mon Sep 17 00:00:00 2001 From: Fredrik Dahlgren Date: Fri, 26 Jun 2026 16:54:56 +0200 Subject: [PATCH] Support randomized Wycheproof signing vectors Signed-off-by: Fredrik Dahlgren --- test/wycheproof/wycheproof_client.py | 92 ++++++++++++++++++---------- test/wycheproof/wycheproof_mldsa.c | 42 +++++++++---- 2 files changed, 91 insertions(+), 43 deletions(-) diff --git a/test/wycheproof/wycheproof_client.py b/test/wycheproof/wycheproof_client.py index 177f68f4a..007b84584 100755 --- a/test/wycheproof/wycheproof_client.py +++ b/test/wycheproof/wycheproof_client.py @@ -18,8 +18,8 @@ exec_prefix = os.environ.get("EXEC_WRAPPER", "") exec_prefix = exec_prefix.split(" ") if exec_prefix != "" else [] -# Pinned to a specific commit (2026-06-04). -WYCHEPROOF_COMMIT = "4f5e05f71e6b724c20e2c1b6934c7bd7ef6d89e7" +# Pinned to a specific commit (2026-06-06). +WYCHEPROOF_COMMIT = "6d7cccd0fcb1917368579adeeac10fe802f1b521" WYCHEPROOF_BASE_URL = f"https://raw.githubusercontent.com/C2SP/wycheproof/{WYCHEPROOF_COMMIT}/testvectors_v1" WYCHEPROOF_FILES = [ @@ -53,6 +53,14 @@ def download_wycheproof_files(data_dir): """Download Wycheproof test vector files if not present.""" data_dir = Path(data_dir) data_dir.mkdir(parents=True, exist_ok=True) + commit_file = data_dir / ".wycheproof-commit" + + if ( + not commit_file.exists() + or commit_file.read_text(encoding="utf-8").strip() != WYCHEPROOF_COMMIT + ): + for filename in WYCHEPROOF_FILES: + (data_dir / filename).unlink(missing_ok=True) for filename in WYCHEPROOF_FILES: local_file = data_dir / filename @@ -67,6 +75,7 @@ def download_wycheproof_files(data_dir): print(f"Error downloading {filename}: {e}", file=sys.stderr) local_file.unlink(missing_ok=True) return False + commit_file.write_text(f"{WYCHEPROOF_COMMIT}\n", encoding="utf-8") return True @@ -118,6 +127,13 @@ def run_binary(args_list): return out +def append_optional_rnd(args_list, tc): + rnd = tc.get("rnd") + if rnd is not None: + args_list.append(f"rnd={rnd}") + return args_list + + class TestResult(Enum): OK = auto() SKIPPED = auto() @@ -159,25 +175,31 @@ def run_sign_seed_test(data_file): info(f" tcId={tc['tcId']} ... ", end="") if "Internal" in tc.get("flags", []): out = run_binary( - [ - binary, - "sigGenSeedDeterministic", - f"seed={seed_hex}", - f"message={tc['mu']}", - "context=", - "externalMu=1", - ] + append_optional_rnd( + [ + binary, + "sigGenSeedDeterministic", + f"seed={seed_hex}", + f"message={tc['mu']}", + "context=", + "externalMu=1", + ], + tc, + ) ) else: out = run_binary( - [ - binary, - "sigGenSeedDeterministic", - f"seed={seed_hex}", - f"message={tc['msg']}", - f"context={tc.get('ctx', '')}", - "externalMu=0", - ] + append_optional_rnd( + [ + binary, + "sigGenSeedDeterministic", + f"seed={seed_hex}", + f"message={tc['msg']}", + f"context={tc.get('ctx', '')}", + "externalMu=0", + ], + tc, + ) ) result = check_sign_result(tc, out) info("skipped" if result == TestResult.SKIPPED else "ok") @@ -199,23 +221,29 @@ def run_sign_noseed_test(data_file): info(f" tcId={tc['tcId']} ... ", end="") if "Internal" in tc.get("flags", []): out = run_binary( - [ - binary, - "sigGenInternalDeterministic", - f"message={tc['mu']}", - f"sk={sk_hex}", - "externalMu=1", - ] + append_optional_rnd( + [ + binary, + "sigGenInternalDeterministic", + f"message={tc['mu']}", + f"sk={sk_hex}", + "externalMu=1", + ], + tc, + ) ) else: out = run_binary( - [ - binary, - "sigGenDeterministic", - f"message={tc['msg']}", - f"context={tc.get('ctx', '')}", - f"sk={sk_hex}", - ] + append_optional_rnd( + [ + binary, + "sigGenDeterministic", + f"message={tc['msg']}", + f"context={tc.get('ctx', '')}", + f"sk={sk_hex}", + ], + tc, + ) ) result = check_sign_result(tc, out) info("skipped" if result == TestResult.SKIPPED else "ok") diff --git a/test/wycheproof/wycheproof_mldsa.c b/test/wycheproof/wycheproof_mldsa.c index 0c0a9eb1f..cd3dce5a9 100644 --- a/test/wycheproof/wycheproof_mldsa.c +++ b/test/wycheproof/wycheproof_mldsa.c @@ -8,9 +8,10 @@ * * Usage: * wycheproof_mldsa{lvl} sigGenSeedDeterministic seed=HEX message=HEX - * context=HEX externalMu=0/1 wycheproof_mldsa{lvl} sigGenDeterministic - * message=HEX context=HEX sk=HEX wycheproof_mldsa{lvl} - * sigGenInternalDeterministic message=HEX sk=HEX externalMu=0/1 + * context=HEX externalMu=0/1 [rnd=HEX] wycheproof_mldsa{lvl} + * sigGenDeterministic message=HEX context=HEX sk=HEX [rnd=HEX] + * wycheproof_mldsa{lvl} sigGenInternalDeterministic message=HEX sk=HEX + * externalMu=0/1 [rnd=HEX] * wycheproof_mldsa{lvl} sigVer message=HEX context=HEX signature=HEX pk=HEX * wycheproof_mldsa{lvl} pkFromSk sk=HEX */ @@ -164,7 +165,7 @@ int main(int argc, char *argv[]) size_t mlen, ctxlen, siglen; int externalMu; - if (argc != 6) + if (argc != 6 && argc != 7) { goto usage; } @@ -205,6 +206,12 @@ int main(int argc, char *argv[]) return 0; } + if (argc == 7 && decode_hex("rnd", rnd, sizeof(rnd), argv[6]) != 0) + { + printf("decode_error=1\n"); + return 0; + } + CHECK(crypto_sign_keypair_internal(pk, sk, seed) == 0); if (externalMu) @@ -228,7 +235,7 @@ int main(int argc, char *argv[]) #if !defined(MLD_CONFIG_NO_SIGN_API) if (strcmp(argv[1], "sigGenDeterministic") == 0) { - /* sigGenDeterministic message=HEX context=HEX sk=HEX */ + /* sigGenDeterministic message=HEX context=HEX sk=HEX [rnd=HEX] */ unsigned char message[MAX_MSG_LENGTH]; unsigned char context[MAX_CTX_LENGTH]; unsigned char sk[CRYPTO_SECRETKEYBYTES]; @@ -237,7 +244,7 @@ int main(int argc, char *argv[]) unsigned char rnd[MLDSA_RNDBYTES] = {0}; size_t mlen, ctxlen, siglen; - if (argc != 5) + if (argc != 5 && argc != 6) { goto usage; } @@ -264,6 +271,12 @@ int main(int argc, char *argv[]) return 0; } + if (argc == 6 && decode_hex("rnd", rnd, sizeof(rnd), argv[5]) != 0) + { + printf("decode_error=1\n"); + return 0; + } + pre[0] = 0; /* Safety: Truncation is safe due to the check above. */ pre[1] = (uint8_t)ctxlen; @@ -275,7 +288,8 @@ int main(int argc, char *argv[]) } else if (strcmp(argv[1], "sigGenInternalDeterministic") == 0) { - /* sigGenInternalDeterministic message=HEX sk=HEX externalMu=0/1 */ + /* sigGenInternalDeterministic message=HEX sk=HEX externalMu=0/1 + * [rnd=HEX] */ unsigned char message[MAX_MSG_LENGTH + MAX_CTX_LENGTH + 2]; unsigned char sk[CRYPTO_SECRETKEYBYTES]; unsigned char sig[CRYPTO_BYTES]; @@ -283,7 +297,7 @@ int main(int argc, char *argv[]) size_t mlen, siglen; int externalMu; - if (argc != 5) + if (argc != 5 && argc != 6) { goto usage; } @@ -316,6 +330,12 @@ int main(int argc, char *argv[]) return 0; } + if (argc == 6 && decode_hex("rnd", rnd, sizeof(rnd), argv[5]) != 0) + { + printf("decode_error=1\n"); + return 0; + } + CHECK(crypto_sign_signature_internal(sig, &siglen, message, mlen, NULL, 0, rnd, sk, externalMu) == 0); print_hex("signature", sig, siglen); @@ -406,11 +426,11 @@ int main(int argc, char *argv[]) fprintf(stderr, "Usage:\n" " wycheproof_mldsa{lvl} sigGenSeedDeterministic seed=HEX " - "message=HEX context=HEX externalMu=0/1\n" + "message=HEX context=HEX externalMu=0/1 [rnd=HEX]\n" " wycheproof_mldsa{lvl} sigGenDeterministic message=HEX context=HEX " - "sk=HEX\n" + "sk=HEX [rnd=HEX]\n" " wycheproof_mldsa{lvl} sigGenInternalDeterministic message=HEX " - "sk=HEX externalMu=0/1\n" + "sk=HEX externalMu=0/1 [rnd=HEX]\n" " wycheproof_mldsa{lvl} sigVer message=HEX context=HEX " "signature=HEX pk=HEX\n" " wycheproof_mldsa{lvl} pkFromSk sk=HEX\n");