second-round version of SQIsign

Co-authored-by: Marius A. Aardal <marius.andre.aardal@gmail.com>
Co-authored-by: Gora Adj <gora.adj@tii.ae>
Co-authored-by: Diego F. Aranha <dfaranha@cs.au.dk>
Co-authored-by: Andrea Basso <sqisign@andreabasso.com>
Co-authored-by: Isaac Andrés Canales Martínez <icanalesm0500@gmail.com>
Co-authored-by: Jorge Chávez-Saab <jorgechavezsaab@gmail.com>
Co-authored-by: Maria Corte-Real Santos <mariascrsantos98@gmail.com>
Co-authored-by: Luca De Feo <github@defeo.lu>
Co-authored-by: Max Duparc <max.duparc@epfl.ch>
Co-authored-by: Jonathan Komada Eriksen <jonathan.eriksen97@gmail.com>
Co-authored-by: Décio Luiz Gazzoni Filho <decio@decpp.net>
Co-authored-by: Basil Hess <bhe@zurich.ibm.com>
Co-authored-by: Antonin Leroux <antonin.leroux@polytechnique.org>
Co-authored-by: Patrick Longa <plonga@microsoft.com>
Co-authored-by: Luciano Maino <mainoluciano.96@gmail.com>
Co-authored-by: Michael Meyer <michael@random-oracles.org>
Co-authored-by: Hiroshi Onuki <onuki@mist.i.u-tokyo.ac.jp>
Co-authored-by: Lorenz Panny <lorenz@yx7.cc>
Co-authored-by: Giacomo Pope <giacomopope@gmail.com>
Co-authored-by: Krijn Reijnders <reijnderskrijn@gmail.com>
Co-authored-by: Damien Robert <damien.robert@inria.fr>
Co-authored-by: Francisco Rodríguez-Henriquez <francisco.rodriguez@tii.ae>
Co-authored-by: Sina Schaeffler <sschaeffle@student.ethz.ch>
Co-authored-by: Benjamin Wesolowski <benjamin.wesolowski@ens-lyon.fr>
This commit is contained in:
SQIsign team
2025-02-06 00:00:00 +00:00
committed by Lorenz Panny
parent ff34a8cd18
commit 91e9e464fe
481 changed files with 80785 additions and 55963 deletions

View File

