Skip to content

Commit 9fe9fe9

Browse files
test(sig): fuzz and KAT-validate derandomized keypair generation
- fuzz_test_sig: exercise OQS_SIG_keypair_derand under libFuzzer/ASAN - vectors_sig: cross-check derand keygen against FIPS-204 ACVP keyGen known answers (the ACVP seed is xi), proving KeyGen_internal(xi) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b5754e7 commit 9fe9fe9

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

tests/fuzz_test_sig.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,16 @@ static OQS_STATUS fuzz_sig(const uint8_t *data, size_t data_len) {
107107
exit(1);
108108
}
109109

110+
/* Exercise derandomized keypair generation under the fuzzer/ASAN, where supported. */
111+
if (sig->keypair_derand != NULL && ctx.data_len >= sig->length_keypair_seed) {
112+
rc = OQS_SIG_keypair_derand(sig, public_key, secret_key, ctx.data);
113+
if (rc != OQS_SUCCESS) {
114+
fprintf(stderr, "ERROR: OQS_SIG_keypair_derand failed!\n");
115+
cleanup_heap(public_key, secret_key, signature, sig);
116+
return OQS_ERROR;
117+
}
118+
}
119+
110120
cleanup_heap(public_key, secret_key, signature, sig);
111121
return OQS_SUCCESS; // success
112122
}

tests/vectors_sig.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,8 @@ static OQS_STATUS sig_kg_vector(const char *method_name,
315315
OQS_SIG *sig = NULL;
316316
uint8_t *public_key = NULL;
317317
uint8_t *secret_key = NULL;
318+
uint8_t *derand_public_key = NULL;
319+
uint8_t *derand_secret_key = NULL;
318320
OQS_STATUS rc, ret = OQS_ERROR;
319321

320322
void (*randombytes_init)(const uint8_t *, const uint8_t *) = NULL;
@@ -370,6 +372,34 @@ static OQS_STATUS sig_kg_vector(const char *method_name,
370372
} else {
371373
ret = OQS_ERROR;
372374
fprintf(stderr, "[vectors_sig] %s ERROR: public key or private key doesn't match!\n", method_name);
375+
goto cleanup;
376+
}
377+
378+
/* For schemes exposing derandomized keygen (ML-DSA), the ACVP keyGen seed is
379+
* exactly FIPS-204's xi. Feed it straight to OQS_SIG_keypair_derand and require
380+
* the result to match the same known-answer pk/sk, proving the public API really
381+
* computes ML-DSA.KeyGen_internal(xi). */
382+
if (sig->keypair_derand != NULL) {
383+
if (sig->length_keypair_seed != MLDSA_RNDBYTES) {
384+
fprintf(stderr, "[vectors_sig] %s ERROR: unexpected length_keypair_seed %zu for derand keyGen vector!\n", method_name, sig->length_keypair_seed);
385+
goto err;
386+
}
387+
derand_public_key = OQS_MEM_malloc(sig->length_public_key);
388+
derand_secret_key = OQS_MEM_malloc(sig->length_secret_key);
389+
if ((derand_public_key == NULL) || (derand_secret_key == NULL)) {
390+
fprintf(stderr, "[vectors_sig] %s ERROR: OQS_MEM_malloc failed!\n", method_name);
391+
goto err;
392+
}
393+
rc = OQS_SIG_keypair_derand(sig, derand_public_key, derand_secret_key, prng_output_stream);
394+
if (rc) {
395+
fprintf(stderr, "[vectors_sig] %s ERROR: OQS_SIG_keypair_derand failed!\n", method_name);
396+
goto err;
397+
}
398+
if (memcmp(derand_public_key, kg_pk, sig->length_public_key) || memcmp(derand_secret_key, kg_sk, sig->length_secret_key)) {
399+
ret = OQS_ERROR;
400+
fprintf(stderr, "[vectors_sig] %s ERROR: derandomized public key or private key doesn't match!\n", method_name);
401+
goto cleanup;
402+
}
373403
}
374404
goto cleanup;
375405

@@ -383,11 +413,13 @@ static OQS_STATUS sig_kg_vector(const char *method_name,
383413
cleanup:
384414
if (sig != NULL) {
385415
OQS_MEM_secure_free(secret_key, sig->length_secret_key);
416+
OQS_MEM_secure_free(derand_secret_key, sig->length_secret_key);
386417
}
387418
if (randombytes_free != NULL) {
388419
randombytes_free();
389420
}
390421
OQS_MEM_insecure_free(public_key);
422+
OQS_MEM_insecure_free(derand_public_key);
391423
OQS_SIG_free(sig);
392424
return ret;
393425
}

0 commit comments

Comments
 (0)