mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
1972 lines
74 KiB
C
1972 lines
74 KiB
C
/* test_falcon.c
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <tests/unit.h>
|
|
|
|
#ifdef NO_INLINE
|
|
#include <wolfssl/wolfcrypt/misc.h>
|
|
#else
|
|
#define WOLFSSL_MISC_INCLUDED
|
|
#include <wolfcrypt/src/misc.c>
|
|
#endif
|
|
|
|
#ifdef HAVE_FALCON
|
|
#include <wolfssl/wolfcrypt/falcon.h>
|
|
#endif
|
|
#include <wolfssl/wolfcrypt/types.h>
|
|
#include <wolfssl/wolfcrypt/error-crypt.h>
|
|
#include <wolfssl/wolfcrypt/random.h>
|
|
#include <wolfssl/wolfcrypt/asn.h>
|
|
#include <wolfssl/wolfcrypt/asn_public.h>
|
|
#include <wolfssl/wolfcrypt/cryptocb.h>
|
|
#include <tests/api/api.h>
|
|
#include <tests/api/test_falcon.h>
|
|
|
|
/*
|
|
* Coverage note: which of Falcon-512 (NIST L1) and Falcon-1024 (NIST L5) is
|
|
* compiled depends on WOLFSSL_NO_FALCON_LEVEL1 / WOLFSSL_NO_FALCON_LEVEL5, so
|
|
* a test must iterate falcon_levels[] / FALCON_NUM_LEVELS below, and a block
|
|
* naming one level has to be #ifdef-guarded.
|
|
*
|
|
* Tests that need key generation or signing are gated on
|
|
* TEST_FALCON_HAVE_SIGN; argument-sanitising tests only need the
|
|
* always-present entry points and run under HAVE_FALCON.
|
|
*/
|
|
|
|
#ifdef HAVE_FALCON
|
|
|
|
/* The levels this build actually has. Disabling one with
|
|
* WOLFSSL_NO_FALCON_LEVEL1 / _LEVEL5 drops it from every loop below. */
|
|
static const byte falcon_levels[] = {
|
|
#ifndef WOLFSSL_NO_FALCON_LEVEL1
|
|
FALCON_LEVEL1,
|
|
#endif
|
|
#ifndef WOLFSSL_NO_FALCON_LEVEL5
|
|
FALCON_LEVEL5
|
|
#endif
|
|
};
|
|
#define FALCON_NUM_LEVELS ((int)(sizeof(falcon_levels) / sizeof(byte)))
|
|
|
|
/* Encoded sizes per the Falcon specification (Table 3.3), keyed by level. */
|
|
static word32 falcon_exp_pub(byte level)
|
|
{
|
|
return (level == FALCON_LEVEL1) ? (word32)FALCON_LEVEL1_PUB_KEY_SIZE
|
|
: (word32)FALCON_LEVEL5_PUB_KEY_SIZE;
|
|
}
|
|
static word32 falcon_exp_key(byte level)
|
|
{
|
|
return (level == FALCON_LEVEL1) ? (word32)FALCON_LEVEL1_KEY_SIZE
|
|
: (word32)FALCON_LEVEL5_KEY_SIZE;
|
|
}
|
|
static word32 falcon_exp_prv(byte level)
|
|
{
|
|
return (level == FALCON_LEVEL1) ? (word32)FALCON_LEVEL1_PRV_KEY_SIZE
|
|
: (word32)FALCON_LEVEL5_PRV_KEY_SIZE;
|
|
}
|
|
static int falcon_exp_sig(byte level)
|
|
{
|
|
return (level == FALCON_LEVEL1) ? FALCON_LEVEL1_SIG_SIZE
|
|
: FALCON_LEVEL5_SIG_SIZE;
|
|
}
|
|
|
|
#endif /* HAVE_FALCON */
|
|
|
|
/*
|
|
* Size-query and level APIs. Runs in every HAVE_FALCON build (no key
|
|
* generation needed): a key only needs its level set to answer size queries.
|
|
*/
|
|
int test_wc_falcon_sizes(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef HAVE_FALCON
|
|
falcon_key key;
|
|
int li;
|
|
|
|
/* NULL key -> BAD_FUNC_ARG for every size query. */
|
|
ExpectIntEQ(wc_falcon_size(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_priv_size(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_pub_size(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_sig_size(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* Valid key but level not set yet -> BAD_FUNC_ARG. */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_size(&key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_priv_size(&key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_pub_size(&key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_sig_size(&key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
|
|
for (li = 0; li < FALCON_NUM_LEVELS; li++) {
|
|
byte level = falcon_levels[li];
|
|
byte gl = 0;
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
|
|
ExpectIntEQ(wc_falcon_size(&key), (int)falcon_exp_key(level));
|
|
ExpectIntEQ(wc_falcon_priv_size(&key), (int)falcon_exp_prv(level));
|
|
ExpectIntEQ(wc_falcon_pub_size(&key), (int)falcon_exp_pub(level));
|
|
ExpectIntEQ(wc_falcon_sig_size(&key), falcon_exp_sig(level));
|
|
|
|
/* get_level round-trips the level that was set. */
|
|
ExpectIntEQ(wc_falcon_get_level(&key, &gl), 0);
|
|
ExpectIntEQ(gl, level);
|
|
|
|
wc_falcon_free(&key);
|
|
}
|
|
|
|
/* Pin the spec constants so an edit to falcon.h surfaces here. */
|
|
ExpectIntEQ(FALCON_LEVEL1_PUB_KEY_SIZE, 897);
|
|
ExpectIntEQ(FALCON_LEVEL1_SIG_SIZE, 666);
|
|
ExpectIntEQ(FALCON_LEVEL5_PUB_KEY_SIZE, 1793);
|
|
ExpectIntEQ(FALCON_LEVEL5_SIG_SIZE, 1280);
|
|
ExpectIntEQ(FALCON_NONCE_SIZE, 40);
|
|
#endif /* HAVE_FALCON */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Key generation: NULL/bad-arg handling and a real keygen for both levels
|
|
* whose output passes check_key.
|
|
*/
|
|
int test_wc_falcon_make_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_HAVE_SIGN
|
|
falcon_key key;
|
|
WC_RNG rng;
|
|
int li;
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
|
|
/* NULL parameter handling. */
|
|
ExpectIntEQ(wc_falcon_make_key(NULL, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* Level must be set before generating. */
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
|
|
for (li = 0; li < FALCON_NUM_LEVELS; li++) {
|
|
byte level = falcon_levels[li];
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
/* A freshly generated key pair is internally consistent. */
|
|
ExpectIntEQ(wc_falcon_check_key(&key), 0);
|
|
wc_falcon_free(&key);
|
|
}
|
|
|
|
wc_FreeRng(&rng);
|
|
#endif /* TEST_FALCON_HAVE_SIGN */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Sign then verify, both levels: genuine signature accepted, wrong message and
|
|
* single-byte tamper rejected, too-small buffer reports BUFFER_E with the
|
|
* required length, and verify on a public-key-less key is rejected.
|
|
*/
|
|
int test_wc_falcon_sign_vfy(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_HAVE_SIGN
|
|
falcon_key key;
|
|
WC_RNG rng;
|
|
byte* sig = NULL;
|
|
word32 sigLen;
|
|
int res;
|
|
int li;
|
|
static const byte msg[] = "wolfSSL Falcon sign/verify unit test";
|
|
|
|
sig = (byte*)XMALLOC(FALCON_MAX_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(sig);
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
|
|
for (li = 0; li < FALCON_NUM_LEVELS; li++) {
|
|
byte level = falcon_levels[li];
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
|
|
/* Too-small output buffer -> BUFFER_E, outLen set to the max size. */
|
|
sigLen = 1;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), WC_NO_ERR_TRACE(BUFFER_E));
|
|
ExpectIntEQ((int)sigLen, falcon_exp_sig(level));
|
|
|
|
/* Genuine signature: compressed length is variable but never exceeds
|
|
* the level maximum, and it must verify. */
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
ExpectIntGT((int)sigLen, 0);
|
|
ExpectIntLE((int)sigLen, falcon_exp_sig(level));
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
/* A different message must not verify. */
|
|
res = 1;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, (const byte*)"x", 1,
|
|
&res, &key), 0);
|
|
ExpectIntNE(res, 1);
|
|
|
|
/* A one-byte tamper in the signature body must not verify. */
|
|
sig[sigLen - 1] ^= 0x01;
|
|
res = 1;
|
|
(void)wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg), &res,
|
|
&key);
|
|
ExpectIntNE(res, 1);
|
|
sig[sigLen - 1] ^= 0x01;
|
|
|
|
wc_falcon_free(&key);
|
|
}
|
|
|
|
/* Verify against a key with no public key set -> BAD_FUNC_ARG. */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, (word32)falcon_exp_sig(
|
|
falcon_levels[0]), msg, (word32)sizeof(msg), &res, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
|
|
wc_FreeRng(&rng);
|
|
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif /* TEST_FALCON_HAVE_SIGN */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Raw import/export round-trips: public, private-only (raw), private (concat
|
|
* priv+pub), and the combined export_key. Each imported form is exercised via
|
|
* sign or verify. NULL, too-small, and wrong-size arguments are checked.
|
|
*/
|
|
int test_wc_falcon_import_export(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_HAVE_SIGN
|
|
falcon_key key;
|
|
falcon_key key2;
|
|
WC_RNG rng;
|
|
byte* pub = NULL;
|
|
byte* prv = NULL; /* raw private, KEY_SIZE */
|
|
byte* prvpub = NULL; /* concat(priv,pub), PRV_KEY_SIZE */
|
|
byte* sig = NULL;
|
|
word32 pubLen;
|
|
word32 prvLen;
|
|
word32 prvpubLen;
|
|
word32 sigLen;
|
|
int res;
|
|
int li;
|
|
static const byte msg[] = "wolfSSL Falcon import/export unit test";
|
|
|
|
pub = (byte*)XMALLOC(FALCON_MAX_PUB_KEY_SIZE, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
prv = (byte*)XMALLOC(FALCON_MAX_KEY_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
prvpub = (byte*)XMALLOC(FALCON_MAX_PRV_KEY_SIZE, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
sig = (byte*)XMALLOC(FALCON_MAX_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(pub);
|
|
ExpectNotNull(prv);
|
|
ExpectNotNull(prvpub);
|
|
ExpectNotNull(sig);
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
|
|
for (li = 0; li < FALCON_NUM_LEVELS; li++) {
|
|
byte level = falcon_levels[li];
|
|
word32 expPub = falcon_exp_pub(level);
|
|
word32 expKey = falcon_exp_key(level);
|
|
word32 expPrv = falcon_exp_prv(level);
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
|
|
/* export_public: too-small -> BUFFER_E with needed length, then OK. */
|
|
pubLen = 1;
|
|
ExpectIntEQ(wc_falcon_export_public(&key, pub, &pubLen),
|
|
WC_NO_ERR_TRACE(BUFFER_E));
|
|
ExpectIntEQ((int)pubLen, (int)expPub);
|
|
pubLen = FALCON_MAX_PUB_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_public(&key, pub, &pubLen), 0);
|
|
ExpectIntEQ((int)pubLen, (int)expPub);
|
|
|
|
/* export_private_only: raw KEY_SIZE. */
|
|
prvLen = 1;
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, prv, &prvLen),
|
|
WC_NO_ERR_TRACE(BUFFER_E));
|
|
ExpectIntEQ((int)prvLen, (int)expKey);
|
|
prvLen = FALCON_MAX_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, prv, &prvLen), 0);
|
|
ExpectIntEQ((int)prvLen, (int)expKey);
|
|
|
|
/* export_private: concat(priv,pub), PRV_KEY_SIZE. */
|
|
prvpubLen = 1;
|
|
ExpectIntEQ(wc_falcon_export_private(&key, prvpub, &prvpubLen),
|
|
WC_NO_ERR_TRACE(BUFFER_E));
|
|
ExpectIntEQ((int)prvpubLen, (int)expPrv);
|
|
prvpubLen = FALCON_MAX_PRV_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_private(&key, prvpub, &prvpubLen), 0);
|
|
ExpectIntEQ((int)prvpubLen, (int)expPrv);
|
|
|
|
/* Reference signature from the original key. */
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
|
|
/* import_public into a fresh key and verify. Wrong length rejected. */
|
|
XMEMSET(&key2, 0, sizeof(key2));
|
|
ExpectIntEQ(wc_falcon_init(&key2), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key2, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_public(pub, expPub - 1, &key2),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_import_public(pub, pubLen, &key2), 0);
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key2), 0);
|
|
ExpectIntEQ(res, 1);
|
|
wc_falcon_free(&key2);
|
|
|
|
/* import_private_only (raw) + re-attach public, then sign & verify. */
|
|
XMEMSET(&key2, 0, sizeof(key2));
|
|
ExpectIntEQ(wc_falcon_init(&key2), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key2, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_only(prv, expKey - 1, &key2),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_import_private_only(prv, prvLen, &key2), 0);
|
|
ExpectIntEQ(wc_falcon_import_public(pub, pubLen, &key2), 0);
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key2, &rng), 0);
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
wc_falcon_free(&key2);
|
|
|
|
/* import_private_only with the legacy concat(priv,pub) layout must
|
|
* recover the public key on its own (pubKeySet), so a signature can be
|
|
* produced and verified from that single import with no separate
|
|
* public-key import. */
|
|
XMEMSET(&key2, 0, sizeof(key2));
|
|
ExpectIntEQ(wc_falcon_init(&key2), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key2, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_only(prvpub, prvpubLen, &key2), 0);
|
|
ExpectIntEQ(wc_falcon_check_key(&key2), 0);
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key2, &rng), 0);
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key2), 0);
|
|
ExpectIntEQ(res, 1);
|
|
wc_falcon_free(&key2);
|
|
|
|
/* import_private_key with the concat layout recovers the public key. */
|
|
XMEMSET(&key2, 0, sizeof(key2));
|
|
ExpectIntEQ(wc_falcon_init(&key2), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key2, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_key(prvpub, prvpubLen, NULL, 0,
|
|
&key2), 0);
|
|
ExpectIntEQ(wc_falcon_check_key(&key2), 0);
|
|
wc_falcon_free(&key2);
|
|
|
|
/* import_private_key with separate raw private + public buffers. */
|
|
XMEMSET(&key2, 0, sizeof(key2));
|
|
ExpectIntEQ(wc_falcon_init(&key2), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key2, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_key(prv, prvLen, pub, pubLen,
|
|
&key2), 0);
|
|
ExpectIntEQ(wc_falcon_check_key(&key2), 0);
|
|
wc_falcon_free(&key2);
|
|
|
|
/* export_key: private (concat) and public in one call. */
|
|
prvpubLen = FALCON_MAX_PRV_KEY_SIZE;
|
|
pubLen = FALCON_MAX_PUB_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_key(&key, prvpub, &prvpubLen, pub,
|
|
&pubLen), 0);
|
|
ExpectIntEQ((int)prvpubLen, (int)expPrv);
|
|
ExpectIntEQ((int)pubLen, (int)expPub);
|
|
|
|
wc_falcon_free(&key);
|
|
}
|
|
|
|
wc_FreeRng(&rng);
|
|
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(prvpub, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(prv, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif /* TEST_FALCON_HAVE_SIGN */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* check_key: valid key passes; a public-only key, a private-only key, and a
|
|
* mismatched public/private pair (which must fail the h*f == g cross-check)
|
|
* all fail; NULL is rejected.
|
|
*/
|
|
int test_wc_falcon_check_key(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_HAVE_SIGN
|
|
falcon_key key;
|
|
WC_RNG rng;
|
|
byte* pub = NULL;
|
|
byte* prv = NULL;
|
|
word32 pubLen;
|
|
word32 prvLen;
|
|
int li;
|
|
|
|
pub = (byte*)XMALLOC(FALCON_MAX_PUB_KEY_SIZE, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
prv = (byte*)XMALLOC(FALCON_MAX_KEY_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(pub);
|
|
ExpectNotNull(prv);
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
|
|
/* NULL key. */
|
|
ExpectIntEQ(wc_falcon_check_key(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
for (li = 0; li < FALCON_NUM_LEVELS; li++) {
|
|
byte level = falcon_levels[li];
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
|
|
/* Neither half present -> PUBLIC_KEY_E. */
|
|
ExpectIntEQ(wc_falcon_check_key(&key), WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
|
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
ExpectIntEQ(wc_falcon_check_key(&key), 0);
|
|
|
|
pubLen = FALCON_MAX_PUB_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_public(&key, pub, &pubLen), 0);
|
|
prvLen = FALCON_MAX_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, prv, &prvLen), 0);
|
|
|
|
wc_falcon_free(&key);
|
|
|
|
/* Public only (no private) -> PUBLIC_KEY_E. */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_public(pub, pubLen, &key), 0);
|
|
ExpectIntEQ(wc_falcon_check_key(&key), WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
|
wc_falcon_free(&key);
|
|
|
|
/* Raw private only (no public) -> PUBLIC_KEY_E. */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_only(prv, prvLen, &key), 0);
|
|
ExpectIntEQ(wc_falcon_check_key(&key), WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
|
wc_falcon_free(&key);
|
|
|
|
/* Public imported FIRST, then a raw (non-concat) private key: both
|
|
* halves are now present, so check_key passes. */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_public(pub, pubLen, &key), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_only(prv, prvLen, &key), 0);
|
|
ExpectIntEQ(wc_falcon_check_key(&key), 0);
|
|
wc_falcon_free(&key);
|
|
|
|
/* Mismatched pair: the public half of a DIFFERENT key of the same
|
|
* level together with the original private key must fail the
|
|
* cryptographic h*f == g (mod q) cross-check. */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
pubLen = FALCON_MAX_PUB_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_public(&key, pub, &pubLen), 0);
|
|
wc_falcon_free(&key);
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_import_public(pub, pubLen, &key), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_only(prv, prvLen, &key), 0);
|
|
ExpectIntEQ(wc_falcon_check_key(&key), WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
|
wc_falcon_free(&key);
|
|
}
|
|
|
|
wc_FreeRng(&rng);
|
|
XFREE(prv, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif /* TEST_FALCON_HAVE_SIGN */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* DER (RFC 5958 / SubjectPublicKeyInfo) round-trips for both levels:
|
|
* - KeyToDer (priv+pub) -> PrivateKeyDecode -> verify
|
|
* - PrivateKeyToDer (priv only) -> PrivateKeyDecode -> re-sign -> verify
|
|
* - PublicKeyToDer -> PublicKeyDecode -> verify
|
|
* plus the size-query (NULL output) and BUFFER_E contracts.
|
|
*/
|
|
int test_wc_falcon_der(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_HAVE_SIGN
|
|
falcon_key key;
|
|
falcon_key key2;
|
|
WC_RNG rng;
|
|
byte* der = NULL;
|
|
byte* sig = NULL;
|
|
const word32 derSz = 8 * 1024;
|
|
word32 derLen;
|
|
word32 idx;
|
|
word32 sigLen;
|
|
int res;
|
|
int qsize;
|
|
int li;
|
|
static const byte msg[] = "wolfSSL Falcon DER round-trip";
|
|
|
|
der = (byte*)XMALLOC(derSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
sig = (byte*)XMALLOC(FALCON_MAX_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(der);
|
|
ExpectNotNull(sig);
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
|
|
for (li = 0; li < FALCON_NUM_LEVELS; li++) {
|
|
byte level = falcon_levels[li];
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
|
|
/* Reference signature from the generated key. */
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
|
|
/* --- KeyToDer (private + public) --- */
|
|
/* Size query: NULL output returns the encoded length. */
|
|
ExpectIntGT(qsize = wc_Falcon_KeyToDer(&key, NULL, 0), 0);
|
|
derLen = (word32)wc_Falcon_KeyToDer(&key, der, derSz);
|
|
ExpectIntGT((int)derLen, 0);
|
|
ExpectIntEQ((int)derLen, qsize);
|
|
/* Buffer one byte too small: SetAsymKeyDer reports an insufficient
|
|
* output buffer as BAD_FUNC_ARG (not BUFFER_E). */
|
|
ExpectIntEQ(wc_Falcon_KeyToDer(&key, der, (word32)(qsize - 1)),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* Decode into a fresh key and verify the reference signature. */
|
|
XMEMSET(&key2, 0, sizeof(key2));
|
|
ExpectIntEQ(wc_falcon_init(&key2), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key2, level), 0);
|
|
idx = 0;
|
|
ExpectIntEQ(wc_Falcon_PrivateKeyDecode(der, &idx, &key2, derLen), 0);
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key2), 0);
|
|
ExpectIntEQ(res, 1);
|
|
wc_falcon_free(&key2);
|
|
|
|
/* --- PrivateKeyToDer (private only) --- */
|
|
derLen = (word32)wc_Falcon_PrivateKeyToDer(&key, der, derSz);
|
|
ExpectIntGT((int)derLen, 0);
|
|
XMEMSET(&key2, 0, sizeof(key2));
|
|
ExpectIntEQ(wc_falcon_init(&key2), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key2, level), 0);
|
|
idx = 0;
|
|
ExpectIntEQ(wc_Falcon_PrivateKeyDecode(der, &idx, &key2, derLen), 0);
|
|
/* Re-sign with the decoded private key; verify with the original. */
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key2, &rng), 0);
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
wc_falcon_free(&key2);
|
|
|
|
/* --- PublicKeyToDer (SubjectPublicKeyInfo) --- */
|
|
derLen = (word32)wc_Falcon_PublicKeyToDer(&key, der, derSz, 1);
|
|
ExpectIntGT((int)derLen, 0);
|
|
XMEMSET(&key2, 0, sizeof(key2));
|
|
ExpectIntEQ(wc_falcon_init(&key2), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key2, level), 0);
|
|
idx = 0;
|
|
ExpectIntEQ(wc_Falcon_PublicKeyDecode(der, &idx, &key2, derLen), 0);
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key2), 0);
|
|
ExpectIntEQ(res, 1);
|
|
wc_falcon_free(&key2);
|
|
|
|
wc_falcon_free(&key);
|
|
}
|
|
|
|
wc_FreeRng(&rng);
|
|
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif /* TEST_FALCON_HAVE_SIGN */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Exhaustive argument sanitising for the always-present entry points. Runs in
|
|
* every HAVE_FALCON build (including verify-only / crypto-cb-only); make_key is
|
|
* only referenced where it is compiled.
|
|
*/
|
|
int test_wc_falcon_error_paths(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef HAVE_FALCON
|
|
falcon_key key;
|
|
byte buf[64];
|
|
byte out[64];
|
|
word32 outLen;
|
|
word32 idx;
|
|
byte level = 0;
|
|
int res = 0;
|
|
|
|
XMEMSET(buf, 0, sizeof(buf));
|
|
|
|
/* init / init_ex */
|
|
ExpectIntEQ(wc_falcon_init(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_init_ex(NULL, NULL, INVALID_DEVID),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* set_level / get_level */
|
|
ExpectIntEQ(wc_falcon_set_level(NULL, FALCON_LEVEL1),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, 0), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_set_level(&key, 2), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_set_level(&key, 3), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_set_level(&key, 255), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#ifdef WOLFSSL_NO_FALCON_LEVEL1
|
|
ExpectIntEQ(wc_falcon_set_level(&key, FALCON_LEVEL1),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
#ifdef WOLFSSL_NO_FALCON_LEVEL5
|
|
ExpectIntEQ(wc_falcon_set_level(&key, FALCON_LEVEL5),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
ExpectIntEQ(wc_falcon_get_level(NULL, &level),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_get_level(&key, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* Level not set on key yet. */
|
|
ExpectIntEQ(wc_falcon_get_level(&key, &level),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
|
|
/* sign_msg: NULL in / out / outLen / key (present in every config). */
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_sign_msg(NULL, 1, out, &outLen, &key, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_sign_msg(buf, 1, NULL, &outLen, &key, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_sign_msg(buf, 1, out, NULL, &key, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_sign_msg(buf, 1, out, &outLen, NULL, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* verify_msg: NULL sig / msg / res / key. */
|
|
ExpectIntEQ(wc_falcon_verify_msg(NULL, 1, buf, 1, &res, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_verify_msg(buf, 1, NULL, 1, &res, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_verify_msg(buf, 1, buf, 1, NULL, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_verify_msg(buf, 1, buf, 1, &res, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
#ifndef WOLFSSL_FALCON_VERIFY_ONLY
|
|
/* make_key is not compiled in verify-only builds. */
|
|
{
|
|
WC_RNG rng;
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_falcon_make_key(NULL, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
}
|
|
#endif
|
|
|
|
/* import: NULL, unset-level, wrong-size. Level checks precede any buffer
|
|
* read, so a short buf with a large declared length is safe here. */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_import_public(NULL, FALCON_LEVEL1_PUB_KEY_SIZE, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_import_public(buf, FALCON_LEVEL1_PUB_KEY_SIZE, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* key level not set -> BAD_FUNC_ARG. */
|
|
ExpectIntEQ(wc_falcon_import_public(buf, FALCON_LEVEL1_PUB_KEY_SIZE, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_import_private_only(NULL, FALCON_LEVEL1_KEY_SIZE,
|
|
&key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_import_private_only(buf, FALCON_LEVEL1_KEY_SIZE,
|
|
&key), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* level unset */
|
|
ExpectIntEQ(wc_falcon_import_private_key(NULL, FALCON_LEVEL1_KEY_SIZE,
|
|
NULL, 0, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* pub == NULL but pubSz != 0 -> BAD_FUNC_ARG. */
|
|
ExpectIntEQ(wc_falcon_import_private_key(buf, FALCON_LEVEL1_KEY_SIZE,
|
|
NULL, FALCON_LEVEL1_PUB_KEY_SIZE, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
|
|
/* export: NULL, unset-level, no-key-set. */
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_public(NULL, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_export_private_only(NULL, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_export_private(NULL, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
ExpectIntEQ(wc_falcon_export_public(&key, NULL, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_export_public(&key, out, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* level set but no public key -> BAD_FUNC_ARG. */
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_public(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* level set but no private key -> BAD_FUNC_ARG. */
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
|
|
/* check_key / size: NULL. */
|
|
ExpectIntEQ(wc_falcon_check_key(NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* DER decode/encode NULL-argument validation. Falcon always pulls in the
|
|
* asymmetric-key ASN.1 machinery, so these entry points are present. */
|
|
idx = 0;
|
|
ExpectIntEQ(wc_Falcon_PrivateKeyDecode(NULL, &idx, &key, 10),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PrivateKeyDecode(buf, NULL, &key, 10),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PrivateKeyDecode(buf, &idx, NULL, 10),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PrivateKeyDecode(buf, &idx, &key, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
idx = 0;
|
|
ExpectIntEQ(wc_Falcon_PublicKeyDecode(NULL, &idx, &key, 10),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PublicKeyDecode(buf, NULL, &key, 10),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PublicKeyDecode(buf, &idx, NULL, 10),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PublicKeyDecode(buf, &idx, &key, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_KeyToDer(NULL, out, (word32)sizeof(out)),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PrivateKeyToDer(NULL, out, (word32)sizeof(out)),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PublicKeyToDer(NULL, out, (word32)sizeof(out), 1),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif /* HAVE_FALCON */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* The pinned digests only reproduce under the DRBG they were produced with:
|
|
* SHA-256 and SHA-512 Hash_DRBG take different seed sizes, so one seed gives
|
|
* two unrelated streams. Hence the SHA-512 requirement below and the run-time
|
|
* check for wc_Sha512Drbg_Disable(); anything bypassing the DRBG is excluded. */
|
|
#if defined(TEST_FALCON_HAVE_SIGN) && defined(WC_RNG_SEED_CB) && \
|
|
defined(HAVE_HASHDRBG) && !defined(NO_SHA256) && \
|
|
!defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) && \
|
|
!defined(HAVE_INTEL_RDRAND) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \
|
|
!defined(WOLFSSL_RNG_USE_FULL_SEED) && defined(WOLFSSL_DRBG_SHA512)
|
|
|
|
/* Fixed seed so the DRBG, and with it key generation and signing, is
|
|
* reproducible. A constant fill would be rejected by the seed health test, so
|
|
* the bytes come from a fixed linear congruential stream. */
|
|
static int falcon_det_seed_cb(OS_Seed* os, byte* seed, word32 sz)
|
|
{
|
|
word32 i;
|
|
word32 s = 0x5A5A5A5AU;
|
|
|
|
(void)os;
|
|
for (i = 0; i < sz; i++) {
|
|
s = (s * 1103515245U) + 12345U;
|
|
seed[i] = (byte)(s >> 24);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* SHA-256 of buf, for pinning a key or signature without embedding it. */
|
|
static int falcon_det_digest(const byte* buf, word32 sz, byte* out)
|
|
{
|
|
wc_Sha256 sha;
|
|
int ret;
|
|
|
|
/* Only free what was initialized: wc_Sha256Free reads members that a
|
|
* failed wc_InitSha256 leaves indeterminate. */
|
|
ret = wc_InitSha256(&sha);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
ret = wc_Sha256Update(&sha, buf, sz);
|
|
if (ret == 0) {
|
|
ret = wc_Sha256Final(&sha, out);
|
|
}
|
|
wc_Sha256Free(&sha);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Deterministic key generation and signing, pinned by digest: the key digests
|
|
* cover key generation, the signature digest covers signing. The integer
|
|
* emulation, native double and assembly fpr backends are bit-exact with each
|
|
* other, so one seed pins all three.
|
|
*
|
|
* AVX2 and NEON fuse multiply-add and falcon.c disclaims bit-identical output
|
|
* for them; the digests still hold empirically, so a mismatch seen only there
|
|
* is a rounding change to investigate before it is called a defect.
|
|
*/
|
|
int test_wc_falcon_deterministic(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(TEST_FALCON_HAVE_SIGN) && defined(WC_RNG_SEED_CB) && \
|
|
defined(HAVE_HASHDRBG) && !defined(NO_SHA256) && \
|
|
!defined(HAVE_SELFTEST) && !defined(HAVE_FIPS) && \
|
|
!defined(HAVE_INTEL_RDRAND) && !defined(CUSTOM_RAND_GENERATE_BLOCK) && \
|
|
!defined(WOLFSSL_RNG_USE_FULL_SEED) && defined(WOLFSSL_DRBG_SHA512)
|
|
/* SHA-256 of the encoded public key, private key and signature the fixed
|
|
* seed produces, indexed by level. Verified identical across the emulated,
|
|
* asm, double, avx2, neon and small-mem builds. */
|
|
static const byte expPub[2][WC_SHA256_DIGEST_SIZE] = {
|
|
{ 0x82, 0x67, 0x64, 0xA9, 0x6D, 0xAB, 0x82, 0xA0,
|
|
0xC2, 0xD5, 0x38, 0x7E, 0xDF, 0x4F, 0xAC, 0x1C,
|
|
0x62, 0xB0, 0x94, 0x2A, 0xEE, 0xA9, 0x50, 0x4E,
|
|
0x3F, 0x3A, 0x95, 0x81, 0x44, 0x06, 0x85, 0x23 },
|
|
{ 0xFB, 0xA2, 0xEC, 0x57, 0x66, 0x98, 0xFA, 0x87,
|
|
0x6D, 0xE4, 0x64, 0x21, 0x7F, 0x49, 0x82, 0x70,
|
|
0xEA, 0x8B, 0x0D, 0x0F, 0x7C, 0xED, 0xAC, 0xA8,
|
|
0x05, 0x20, 0x71, 0x0B, 0x49, 0xC4, 0xF3, 0x8E }
|
|
};
|
|
static const byte expPrv[2][WC_SHA256_DIGEST_SIZE] = {
|
|
{ 0x5B, 0x09, 0xEF, 0xE6, 0x49, 0xBF, 0x12, 0x5C,
|
|
0x86, 0x7F, 0xEF, 0xC3, 0xC7, 0x0F, 0xA9, 0x3F,
|
|
0x88, 0x31, 0x03, 0xC1, 0xFD, 0xDF, 0x28, 0x01,
|
|
0x9E, 0xD1, 0x72, 0x74, 0x01, 0xE9, 0x06, 0x2C },
|
|
{ 0xFD, 0x4D, 0xB3, 0xB1, 0x96, 0x0F, 0x80, 0xEB,
|
|
0x7E, 0x37, 0x49, 0x9E, 0x03, 0x3D, 0x53, 0x68,
|
|
0x0B, 0x12, 0x16, 0xA9, 0xF6, 0x92, 0x76, 0xFA,
|
|
0xCD, 0x89, 0x91, 0x3B, 0xE7, 0xEB, 0x11, 0x29 }
|
|
};
|
|
static const byte expSig[2][WC_SHA256_DIGEST_SIZE] = {
|
|
{ 0x52, 0x37, 0x4D, 0x5B, 0xFC, 0x1A, 0xCE, 0x05,
|
|
0x86, 0xFA, 0x76, 0x9F, 0x07, 0xE6, 0x7D, 0x46,
|
|
0xC9, 0x1D, 0x5A, 0xF9, 0x5B, 0x3E, 0x37, 0xDE,
|
|
0x34, 0x58, 0x5E, 0x7E, 0xC0, 0xD3, 0xDB, 0x7F },
|
|
{ 0x78, 0x32, 0xB7, 0xB5, 0xDB, 0xA6, 0x65, 0x56,
|
|
0x8B, 0x3E, 0x9D, 0xB4, 0xD7, 0x7D, 0x99, 0xD7,
|
|
0x28, 0x85, 0x2B, 0x4D, 0xC4, 0x96, 0x26, 0xE9,
|
|
0x08, 0x49, 0x91, 0x4A, 0x6D, 0x16, 0x6C, 0xC0 }
|
|
};
|
|
static const byte msg[] = "wolfSSL Falcon deterministic vector";
|
|
falcon_key key;
|
|
WC_RNG rng;
|
|
byte* buf = NULL;
|
|
byte* pub = NULL;
|
|
byte* prv = NULL;
|
|
byte* sig = NULL;
|
|
byte dig[WC_SHA256_DIGEST_SIZE];
|
|
int li;
|
|
int seedCbSet = 0;
|
|
|
|
buf = (byte*)XMALLOC(FALCON_MAX_PUB_KEY_SIZE + FALCON_MAX_KEY_SIZE +
|
|
FALCON_MAX_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(buf);
|
|
if (buf != NULL) {
|
|
pub = buf;
|
|
prv = pub + FALCON_MAX_PUB_KEY_SIZE;
|
|
sig = prv + FALCON_MAX_KEY_SIZE;
|
|
}
|
|
|
|
/* The random group can leave the SHA-512 DRBG disabled process-wide, which
|
|
* silently switches the stream. Skip rather than report six digest
|
|
* mismatches for a reason that has nothing to do with Falcon. */
|
|
if ((buf != NULL) && wc_Sha512Drbg_IsDisabled()) {
|
|
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
return TEST_SKIPPED;
|
|
}
|
|
|
|
/* DoExpect, not ExpectIntEQ: the latter evaluates its argument only while
|
|
* no earlier expectation has failed, so the restore at the end would be
|
|
* skipped by the very failure it has to clean up after. */
|
|
if (buf != NULL) {
|
|
DoExpectIntEQ(wc_SetSeed_Cb(falcon_det_seed_cb), 0);
|
|
seedCbSet = 1;
|
|
}
|
|
|
|
for (li = 0; (buf != NULL) && (li < FALCON_NUM_LEVELS); li++) {
|
|
byte level = falcon_levels[li];
|
|
/* The vectors are per level; the loop index is not, once a level can
|
|
* be compiled out. */
|
|
int gi = (level == FALCON_LEVEL1) ? 0 : 1;
|
|
word32 pubLen = FALCON_MAX_PUB_KEY_SIZE;
|
|
word32 prvLen = FALCON_MAX_KEY_SIZE;
|
|
word32 sigLen = FALCON_MAX_SIG_SIZE;
|
|
int res = 0;
|
|
int rngInited = 0;
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
|
|
/* Same reason: pair the free with an init that actually ran. */
|
|
if (wc_InitRng(&rng) == 0) {
|
|
rngInited = 1;
|
|
}
|
|
ExpectIntEQ(rngInited, 1);
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
|
|
ExpectIntEQ(wc_falcon_export_public(&key, pub, &pubLen), 0);
|
|
ExpectIntEQ(falcon_det_digest(pub, pubLen, dig), 0);
|
|
ExpectBufEQ(dig, expPub[gi], WC_SHA256_DIGEST_SIZE);
|
|
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, prv, &prvLen), 0);
|
|
ExpectIntEQ(falcon_det_digest(prv, prvLen, dig), 0);
|
|
ExpectBufEQ(dig, expPrv[gi], WC_SHA256_DIGEST_SIZE);
|
|
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
ExpectIntEQ(falcon_det_digest(sig, sigLen, dig), 0);
|
|
ExpectBufEQ(dig, expSig[gi], WC_SHA256_DIGEST_SIZE);
|
|
|
|
/* The pinned signature must still verify, so a stale digest cannot
|
|
* hide a signature that no longer works. */
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
wc_falcon_free(&key);
|
|
if (rngInited) {
|
|
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
|
}
|
|
}
|
|
|
|
if (seedCbSet) {
|
|
DoExpectIntEQ(wc_SetSeed_Cb(WC_GENERATE_SEED_DEFAULT), 0);
|
|
}
|
|
|
|
if (buf != NULL) {
|
|
/* Holds an exported private key. */
|
|
ForceZero(buf, FALCON_MAX_PUB_KEY_SIZE + FALCON_MAX_KEY_SIZE +
|
|
FALCON_MAX_SIG_SIZE);
|
|
}
|
|
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Signing the same key repeatedly, and reusing one key structure for different
|
|
* keys and levels. With the per-key caches this covers the reuse and the three
|
|
* points that have to drop the cache - key generation, private-key import and
|
|
* a level change - which the verify against the current public key catches.
|
|
*/
|
|
int test_wc_falcon_key_reuse(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_HAVE_SIGN
|
|
falcon_key key;
|
|
falcon_key other;
|
|
WC_RNG rng;
|
|
byte* sig = NULL;
|
|
byte* prv = NULL;
|
|
byte* pub = NULL;
|
|
word32 sigLen;
|
|
word32 prvLen;
|
|
word32 pubLen;
|
|
static const byte msg[] = "wolfSSL Falcon key reuse";
|
|
int res = 0;
|
|
int rngInited = 0;
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(&other, 0, sizeof(other));
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
|
|
sig = (byte*)XMALLOC(FALCON_MAX_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
prv = (byte*)XMALLOC(FALCON_MAX_KEY_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
pub = (byte*)XMALLOC(FALCON_MAX_PUB_KEY_SIZE, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(sig);
|
|
ExpectNotNull(prv);
|
|
ExpectNotNull(pub);
|
|
|
|
/* Pair the free below with an init that actually ran: ExpectIntEQ skips its
|
|
* argument once an earlier expectation has failed. */
|
|
if (wc_InitRng(&rng) == 0) {
|
|
rngInited = 1;
|
|
}
|
|
ExpectIntEQ(rngInited, 1);
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
|
|
/* Two signatures with one key: the second runs off the cache. */
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
res = 0;
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
/* A different key imported over the same structure must be the one that
|
|
* signs from then on. */
|
|
ExpectIntEQ(wc_falcon_init(&other), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&other, falcon_levels[0]), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&other, &rng), 0);
|
|
prvLen = FALCON_MAX_KEY_SIZE;
|
|
pubLen = FALCON_MAX_PUB_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_private_only(&other, prv, &prvLen), 0);
|
|
ExpectIntEQ(wc_falcon_export_public(&other, pub, &pubLen), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_key(prv, prvLen, pub, pubLen, &key),
|
|
0);
|
|
|
|
res = 0;
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &other), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
#if !defined(WOLFSSL_NO_FALCON_LEVEL1) && !defined(WOLFSSL_NO_FALCON_LEVEL5)
|
|
/* Switching the level resizes everything the cache holds. */
|
|
ExpectIntEQ(wc_falcon_set_level(&key, FALCON_LEVEL5), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
res = 0;
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
/* Back down to the smaller level, to cover shrinking as well. */
|
|
ExpectIntEQ(wc_falcon_set_level(&key, FALCON_LEVEL1), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
res = 0;
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
#endif /* both levels */
|
|
|
|
wc_falcon_free(&other);
|
|
wc_falcon_free(&key);
|
|
if (rngInited) {
|
|
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
|
}
|
|
XFREE(pub, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
if (prv != NULL) {
|
|
/* Holds an exported private key. */
|
|
ForceZero(prv, FALCON_MAX_KEY_SIZE);
|
|
}
|
|
XFREE(prv, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* key->level written directly, bypassing wc_falcon_set_level: no copy may
|
|
* outgrow the key buffers, and a signing cache built at the old level is dropped.
|
|
*/
|
|
int test_wc_falcon_level_overwrite(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_HAVE_SIGN
|
|
falcon_key key;
|
|
WC_RNG rng;
|
|
byte* sig = NULL;
|
|
byte* buf = NULL;
|
|
word32 sigLen;
|
|
word32 bufLen;
|
|
static const byte msg[] = "wolfSSL Falcon level overwrite";
|
|
const byte level = falcon_levels[0];
|
|
const byte other = (level == FALCON_LEVEL1) ? FALCON_LEVEL5 :
|
|
FALCON_LEVEL1;
|
|
int res = 0;
|
|
int rngInited = 0;
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
|
|
sig = (byte*)XMALLOC(FALCON_LEVEL5_SIG_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
buf = (byte*)XMALLOC(FALCON_LEVEL5_PRV_KEY_SIZE, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER);
|
|
ExpectNotNull(sig);
|
|
ExpectNotNull(buf);
|
|
if (buf != NULL) {
|
|
XMEMSET(buf, 0, FALCON_LEVEL5_PRV_KEY_SIZE);
|
|
}
|
|
|
|
if (wc_InitRng(&rng) == 0) {
|
|
rngInited = 1;
|
|
}
|
|
ExpectIntEQ(rngInited, 1);
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, level), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
/* Fills the signing caches at the key's own level. */
|
|
sigLen = FALCON_LEVEL5_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
|
|
key.level = other;
|
|
#if defined(WOLFSSL_FALCON_DYNAMIC_KEYS) || \
|
|
defined(WOLFSSL_NO_FALCON_LEVEL1) || defined(WOLFSSL_NO_FALCON_LEVEL5)
|
|
/* p and k are sized for the old level only. */
|
|
bufLen = FALCON_LEVEL5_PRV_KEY_SIZE;
|
|
ExpectIntEQ(wc_falcon_export_public(&key, buf, &bufLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, buf, &bufLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_export_private(&key, buf, &bufLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_import_public(buf, (other == FALCON_LEVEL1) ?
|
|
FALCON_LEVEL1_PUB_KEY_SIZE : FALCON_LEVEL5_PUB_KEY_SIZE, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_import_private_only(buf, (other == FALCON_LEVEL1) ?
|
|
FALCON_LEVEL1_KEY_SIZE : FALCON_LEVEL5_KEY_SIZE, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_KeyToDer(&key, NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_Falcon_PrivateKeyToDer(&key, NULL, 0),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_check_key(&key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#else
|
|
(void)bufLen;
|
|
#endif
|
|
/* The old-level key bytes do not decode at this level. */
|
|
sigLen = FALCON_LEVEL5_SIG_SIZE;
|
|
ExpectIntNE(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
|
|
/* Back at its own level the key signs again, from a rebuilt cache. */
|
|
key.level = level;
|
|
sigLen = FALCON_LEVEL5_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
wc_falcon_free(&key);
|
|
if (rngInited) {
|
|
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
|
}
|
|
if (buf != NULL) {
|
|
ForceZero(buf, FALCON_LEVEL5_PRV_KEY_SIZE);
|
|
}
|
|
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(WC_FALCON_HAVE_NATIVE_SIGN) && defined(USE_WOLFSSL_MEMORY) && \
|
|
!defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_DEBUG_MEMORY)
|
|
#if defined(WOLFSSL_FALCON_DYNAMIC_KEYS) && !defined(NO_TLS) && \
|
|
!defined(NO_CERTS) && \
|
|
(!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER))
|
|
#define FALCON_OOM_TEST
|
|
#endif
|
|
/* swdev verifies in its own copy of wolfCrypt, out of the hook's reach. */
|
|
#if !defined(WOLFSSL_NO_MALLOC) && !defined(WOLFSSL_SWDEV)
|
|
#define FALCON_VERIFY_OOM_TEST
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(FALCON_OOM_TEST) || defined(FALCON_VERIFY_OOM_TEST)
|
|
static wolfSSL_Malloc_cb falcon_oom_mf;
|
|
static wolfSSL_Free_cb falcon_oom_ff;
|
|
static wolfSSL_Realloc_cb falcon_oom_rf;
|
|
static int falcon_oom_failAt = -1;
|
|
static size_t falcon_oom_sz;
|
|
static int falcon_oom_kCount;
|
|
static int falcon_oom_live;
|
|
|
|
/* Only wc_falcon_set_level allocates exactly a level's private key size. */
|
|
static int falcon_oom_is_kbuf(size_t n)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < FALCON_NUM_LEVELS; i++) {
|
|
if (n == (size_t)falcon_exp_key(falcon_levels[i])) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void* falcon_oom_malloc(size_t n)
|
|
{
|
|
void* p;
|
|
|
|
if (((falcon_oom_sz != 0) ? (n == falcon_oom_sz) : falcon_oom_is_kbuf(n)) &&
|
|
(falcon_oom_kCount++ == falcon_oom_failAt)) {
|
|
return NULL;
|
|
}
|
|
p = (falcon_oom_mf != NULL) ? falcon_oom_mf(n) : malloc(n);
|
|
if (p != NULL) {
|
|
falcon_oom_live++;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
static void falcon_oom_free(void* p)
|
|
{
|
|
if (p != NULL) {
|
|
falcon_oom_live--;
|
|
}
|
|
if (falcon_oom_ff != NULL) {
|
|
falcon_oom_ff(p);
|
|
}
|
|
else {
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
static void* falcon_oom_realloc(void* p, size_t n)
|
|
{
|
|
void* r = (falcon_oom_rf != NULL) ? falcon_oom_rf(p, n) : realloc(p, n);
|
|
|
|
if ((p == NULL) && (r != NULL)) {
|
|
falcon_oom_live++;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static int falcon_oom_save(void)
|
|
{
|
|
return wolfSSL_GetAllocators(&falcon_oom_mf, &falcon_oom_ff,
|
|
&falcon_oom_rf);
|
|
}
|
|
|
|
/* failAt -1 only counts; sz 0 counts the private key buffer allocations. */
|
|
static int falcon_oom_install(int failAt, size_t sz)
|
|
{
|
|
falcon_oom_failAt = failAt;
|
|
falcon_oom_sz = sz;
|
|
falcon_oom_kCount = 0;
|
|
falcon_oom_live = 0;
|
|
return wolfSSL_SetAllocators(falcon_oom_malloc, falcon_oom_free,
|
|
falcon_oom_realloc);
|
|
}
|
|
|
|
static void falcon_oom_restore(void)
|
|
{
|
|
(void)wolfSSL_SetAllocators(falcon_oom_mf, falcon_oom_ff, falcon_oom_rf);
|
|
}
|
|
#endif
|
|
|
|
#ifdef FALCON_OOM_TEST
|
|
|
|
/* Runs one caller of wc_falcon_set_level; returns 1 when it accepted the key. */
|
|
static int falcon_oom_run(int which, const byte* der, word32 derSz,
|
|
const byte* pub, word32 pubSz)
|
|
{
|
|
int ok = 0;
|
|
|
|
if (which == 0) {
|
|
WOLFSSL_CTX* ctx;
|
|
|
|
#ifndef NO_WOLFSSL_CLIENT
|
|
ctx = wolfSSL_CTX_new(wolfSSLv23_client_method());
|
|
#elif !defined(NO_WOLFSSL_SERVER)
|
|
ctx = wolfSSL_CTX_new(wolfSSLv23_server_method());
|
|
#endif
|
|
if (ctx != NULL) {
|
|
ok = (wolfSSL_CTX_use_PrivateKey_buffer(ctx, der, (long)derSz,
|
|
WOLFSSL_FILETYPE_ASN1) == WOLFSSL_SUCCESS);
|
|
wolfSSL_CTX_free(ctx);
|
|
}
|
|
}
|
|
#ifdef OPENSSL_EXTRA
|
|
else {
|
|
const unsigned char* p = pub;
|
|
WOLFSSL_EVP_PKEY* pkey = wolfSSL_d2i_PUBKEY(NULL, &p, (long)pubSz);
|
|
|
|
ok = (pkey != NULL);
|
|
wolfSSL_EVP_PKEY_free(pkey);
|
|
}
|
|
#endif
|
|
#if defined(OPENSSL_EXTRA) || defined(DEBUG_WOLFSSL_VERBOSE)
|
|
wolfSSL_ERR_clear_error();
|
|
#endif
|
|
(void)pub;
|
|
(void)pubSz;
|
|
return ok;
|
|
}
|
|
#endif
|
|
|
|
/* A failed wc_falcon_set_level allocation must fail its caller outright:
|
|
* nothing leaked, and no further level tried. */
|
|
int test_wc_falcon_set_level_oom_callers(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef FALCON_OOM_TEST
|
|
falcon_key key;
|
|
WC_RNG rng;
|
|
byte* der = NULL;
|
|
byte pub[FALCON_MAX_PUB_KEY_SIZE];
|
|
word32 pubSz;
|
|
int derSz = 0;
|
|
int li;
|
|
int which;
|
|
int n;
|
|
int total;
|
|
int ok;
|
|
const word32 derMax = 2 * FALCON_MAX_PRV_KEY_SIZE;
|
|
#ifdef OPENSSL_EXTRA
|
|
const int numCallers = 2;
|
|
#else
|
|
const int numCallers = 1;
|
|
#endif
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
ExpectNotNull(der = (byte*)XMALLOC(derMax, NULL, DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectIntEQ(falcon_oom_save(), 0);
|
|
|
|
for (li = 0; EXPECT_SUCCESS() && (li < FALCON_NUM_LEVELS); li++) {
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[li]), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
ExpectIntGT(derSz = wc_Falcon_KeyToDer(&key, der, derMax), 0);
|
|
pubSz = (word32)sizeof(pub);
|
|
ExpectIntEQ(wc_falcon_export_public(&key, pub, &pubSz), 0);
|
|
wc_falcon_free(&key);
|
|
|
|
for (which = 0; EXPECT_SUCCESS() && (which < numCallers); which++) {
|
|
ExpectIntEQ(falcon_oom_install(-1, 0), 0);
|
|
ok = falcon_oom_run(which, der, (word32)derSz, pub, pubSz);
|
|
total = falcon_oom_kCount;
|
|
falcon_oom_restore();
|
|
ExpectIntEQ(ok, 1);
|
|
ExpectIntGT(total, 0);
|
|
ExpectIntEQ(falcon_oom_live, 0);
|
|
|
|
for (n = 0; EXPECT_SUCCESS() && (n < total); n++) {
|
|
ExpectIntEQ(falcon_oom_install(n, 0), 0);
|
|
ok = falcon_oom_run(which, der, (word32)derSz, pub, pubSz);
|
|
falcon_oom_restore();
|
|
ExpectIntEQ(ok, 0);
|
|
ExpectIntEQ(falcon_oom_kCount, n + 1);
|
|
ExpectIntEQ(falcon_oom_live, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
falcon_oom_failAt = -1;
|
|
if (der != NULL) {
|
|
ForceZero(der, derMax);
|
|
}
|
|
XFREE(der, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/* A failed verify arena allocation returns MEMORY_E with res left at 0, and
|
|
* WOLFSSL_FALCON_VERIFY_NO_MALLOC makes no such allocation. */
|
|
int test_wc_falcon_verify_oom(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef FALCON_VERIFY_OOM_TEST
|
|
falcon_key key;
|
|
WC_RNG rng;
|
|
byte* sig = NULL;
|
|
word32 sigLen;
|
|
size_t arenaSz;
|
|
int res;
|
|
int ret;
|
|
int li;
|
|
static const byte msg[] = "wolfSSL Falcon verify OOM test";
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
ExpectNotNull(sig = (byte*)XMALLOC(FALCON_MAX_SIG_SIZE, NULL,
|
|
DYNAMIC_TYPE_TMP_BUFFER));
|
|
ExpectIntEQ(falcon_oom_save(), 0);
|
|
|
|
for (li = 0; EXPECT_SUCCESS() && (li < FALCON_NUM_LEVELS); li++) {
|
|
arenaSz = sizeof(word16) * 2 * (size_t)((falcon_levels[li] ==
|
|
FALCON_LEVEL1) ? FALCON_LEVEL1_N : FALCON_LEVEL5_N);
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[li]), 0);
|
|
ExpectIntEQ(wc_falcon_make_key(&key, &rng), 0);
|
|
sigLen = FALCON_MAX_SIG_SIZE;
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), sig, &sigLen,
|
|
&key, &rng), 0);
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg),
|
|
&res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
ExpectIntEQ(falcon_oom_install(0, arenaSz), 0);
|
|
res = 1;
|
|
ret = wc_falcon_verify_msg(sig, sigLen, msg, (word32)sizeof(msg), &res,
|
|
&key);
|
|
falcon_oom_restore();
|
|
#ifdef WOLFSSL_FALCON_VERIFY_NO_MALLOC
|
|
ExpectIntEQ(ret, 0);
|
|
ExpectIntEQ(res, 1);
|
|
ExpectIntEQ(falcon_oom_kCount, 0);
|
|
#else
|
|
ExpectIntEQ(ret, WC_NO_ERR_TRACE(MEMORY_E));
|
|
ExpectIntEQ(res, 0);
|
|
ExpectIntEQ(falcon_oom_kCount, 1);
|
|
#endif
|
|
ExpectIntEQ(falcon_oom_live, 0);
|
|
|
|
wc_falcon_free(&key);
|
|
}
|
|
|
|
falcon_oom_failAt = -1;
|
|
XFREE(sig, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
|
DoExpectIntEQ(wc_FreeRng(&rng), 0);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* MC/DC decision coverage for the public wc_falcon_* wrapper decisions that the
|
|
* functional tests above leave with an unshown independence pair. Each block
|
|
* targets one decision and supplies the operand combinations that flip exactly
|
|
* one condition at a time while the others are held non-determining (the MC/DC
|
|
* requirement), using cheap negative/edge inputs that stop at the guard under
|
|
* test. No key generation is performed here (the positive fall-through half of
|
|
* each guard is owned by test_wc_falcon_make_key / _sign_vfy above, which run a
|
|
* real key).
|
|
*
|
|
* Documented residuals (operands whose determined half is unreachable in this
|
|
* software build): the `ret == 0` operands guarding the !prvKeySet and
|
|
* rng==NULL tests in wc_falcon_sign_msg, and the !pubKeySet test in
|
|
* wc_falcon_verify_msg, are only ever 0 on the software path, where ret is
|
|
* assigned non-zero solely by a WOLF_CRYPTO_CB callback error, absent here, so
|
|
* their FALSE half cannot be demonstrated without a crypto-callback harness
|
|
* (same residual class as the rsa/mldsa `ret==0`-chain guards).
|
|
*/
|
|
int test_wc_FalconDecisionCoverage(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef HAVE_FALCON
|
|
falcon_key key;
|
|
byte out[64];
|
|
word32 outLen;
|
|
/* Buffers sized for a raw Falcon-512 private/public import so the ret==0
|
|
* arm of wc_falcon_import_private_key is reachable without keygen. */
|
|
/* Sized for the largest level built; the imports below use the exact size
|
|
* of the level actually in use, since a mismatch is itself rejected. */
|
|
static byte prv[FALCON_MAX_KEY_SIZE];
|
|
static byte pub[FALCON_MAX_PUB_KEY_SIZE];
|
|
|
|
XMEMSET(prv, 0, sizeof(prv));
|
|
XMEMSET(pub, 0, sizeof(pub));
|
|
|
|
/* ---- (key->level != 1) && (key->level != 5) -------------------------
|
|
* export_public / export_private_only / export_private / check_key each
|
|
* open with this AND. Three levels flip each operand independently:
|
|
* level 1 -> (F, .) op0 determines the result false
|
|
* level 5 -> (T, F) op1 determines the result false
|
|
* level 2 -> (T, T) both true -> BAD_FUNC_ARG
|
|
* level 2 is not settable via wc_falcon_set_level (it rejects non-1/5), so
|
|
* it is written directly on the public struct. With no key material set,
|
|
* level 1/5 fall through the AND to the key-not-set guard (which returns
|
|
* BAD_FUNC_ARG for export, PUBLIC_KEY_E for check_key); no buffer copy
|
|
* occurs, so the small out[] buffer is never touched. */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
#ifndef WOLFSSL_NO_FALCON_LEVEL1
|
|
ExpectIntEQ(wc_falcon_set_level(&key, FALCON_LEVEL1), 0);
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_public(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* level ok, pubKey unset */
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_check_key(&key),
|
|
WC_NO_ERR_TRACE(PUBLIC_KEY_E)); /* level ok, halves unset */
|
|
#endif
|
|
|
|
#ifndef WOLFSSL_NO_FALCON_LEVEL5
|
|
ExpectIntEQ(wc_falcon_set_level(&key, FALCON_LEVEL5), 0);
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_public(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_check_key(&key), WC_NO_ERR_TRACE(PUBLIC_KEY_E));
|
|
#endif
|
|
|
|
key.level = 2; /* invalid -> (level!=1)&&(level!=5) both true */
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_public(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private(&key, out, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_check_key(&key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
|
|
/* ---- export NULL-argument independence -----------------------------
|
|
* (key==NULL) || (out==NULL) || (outLen==NULL): the key==NULL half is
|
|
* shown by test_wc_falcon_error_paths; here we flip the middle and last
|
|
* operands with the earlier ones held false (valid key, level set). */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, NULL, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* out==NULL determines */
|
|
ExpectIntEQ(wc_falcon_export_private_only(&key, out, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* outLen==NULL determines */
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_export_private(&key, NULL, &outLen),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_export_private(&key, out, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
wc_falcon_free(&key);
|
|
|
|
/* ---- import (priv==NULL) || (key==NULL) ----------------------------
|
|
* priv==NULL shown by error_paths; here flip key==NULL with priv held
|
|
* non-NULL. */
|
|
ExpectIntEQ(wc_falcon_import_private_only(prv,
|
|
falcon_exp_key(falcon_levels[0]), NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_falcon_import_private_key(prv,
|
|
falcon_exp_key(falcon_levels[0]),
|
|
NULL, 0, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* ---- wc_falcon_import_private_key: (ret==0) && (pub != NULL) --------
|
|
* A correctly sized raw private import returns 0 without keygen, so the
|
|
* ret==0 operand is genuinely true here. Flip pub between NULL and set:
|
|
* valid priv, pub==NULL -> ret==0, pub!=NULL F -> import stops, 0
|
|
* valid priv, pub set -> ret==0, pub!=NULL T -> public import runs
|
|
* bad priv size, pub NULL-> ret!=0 (op0 F) short-circuits the AND */
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_key(prv,
|
|
falcon_exp_key(falcon_levels[0]),
|
|
NULL, 0, &key), 0); /* pub!=NULL F */
|
|
wc_falcon_free(&key);
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_key(prv,
|
|
falcon_exp_key(falcon_levels[0]),
|
|
pub, falcon_exp_pub(falcon_levels[0]), &key), 0); /* pub!=NULL T */
|
|
wc_falcon_free(&key);
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
ExpectIntEQ(wc_falcon_import_private_key(prv, 1 /* bad size */,
|
|
NULL, 0, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* ret!=0 (op0 F) */
|
|
wc_falcon_free(&key);
|
|
|
|
#ifdef WC_FALCON_HAVE_NATIVE_SIGN
|
|
/* ---- wc_falcon_sign_msg: (ret==0) && (!prvKeySet), then
|
|
* (ret==0) && (rng==NULL) --------------------------------------
|
|
* All of in/out/outLen/key are non-NULL so the front guard falls through.
|
|
* level set, prvKeySet=0 -> (!prvKeySet) T -> BAD
|
|
* prvKeySet forced, rng==NULL -> (!prvKeySet) F (falls through),
|
|
* (rng==NULL) T -> BAD (native signer
|
|
* never entered, so the unset key
|
|
* material is never dereferenced).
|
|
* The (!prvKeySet) F + valid-rng fall-through into the native signer is
|
|
* owned by test_wc_falcon_sign_vfy (real key). */
|
|
{
|
|
WC_RNG rng;
|
|
byte msg[4];
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
XMEMSET(msg, 0, sizeof(msg));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), out, &outLen,
|
|
&key, &rng), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* !prvKeySet T */
|
|
key.prvKeySet = 1; /* !prvKeySet F */
|
|
outLen = (word32)sizeof(out);
|
|
ExpectIntEQ(wc_falcon_sign_msg(msg, (word32)sizeof(msg), out, &outLen,
|
|
&key, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* rng==NULL T */
|
|
wc_falcon_free(&key);
|
|
}
|
|
#endif /* WC_FALCON_HAVE_NATIVE_SIGN */
|
|
|
|
#ifndef WOLF_CRYPTO_CB_ONLY_FALCON
|
|
/* ---- wc_falcon_verify_msg: (ret==0) && (!pubKeySet) ----------------
|
|
* Valid args, level set, pubKeySet=0 -> (!pubKeySet) T -> BAD. The
|
|
* (!pubKeySet) F fall-through into the native verifier is owned by
|
|
* test_wc_falcon_sign_vfy (real key). */
|
|
{
|
|
byte msg[4];
|
|
int res = 0;
|
|
XMEMSET(msg, 0, sizeof(msg));
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init(&key), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
ExpectIntEQ(wc_falcon_verify_msg(msg, (word32)sizeof(msg), msg,
|
|
(word32)sizeof(msg), &res, &key),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG)); /* !pubKeySet T */
|
|
wc_falcon_free(&key);
|
|
}
|
|
#endif /* !WOLF_CRYPTO_CB_ONLY_FALCON */
|
|
|
|
#ifdef WOLF_PRIVATE_KEY_ID
|
|
/* ---- wc_falcon_init_id: ret==0 && (len<0 || len>FALCON_MAX_ID_LEN),
|
|
* then ret==0 && id!=NULL && len!=0 ----------------------------- */
|
|
{
|
|
falcon_key idkey;
|
|
static const byte idbytes[FALCON_MAX_ID_LEN] = { 0 };
|
|
|
|
/* The frees below are unconditional; ExpectIntEQ skips the paired init
|
|
* once an earlier expectation has failed. */
|
|
XMEMSET(&idkey, 0, sizeof(idkey));
|
|
|
|
/* key==NULL -> ret!=0 before the length AND -> ret==0 operand F */
|
|
ExpectIntEQ(wc_falcon_init_id(NULL, idbytes, 4, NULL, INVALID_DEVID),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* valid len: length test all-false, then id!=NULL && len!=0 all-true.
|
|
* A successful init sets the level, which owns two allocations with
|
|
* WOLFSSL_FALCON_DYNAMIC_KEYS, and re-initializing drops the pointers
|
|
* to them - so each one is released before the next init runs. */
|
|
ExpectIntEQ(wc_falcon_init_id(&idkey, idbytes, 4, NULL, INVALID_DEVID),
|
|
0);
|
|
wc_falcon_free(&idkey);
|
|
/* len < 0 -> first length operand determines */
|
|
ExpectIntEQ(wc_falcon_init_id(&idkey, idbytes, -1, NULL, INVALID_DEVID),
|
|
WC_NO_ERR_TRACE(BUFFER_E));
|
|
/* len > FALCON_MAX_ID_LEN -> second length operand determines */
|
|
ExpectIntEQ(wc_falcon_init_id(&idkey, idbytes, FALCON_MAX_ID_LEN + 1,
|
|
NULL, INVALID_DEVID), WC_NO_ERR_TRACE(BUFFER_E));
|
|
/* id==NULL with a positive len -> no identifier to store */
|
|
ExpectIntEQ(wc_falcon_init_id(&idkey, NULL, 4, NULL, INVALID_DEVID),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* id==NULL with len==0 -> (id!=NULL) operand F, skips copy */
|
|
ExpectIntEQ(wc_falcon_init_id(&idkey, NULL, 0, NULL, INVALID_DEVID), 0);
|
|
wc_falcon_free(&idkey);
|
|
/* len==0 with non-NULL id -> (len!=0) operand F, skips copy */
|
|
ExpectIntEQ(wc_falcon_init_id(&idkey, idbytes, 0, NULL, INVALID_DEVID),
|
|
0);
|
|
wc_falcon_free(&idkey);
|
|
}
|
|
|
|
/* ---- wc_falcon_init_label: (key==NULL)||(label==NULL), then
|
|
* (labelLen==0)||(labelLen>FALCON_MAX_LABEL_LEN) ---------------- */
|
|
{
|
|
falcon_key lblkey;
|
|
char toolong[FALCON_MAX_LABEL_LEN + 2];
|
|
XMEMSET(&lblkey, 0, sizeof(lblkey));
|
|
XMEMSET(toolong, 'a', sizeof(toolong));
|
|
toolong[sizeof(toolong) - 1] = '\0';
|
|
|
|
/* key==NULL determines the first OR */
|
|
ExpectIntEQ(wc_falcon_init_label(NULL, "lbl", NULL, INVALID_DEVID),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* key ok, label==NULL determines the first OR */
|
|
ExpectIntEQ(wc_falcon_init_label(&lblkey, NULL, NULL, INVALID_DEVID),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* both non-NULL -> falls through to the length OR; "" -> len==0 T */
|
|
ExpectIntEQ(wc_falcon_init_label(&lblkey, "", NULL, INVALID_DEVID),
|
|
WC_NO_ERR_TRACE(BUFFER_E));
|
|
/* valid label -> length OR all-false -> success. Released for the same
|
|
* reason as the init_id block above. */
|
|
ExpectIntEQ(wc_falcon_init_label(&lblkey, "lbl", NULL, INVALID_DEVID),
|
|
0);
|
|
wc_falcon_free(&lblkey);
|
|
/* over-long label -> (labelLen>MAX) operand determines */
|
|
ExpectIntEQ(wc_falcon_init_label(&lblkey, toolong, NULL, INVALID_DEVID),
|
|
WC_NO_ERR_TRACE(BUFFER_E));
|
|
}
|
|
#endif /* WOLF_PRIVATE_KEY_ID */
|
|
#endif /* HAVE_FALCON */
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_FALCON) && defined(WOLF_CRYPTO_CB) && \
|
|
defined(WOLF_CRYPTO_CB_FREE)
|
|
#define TEST_FALCON_CB_FREE
|
|
#define TEST_FALCON_CB_FREE_DEVID 0x46414C43
|
|
#endif
|
|
|
|
#ifdef TEST_FALCON_CB_FREE
|
|
/* What the free callback saw, so the test can check the contract rather than
|
|
* just that something fired. */
|
|
typedef struct {
|
|
int frees; /* matching free callbacks seen */
|
|
int badObj; /* callback was handed the wrong object */
|
|
int wiped; /* callback saw a key already cleaned up */
|
|
int ret; /* what the callback returns */
|
|
const void* obj; /* object the free is expected to name */
|
|
} FalconCbFreeCtx;
|
|
|
|
/* Stands in for a device holding state for the key. Counting the call proves
|
|
* wc_falcon_free told the device rather than only cleaning up in software,
|
|
* which would leave the device side of the key behind. */
|
|
static int falcon_cb_free_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
|
{
|
|
FalconCbFreeCtx* seen = (FalconCbFreeCtx*)ctx;
|
|
|
|
(void)devIdArg;
|
|
|
|
if ((seen != NULL) && (info != NULL) &&
|
|
(info->algo_type == WC_ALGO_TYPE_FREE) &&
|
|
(info->free.algo == WC_ALGO_TYPE_PK) &&
|
|
(info->free.type == WC_PK_TYPE_PQC_SIG_KEYGEN) &&
|
|
(info->free.subType == WC_PQC_SIG_TYPE_FALCON)) {
|
|
const falcon_key* fk = (const falcon_key*)info->free.obj;
|
|
|
|
seen->frees++;
|
|
if ((fk == NULL) || ((const void*)fk != seen->obj)) {
|
|
seen->badObj++;
|
|
}
|
|
/* The device gets the key while it is still whole: it may need to
|
|
* read it to release the right resource, so the software wipe has
|
|
* to come after this call, not before. */
|
|
else if (fk->devId != TEST_FALCON_CB_FREE_DEVID) {
|
|
seen->wiped++;
|
|
}
|
|
return seen->ret;
|
|
}
|
|
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
#endif /* TEST_FALCON_CB_FREE */
|
|
|
|
/* Freeing a key that names a device has to tell that device, so it can
|
|
* release what it holds. A key with no device must not, and neither must a
|
|
* second free of a key already freed: a freed key names no device. A device
|
|
* that reports an error does not stop the software cleanup. */
|
|
int test_falcon_cb_free(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_CB_FREE
|
|
falcon_key key;
|
|
FalconCbFreeCtx seen;
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(&seen, 0, sizeof(seen));
|
|
seen.obj = &key;
|
|
|
|
/* No key to free, and nothing to tell a device about. */
|
|
wc_falcon_free(NULL);
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_FALCON_CB_FREE_DEVID,
|
|
falcon_cb_free_cb, &seen), 0);
|
|
|
|
ExpectIntEQ(wc_falcon_init_ex(&key, NULL, TEST_FALCON_CB_FREE_DEVID), 0);
|
|
wc_falcon_free(&key);
|
|
ExpectIntEQ(seen.frees, 1);
|
|
ExpectIntEQ(seen.badObj, 0);
|
|
ExpectIntEQ(seen.wiped, 0);
|
|
ExpectIntEQ(key.devId, INVALID_DEVID);
|
|
|
|
wc_falcon_free(&key);
|
|
ExpectIntEQ(seen.frees, 1);
|
|
|
|
/* A device that fails still leaves the key cleaned up locally. */
|
|
seen.ret = WC_NO_ERR_TRACE(WC_HW_E);
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init_ex(&key, NULL, TEST_FALCON_CB_FREE_DEVID), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
wc_falcon_free(&key);
|
|
ExpectIntEQ(seen.frees, 2);
|
|
ExpectIntEQ(key.devId, INVALID_DEVID);
|
|
ExpectIntEQ(key.level, 0);
|
|
seen.ret = 0;
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
ExpectIntEQ(wc_falcon_init_ex(&key, NULL, INVALID_DEVID), 0);
|
|
wc_falcon_free(&key);
|
|
ExpectIntEQ(seen.frees, 2);
|
|
|
|
wc_CryptoCb_UnRegisterDevice(TEST_FALCON_CB_FREE_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
#if defined(HAVE_FALCON) && defined(WOLF_CRYPTO_CB)
|
|
#define TEST_FALCON_VERIFY_CB_RES
|
|
#define TEST_FALCON_VERIFY_CB_RES_DEVID 0x46414C56
|
|
#endif
|
|
|
|
#ifdef TEST_FALCON_VERIFY_CB_RES
|
|
typedef struct FalconVerifyResCtx {
|
|
int ret; /* Value the device returns. */
|
|
int writeRes; /* Whether the device writes *res = 1 first. */
|
|
} FalconVerifyResCtx;
|
|
|
|
/* A device that may claim the signature is valid and then returns ctx->ret,
|
|
* which may be 0, an error or CRYPTOCB_UNAVAILABLE. */
|
|
static int falcon_verify_res_cb(int devIdArg, wc_CryptoInfo* info, void* ctx)
|
|
{
|
|
FalconVerifyResCtx* cbCtx = (FalconVerifyResCtx*)ctx;
|
|
|
|
(void)devIdArg;
|
|
|
|
if ((info != NULL) && (info->algo_type == WC_ALGO_TYPE_PK) &&
|
|
(info->pk.type == WC_PK_TYPE_PQC_SIG_VERIFY) &&
|
|
(info->pk.pqc_verify.type == WC_PQC_SIG_TYPE_FALCON)) {
|
|
if (cbCtx->writeRes) {
|
|
*info->pk.pqc_verify.res = 1;
|
|
}
|
|
return cbCtx->ret;
|
|
}
|
|
return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
}
|
|
#endif /* TEST_FALCON_VERIFY_CB_RES */
|
|
|
|
/* *res must only be 1 when verify returns 0 and the signature was found
|
|
* valid. Cases expecting 0 start res at 1 to catch a stale value. */
|
|
int test_falcon_verify_cb_res(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#ifdef TEST_FALCON_VERIFY_CB_RES
|
|
falcon_key key;
|
|
byte sig[16];
|
|
byte msg[16];
|
|
FalconVerifyResCtx cbCtx;
|
|
int res;
|
|
#ifdef WOLF_CRYPTO_CB_ONLY_FALCON
|
|
const int declineRet = WC_NO_ERR_TRACE(NO_VALID_DEVID);
|
|
#else
|
|
/* The software fallback rejects a key with no public key set. */
|
|
const int declineRet = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
|
|
#endif
|
|
|
|
XMEMSET(&key, 0, sizeof(key));
|
|
XMEMSET(&cbCtx, 0, sizeof(cbCtx));
|
|
XMEMSET(sig, 0, sizeof(sig));
|
|
XMEMSET(msg, 0x5a, sizeof(msg));
|
|
|
|
ExpectIntEQ(wc_CryptoCb_RegisterDevice(TEST_FALCON_VERIFY_CB_RES_DEVID,
|
|
falcon_verify_res_cb, &cbCtx), 0);
|
|
ExpectIntEQ(wc_falcon_init_ex(&key, NULL,
|
|
TEST_FALCON_VERIFY_CB_RES_DEVID), 0);
|
|
ExpectIntEQ(wc_falcon_set_level(&key, falcon_levels[0]), 0);
|
|
|
|
/* The device verifies the signature. */
|
|
cbCtx.ret = 0;
|
|
cbCtx.writeRes = 1;
|
|
res = 0;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, (word32)sizeof(sig), msg,
|
|
(word32)sizeof(msg), &res, &key), 0);
|
|
ExpectIntEQ(res, 1);
|
|
|
|
/* The device completes without writing *res. */
|
|
cbCtx.writeRes = 0;
|
|
res = 1;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, (word32)sizeof(sig), msg,
|
|
(word32)sizeof(msg), &res, &key), 0);
|
|
ExpectIntEQ(res, 0);
|
|
|
|
/* The device fails. */
|
|
cbCtx.ret = WC_NO_ERR_TRACE(WC_HW_E);
|
|
cbCtx.writeRes = 1;
|
|
res = 1;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, (word32)sizeof(sig), msg,
|
|
(word32)sizeof(msg), &res, &key), WC_NO_ERR_TRACE(WC_HW_E));
|
|
ExpectIntEQ(res, 0);
|
|
|
|
/* The device declines, then the fallback rejects the call. */
|
|
cbCtx.ret = WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE);
|
|
cbCtx.writeRes = 1;
|
|
res = 1;
|
|
ExpectIntEQ(wc_falcon_verify_msg(sig, (word32)sizeof(sig), msg,
|
|
(word32)sizeof(msg), &res, &key), declineRet);
|
|
ExpectIntEQ(res, 0);
|
|
|
|
/* A bad argument is rejected before the device is reached. */
|
|
res = 1;
|
|
ExpectIntEQ(wc_falcon_verify_msg(NULL, (word32)sizeof(sig), msg,
|
|
(word32)sizeof(msg), &res, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(res, 0);
|
|
|
|
wc_falcon_free(&key);
|
|
wc_CryptoCb_UnRegisterDevice(TEST_FALCON_VERIFY_CB_RES_DEVID);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|