@@ -1,15 +1,58 @@
# NIST KAT generation apps
foreach(SVARIANT ${SVARIANT_S})
string(TOLOWER ${SVARIANT} SVARIANT_LOWER)
add_executable(PQCgenKAT_sign_${SVARIANT_LOWER} PQCgenKAT_sign.c)
target_link_libraries(PQCgenKAT_sign_${SVARIANT_LOWER} PRIVATE sqisign_${SVARIANT_LOWER}_test_nistapi)
target_include_directories(PQCgenKAT_sign_${SVARIANT_LOWER} PRIVATE ../include)
endforeach()
if (ENABLE_SIGN)
# Examples with NIST API
# NIST KAT generation apps
foreach(SVARIANT ${SVARIANT_S})
string(TOLOWER ${SVARIANT} SVARIANT_LOWER)
add_executable(PQCgenKAT_sign_${SVARIANT_LOWER} PQCgenKAT_sign.c)
target_link_libraries(PQCgenKAT_sign_${SVARIANT_LOWER} PRIVATE sqisign_${SVARIANT_LOWER}_test_nistapi)
target_include_directories(PQCgenKAT_sign_${SVARIANT_LOWER} PRIVATE ../include)
target_compile_definitions(PQCgenKAT_sign_${SVARIANT_LOWER} PUBLIC SQISIGN_VARIANT=${SVARIANT_LOWER})
endforeach()
# pqm4 KAT generation apps
foreach(SVARIANT ${SVARIANT_S})
string(TOLOWER ${SVARIANT} SVARIANT_LOWER)
add_executable(PQCgenKAT_sign_pqm4_${SVARIANT_LOWER} PQCgenKAT_sign_pqm4.c)
target_link_libraries(PQCgenKAT_sign_pqm4_${SVARIANT_LOWER} PRIVATE sqisign_${SVARIANT_LOWER}_test_nistapi)
target_include_directories(PQCgenKAT_sign_pqm4_${SVARIANT_LOWER} PRIVATE ../include)
target_compile_definitions(PQCgenKAT_sign_pqm4_${SVARIANT_LOWER} PUBLIC SQISIGN_VARIANT=${SVARIANT_LOWER})
endforeach()
# Examples with NIST API
foreach(SVARIANT ${SVARIANT_S})
string(TOLOWER ${SVARIANT} SVARIANT_LOWER)
add_executable(example_nistapi_${SVARIANT_LOWER} example_nistapi.c)
target_link_libraries(example_nistapi_${SVARIANT_LOWER} PRIVATE sqisign_${SVARIANT_LOWER}_nistapi)
target_include_directories(example_nistapi_${SVARIANT_LOWER} PRIVATE ../include ../src/${SVARIANT_LOWER})
target_compile_definitions(example_nistapi_${SVARIANT_LOWER} PUBLIC SQISIGN_VARIANT=${SVARIANT_LOWER})
add_test(sqisign_test_nistapi_${SVARIANT_LOWER} example_nistapi_${SVARIANT_LOWER})
endforeach()
# Benchmarking tool
foreach(SVARIANT ${SVARIANT_S})
string(TOLOWER ${SVARIANT} SVARIANT_LOWER)
add_executable(benchmark_${SVARIANT_LOWER} benchmark.c)
target_link_libraries(benchmark_${SVARIANT_LOWER} PRIVATE sqisign_${SVARIANT_LOWER}_nistapi)
target_include_directories(benchmark_${SVARIANT_LOWER} PRIVATE ../include ../src/common/generic/include)
target_compile_definitions(benchmark_${SVARIANT_LOWER} PUBLIC SQISIGN_VARIANT=${SVARIANT_LOWER})
endforeach()
# Fuzzing tool -- signature generation
foreach(SVARIANT ${SVARIANT_S})
string(TOLOWER ${SVARIANT} SVARIANT_LOWER)
add_executable(fuzz_sign_${SVARIANT_LOWER} fuzz_sign.c)
target_link_libraries(fuzz_sign_${SVARIANT_LOWER} PRIVATE sqisign_${SVARIANT_LOWER}_nistapi)
target_include_directories(fuzz_sign_${SVARIANT_LOWER} PRIVATE ../include)
target_compile_definitions(fuzz_sign_${SVARIANT_LOWER} PUBLIC SQISIGN_VARIANT=${SVARIANT_LOWER})
endforeach()
endif()
# Fuzzing tool -- signature verification
foreach(SVARIANT ${SVARIANT_S})
string(TOLOWER ${SVARIANT} SVARIANT_LOWER)
add_executable(example_nistapi_${SVARIANT_LOWER} example_nistapi.c)
target_link_libraries(example_nistapi_${SVARIANT_LOWER} PRIVATE sqisign_${SVARIANT_LOWER}_nistapi)
target_include_directories(example_nistapi_${SVARIANT_LOWER} PRIVATE ../include ../src/${SVARIANT_LOWER})
add_executable(fuzz_verify_${SVARIANT_LOWER} fuzz_verify.c)
target_link_libraries(fuzz_verify_${SVARIANT_LOWER} PRIVATE sqisign_${SVARIANT_LOWER}_nistapi)
target_include_directories(fuzz_verify_${SVARIANT_LOWER} PRIVATE ../include ../src/precomp/ref/${SVARIANT_LOWER}/include)
target_compile_definitions(fuzz_verify_${SVARIANT_LOWER} PUBLIC SQISIGN_VARIANT=${SVARIANT_LOWER})
endforeach()

297
apps/PQCgenKAT_sign_pqm4.c Normal file
View File

