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:
committed by
Lorenz Panny
parent
ff34a8cd18
commit
91e9e464fe
@@ -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
297
apps/PQCgenKAT_sign_pqm4.c
Normal 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
124
apps/benchmark.c
Normal 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;
|
||||
}
|
||||
@@ -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
151
apps/fuzz_sign.c
Normal 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
117
apps/fuzz_verify.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user