2025-01-17 18:27:46 +03:00

1240 lines
37 KiB
C

// SPDX-License-Identifier: MIT
#if defined(_WIN32)
#pragma warning(disable : 4244 4293)
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#if defined(_WIN32)
#include <string.h>
#define strcasecmp _stricmp
#else
#include <strings.h>
#endif
#include <time.h>
#include <oqs/oqs.h>
#include "tmp_store.c"
#include "system_info.c"
#if OQS_USE_PTHREADS_IN_TESTS
#include <pthread.h>
static pthread_mutex_t *test_sk_lock = NULL;
static pthread_mutex_t *sk_lock = NULL;
#endif
#ifdef OQS_ENABLE_TEST_CONSTANT_TIME
#include <valgrind/memcheck.h>
#define OQS_TEST_CT_CLASSIFY(addr, len) VALGRIND_MAKE_MEM_UNDEFINED(addr, len)
#define OQS_TEST_CT_DECLASSIFY(addr, len) VALGRIND_MAKE_MEM_DEFINED(addr, len)
#else
#define OQS_TEST_CT_CLASSIFY(addr, len)
#define OQS_TEST_CT_DECLASSIFY(addr, len)
#endif
#ifdef __GNUC__
#define UNUSED __attribute__((unused))
#else
#define UNUSED
#endif
/*
* For stateful signature, we skip key generation because it can takes hours to complete.
* So the ReadHex and and FindMarker serve the purpose of reading pre-generate keypair from KATs.
*/
#define MAX_MARKER_LEN 50
//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int FindMarker(FILE *infile, const char *marker) {
char line[MAX_MARKER_LEN];
unsigned long i, len;
int curr_line;
memset(line, 0, MAX_MARKER_LEN);
len = strlen(marker);
if (len > MAX_MARKER_LEN - 1) {
len = MAX_MARKER_LEN - 1;
}
for (i = 0; i < len; i++) {
curr_line = fgetc(infile);
line[i] = (char)curr_line;
if (curr_line == EOF) {
return 0;
}
}
line[len] = '\0';
while (1) {
if (!strncmp(line, marker, len)) {
return 1;
}
for (i = 0; i < len - 1; i++) {
line[i] = line[i + 1];
}
curr_line = fgetc(infile);
line[len - 1] = (char)curr_line;
if (curr_line == EOF) {
return 0;
}
line[len] = '\0';
}
// shouldn't get here
return 0;
}
//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int ReadHex(FILE *infile, unsigned char *a, unsigned long Length, char *str) {
int ch, started;
unsigned long i;
unsigned char ich;
if (Length == 0) {
a[0] = 0x00;
return 1;
}
memset(a, 0x00, Length);
started = 0;
if (FindMarker(infile, str))
while ((ch = fgetc(infile)) != EOF) {
if (!isxdigit(ch)) {
if (!started) {
if (ch == '\n') {
break;
} else {
continue;
}
} else {
break;
}
}
started = 1;
if ((ch >= '0') && (ch <= '9')) {
ich = (unsigned char)ch - '0';
} else if ((ch >= 'A') && (ch <= 'F')) {
ich = (unsigned char)ch - 'A' + 10;
} else if ((ch >= 'a') && (ch <= 'f')) {
ich = (unsigned char)ch - 'a' + 10;
} else {
// shouldn't ever get here
ich = 0;
}
for (i = 0; i < Length - 1; i++) {
a[i] = (unsigned char) (a[i] << 4) | (unsigned char) (a[i + 1] >> 4);
}
a[Length - 1] = (unsigned char) (a[Length - 1] << 4) | (unsigned char) ich;
} else {
return 0;
}
return 1;
}
/*
* Write stateful secret keys to disk.
*/
static OQS_STATUS save_secret_key(uint8_t *key_buf, size_t buf_len, void *context) {
if (key_buf == NULL || buf_len == 0 || context == NULL) {
return OQS_ERROR;
}
const char *context_char = context;
if (oqs_fstore("sk", context_char, key_buf, buf_len) == OQS_SUCCESS) {
printf("\n================================================================================\n");
printf("Updated STFL SK <%s>.\n", context_char);
printf("================================================================================\n");
return OQS_SUCCESS;
}
return OQS_ERROR;
}
#if OQS_USE_PTHREADS_IN_TESTS
static OQS_SIG_STFL_SECRET_KEY *lock_test_sk = NULL;
static OQS_SIG_STFL *lock_test_sig_obj = NULL;
static uint8_t *lock_test_public_key = NULL;
static char *lock_test_context = NULL;
static uint8_t *signature_1 = NULL;
static uint8_t *signature_2 = NULL;
static size_t signature_len_1;
static size_t signature_len_2;
static uint8_t message_1[] = "The quick brown fox ...";
static uint8_t message_2[] = "The quick brown fox jumped from the tree.";
static OQS_STATUS lock_sk_key(void *mutex) {
if (mutex == NULL) {
return OQS_ERROR;
}
if (pthread_mutex_lock((pthread_mutex_t *)mutex)) {
return OQS_ERROR;
}
return OQS_SUCCESS;
}
static OQS_STATUS unlock_sk_key(void *mutex) {
if (mutex == NULL) {
return OQS_ERROR;
}
if (pthread_mutex_unlock((pthread_mutex_t *)mutex)) {
return OQS_ERROR;
}
return OQS_SUCCESS;
}
#else
static OQS_STATUS lock_sk_key(UNUSED void *mutex) {
return OQS_SUCCESS;
}
static OQS_STATUS unlock_sk_key(UNUSED void *mutex) {
return OQS_SUCCESS;
}
#endif
OQS_STATUS sig_stfl_keypair_from_keygen(OQS_SIG_STFL *sig, uint8_t *public_key, OQS_SIG_STFL_SECRET_KEY *secret_key) {
OQS_STATUS rc;
rc = OQS_SIG_STFL_keypair(sig, public_key, secret_key);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
return OQS_ERROR;
}
return OQS_SUCCESS;
}
OQS_STATUS sig_stfl_keypair_from_KATs(OQS_SIG_STFL *sig, uint8_t *public_key, OQS_SIG_STFL_SECRET_KEY *secret_key, const char *katfile) {
OQS_STATUS ret = OQS_ERROR;
FILE *fp_rsp = NULL;
if ((fp_rsp = fopen(katfile, "r")) == NULL) {
fprintf(stderr, "Couldn't open <%s> for read\n", katfile);
goto err;
}
// Grab the pk and sk from KAT file
if (!ReadHex(fp_rsp, public_key, sig->length_public_key, (char *)"pk = ")) {
fprintf(stderr, "ERROR: unable to read 'pk' from <%s>\n", katfile);
goto err;
}
if (!ReadHex(fp_rsp, secret_key->secret_key_data, sig->length_secret_key, (char *)"sk = ")) {
fprintf(stderr, "ERROR: unable to read 'sk' from <%s>\n", katfile);
goto err;
}
// We are done reading, clean up and exit
ret = OQS_SUCCESS;
goto cleanup;
err:
ret = OQS_ERROR;
cleanup:
if (fp_rsp) {
fclose(fp_rsp);
}
return ret;
}
/*
* We read from KATs these parameters:
* XMSS-SHA2_16_256
* XMSS-SHA2_20_256
* XMSS-SHAKE_16_256
* XMSS-SHAKE_20_256
* XMSSMT-SHA2_40/2_256
* XMSSMT-SHA2_60/3_256
* XMSSMT-SHAKE_40/2_256
* XMSSMT-SHAKE_60/3_256
*/
OQS_STATUS sig_stfl_KATs_keygen(OQS_SIG_STFL *sig, uint8_t *public_key, OQS_SIG_STFL_SECRET_KEY *secret_key, const char *katfile) {
if (sig == NULL || public_key == NULL || secret_key == NULL ) {
return OQS_ERROR;
}
#ifdef OQS_ENABLE_SIG_STFL_XMSS
if (0) {
#ifdef OQS_ENABLE_SIG_STFL_xmss_sha256_h16
} else if (strcmp(sig->method_name, OQS_SIG_STFL_alg_xmss_sha256_h16) == 0) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_sha256_h20
} else if (strcmp(sig->method_name, OQS_SIG_STFL_alg_xmss_sha256_h20) == 0) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_shake128_h16
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_shake128_h16)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_shake128_h20
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_shake128_h20)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_sha512_h16
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_sha512_h16)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_sha512_h20
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_sha512_h20)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_shake256_h16
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_shake256_h16)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_shake256_h20
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_shake256_h20)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_sha256_h16_192
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_sha256_h16_192)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_sha256_h20_192
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_sha256_h20_192)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_shake256_h16_192
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_shake256_h16_192)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_shake256_h20_192
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_shake256_h20_192)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_shake256_h16_256
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_shake256_h16_256)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmss_shake256_h20_256
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmss_shake256_h20_256)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmssmt_sha256_h40_2
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h40_2)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmssmt_sha256_h60_3
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h60_3)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmssmt_shake128_h40_2
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h40_2)) {
goto from_kats;
#endif
#ifdef OQS_ENABLE_SIG_STFL_xmssmt_shake128_h60_3
} else if (0 == strcasecmp(sig->method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h60_3)) {
goto from_kats;
#endif
} else {
goto from_keygen;
}
from_kats:
return sig_stfl_keypair_from_KATs(sig, public_key, secret_key, katfile);
from_keygen:
#endif //OQS_ENABLE_SIG_STFL_XMSS
(void)(katfile);
return sig_stfl_keypair_from_keygen(sig, public_key, secret_key);
}
typedef struct magic_s {
uint8_t val[31];
} magic_t;
static char *convert_method_name_to_file_name(const char *method_name) {
if (method_name == NULL) {
return NULL;
}
const char *file_store = NULL;
if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h20_2) == 0) {
file_store = "XMSSMT-SHA2_20-2_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h20_4) == 0) {
file_store = "XMSSMT-SHA2_20-4_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h40_2) == 0) {
file_store = "XMSSMT-SHA2_40-2_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h40_4) == 0) {
file_store = "XMSSMT-SHA2_40-4_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h40_8) == 0) {
file_store = "XMSSMT-SHA2_40-8_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h60_3) == 0) {
file_store = "XMSSMT-SHA2_60-3_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h60_6) == 0) {
file_store = "XMSSMT-SHA2_60-6_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_sha256_h60_12) == 0) {
file_store = "XMSSMT-SHA2_60-12_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h20_2) == 0) {
file_store = "XMSSMT-SHAKE_20-2_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h20_4) == 0) {
file_store = "XMSSMT-SHAKE_20-4_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h40_2) == 0) {
file_store = "XMSSMT-SHAKE_40-2_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h40_4) == 0) {
file_store = "XMSSMT-SHAKE_40-4_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h40_8) == 0) {
file_store = "XMSSMT-SHAKE_40-8_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h60_3) == 0) {
file_store = "XMSSMT-SHAKE_60-3_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h60_6) == 0) {
file_store = "XMSSMT-SHAKE_60-6_256";
} else if (strcmp(method_name, OQS_SIG_STFL_alg_xmssmt_shake128_h60_12) == 0) {
file_store = "XMSSMT-SHAKE_60-12_256";
} else {
file_store = method_name;
}
return strdup(file_store);
}
static OQS_STATUS sig_stfl_test_correctness(const char *method_name, const char *katfile) {
OQS_SIG_STFL *sig = NULL;
uint8_t *public_key = NULL;
OQS_SIG_STFL_SECRET_KEY *secret_key = NULL;
OQS_SIG_STFL_SECRET_KEY *secret_key_rd = NULL;
uint8_t *message = NULL;
size_t message_len = 100;
uint8_t *signature = NULL;
size_t signature_len;
uint8_t *sk_buf = NULL;
uint8_t *read_pk_buf = NULL;
char *context = NULL;
char *file_store = NULL;
size_t sk_buf_len = 0;
size_t read_pk_len = 0;
magic_t magic;
OQS_STATUS rc, ret = OQS_ERROR;
//The magic numbers are random values.
//The length of the magic number was chosen to be 31 to break alignment
OQS_randombytes(magic.val, sizeof(magic_t));
sig = OQS_SIG_STFL_new(method_name);
if (sig == NULL) {
fprintf(stderr, "ERROR: OQS_SIG_STFL_new failed\n");
goto err;
}
printf("================================================================================\n");
printf("Sample computation for stateful signature %s\n", sig->method_name);
printf("================================================================================\n");
secret_key = OQS_SIG_STFL_SECRET_KEY_new(sig->method_name);
secret_key_rd = OQS_SIG_STFL_SECRET_KEY_new(sig->method_name);
OQS_SIG_STFL_SECRET_KEY_SET_lock(secret_key, lock_sk_key);
OQS_SIG_STFL_SECRET_KEY_SET_unlock(secret_key, unlock_sk_key);
file_store = convert_method_name_to_file_name(sig->method_name);
if (file_store == NULL) {
fprintf(stderr, "%s: file_store is null\n", __func__);
goto err;
}
/* set context and secure store callback */
context = strdup(((file_store)));
OQS_SIG_STFL_SECRET_KEY_SET_store_cb(secret_key, save_secret_key, (void *)context);
#if OQS_USE_PTHREADS_IN_TESTS
OQS_SIG_STFL_SECRET_KEY_SET_mutex(secret_key, sk_lock);
#endif
public_key = OQS_MEM_malloc(sig->length_public_key + 2 * sizeof(magic_t));
message = OQS_MEM_malloc(message_len + 2 * sizeof(magic_t));
signature = OQS_MEM_malloc(sig->length_signature + 2 * sizeof(magic_t));
if ((public_key == NULL) || (secret_key == NULL) || (message == NULL) || (signature == NULL)) {
fprintf(stderr, "ERROR: OQS_MEM_malloc failed\n");
goto err;
}
//Set the magic numbers before
memcpy(public_key, magic.val, sizeof(magic_t));
memcpy(message, magic.val, sizeof(magic_t));
memcpy(signature, magic.val, sizeof(magic_t));
public_key += sizeof(magic_t);
message += sizeof(magic_t);
signature += sizeof(magic_t);
// and after
memcpy(public_key + sig->length_public_key, magic.val, sizeof(magic_t));
memcpy(message + message_len, magic.val, sizeof(magic_t));
memcpy(signature + sig->length_signature, magic.val, sizeof(magic_t));
OQS_randombytes(message, message_len);
OQS_TEST_CT_DECLASSIFY(message, message_len);
/*
* Some keypair generation is fast, so we only read keypair from KATs for slow XMSS parameters
*/
rc = sig_stfl_KATs_keygen(sig, public_key, secret_key, katfile);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: OQS_SIG_STFL_keypair failed\n");
goto err;
}
rc = OQS_SIG_STFL_SECRET_KEY_serialize(&sk_buf, &sk_buf_len, secret_key);
if (rc != OQS_SUCCESS) {
goto err;
}
/* write key pair to disk */
if (oqs_fstore("sk", file_store, sk_buf, sk_buf_len) != OQS_SUCCESS) {
goto err;
}
if (oqs_fstore("pk", file_store, public_key, sig->length_public_key) != OQS_SUCCESS) {
goto err;
}
rc = OQS_SIG_STFL_sign(sig, signature, &signature_len, message, message_len, secret_key);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: OQS_SIG_STFL_sign failed\n");
goto err;
}
OQS_TEST_CT_DECLASSIFY(public_key, sig->length_public_key);
OQS_TEST_CT_DECLASSIFY(signature, signature_len);
rc = OQS_SIG_STFL_verify(sig, message, message_len, signature, signature_len, public_key);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: OQS_SIG_STFL_verify failed\n");
goto err;
}
/* Read public key and re-test verify.*/
read_pk_buf = OQS_MEM_malloc(sig->length_public_key);
if (oqs_fload("pk", file_store, read_pk_buf, sig->length_public_key, &read_pk_len) != OQS_SUCCESS) {
goto err;
}
rc = OQS_SIG_STFL_verify(sig, message, message_len, signature, signature_len, read_pk_buf);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: 2nd Verify with restored public key OQS_SIG_STFL_verify failed\n");
}
/* modify the signature to invalidate it */
OQS_randombytes(signature, signature_len);
OQS_TEST_CT_DECLASSIFY(signature, signature_len);
rc = OQS_SIG_STFL_verify(sig, message, message_len, signature, signature_len, public_key);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_ERROR) {
fprintf(stderr, "ERROR: OQS_SIG_STFL_verify should have failed!\n");
goto err;
}
#ifndef OQS_ENABLE_TEST_CONSTANT_TIME
/* check magic values */
int rv = memcmp(public_key + sig->length_public_key, magic.val, sizeof(magic_t));
rv |= memcmp(message + message_len, magic.val, sizeof(magic_t));
rv |= memcmp(signature + sig->length_signature, magic.val, sizeof(magic_t));
rv |= memcmp(public_key - sizeof(magic_t), magic.val, sizeof(magic_t));
rv |= memcmp(message - sizeof(magic_t), magic.val, sizeof(magic_t));
rv |= memcmp(signature - sizeof(magic_t), magic.val, sizeof(magic_t));
if (rv) {
fprintf(stderr, "ERROR: Magic numbers do not match\n");
goto err;
}
#endif
ret = OQS_SUCCESS;
goto cleanup;
err:
ret = OQS_ERROR;
cleanup:
OQS_SIG_STFL_SECRET_KEY_free(secret_key);
OQS_SIG_STFL_SECRET_KEY_free(secret_key_rd);
if (public_key) {
OQS_MEM_insecure_free(public_key - sizeof(magic_t));
}
if (message) {
OQS_MEM_insecure_free(message - sizeof(magic_t));
}
if (signature) {
OQS_MEM_insecure_free(signature - sizeof(magic_t));
}
OQS_MEM_secure_free(sk_buf, sk_buf_len);
OQS_SIG_STFL_free(sig);
OQS_MEM_insecure_free(read_pk_buf);
OQS_MEM_insecure_free(context);
OQS_MEM_insecure_free(file_store);
return ret;
}
static OQS_STATUS sig_stfl_test_secret_key(const char *method_name, const char *katfile) {
OQS_STATUS rc = OQS_SUCCESS;
OQS_SIG_STFL_SECRET_KEY *sk = NULL;
OQS_SIG_STFL_SECRET_KEY *sk_from_file = NULL;
unsigned long long num_sig_left = 0, max_num_sigs = 0;
OQS_SIG_STFL *sig_obj = NULL;
uint8_t *public_key = NULL;
uint8_t *from_file_sk_buf = NULL;
uint8_t *to_file_sk_buf = NULL;
size_t from_file_sk_len = 0;
size_t to_file_sk_len = 0;
char *context = NULL;
char *context_2 = NULL;
char *file_store_name = NULL;
/*
* Temporarily skip algs with long key generation times.
*/
printf("================================================================================\n");
printf("Create stateful Signature %s\n", method_name);
printf("================================================================================\n");
sig_obj = OQS_SIG_STFL_new(method_name);
if (sig_obj == NULL) {
fprintf(stderr, "ERROR: OQS_SIG_STFL_new failed\n");
goto err;
}
public_key = OQS_MEM_malloc(sig_obj->length_public_key * sizeof(uint8_t));
printf("================================================================================\n");
printf("Create stateful Secret Key %s\n", method_name);
printf("================================================================================\n");
sk = OQS_SIG_STFL_SECRET_KEY_new(method_name);
if (sk == NULL) {
fprintf(stderr, "ERROR: OQS_SECRET_KEY_new failed\n");
goto err;
}
printf("================================================================================\n");
printf("Generate keypair %s\n", method_name);
printf("================================================================================\n");
rc = sig_stfl_KATs_keygen(sig_obj, public_key, sk, katfile);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key gen failed.\n");
goto err;
}
/*
* Get max num signature and the amount remaining
*/
rc = OQS_SIG_STFL_sigs_total((const OQS_SIG_STFL *)sig_obj, &max_num_sigs, (const OQS_SIG_STFL_SECRET_KEY *)sk);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key: Failed to get max number of sig from %s.\n", method_name);
goto err;
}
rc = OQS_SIG_STFL_sigs_remaining((const OQS_SIG_STFL *)sig_obj, &num_sig_left, (const OQS_SIG_STFL_SECRET_KEY *)sk);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key: Failed to get the remaining number of sig from %s.\n", method_name);
goto err;
}
/* write sk key to disk */
rc = OQS_SIG_STFL_SECRET_KEY_serialize(&to_file_sk_buf, &to_file_sk_len, sk);
if (rc != OQS_SUCCESS) {
goto err;
}
file_store_name = convert_method_name_to_file_name(sig_obj->method_name);
if (oqs_fstore("sk", file_store_name, to_file_sk_buf, to_file_sk_len) != OQS_SUCCESS) {
goto err;
}
if (sk->secret_key_data == NULL) {
fprintf(stderr, "ERROR: OQS_SECRET_KEY_new incomplete.\n");
goto err;
}
/* set context and secure store callback */
if (sk->set_scrt_key_store_cb != NULL) {
context = strdup(file_store_name);
sk->set_scrt_key_store_cb(sk, save_secret_key, (void *)context);
}
/* read secret key from disk */
from_file_sk_buf = OQS_MEM_malloc(to_file_sk_len);
if (oqs_fload("sk", file_store_name, from_file_sk_buf, to_file_sk_len, &from_file_sk_len) != OQS_SUCCESS) {
goto err;
}
if (to_file_sk_len != from_file_sk_len) {
fprintf(stderr, "ERROR: OQS_SECRET_KEY_new stored length not equal read length\n");
goto err;
}
sk_from_file = OQS_SIG_STFL_SECRET_KEY_new(method_name);
if (sk_from_file == NULL) {
fprintf(stderr, "ERROR: 2nd OQS_SECRET_KEY_new failed\n");
goto err;
}
context_2 = strdup(file_store_name);
rc = OQS_SIG_STFL_SECRET_KEY_deserialize(sk_from_file, from_file_sk_buf, from_file_sk_len, (void *)context_2);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS restore %s from file failed.\n", method_name);
goto err;
}
rc = OQS_SUCCESS;
goto cleanup;
err:
rc = OQS_ERROR;
cleanup:
OQS_SIG_STFL_SECRET_KEY_free(sk);
OQS_SIG_STFL_SECRET_KEY_free(sk_from_file);
OQS_MEM_insecure_free(public_key);
OQS_MEM_secure_free(to_file_sk_buf, to_file_sk_len);
OQS_MEM_secure_free(from_file_sk_buf, from_file_sk_len);
OQS_SIG_STFL_free(sig_obj);
OQS_MEM_insecure_free(context);
OQS_MEM_insecure_free(context_2);
OQS_MEM_insecure_free(file_store_name);
return rc;
}
#ifdef OQS_ENABLE_TEST_CONSTANT_TIME
static void TEST_SIG_STFL_randombytes(uint8_t *random_array, size_t bytes_to_read) {
// We can't make direct calls to the system randombytes on some platforms,
// so we have to swap out the OQS_randombytes provider.
OQS_randombytes_switch_algorithm("system");
OQS_randombytes(random_array, bytes_to_read);
OQS_randombytes_custom_algorithm(&TEST_SIG_STFL_randombytes);
// OQS_TEST_CT_CLASSIFY tells Valgrind's memcheck tool to issue a warning if
// the program branches on any byte that depends on random_array. This helps us
// identify timing side-channels, as these bytes often contain secret data.
OQS_TEST_CT_CLASSIFY(random_array, bytes_to_read);
}
#endif
#if OQS_USE_PTHREADS_IN_TESTS
static OQS_STATUS sig_stfl_test_query_key(const char *method_name) {
OQS_STATUS rc = OQS_SUCCESS;
size_t message_len_1 = sizeof(message_1);
size_t message_len_2 = sizeof(message_2);
/*
* Temporarily skip algs with long key generation times.
*/
printf("================================================================================\n");
printf("Testing stateful Signature Verification %s\n", method_name);
printf("================================================================================\n");
if ( lock_test_sk == NULL || lock_test_sig_obj == NULL ||
signature_1 == NULL || signature_2 == NULL ||
lock_test_public_key == NULL) {
return OQS_ERROR;
}
printf("================================================================================\n");
printf("Sig Verify 1 %s\n", method_name);
printf("================================================================================\n");
rc = OQS_SIG_STFL_verify(lock_test_sig_obj, message_1, message_len_1, signature_1, signature_len_1, lock_test_public_key);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: lock thread test OQS_SIG_STFL_verify failed\n");
goto err;
}
printf("================================================================================\n");
printf("Sig Verify 2 %s\n", method_name);
printf("================================================================================\n");
rc = OQS_SIG_STFL_verify(lock_test_sig_obj, message_2, message_len_2, signature_2, signature_len_2, lock_test_public_key);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: lock thread test OQS_SIG_STFL_verify failed\n");
goto err;
}
printf("================================================================================\n");
printf("Stateful Signature Verification %s Passed.\n", method_name);
printf("================================================================================\n");
return OQS_SUCCESS;
err:
return OQS_ERROR;
}
static OQS_STATUS sig_stfl_test_sig_gen(const char *method_name) {
OQS_STATUS rc = OQS_SUCCESS;
size_t message_len_1 = sizeof(message_1);
size_t message_len_2 = sizeof(message_2);
char *context = NULL;
char *key_store_name = NULL;
/*
* Temporarily skip algs with long key generation times.
*/
printf("================================================================================\n");
printf("Testing stateful Signature Generation %s\n", method_name);
printf("================================================================================\n");
if ( lock_test_sk == NULL || lock_test_sig_obj == NULL) {
return OQS_ERROR;
}
key_store_name = convert_method_name_to_file_name(method_name);
/* set context and secure store callback */
context = strdup(((key_store_name)));
OQS_SIG_STFL_SECRET_KEY_SET_store_cb(lock_test_sk, save_secret_key, (void *)context);
/*
* Get max num signature and the amount remaining
*/
unsigned long long num_sig_left = 0, max_num_sigs = 0;
rc = OQS_SIG_STFL_sigs_total((const OQS_SIG_STFL *)lock_test_sig_obj, &max_num_sigs, (const OQS_SIG_STFL_SECRET_KEY *)lock_test_sk);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key: Failed to get max number of sig from %s.\n", method_name);
goto err;
}
rc = OQS_SIG_STFL_sigs_remaining((const OQS_SIG_STFL *)lock_test_sig_obj, &num_sig_left, (const OQS_SIG_STFL_SECRET_KEY *)lock_test_sk);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key: Failed to get the remaining number of sig from %s.\n", method_name);
goto err;
}
printf("================================================================================\n");
printf("Sig Gen 1 %s\n", method_name);
printf("================================================================================\n");
signature_1 = OQS_MEM_malloc(lock_test_sig_obj->length_signature);
rc = OQS_SIG_STFL_sign(lock_test_sig_obj, signature_1, &signature_len_1, message_1, message_len_1, lock_test_sk);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: lock thread test OQS_SIG_STFL_sign failed\n");
goto err;
}
/*
* Get max num signature and the amount remaining
*/
num_sig_left = 0, max_num_sigs = 0;
rc = OQS_SIG_STFL_sigs_total((const OQS_SIG_STFL *)lock_test_sig_obj, &max_num_sigs, (const OQS_SIG_STFL_SECRET_KEY *)lock_test_sk);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key: Failed to get max number of sig from %s.\n", method_name);
goto err;
}
rc = OQS_SIG_STFL_sigs_remaining((const OQS_SIG_STFL *)lock_test_sig_obj, &num_sig_left, (const OQS_SIG_STFL_SECRET_KEY *)lock_test_sk);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key: Failed to get the remaining number of sig from %s.\n", method_name);
goto err;
}
printf("================================================================================\n");
printf("Sig Gen 2 %s\n", method_name);
printf("================================================================================\n");
signature_2 = OQS_MEM_malloc(lock_test_sig_obj->length_signature);
rc = OQS_SIG_STFL_sign(lock_test_sig_obj, signature_2, &signature_len_2, message_2, message_len_2, lock_test_sk);
OQS_TEST_CT_DECLASSIFY(&rc, sizeof rc);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "ERROR: lock thread test OQS_SIG_STFL_sign failed\n");
goto err;
}
printf("================================================================================\n");
printf("Stateful Key Gen %s Passed.\n", method_name);
printf("================================================================================\n");
/*
* Get max num signature and the amount remaining
*/
num_sig_left = 0, max_num_sigs = 0;
rc = OQS_SIG_STFL_sigs_total((const OQS_SIG_STFL *)lock_test_sig_obj, &max_num_sigs, (const OQS_SIG_STFL_SECRET_KEY *)lock_test_sk);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key: Failed to get max number of sig from %s.\n", method_name);
goto err;
}
rc = OQS_SIG_STFL_sigs_remaining((const OQS_SIG_STFL *)lock_test_sig_obj, &num_sig_left, (const OQS_SIG_STFL_SECRET_KEY *)lock_test_sk);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key: Failed to get the remaining number of sig from %s.\n", method_name);
goto err;
}
rc = OQS_SUCCESS;
goto cleanup;
err:
rc = OQS_ERROR;
cleanup:
OQS_MEM_insecure_free(context);
OQS_MEM_insecure_free(key_store_name);
return rc;
}
static OQS_STATUS sig_stfl_test_secret_key_lock(const char *method_name, const char *katfile) {
OQS_STATUS rc = OQS_SUCCESS;
printf("================================================================================\n");
printf("Testing stateful Signature locks %s\n", method_name);
printf("================================================================================\n");
/*
* Temporarily skip algs with long key generation times.
*/
printf("================================================================================\n");
printf("Create stateful Signature %s\n", method_name);
printf("================================================================================\n");
lock_test_sig_obj = OQS_SIG_STFL_new(method_name);
if (lock_test_sig_obj == NULL) {
fprintf(stderr, "ERROR: OQS_SIG_STFL_new failed\n");
goto err;
}
lock_test_public_key = OQS_MEM_malloc(lock_test_sig_obj->length_public_key * sizeof(uint8_t));
printf("================================================================================\n");
printf("Create stateful Secret Key %s\n", method_name);
printf("================================================================================\n");
lock_test_sk = OQS_SIG_STFL_SECRET_KEY_new(method_name);
if (lock_test_sk == NULL) {
fprintf(stderr, "ERROR: OQS_SECRET_KEY_new failed\n");
goto err;
}
OQS_SIG_STFL_SECRET_KEY_SET_lock(lock_test_sk, lock_sk_key);
OQS_SIG_STFL_SECRET_KEY_SET_unlock(lock_test_sk, unlock_sk_key);
OQS_SIG_STFL_SECRET_KEY_SET_mutex(lock_test_sk, test_sk_lock);
printf("================================================================================\n");
printf("Generate keypair %s\n", method_name);
printf("================================================================================\n");
rc = sig_stfl_KATs_keygen(lock_test_sig_obj, lock_test_public_key, lock_test_sk, katfile);
if (rc != OQS_SUCCESS) {
fprintf(stderr, "OQS STFL key gen failed.\n");
goto err;
}
if (lock_test_sk->secret_key_data == NULL) {
fprintf(stderr, "ERROR: OQS_SECRET_KEY_new incomplete.\n");
goto err;
}
/* set context and secure store callback */
if (lock_test_sk->set_scrt_key_store_cb) {
lock_test_context = convert_method_name_to_file_name(method_name);
lock_test_sk->set_scrt_key_store_cb(lock_test_sk, save_secret_key, (void *)lock_test_context);
}
return OQS_SUCCESS;
err:
return OQS_ERROR;
}
typedef struct thread_data {
const char *alg_name;
const char *katfile;
OQS_STATUS rc;
// OQS_STATUS rc1;
} thread_data_t;
typedef struct lock_test_data {
const char *alg_name;
const char *katfile;
OQS_STATUS rc;
} lock_test_data_t;
void *test_query_key(void *arg) {
struct lock_test_data *td = arg;
printf("\n%s: Start Query Stateful Key info\n", __func__);
td->rc = sig_stfl_test_query_key(td->alg_name);
OQS_thread_stop();
printf("%s: End Query Stateful Key info\n\n", __func__);
return NULL;
}
void *test_sig_gen(void *arg) {
struct lock_test_data *td = arg;
printf("\n%s: Start Generate Stateful Signature\n", __func__);
td->rc = sig_stfl_test_sig_gen(td->alg_name);
OQS_thread_stop();
printf("%s: End Generate Stateful Signature\n\n", __func__);
return NULL;
}
void *test_create_keys(void *arg) {
struct lock_test_data *td = arg;
printf("\n%s: Start Generate Keys\n", __func__);
td->rc = sig_stfl_test_secret_key_lock(td->alg_name, td->katfile);
OQS_thread_stop();
printf("%s: End Generate Stateful Keys\n\n", __func__);
return NULL;
}
void *test_correctness_wrapper(void *arg) {
struct thread_data *td = arg;
td->rc = sig_stfl_test_correctness(td->alg_name, td->katfile);
OQS_thread_stop();
return NULL;
}
void *test_secret_key_wrapper(void *arg) {
struct thread_data *td = arg;
td->rc = sig_stfl_test_secret_key(td->alg_name, td->katfile);
OQS_thread_stop();
return NULL;
}
#endif
/*
* When key and signature generation is off
* these operations should fail. So flip the results.
*/
static OQS_STATUS update_test_result( OQS_STATUS rc, int xmss_or_lms) {
OQS_STATUS rc_update = rc;
if (xmss_or_lms) {
;
#ifndef OQS_ALLOW_XMSS_KEY_AND_SIG_GEN
if (rc_update == OQS_ERROR) {
rc_update = OQS_SUCCESS;
} else {
rc_update = OQS_ERROR;
}
#endif
} else {
;
#ifndef OQS_ALLOW_LMS_KEY_AND_SIG_GEN
if (rc_update == OQS_ERROR) {
rc_update = OQS_SUCCESS;
} else {
rc_update = OQS_ERROR;
}
#endif
}
return rc_update;
}
int main(int argc, char **argv) {
OQS_init();
oqs_fstore_init();
printf("Testing stateful signature algorithms using liboqs version %s\n", OQS_version());
if (argc < 2) {
fprintf(stderr, "Usage: test_sig_stfl algname [katfile]\n");
fprintf(stderr, " algname: ");
for (size_t i = 0; i < OQS_SIG_STFL_algs_length; i++) {
if (i > 0) {
fprintf(stderr, ", ");
}
fprintf(stderr, "%s", OQS_SIG_STFL_alg_identifier(i));
}
fprintf(stderr, "\n");
OQS_destroy();
return EXIT_FAILURE;
}
print_system_info();
const char *alg_name = argv[1];
const char *katfile = argv[2];
int is_xmss = 0;
if (strstr(alg_name, "XMSS") != NULL) {
is_xmss = 1;
if (argc < 3) {
fprintf(stderr, "KAT file must be provided for XMSS.\n");
OQS_destroy();
return EXIT_FAILURE;
}
}
/*
* Tests executed by CI/DI only run algoritms that have been emabled.
*
*/
if (!OQS_SIG_STFL_alg_is_enabled(alg_name)) {
printf("Stateful signature algorithm %s not enabled!\n", alg_name);
OQS_destroy();
if (is_xmss) {
#ifndef OQS_ENABLE_SIG_STFL_XMSS
return EXIT_SUCCESS;
#else
return EXIT_FAILURE;
#endif
} else {
#ifndef OQS_ENABLE_SIG_STFL_LMS
return EXIT_SUCCESS;
#else
return EXIT_FAILURE;
#endif
}
}
#ifdef OQS_ENABLE_TEST_CONSTANT_TIME
OQS_randombytes_custom_algorithm(&TEST_SIG_STFL_randombytes);
#else
OQS_randombytes_switch_algorithm("system");
#endif
OQS_STATUS rc = OQS_ERROR, rc1 = OQS_ERROR;
int exit_status = EXIT_SUCCESS;
#if OQS_USE_PTHREADS_IN_TESTS
#define MAX_LEN_SIG_NAME_ 64
OQS_STATUS rc_create = OQS_ERROR, rc_sign = OQS_ERROR, rc_query = OQS_ERROR;
pthread_t thread;
pthread_t create_key_thread;
pthread_t sign_key_thread;
pthread_t query_key_thread;
thread_data_t td = {.alg_name = alg_name, .katfile = katfile, .rc = OQS_ERROR};
thread_data_t td_2 = {.alg_name = alg_name, .katfile = katfile, .rc = OQS_ERROR};
lock_test_data_t td_create = {.alg_name = alg_name, .katfile = katfile, .rc = OQS_ERROR};
lock_test_data_t td_sign = {.alg_name = alg_name, .katfile = katfile, .rc = OQS_ERROR};
lock_test_data_t td_query = {.alg_name = alg_name, .katfile = katfile, .rc = OQS_ERROR};
test_sk_lock = (pthread_mutex_t *)OQS_MEM_malloc(sizeof(pthread_mutex_t));
if (test_sk_lock == NULL) {
goto err;
}
sk_lock = (pthread_mutex_t *)OQS_MEM_malloc(sizeof(pthread_mutex_t));
if (sk_lock == NULL) {
goto err;
}
if (pthread_mutex_init(test_sk_lock, NULL) || pthread_mutex_init(sk_lock, NULL)) {
fprintf(stderr, "ERROR: Initializing mutex\n");
exit_status = EXIT_FAILURE;
goto err;
}
if (pthread_create(&thread, NULL, test_correctness_wrapper, &td)) {
fprintf(stderr, "ERROR: Creating pthread for test_wrapper\n");
exit_status = EXIT_FAILURE;
goto err;
}
pthread_join(thread, NULL);
rc = td.rc;
rc = update_test_result(rc, is_xmss);
if (pthread_create(&thread, NULL, test_secret_key_wrapper, &td_2)) {
fprintf(stderr, "ERROR: Creating pthread for test_wrapper_2\n");
exit_status = EXIT_FAILURE;
goto err;
}
pthread_join(thread, NULL);
rc1 = td_2.rc;
rc1 = update_test_result(rc1, is_xmss);
if (pthread_create(&create_key_thread, NULL, test_create_keys, &td_create)) {
fprintf(stderr, "ERROR: Creating pthread for test_create_keys\n");
exit_status = EXIT_FAILURE;
goto err;
}
pthread_join(create_key_thread, NULL);
rc_create = td_create.rc;
rc_create = update_test_result(rc_create, is_xmss);
if (pthread_create(&sign_key_thread, NULL, test_sig_gen, &td_sign)) {
fprintf(stderr, "ERROR: Creating pthread for test_sig_gen\n");
exit_status = EXIT_FAILURE;
goto err;
}
pthread_join(sign_key_thread, NULL);
rc_sign = td_sign.rc;
rc_sign = update_test_result(rc_sign, is_xmss);
if (pthread_create(&query_key_thread, NULL, test_query_key, &td_query)) {
fprintf(stderr, "ERROR: Creating pthread for test_query_key\n");
exit_status = EXIT_FAILURE;
goto err;
}
pthread_join(query_key_thread, NULL);
rc_query = td_query.rc;
rc_query = update_test_result(rc_query, is_xmss);
err:
if (test_sk_lock) {
pthread_mutex_destroy(test_sk_lock);
}
if (sk_lock) {
pthread_mutex_destroy(sk_lock);
}
OQS_SIG_STFL_SECRET_KEY_free(lock_test_sk);
OQS_MEM_insecure_free(lock_test_public_key);
OQS_SIG_STFL_free(lock_test_sig_obj);
OQS_MEM_insecure_free(lock_test_context);
OQS_MEM_insecure_free(signature_1);
OQS_MEM_insecure_free(signature_2);
OQS_destroy();
if (rc != OQS_SUCCESS || rc1 != OQS_SUCCESS) {
return EXIT_FAILURE;
}
#if OQS_USE_PTHREADS_IN_TESTS
if (rc_create != OQS_SUCCESS || rc_sign != OQS_SUCCESS || rc_query != OQS_SUCCESS) {
return EXIT_FAILURE;
}
#endif
return exit_status;
#else
rc = sig_stfl_test_correctness(alg_name, katfile);
rc1 = sig_stfl_test_secret_key(alg_name, katfile);
OQS_destroy();
rc = update_test_result(rc, is_xmss);
rc1 = update_test_result(rc1, is_xmss);
if (rc != OQS_SUCCESS || rc1 != OQS_SUCCESS) {
return EXIT_FAILURE;
}
return exit_status;
#endif
}