@@ -0,0 +1,297 @@
// pqm4 KAT generator
// SPDX-License-Identifier: Apache-2.0 and Unknown
/*
NIST-developed software is provided by NIST as a public service. You may use,
copy, and distribute copies of the software in any medium, provided that you
keep intact this entire notice. You may improve, modify, and create derivative
works of the software or any portion of the software, and you may copy and
distribute such modifications or works. Modified works should carry a notice
stating that you changed the software and should note the date and nature of any
such change. Please explicitly acknowledge the National Institute of Standards
and Technology as the source of the software.
NIST-developed software is expressly provided "AS IS." NIST MAKES NO WARRANTY OF
ANY KIND, EXPRESS, IMPLIED, IN FACT, OR ARISING BY OPERATION OF LAW, INCLUDING,
WITHOUT LIMITATION, THE IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, NON-INFRINGEMENT, AND DATA ACCURACY. NIST NEITHER REPRESENTS
NOR WARRANTS THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR
ERROR-FREE, OR THAT ANY DEFECTS WILL BE CORRECTED. NIST DOES NOT WARRANT OR MAKE
ANY REPRESENTATIONS REGARDING THE USE OF THE SOFTWARE OR THE RESULTS THEREOF,
INCLUDING BUT NOT LIMITED TO THE CORRECTNESS, ACCURACY, RELIABILITY, OR
USEFULNESS OF THE SOFTWARE.
You are solely responsible for determining the appropriateness of using and
distributing the software and you assume all risks associated with its use,
including but not limited to the risks and costs of program errors, compliance
with applicable laws, damage to or loss of data, programs or equipment, and the
unavailability or interruption of operation. This software is not intended to be
used in any situation where a failure could cause risk of injury or damage to
property. The software developed by NIST employees is not subject to copyright
protection within the United States.
*/
#include "api.h"
#include "rng.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRINGIFY2(x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define MAX_MARKER_LEN 50
#define KAT_SUCCESS 0
#define KAT_FILE_OPEN_ERROR -1
#define KAT_DATA_ERROR -3
#define KAT_CRYPTO_FAILURE -4
#define NUM_KATS 2
#define MAX_MSG_LEN 59
void output_header(FILE *fp) {
const char header[] =
"// SPDX-License-Identifier: Apache-2.0\n"
"\n"
"#ifndef api_h\n"
"#define api_h\n"
"\n"
"#include <stddef.h>\n"
"#include <sqisign_namespace.h>\n"
"\n"
"#define CRYPTO_SECRETKEYBYTES " STRINGIFY(CRYPTO_SECRETKEYBYTES) "\n"
"#define CRYPTO_PUBLICKEYBYTES " STRINGIFY(CRYPTO_PUBLICKEYBYTES) "\n"
"#define CRYPTO_BYTES " STRINGIFY(CRYPTO_BYTES) "\n"
"\n"
"#define CRYPTO_ALGNAME \"SQIsign_" STRINGIFY(SQISIGN_VARIANT) "\"\n"
"\n"
"SQISIGN_API\n"
"int\n"
"crypto_sign_keypair(unsigned char *pk, unsigned char *sk);\n"
"\n"
"SQISIGN_API\n"
"int\n"
"crypto_sign(unsigned char *sm, size_t *smlen,\n"
" const unsigned char *m, size_t mlen,\n"
" const unsigned char *sk);\n"
"\n"
"SQISIGN_API\n"
"int\n"
"crypto_sign_open(unsigned char *m, size_t *mlen,\n"
" const unsigned char *sm, size_t smlen,\n"
" const unsigned char *pk);\n"
"\n"
"#endif /* api_h */\n";
fputs(header, fp);
}
void output_rng(FILE *fp) {
const char rng[] =
"// SPDX-License-Identifier: Apache-2.0\n"
"\n"
"#ifndef rng_h\n"
"#define rng_h\n"
"\n"
"#include \"randombytes.h\"\n"
"\n"
"#endif /* rng_h */\n";
fputs(rng, fp);
}
void output_preamble(FILE *fp) {
const char preamble[] =
"// SPDX-License-Identifier: Apache-2.0\n"
"\n"
"#include <api.h>\n"
"#include <sig.h>\n"
"#include <string.h>\n"
"\n"
"typedef struct {\n"
" size_t mlen;\n"
" char msg[" STRINGIFY(MAX_MSG_LEN) "];\n"
" size_t smlen;\n"
" char sm[" STRINGIFY(MAX_MSG_LEN) " + CRYPTO_BYTES];\n"
"} SQISign_KAT_t;\n"
"\n";
fputs(preamble, fp);
}
void output_pk(FILE *fp, const unsigned char *pk) {
fprintf(fp, "const char kat_" STRINGIFY(SQISIGN_VARIANT) "_pk[CRYPTO_PUBLICKEYBYTES] = {\n ");
for (int i = 0; i < CRYPTO_PUBLICKEYBYTES; i++) {
fprintf(fp, "0x%02X, ", pk[i]);
}
fprintf(fp, "\n};\n\n");
}
void output_message_signature(FILE *fp, const unsigned char *m, unsigned long long mlen, const unsigned char *sm, unsigned long long smlen) {
fprintf(fp, " {\n"
" .mlen = %llu,\n"
" .msg = { ", mlen);
for (unsigned long long i = 0; i < mlen; i++) {
fprintf(fp, "0x%02X, ", m[i]);
}
fprintf(fp, "},\n"
" .smlen = %llu + CRYPTO_BYTES,\n"
" .sm = { ", mlen);
for (unsigned long long i = 0; i < smlen; i++) {
fprintf(fp, "0x%02X, ", sm[i]);
}
fprintf(fp, "},\n"
" },\n");
}
void output_implementation(FILE *fp) {
const char api[] =
"int crypto_sign_keypair(unsigned char *pk, unsigned char *sk) {\n"
" memcpy(pk, kat_" STRINGIFY(SQISIGN_VARIANT) "_pk, CRYPTO_PUBLICKEYBYTES);\n"
" // We don't need the secret key\n"
" memset(sk, 0, CRYPTO_SECRETKEYBYTES);\n"
"}\n"
"\n"
"int crypto_sign(unsigned char *sm, size_t *smlen, const unsigned char *m,\n"
" size_t mlen, const unsigned char *sk) {\n"
" for (size_t i = 0; i < sizeof(kat_" STRINGIFY(SQISIGN_VARIANT) ") / sizeof(kat_" STRINGIFY(SQISIGN_VARIANT) "[0]); i++) {\n"
" if (mlen == kat_" STRINGIFY(SQISIGN_VARIANT) "[i].mlen) {\n"
" memcpy(sm, kat_" STRINGIFY(SQISIGN_VARIANT) "[i].sm, kat_" STRINGIFY(SQISIGN_VARIANT) "[i].smlen);\n"
" *smlen = kat_" STRINGIFY(SQISIGN_VARIANT) "[i].smlen;\n"
" return 0;\n"
" }\n"
" }\n"
"\n"
" return 1;\n"
"}\n"
"\n"
"int crypto_sign_open(unsigned char *m, size_t *mlen, const unsigned char *sm,\n"
" size_t smlen, const unsigned char *pk) {\n"
" unsigned long long mlen_ull = *mlen;\n"
" int ret = sqisign_open(m, &mlen_ull, sm, smlen, pk);\n"
" if (mlen) {\n"
" *mlen = mlen_ull;\n"
" }\n"
" return ret;\n"
"}\n";
fputs(api, fp);
}
int main(void) {
// pqm4 KATs use only all-zeros messages, one of length 32 and another of length 59;
// arrays whose dimension are the length of the message are declared the size of the
// largest of the two, but for the former, only 32 out of 59 bytes are actually used
unsigned char seed[NUM_KATS][48];
unsigned char entropy_input[48];
const unsigned char m[NUM_KATS][MAX_MSG_LEN] = { { 0 }, { 0 } };
unsigned char sm[NUM_KATS][MAX_MSG_LEN + CRYPTO_BYTES] = { { 0 }, { 0 } };
unsigned char m1[MAX_MSG_LEN];
const unsigned long long mlen[2] = { 32, 59 };
unsigned long long smlen[2], mlen1;
unsigned char pk[CRYPTO_PUBLICKEYBYTES], sk[CRYPTO_SECRETKEYBYTES];
int ret_val;
for (int i = 0; i < 48; i++)
entropy_input[i] = i;
randombytes_init(entropy_input, NULL, 256);
// Generate the keypair, shared between both KATs, as required for pqm4
if ((ret_val = crypto_sign_keypair(pk, sk)) != 0) {
printf("crypto_sign_keypair returned <%d>\n", ret_val);
return KAT_CRYPTO_FAILURE;
}
// Choose two seeds (for the 32-byte and 59-byte KATs)
for (int i = 0; i < NUM_KATS; i++)
randombytes(seed[i], 48);
// Fill m1 with random bytes. Note that the memcmp check below for a valid signature
// compares m1 with m[i], but since the KATs use all-zero messages, the comparison
// may suceed even if m was untouched from a previous iteration. This ensures that
// memcmp will fail in that case.
randombytes(m1, MAX_MSG_LEN);
for (int i = 0; i < NUM_KATS; i++) {
randombytes_init(seed[i], NULL, 256);
if ((ret_val = crypto_sign(sm[i], &smlen[i], m[i], mlen[i], sk)) != 0) {
printf("crypto_sign returned <%d>\n", ret_val);
return KAT_CRYPTO_FAILURE;
}
if ((ret_val = crypto_sign_open(m1, &mlen1, sm[i], smlen[i], pk)) != 0) {
printf("crypto_sign_open returned <%d>\n", ret_val);
return KAT_CRYPTO_FAILURE;
}
if (mlen[i] != mlen1) {
printf(
"crypto_sign_open returned bad 'mlen': Got <%llu>, expected <%llu>\n",
mlen1, mlen[i]);
return KAT_CRYPTO_FAILURE;
}
if (memcmp(m, m1, mlen[i])) {
printf("crypto_sign_open returned bad 'm' value\n");
return KAT_CRYPTO_FAILURE;
}
// Fill m1 with random bytes for the next iteration
randombytes(m1, MAX_MSG_LEN);
}
// Output rng.h
FILE *fp = fopen("src/pqm4/sqisign_" STRINGIFY(SQISIGN_VARIANT) "/ref/rng.h", "w");
if (!fp) {
printf("Couldn't open rng.h file for writing. Are you in the correct folder?\n");
return KAT_FILE_OPEN_ERROR;
}
output_rng(fp);
fclose(fp);
// Output the header file
fp = fopen("src/pqm4/sqisign_" STRINGIFY(SQISIGN_VARIANT) "/ref/api.h", "w");
if (!fp) {
printf("Couldn't open api.h file for writing. Are you in the correct folder?\n");
return KAT_FILE_OPEN_ERROR;
}
output_header(fp);
fclose(fp);
// Output the implementation
fp = fopen("src/pqm4/sqisign_" STRINGIFY(SQISIGN_VARIANT) "/ref/pqm4_api.c", "w");
if (!fp) {
printf("Couldn't open pqm4_api.c file for writing. Are you in the correct folder?\n");
return KAT_FILE_OPEN_ERROR;
}
output_preamble(fp);
output_pk(fp, pk);
fprintf(fp, "const SQISign_KAT_t kat_" STRINGIFY(SQISIGN_VARIANT) "[%d] = {\n", NUM_KATS);
for (int i = 0; i < NUM_KATS; i++) {
output_message_signature(fp, m[i], mlen[i], sm[i], smlen[i]);
}
fprintf(fp, "};\n\n");
output_implementation(fp);
fclose(fp);
return KAT_SUCCESS;
}

124
apps/benchmark.c Normal file
View File

@@ -0,0 +1,124 @@
// SPDX-License-Identifier: Apache-2.0
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <inttypes.h>
#include <api.h>
#include <rng.h>
#include <bench.h>
#include <bench_test_arguments.h>
#if defined(TARGET_BIG_ENDIAN)
#include <tutil.h>
#endif
void
bench(size_t runs)
{
const size_t m_len = 32;
const size_t sm_len = CRYPTO_BYTES + m_len;
unsigned char *pkbuf = calloc(runs, CRYPTO_PUBLICKEYBYTES);
unsigned char *skbuf = calloc(runs, CRYPTO_SECRETKEYBYTES);
unsigned char *smbuf = calloc(runs, sm_len);
unsigned char *mbuf = calloc(runs, m_len);
unsigned char *pk[runs], *sk[runs], *sm[runs], *m[runs];
for (size_t i = 0; i < runs; ++i) {
pk[i] = pkbuf + i * CRYPTO_PUBLICKEYBYTES;
sk[i] = skbuf + i * CRYPTO_SECRETKEYBYTES;
sm[i] = smbuf + i * sm_len;
m[i] = mbuf + i * m_len;
if (randombytes(m[i], m_len))
abort();
}
unsigned long long len;
printf("%s (%zu iterations)\n", CRYPTO_ALGNAME, runs);
BENCH_CODE_1(runs);
crypto_sign_keypair(pk[i], sk[i]);
BENCH_CODE_2("keypair");
BENCH_CODE_1(runs);
len = sm_len;
crypto_sign(sm[i], &len, m[i], m_len, sk[i]);
if (len != sm_len)
abort();
BENCH_CODE_2("sign");
int ret;
BENCH_CODE_1(runs);
len = m_len;
ret = crypto_sign_open(m[i], &len, sm[i], sm_len, pk[i]);
if (ret)
abort();
BENCH_CODE_2("verify");
free(pkbuf);
free(skbuf);
free(smbuf);
free(mbuf);
}
int
main(int argc, char *argv[])
{
uint32_t seed[12] = { 0 };
int iterations = SQISIGN_TEST_REPS;
int help = 0;
int seed_set = 0;
#ifndef NDEBUG
fprintf(stderr,
"\x1b[31mIt looks like SQIsign was compiled with assertions enabled.\n"
"This will severely impact performance measurements.\x1b[0m\n");
#endif
for (int i = 1; i < argc; i++) {
if (!help && strcmp(argv[i], "--help") == 0) {
help = 1;
continue;
}
if (!seed_set && !parse_seed(argv[i], seed)) {
seed_set = 1;
continue;
}
if (sscanf(argv[i], "--iterations=%d", &iterations) == 1) {
continue;
}
}
if (help || iterations <= 0) {
printf("Usage: %s [--iterations=<iterations>] [--seed=<seed>]\n", argv[0]);
printf("Where <iterations> is the number of iterations used for benchmarking; if not "
"present, uses the default: %d)\n",
iterations);
printf("Where <seed> is the random seed to be used; if not present, a random seed is "
"generated\n");
return 1;
}
if (!seed_set) {
randombytes_select((unsigned char *)seed, sizeof(seed));
}
print_seed(seed);
#if defined(TARGET_BIG_ENDIAN)
for (int i = 0; i < 12; i++) {
seed[i] = BSWAP32(seed[i]);
}
#endif
randombytes_init((unsigned char *)seed, NULL, 256);
cpucycles_init();
bench(iterations);
return 0;
}

View File

@@ -4,32 +4,53 @@
* An example to demonstrate how to use SQIsign with the NIST API.
*/
#include <api.h>
#include <inttypes.h>
#include <mem.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <api.h>
#include <rng.h>
#include <bench_test_arguments.h>
#if defined(TARGET_BIG_ENDIAN)
#include <tutil.h>
#endif
static uint32_t rand_u32()
{
unsigned char buf[4];
if (randombytes(buf, sizeof(buf)))
abort();
return ((uint32_t) buf[3] << 24)
| ((uint32_t) buf[2] << 16)
| ((uint32_t) buf[1] << 8)
| ((uint32_t) buf[0] << 0);
}
/**
* Example for SQIsign variant:
* - crypto_sign_keypair
* - crypto_sign
* - crypto_sign_open
*
*
* @return int return code
*/
static int example_sqisign(void) {
static int
example_sqisign(void)
{
unsigned long long msglen = 32;
unsigned long long msglen = rand_u32() % 100;
unsigned long long smlen = CRYPTO_BYTES + msglen;
unsigned char *pk = calloc(CRYPTO_PUBLICKEYBYTES, 1);
unsigned char *sk = calloc(CRYPTO_SECRETKEYBYTES, 1);
unsigned char *sk = calloc(CRYPTO_SECRETKEYBYTES, 1);
unsigned char *pk = calloc(CRYPTO_PUBLICKEYBYTES, 1);
unsigned char *sig = calloc(smlen, 1);
unsigned char *sm = calloc(smlen, 1);
unsigned char msg[32] = { 0xe };
unsigned char msg2[32] = { 0 };
unsigned char msg[msglen], msg2[msglen];
printf("Example with %s\n", CRYPTO_ALGNAME);
@@ -37,53 +58,111 @@ static int example_sqisign(void) {
int res = crypto_sign_keypair(pk, sk);
if (res) {
printf("FAIL\n");
res = -1;
goto err;
} else {
printf("OK\n");
}
// choose a random message
for (size_t i = 0; i < msglen; ++i)
msg[i] = rand_u32();
printf("crypto_sign -> ");
res = crypto_sign(sig, &smlen, msg, msglen, sk);
res = crypto_sign(sm, &smlen, msg, msglen, sk);
if (res) {
printf("FAIL\n");
res = -1;
goto err;
} else {
printf("OK\n");
}
printf("crypto_sign_open (with correct signature) -> ");
res = crypto_sign_open(msg2, &msglen, sig, smlen, pk);
if (res || memcmp(msg, msg2, msglen)) {
printf("FAIL\n");
res = -1;
res = crypto_sign_open(msg2, &msglen, sm, smlen, pk);
if (res || msglen != sizeof(msg) || memcmp(msg, msg2, msglen)) {
printf("FAIL\n"); // signature was not accepted!?
goto err;
} else {
res = 0;
printf("OK\n");
}
// fill with random bytes
for (size_t i = 0; i < msglen; ++i)
msg2[i] = rand_u32();
// let's try a single bit flip
size_t pos = rand_u32() % smlen;
sm[pos / 8] ^= 1 << pos % 8;
res = crypto_sign_open(msg2, &msglen, sm, smlen, pk);
printf("crypto_sign_open (with altered signature) -> ");
sig[0] = ~sig[0];
memset(msg2, 0, msglen);
res = crypto_sign_open(msg2, &msglen, sig, smlen, pk);
if (!res || !memcmp(msg, msg2, msglen)) {
printf("FAIL\n");
if (!res) {
printf("FAIL\n"); // signature was accepted anyway!?
res = -1;
goto err;
} else {
res = 0;
}
else {
printf("OK\n");
res = 0;
if (msglen)
printf("WARNING: verification failed but the message length was returned nonzero; misuse-prone API\n");
unsigned char any = 0;
for (size_t i = 0; i < msglen; ++i)
any |= msg2[i];
if (any)
printf("WARNING: verification failed but the message buffer was not zeroed out; misuse-prone API\n");
}
err:
free(pk);
sqisign_secure_free(sk, CRYPTO_SECRETKEYBYTES);
free(sig);
free(pk);
free(sm);
return res;
}
int main(void) {
int
main(int argc, char *argv[])
{
uint32_t seed[12] = { 0 };
int help = 0;
int seed_set = 0;
for (int i = 1; i < argc; i++) {
if (!help && strcmp(argv[i], "--help") == 0) {
help = 1;
continue;
}
if (!seed_set && !parse_seed(argv[i], seed)) {
seed_set = 1;
continue;
}
}
if (help) {
printf("Usage: %s [--seed=<seed>]\n", argv[0]);
printf("Where <seed> is the random seed to be used; if not present, a random seed is "
"generated\n");
return 1;
}
if (!seed_set) {
randombytes_select((unsigned char *)seed, sizeof(seed));
}
print_seed(seed);
#if defined(TARGET_BIG_ENDIAN)
for (int i = 0; i < 12; i++) {
seed[i] = BSWAP32(seed[i]);
}
#endif
randombytes_init((unsigned char *)seed, NULL, 256);
return example_sqisign();
}

151
apps/fuzz_sign.c Normal file
View File

@@ -0,0 +1,151 @@
// SPDX-License-Identifier: Apache-2.0
#include <mem.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <api.h>
#include <rng.h>
/**
* Example for SQIsign variant:
* - crypto_sign_keypair
* - crypto_sign
* - crypto_sign_open
*
* @return int return code
*/
static int example_sqisign(int iter) {
int ret = 0;
unsigned long long msglen = 64;
unsigned long long smlen = CRYPTO_BYTES + msglen;
unsigned char *sk = calloc(CRYPTO_SECRETKEYBYTES, 1);
unsigned char *pk = calloc(CRYPTO_PUBLICKEYBYTES, 1);
unsigned char *sm = calloc(smlen, 1);
unsigned char msg[msglen];
FILE *f = NULL;
int res = crypto_sign_keypair(pk, sk);
if (res) {
fprintf(stderr, "crypto_sign_keypair -> FAIL\n");
ret = 1;
goto end;
}
// choose a random message
randombytes(msg, msglen);
res = crypto_sign(sm, &smlen, msg, msglen, sk);
if (res) {
fprintf(stderr, "crypto_sign -> FAIL\n");
ret = 1;
goto end;
}
// This string is larger than necessary, but gcc is not smart enough
// to detect that iter < 1000000 in the snprintf call below
char filename[sizeof("testcases/SQIsign_lvl1/signature4294967296.bin") + 1];
if (iter > 999999) {
fprintf(stderr, "Too many iterations: %d\n", iter);
ret = 1;
goto end;
}
snprintf(filename, sizeof(filename), "testcases/%s/signature%06d.bin",
CRYPTO_ALGNAME, iter);
f = fopen(filename, "wb");
if (!f) {
fprintf(stderr,
"Can't open file: %s (have you created the testcases/%s folder?)\n",
filename, CRYPTO_ALGNAME);
ret = 1;
goto end;
}
if (fwrite(pk, CRYPTO_PUBLICKEYBYTES, 1, f) != 1) {
fprintf(stderr, "Error writing public key to file\n");
ret = 1;
goto end;
}
if (fwrite(sm, smlen, 1, f) != 1) {
fprintf(stderr, "Error writing signature to file\n");
ret = 1;
goto end;
}
end:
if (f)
fclose(f);
free(sk);
free(pk);
free(sm);
return ret;
}
// Brief fuzzing tutorial (assumes level 1, but works for other levels)
// Assumes an Intel Linux system
//
// 0. Some OS configurations required for AFL to work:
// echo core | sudo tee /proc/sys/kernel/core_pattern
// echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
//
// 1. Install AFL++ (https://aflplus.plus) -- on Ubuntu, install packages
// afl++, clang, lld, tmux
//
// 2. Configure CMake using AFL's compilers:
// AFL_HARDEN=1 cmake -DCMAKE_C_COMPILER=afl-clang-lto <other params>
//
// 3. Build
//
// 4. cd to the build/apps folder
//
// 5. Create required folders:
// mkdir -p testcases/SQIsign_lvl{1,3,5}
//
// 6. Run ./fuzz_sign_lvl1 to create some initial testcases
//
// 7. Run:
// tmux new-session -s afl1 afl-fuzz -i testcases/SQIsign_lvl1/ -o syncdir/ -D -M fuzz1 -- ./fuzz_verify_lvl1
//
// 8. Optionally run using other cores in the machine (e.g. for 24 cores),
// for i in $(seq 2 24)
// do
// tmux new-session -s afl$i -d afl-fuzz -d -i testcases/SQIsign_lvl1/ -o syncdir/ -S fuzz$i -- ./fuzz_verify_lvl1
// done
//
// 9. Attach to a specific instance by running:
// tmux attach -t afl$i
//
// 10. To get summary statistics for all runs, run:
// afl-whatsup syncdir/
//
// 11. "Interesting" signatures, in a binary format understood by
// fuzz_verify_lvl1, will be found in syncdir/fuzz$i/crashes; to
// reproduce the crash, pipe one of these files to fuzz_verify_lvl1
int
main(int argc, char *argv[]) {
int testcases = 10;
unsigned char seed[48];
randombytes_select(seed, sizeof(seed));
randombytes_init(seed, NULL, 256);
if (argc == 2) {
sscanf(argv[1], "--testcases=%d", &testcases);
}
for (int i = 0; i < testcases; ++i) {
example_sqisign(i);
}
return 0;
}

117
apps/fuzz_verify.c Normal file
View File

@@ -0,0 +1,117 @@
// SPDX-License-Identifier: Apache-2.0
#include <mem.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <api.h>
#include <rng.h>
#include "encoded_sizes.h"
typedef struct {
unsigned char pk[CRYPTO_PUBLICKEYBYTES];
unsigned char sm[CRYPTO_BYTES + 64];
} signature_t;
static void crash() {
int *p = 0;
*p = 0;
}
static int load_signature(signature_t *sig, int iter) {
char filename[sizeof("testcases/SQIsign_lvl1/signature000000.bin")];
snprintf(filename, sizeof(filename), "testcases/%s/signature%06d.bin", CRYPTO_ALGNAME, iter);
FILE *f = fopen(filename, "rb");
if (!f) {
fprintf(stderr, "Can't open file: %s\n", filename);
return 1;
}
if (fread(sig->pk, CRYPTO_PUBLICKEYBYTES, 1, f) != 1) {
fprintf(stderr, "Can't read public key from file: %s\n", filename);
fclose(f);
return 1;
}
if (fread(sig->sm, CRYPTO_BYTES + 64, 1, f) != 1) {
fprintf(stderr, "Can't read signature from file: %s\n", filename);
fclose(f);
return 1;
}
fclose(f);
return 0;
}
static void verify_signature(signature_t corpus[], int testcases) {
unsigned long long msglen = 64;
unsigned long long smlen = CRYPTO_BYTES + msglen;
unsigned char *pk = calloc(CRYPTO_PUBLICKEYBYTES, 1);
unsigned char *sm = calloc(smlen, 1);
unsigned char msg[msglen];
if (fread(pk, CRYPTO_PUBLICKEYBYTES, 1, stdin) != 1) {
fprintf(stderr, "Error reading public key from stdin\n");
free(pk);
free(sm);
return;
}
if (fread(sm, smlen, 1, stdin) != 1) {
fprintf(stderr, "Error reading signature from stdin\n");
free(pk);
free(sm);
return;
}
int res = crypto_sign_open(msg, &msglen, sm, smlen, pk);
if (res || msglen != sizeof(msg) || memcmp(msg, sm + SIGNATURE_BYTES, msglen)) {
// Signature was not accepted -- check if it was in the corpus and, in that case, crash
for (int i = 0; i < testcases; ++i)
if (!memcmp(pk, corpus[i].pk, CRYPTO_PUBLICKEYBYTES) || !memcmp(sm, corpus[i].sm, smlen))
crash();
} else {
// Signature was accepted -- check if it was not in the corpus and, in that case, crash
int in_corpus = 0;
for (int i = 0; i < testcases; ++i)
if (!memcmp(pk, corpus[i].pk, CRYPTO_PUBLICKEYBYTES) || !memcmp(sm, corpus[i].sm, smlen)) {
in_corpus = 1;
break;
}
if (!in_corpus)
crash();
}
free(pk);
free(sm);
}
int
main(int argc, char *argv[]) {
int testcases = 10;
if (argc == 2) {
sscanf(argv[1], "--testcases=%d", &testcases);
}
signature_t corpus[testcases];
for (int i = 0; i < testcases; ++i)
if (!load_signature(&corpus[i], i))
return 1;
#ifdef __AFL_LOOP
while (__AFL_LOOP(1000))
verify_signature(corpus, testcases);
#else
verify_signature(corpus, testcases);
#endif
return 0;
}