Add generation of keypairs. I suspect we don't want to do this in the kernel.
Signed-off-by: David Howells <[email protected]> cc: Stephan Mueller <[email protected]> cc: Eric Biggers <[email protected]> cc: Jason A. Donenfeld <[email protected]> cc: Ard Biesheuvel <[email protected]> cc: Herbert Xu <[email protected]> cc: [email protected] --- crypto/ml_dsa/dilithium.h | 54 +++++++ crypto/ml_dsa/dilithium_44.h | 35 ++++ crypto/ml_dsa/dilithium_65.h | 35 ++++ crypto/ml_dsa/dilithium_87.h | 35 ++++ crypto/ml_dsa/dilithium_api.c | 92 +++++++++++ crypto/ml_dsa/dilithium_pack.h | 75 +++++++++ crypto/ml_dsa/dilithium_pct.h | 53 ++++++ crypto/ml_dsa/dilithium_polyvec.h | 20 +++ crypto/ml_dsa/dilithium_polyvec_c.h | 20 +++ crypto/ml_dsa/dilithium_signature_c.c | 24 +++ crypto/ml_dsa/dilithium_signature_c.h | 7 + crypto/ml_dsa/dilithium_signature_impl.h | 195 +++++++++++++++++++++++ crypto/ml_dsa/dilithium_type.h | 22 +++ 13 files changed, 667 insertions(+) create mode 100644 crypto/ml_dsa/dilithium_pct.h diff --git a/crypto/ml_dsa/dilithium.h b/crypto/ml_dsa/dilithium.h index 6d4982164201..d2c9dac4967e 100644 --- a/crypto/ml_dsa/dilithium.h +++ b/crypto/ml_dsa/dilithium.h @@ -545,6 +545,60 @@ int dilithium_pk_ptr(uint8_t **dilithium_key, size_t *dilithium_key_len, int dilithium_sig_ptr(uint8_t **dilithium_sig, size_t *dilithium_sig_len, struct dilithium_sig *sig); +/** + * @ingroup Dilithium + * @brief Generates Dilithium public and private key. + * + * @param [out] pk pointer to allocated output public key + * @param [out] sk pointer to allocated output private key + * @param [in] rng_ctx pointer to seeded random number generator context + * @param [in] dilithium_type type of the Dilithium key to generate + * + * @return 0 (success) or < 0 on error + */ +int dilithium_keypair(struct dilithium_pk *pk, struct dilithium_sk *sk, + struct crypto_rng *rng_ctx, + enum dilithium_type dilithium_type); + +/** + * @ingroup Dilithium + * @brief Generates Dilithium public and private key from a given seed. + * + * The idea of the function is the allowance of FIPS 204 to maintain the seed + * used to generate a key pair in lieu of maintaining a private key or the + * key pair (which used much more memory). The seed must be treated equally + * sensitive as a private key. + * + * The seed is generated by simply obtaining 32 bytes from a properly seeded + * DRNG, i.e. the same way as a symmetric key would be generated. + * + * @param [out] pk pointer to allocated output public key + * @param [out] sk pointer to allocated output private key + * @param [in] seed buffer with the seed data which must be exactly 32 bytes + * in size + * @param [in] seedlen length of the seed buffer + * @param [in] dilithium_type type of the Dilithium key to generate + * + * @return 0 (success) or < 0 on error + */ +int dilithium_keypair_from_seed(struct dilithium_pk *pk, + struct dilithium_sk *sk, + const uint8_t *seed, size_t seedlen, + enum dilithium_type dilithium_type); + +/** + * @brief Pairwise consistency check as per FIPS 140 IG + * + * This call should be invoked after generating a key pair in FIPS mode + * + * @param [in] pk Public key + * @param [in] sk Secret key + * + * @return 0 on success, < 0 on error + */ +int dilithium_pct(const struct dilithium_pk *pk, + const struct dilithium_sk *sk); + /** * @ingroup Dilithium * @brief Computes signature in one shot diff --git a/crypto/ml_dsa/dilithium_44.h b/crypto/ml_dsa/dilithium_44.h index 6490c83e7100..d5a03bbebb1a 100644 --- a/crypto/ml_dsa/dilithium_44.h +++ b/crypto/ml_dsa/dilithium_44.h @@ -195,6 +195,41 @@ static inline unsigned int dilithium_44_sig_size(void) return sizeof_field(struct dilithium_44_sig, sig); } +/** + * @brief Generates Dilithium public and private key. + * + * @param [out] pk pointer to allocated output public key + * @param [out] sk pointer to allocated output private key + * @param [in] rng_ctx pointer to seeded random number generator context + * + * @return 0 (success) or < 0 on error + */ +int dilithium_44_keypair(struct dilithium_44_pk *pk, struct dilithium_44_sk *sk, + struct crypto_rng *rng_ctx); + +/** + * @brief Generates Dilithium public and private key from a given seed. + * + * The idea of the function is the allowance of FIPS 204 to maintain the seed + * used to generate a key pair in lieu of maintaining a private key or the + * key pair (which used much more memory). The seed must be treated equally + * sensitive as a private key. + * + * The seed is generated by simply obtaining 32 bytes from a properly seeded + * DRNG, i.e. the same way as a symmetric key would be generated. + * + * @param [out] pk pointer to allocated output public key + * @param [out] sk pointer to allocated output private key + * @param [in] seed buffer with the seed data which must be exactly 32 bytes + * in size + * @param [in] seedlen length of the seed buffer + * + * @return 0 (success) or < 0 on error + */ +int dilithium_44_keypair_from_seed(struct dilithium_44_pk *pk, + struct dilithium_44_sk *sk, + const uint8_t *seed, size_t seedlen); + /** * @brief Computes ML-DSA signature in one shot * diff --git a/crypto/ml_dsa/dilithium_65.h b/crypto/ml_dsa/dilithium_65.h index 0a36f7eed63e..d93fea2a8798 100644 --- a/crypto/ml_dsa/dilithium_65.h +++ b/crypto/ml_dsa/dilithium_65.h @@ -195,6 +195,41 @@ static inline unsigned int dilithium_65_sig_size(void) return sizeof_field(struct dilithium_65_sig, sig); } +/** + * @brief Generates Dilithium public and private key. + * + * @param [out] pk pointer to allocated output public key + * @param [out] sk pointer to allocated output private key + * @param [in] rng_ctx pointer to seeded random number generator context + * + * @return 0 (success) or < 0 on error + */ +int dilithium_65_keypair(struct dilithium_65_pk *pk, struct dilithium_65_sk *sk, + struct crypto_rng *rng_ctx); + +/** + * @brief Generates Dilithium public and private key from a given seed. + * + * The idea of the function is the allowance of FIPS 204 to maintain the seed + * used to generate a key pair in lieu of maintaining a private key or the + * key pair (which used much more memory). The seed must be treated equally + * sensitive as a private key. + * + * The seed is generated by simply obtaining 32 bytes from a properly seeded + * DRNG, i.e. the same way as a symmetric key would be generated. + * + * @param [out] pk pointer to allocated output public key + * @param [out] sk pointer to allocated output private key + * @param [in] seed buffer with the seed data which must be exactly 32 bytes + * in size + * @param [in] seedlen length of the seed buffer + * + * @return 0 (success) or < 0 on error + */ +int dilithium_65_keypair_from_seed(struct dilithium_65_pk *pk, + struct dilithium_65_sk *sk, + const uint8_t *seed, size_t seedlen); + /** * @brief Computes ML-DSA signature in one shot * diff --git a/crypto/ml_dsa/dilithium_87.h b/crypto/ml_dsa/dilithium_87.h index eaed8e675383..69d9f03643d3 100644 --- a/crypto/ml_dsa/dilithium_87.h +++ b/crypto/ml_dsa/dilithium_87.h @@ -195,6 +195,41 @@ static inline unsigned int dilithium_87_sig_size(void) return sizeof_field(struct dilithium_87_sig, sig); } +/** + * @brief Generates Dilithium public and private key. + * + * @param [out] pk pointer to allocated output public key + * @param [out] sk pointer to allocated output private key + * @param [in] rng_ctx pointer to seeded random number generator context + * + * @return 0 (success) or < 0 on error + */ +int dilithium_87_keypair(struct dilithium_87_pk *pk, struct dilithium_87_sk *sk, + struct crypto_rng *rng_ctx); + +/** + * @brief Generates Dilithium public and private key from a given seed. + * + * The idea of the function is the allowance of FIPS 204 to maintain the seed + * used to generate a key pair in lieu of maintaining a private key or the + * key pair (which used much more memory). The seed must be treated equally + * sensitive as a private key. + * + * The seed is generated by simply obtaining 32 bytes from a properly seeded + * DRNG, i.e. the same way as a symmetric key would be generated. + * + * @param [out] pk pointer to allocated output public key + * @param [out] sk pointer to allocated output private key + * @param [in] seed buffer with the seed data which must be exactly 32 bytes + * in size + * @param [in] seedlen length of the seed buffer + * + * @return 0 (success) or < 0 on error + */ +int dilithium_87_keypair_from_seed(struct dilithium_87_pk *pk, + struct dilithium_87_sk *sk, + const uint8_t *seed, size_t seedlen); + /** * @brief Computes ML-DSA signature in one shot * diff --git a/crypto/ml_dsa/dilithium_api.c b/crypto/ml_dsa/dilithium_api.c index 363984f01169..215ab5fd7195 100644 --- a/crypto/ml_dsa/dilithium_api.c +++ b/crypto/ml_dsa/dilithium_api.c @@ -29,6 +29,7 @@ #include <linux/export.h> #include "dilithium.h" +#include "dilithium_pct.h" void dilithium_ctx_zero(struct dilithium_ctx *ctx) { @@ -405,6 +406,97 @@ int dilithium_sig_ptr(uint8_t **dilithium_sig, } EXPORT_SYMBOL(dilithium_sig_ptr); +int dilithium_keypair(struct dilithium_pk *pk, + struct dilithium_sk *sk, struct crypto_rng *rng_ctx, + enum dilithium_type dilithium_type) +{ + if (!pk || !sk) + return -EINVAL; + + switch (dilithium_type) { + case DILITHIUM_87: +#ifdef CONFIG_CRYPTO_DILITHIUM_87 + pk->dilithium_type = dilithium_type; + sk->dilithium_type = dilithium_type; + return dilithium_87_keypair(&pk->key.pk_87, &sk->key.sk_87, + rng_ctx); +#else + return -EOPNOTSUPP; +#endif + case DILITHIUM_65: +#ifdef CONFIG_CRYPTO_DILITHIUM_65 + pk->dilithium_type = dilithium_type; + sk->dilithium_type = dilithium_type; + return dilithium_65_keypair(&pk->key.pk_65, &sk->key.sk_65, + rng_ctx); +#else + return -EOPNOTSUPP; +#endif + case DILITHIUM_44: +#ifdef CONFIG_CRYPTO_DILITHIUM_44 + pk->dilithium_type = dilithium_type; + sk->dilithium_type = dilithium_type; + return dilithium_44_keypair(&pk->key.pk_44, &sk->key.sk_44, + rng_ctx); +#else + return -EOPNOTSUPP; +#endif + case DILITHIUM_UNKNOWN: + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL(dilithium_keypair); + +int dilithium_keypair_from_seed(struct dilithium_pk *pk, struct dilithium_sk *sk, + const uint8_t *seed, size_t seedlen, + enum dilithium_type dilithium_type) +{ + if (!pk || !sk) + return -EINVAL; + + switch (dilithium_type) { + case DILITHIUM_87: +#ifdef CONFIG_CRYPTO_DILITHIUM_87 + pk->dilithium_type = dilithium_type; + sk->dilithium_type = dilithium_type; + return dilithium_87_keypair_from_seed( + &pk->key.pk_87, &sk->key.sk_87, seed, seedlen); +#else + return -EOPNOTSUPP; +#endif + case DILITHIUM_65: +#ifdef CONFIG_CRYPTO_DILITHIUM_65 + pk->dilithium_type = dilithium_type; + sk->dilithium_type = dilithium_type; + return dilithium_65_keypair_from_seed( + &pk->key.pk_65, &sk->key.sk_65, seed, seedlen); +#else + return -EOPNOTSUPP; +#endif + case DILITHIUM_44: +#ifdef CONFIG_CRYPTO_DILITHIUM_44 + pk->dilithium_type = dilithium_type; + sk->dilithium_type = dilithium_type; + return dilithium_44_keypair_from_seed( + &pk->key.pk_44, &sk->key.sk_44, seed, seedlen); +#else + return -EOPNOTSUPP; +#endif + case DILITHIUM_UNKNOWN: + default: + return -EOPNOTSUPP; + } +} +EXPORT_SYMBOL(dilithium_keypair_from_seed); + +int dilithium_pct(const struct dilithium_pk *pk, + const struct dilithium_sk *sk) +{ + return _dilithium_pct_fips(pk, sk); +} +EXPORT_SYMBOL(dilithium_pct); + int dilithium_sign(struct dilithium_sig *sig, const uint8_t *m, size_t mlen, const struct dilithium_sk *sk, diff --git a/crypto/ml_dsa/dilithium_pack.h b/crypto/ml_dsa/dilithium_pack.h index e17c6256a16d..152aafd1df30 100644 --- a/crypto/ml_dsa/dilithium_pack.h +++ b/crypto/ml_dsa/dilithium_pack.h @@ -30,6 +30,21 @@ /******************************************************************************* * Pack / Unpack public key ******************************************************************************/ +static inline void pack_pk_rho(struct dilithium_pk *pk, + const uint8_t rho[DILITHIUM_SEEDBYTES]) +{ + memcpy(pk->pk, rho, DILITHIUM_SEEDBYTES); +} + +static inline void pack_pk_t1(struct dilithium_pk *pk, const polyveck *t1) +{ + unsigned int i; + uint8_t *pubkey = pk->pk + DILITHIUM_SEEDBYTES; + + for (i = 0; i < DILITHIUM_K; ++i) + polyt1_pack(pubkey + i * DILITHIUM_POLYT1_PACKEDBYTES, + &t1->vec[i]); +} static inline void unpack_pk_rho(uint8_t rho[DILITHIUM_SEEDBYTES], const struct dilithium_pk *pk) @@ -50,6 +65,66 @@ static inline void unpack_pk_t1(polyveck *t1, const struct dilithium_pk *pk) /******************************************************************************* * Pack / Unpack secret key ******************************************************************************/ +static inline void pack_sk_rho(struct dilithium_sk *sk, + const uint8_t rho[DILITHIUM_SEEDBYTES]) +{ + memcpy(sk->sk, rho, DILITHIUM_SEEDBYTES); +} + +static inline void pack_sk_key(struct dilithium_sk *sk, + const uint8_t key[DILITHIUM_SEEDBYTES]) +{ + memcpy(sk->sk + DILITHIUM_SEEDBYTES, key, DILITHIUM_SEEDBYTES); +} + +static inline void pack_sk_tr(struct dilithium_sk *sk, + const uint8_t tr[DILITHIUM_TRBYTES]) +{ + memcpy(sk->sk + 2 * DILITHIUM_SEEDBYTES, tr, DILITHIUM_TRBYTES); +} + +static inline void pack_sk_s1(struct dilithium_sk *sk, const polyvecl *s1) +{ + unsigned int i; + uint8_t *seckey = + sk->sk + 2 * DILITHIUM_SEEDBYTES + DILITHIUM_TRBYTES; + + for (i = 0; i < DILITHIUM_L; ++i) + polyeta_pack(seckey + i * DILITHIUM_POLYETA_PACKEDBYTES, + &s1->vec[i]); +} + +static inline void pack_sk_s2(struct dilithium_sk *sk, const polyveck *s2) +{ + unsigned int i; + uint8_t *seckey = sk->sk + 2 * DILITHIUM_SEEDBYTES + + DILITHIUM_TRBYTES + + DILITHIUM_L * DILITHIUM_POLYETA_PACKEDBYTES; + + for (i = 0; i < DILITHIUM_K; ++i) + polyeta_pack(seckey + i * DILITHIUM_POLYETA_PACKEDBYTES, + &s2->vec[i]); +} + +static inline void pack_sk_t0(struct dilithium_sk *sk, const polyveck *t0) +{ + unsigned int i; + uint8_t *seckey = sk->sk + 2 * DILITHIUM_SEEDBYTES + + DILITHIUM_TRBYTES + + DILITHIUM_L * DILITHIUM_POLYETA_PACKEDBYTES + + DILITHIUM_K * DILITHIUM_POLYETA_PACKEDBYTES; + + for (i = 0; i < DILITHIUM_K; ++i) + polyt0_pack(seckey + i * DILITHIUM_POLYT0_PACKEDBYTES, + &t0->vec[i]); +} + +static inline void unpack_sk_rho(uint8_t rho[DILITHIUM_SEEDBYTES], + const struct dilithium_sk *sk) +{ + memcpy(rho, sk->sk, DILITHIUM_SEEDBYTES); +} + static inline void unpack_sk_key(uint8_t key[DILITHIUM_SEEDBYTES], const struct dilithium_sk *sk) { diff --git a/crypto/ml_dsa/dilithium_pct.h b/crypto/ml_dsa/dilithium_pct.h new file mode 100644 index 000000000000..d153e5a95dd1 --- /dev/null +++ b/crypto/ml_dsa/dilithium_pct.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 - 2025, Stephan Mueller <[email protected]> + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF + * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + */ + +#ifndef DILITHIUM_PCT_H +#define DILITHIUM_PCT_H + +#include "fips_mode.h" +#include "small_stack_support.h" + +static inline int _dilithium_pct_fips(const struct dilithium_pk *pk, + const struct dilithium_sk *sk) +{ + struct workspace { + uint8_t m[32]; + struct dilithium_sig sig; + }; + int ret; + LC_DECLARE_MEM(ws, struct workspace, sizeof(uint64_t)); + + ret = dilithium_sign(&ws->sig, ws->m, sizeof(ws->m), sk, + lc_seeded_rng); + if (ret < 0) + goto out; + ret = dilithium_verify(&ws->sig, ws->m, sizeof(ws->m), pk); +out: + LC_RELEASE_MEM(ws); + return ret; +} + +static inline int dilithium_pct_fips(const struct dilithium_pk *pk, + const struct dilithium_sk *sk) +{ + FIPS140_PCT_LOOP(_dilithium_pct_fips(pk, sk)) + + return 0; +} + +#endif /* DILITHIUM_PCT_H */ diff --git a/crypto/ml_dsa/dilithium_polyvec.h b/crypto/ml_dsa/dilithium_polyvec.h index 7e428e3becfd..609df0769f5c 100644 --- a/crypto/ml_dsa/dilithium_polyvec.h +++ b/crypto/ml_dsa/dilithium_polyvec.h @@ -269,6 +269,26 @@ static inline int polyveck_chknorm(const polyveck *v, int32_t bound) return 0; } +/** + * @brief polyveck_power2round - For all coefficients a of polynomials in vector + * of length K, compute a0, a1 such that + * a mod^+ Q = a1*2^D + a0 with + * -2^{D-1} < a0 <= 2^{D-1}. Assumes coefficients + * to be standard representatives. + * + * @param [out] v1 pointer to output vector of polynomials with coefficients a1 + * @param [in] v0 pointer to output vector of polynomials with coefficients a0 + * @param [in] v pointer to input vector + */ +static inline void polyveck_power2round(polyveck *v1, polyveck *v0, + const polyveck *v) +{ + unsigned int i; + + for (i = 0; i < DILITHIUM_K; ++i) + poly_power2round(&v1->vec[i], &v0->vec[i], &v->vec[i]); +} + /** * @brief polyveck_decompose - For all coefficients a of polynomials in vector * of length K, compute high and low bits a0, a1 diff --git a/crypto/ml_dsa/dilithium_polyvec_c.h b/crypto/ml_dsa/dilithium_polyvec_c.h index 0f8c2d914e4d..1628e3217cbc 100644 --- a/crypto/ml_dsa/dilithium_polyvec_c.h +++ b/crypto/ml_dsa/dilithium_polyvec_c.h @@ -19,6 +19,16 @@ #ifndef DILITHIUM_POLYVEC_C_H #define DILITHIUM_POLYVEC_C_H +static inline void +polyvecl_uniform_eta(polyvecl *v, const uint8_t seed[DILITHIUM_CRHBYTES], + uint16_t nonce, void *ws_buf) +{ + unsigned int i; + + for (i = 0; i < DILITHIUM_L; ++i) + poly_uniform_eta(&v->vec[i], seed, cpu_to_le16(nonce++), ws_buf); +} + static inline void polyvecl_uniform_gamma1(polyvecl *v, const uint8_t seed[DILITHIUM_CRHBYTES], uint16_t nonce, void *ws_buf) @@ -32,6 +42,16 @@ polyvecl_uniform_gamma1(polyvecl *v, const uint8_t seed[DILITHIUM_CRHBYTES], ws_buf); } +static inline void +polyveck_uniform_eta(polyveck *v, const uint8_t seed[DILITHIUM_CRHBYTES], + uint16_t nonce, void *ws_buf) +{ + unsigned int i; + + for (i = 0; i < DILITHIUM_K; ++i) + poly_uniform_eta(&v->vec[i], seed, cpu_to_le16(nonce++), ws_buf); +} + /** * @brief expand_mat - Implementation of ExpandA. Generates matrix A with * uniformly random coefficients a_{i,j} by performing diff --git a/crypto/ml_dsa/dilithium_signature_c.c b/crypto/ml_dsa/dilithium_signature_c.c index a4a9d0bfdd37..ebaab0db7fda 100644 --- a/crypto/ml_dsa/dilithium_signature_c.c +++ b/crypto/ml_dsa/dilithium_signature_c.c @@ -37,6 +37,18 @@ #include "dilithium_pack.h" #include "dilithium_signature_impl.h" +int dilithium_keypair_from_seed_c(struct dilithium_pk *pk, struct dilithium_sk *sk, + const uint8_t *seed, size_t seedlen) +{ + return dilithium_keypair_from_seed_impl(pk, sk, seed, seedlen); +} + +int dilithium_keypair_c(struct dilithium_pk *pk, + struct dilithium_sk *sk, struct crypto_rng *rng_ctx) +{ + return dilithium_keypair_impl(pk, sk, rng_ctx); +} + int dilithium_sign_c(struct dilithium_sig *sig, const uint8_t *m, size_t mlen, const struct dilithium_sk *sk, @@ -105,6 +117,18 @@ int dilithium_verify_final_c(const struct dilithium_sig *sig, return dilithium_verify_final_impl(sig, ctx, pk); } +int dilithium_keypair_from_seed(struct dilithium_pk *pk, struct dilithium_sk *sk, + const uint8_t *seed, size_t seedlen) +{ + return dilithium_keypair_from_seed_c(pk, sk, seed, seedlen); +} + +int dilithium_keypair(struct dilithium_pk *pk, + struct dilithium_sk *sk, struct crypto_rng *rng_ctx) +{ + return dilithium_keypair_c(pk, sk, rng_ctx); +} + int dilithium_sign(struct dilithium_sig *sig, const uint8_t *m, size_t mlen, const struct dilithium_sk *sk, diff --git a/crypto/ml_dsa/dilithium_signature_c.h b/crypto/ml_dsa/dilithium_signature_c.h index a7d20cd49672..eab4d5e8c686 100644 --- a/crypto/ml_dsa/dilithium_signature_c.h +++ b/crypto/ml_dsa/dilithium_signature_c.h @@ -21,6 +21,13 @@ #include "dilithium_type.h" +int dilithium_keypair_c(struct dilithium_pk *pk, + struct dilithium_sk *sk, + struct crypto_rng *rng_ctx); +int dilithium_keypair_from_seed_c(struct dilithium_pk *pk, + struct dilithium_sk *sk, + const uint8_t *seed, size_t seedlen); + int dilithium_sign_c(struct dilithium_sig *sig, const uint8_t *m, size_t mlen, const struct dilithium_sk *sk, struct crypto_rng *rng_ctx); diff --git a/crypto/ml_dsa/dilithium_signature_impl.h b/crypto/ml_dsa/dilithium_signature_impl.h index 1f0ec0f7c27c..1682f64b6877 100644 --- a/crypto/ml_dsa/dilithium_signature_impl.h +++ b/crypto/ml_dsa/dilithium_signature_impl.h @@ -31,6 +31,7 @@ #include "dilithium_type.h" #include "dilithium_debug.h" #include "dilithium_pack.h" +#include "dilithium_pct.h" #include "dilithium_signature_impl.h" #include "signature_domain_separation.h" #include "small_stack_support.h" @@ -52,6 +53,200 @@ #define WS_POLY_UNIFORM_BUF_SIZE \ (_WS_POLY_UNIFORM_BUF_SIZE * LC_POLY_UNIFOR_BUF_SIZE_MULTIPLIER) +static int dilithium_keypair_from_seed_impl(struct dilithium_pk *pk, + struct dilithium_sk *sk, + const uint8_t *seed, + size_t seedlen); + +static int dilithium_keypair_impl(struct dilithium_pk *pk, + struct dilithium_sk *sk, + struct crypto_rng *rng_ctx) +{ + struct workspace { + union { + polyvecl s1, s1hat; + } s1; + union { + polyvecl mat[DILITHIUM_K]; + polyveck t0; + } matrix; + polyveck s2, t1; + uint8_t seedbuf[2 * DILITHIUM_SEEDBYTES + + DILITHIUM_CRHBYTES]; + union { + poly polyvecl_pointwise_acc_montgomery_buf; + uint8_t poly_uniform_buf[WS_POLY_UNIFORM_BUF_SIZE]; + uint8_t poly_uniform_eta_buf[POLY_UNIFORM_ETA_BYTES]; + uint8_t tr[DILITHIUM_TRBYTES]; + } tmp; + }; + static const uint8_t dimension[2] = { DILITHIUM_K, DILITHIUM_L }; + const uint8_t *rho, *rhoprime, *key; + int ret; + struct shake256_ctx shake256_ctx; + LC_DECLARE_MEM(ws, struct workspace, sizeof(uint64_t)); + + if (WARN_ON_ONCE(!pk) || + WARN_ON_ONCE(!sk)) { + ret = -EINVAL; + goto out; + } + + /* Get randomness for rho, rhoprime and key */ + ret = crypto_rng_generate(rng_ctx, NULL, 0, ws->seedbuf, + DILITHIUM_SEEDBYTES); + if (ret < 0) + goto out; + dilithium_print_buffer(ws->seedbuf, DILITHIUM_SEEDBYTES, + "Keygen - Seed"); + + shake256_init(&shake256_ctx); + shake256_update(&shake256_ctx, ws->seedbuf, DILITHIUM_SEEDBYTES); + shake256_update(&shake256_ctx, dimension, sizeof(dimension)); + shake256_squeeze(&shake256_ctx, ws->seedbuf, sizeof(ws->seedbuf)); + shake256_clear(&shake256_ctx); + + rho = ws->seedbuf; + dilithium_print_buffer(ws->seedbuf, DILITHIUM_SEEDBYTES, + "Keygen - RHO"); + pack_pk_rho(pk, rho); + pack_sk_rho(sk, rho); + + /* + * Timecop: RHO' is a random number which is enlarged to sample the + * vectors S1 and S2 from. The sampling operation is not considered + * relevant for the side channel operation as (a) an attacker does not + * have access to the random number and (b) only the result after the + * sampling operation of S1 and S2 is released. + */ + rhoprime = rho + DILITHIUM_SEEDBYTES; + dilithium_print_buffer(rhoprime, DILITHIUM_CRHBYTES, + "Keygen - RHOPrime"); + + key = rhoprime + DILITHIUM_CRHBYTES; + dilithium_print_buffer(key, DILITHIUM_SEEDBYTES, "Keygen - Key"); + + /* Timecop: key goes into the secret key */ + poison(key, DILITHIUM_SEEDBYTES); + + pack_sk_key(sk, key); + + /* Sample short vectors s1 and s2 */ + + polyvecl_uniform_eta(&ws->s1.s1, rhoprime, 0, + ws->tmp.poly_uniform_eta_buf); + polyveck_uniform_eta(&ws->s2, rhoprime, DILITHIUM_L, + ws->tmp.poly_uniform_eta_buf); + + /* Timecop: s1 and s2 are secret */ + poison(&ws->s1.s1, sizeof(polyvecl)); + poison(&ws->s2, sizeof(polyveck)); + + dilithium_print_polyvecl(&ws->s1.s1, + "Keygen - S1 L x N matrix after ExpandS:"); + dilithium_print_polyveck(&ws->s2, + "Keygen - S2 K x N matrix after ExpandS:"); + + pack_sk_s1(sk, &ws->s1.s1); + pack_sk_s2(sk, &ws->s2); + + polyvecl_ntt(&ws->s1.s1hat); + dilithium_print_polyvecl(&ws->s1.s1hat, + "Keygen - S1 L x N matrix after NTT:"); + + /* Expand matrix */ + polyvec_matrix_expand(ws->matrix.mat, rho, ws->tmp.poly_uniform_buf); + dilithium_print_polyvecl_k( + ws->matrix.mat, "Keygen - MAT K x L x N matrix after ExpandA:"); + + polyvec_matrix_pointwise_montgomery( + &ws->t1, ws->matrix.mat, &ws->s1.s1hat, + &ws->tmp.polyvecl_pointwise_acc_montgomery_buf); + dilithium_print_polyveck(&ws->t1, + "Keygen - T K x N matrix after A*NTT(s1):"); + + polyveck_reduce(&ws->t1); + dilithium_print_polyveck( + &ws->t1, "Keygen - T K x N matrix reduce after A*NTT(s1):"); + + polyveck_invntt_tomont(&ws->t1); + dilithium_print_polyveck(&ws->t1, + "Keygen - T K x N matrix after NTT-1:"); + + /* Add error vector s2 */ + polyveck_add(&ws->t1, &ws->t1, &ws->s2); + dilithium_print_polyveck(&ws->t1, + "Keygen - T K x N matrix after add S2:"); + + /* Extract t1 and write public key */ + polyveck_caddq(&ws->t1); + dilithium_print_polyveck(&ws->t1, "Keygen - T K x N matrix caddq:"); + + polyveck_power2round(&ws->t1, &ws->matrix.t0, &ws->t1); + dilithium_print_polyveck(&ws->matrix.t0, + "Keygen - T0 K x N matrix after power2round:"); + dilithium_print_polyveck(&ws->t1, + "Keygen - T1 K x N matrix after power2round:"); + + pack_sk_t0(sk, &ws->matrix.t0); + pack_pk_t1(pk, &ws->t1); + dilithium_print_buffer(pk->pk, DILITHIUM_PUBLICKEYBYTES, + "Keygen - PK after pkEncode:"); + + /* Compute H(rho, t1) and write secret key */ + shake256(pk->pk, sizeof(pk->pk), ws->tmp.tr, sizeof(ws->tmp.tr)); + dilithium_print_buffer(ws->tmp.tr, sizeof(ws->tmp.tr), "Keygen - TR:"); + pack_sk_tr(sk, ws->tmp.tr); + + dilithium_print_buffer(sk->sk, DILITHIUM_SECRETKEYBYTES, + "Keygen - SK:"); + + /* Timecop: pk and sk are not relevant for side-channels any more. */ + unpoison(pk->pk, sizeof(pk->pk)); + unpoison(sk->sk, sizeof(sk->sk)); + + ret = dilithium_pct_fips(pk, sk); + +out: + LC_RELEASE_MEM(ws); + return ret; +} + +static int dilithium_keypair_from_seed_impl(struct dilithium_pk *pk, + struct dilithium_sk *sk, + const uint8_t *seed, + size_t seedlen) +{ + struct crypto_rng *rng; + int ret; + + if (seedlen != DILITHIUM_SEEDBYTES) + return -EINVAL; + + rng = crypto_alloc_rng("stdrng", 0, 0); + if (IS_ERR(rng)) + return PTR_ERR(rng); + + ret = crypto_rng_seedsize(rng); + if (ret < 0) + goto out; + if (seedlen != ret) { + ret = -EINVAL; + goto out; + } + + /* Set the seed that the key generation can pull via the RNG. */ + ret = crypto_rng_reset(rng, seed, seedlen); + if (ret < 0) + goto out; + + /* Generate the key pair from the seed. */ + ret = dilithium_keypair_impl(pk, sk, rng); + +out: + return ret; +} + static int dilithium_sign_internal_ahat(struct dilithium_sig *sig, const struct dilithium_sk *sk, struct dilithium_ctx *ctx, diff --git a/crypto/ml_dsa/dilithium_type.h b/crypto/ml_dsa/dilithium_type.h index f9f7ffa2cd38..cc2664b62048 100644 --- a/crypto/ml_dsa/dilithium_type.h +++ b/crypto/ml_dsa/dilithium_type.h @@ -63,6 +63,8 @@ * The following defines simply allow duplicate compilation of the * respective functions. */ +#define dilithium_keypair DILITHIUM_F(keypair) +#define dilithium_keypair_from_seed DILITHIUM_F(keypair_from_seed) #define dilithium_sign DILITHIUM_F(sign) #define dilithium_sign_ctx DILITHIUM_F(sign_ctx) #define dilithium_sign_init DILITHIUM_F(sign_init) @@ -74,6 +76,8 @@ #define dilithium_verify_update DILITHIUM_F(verify_update) #define dilithium_verify_final DILITHIUM_F(verify_final) +#define dilithium_keypair_c DILITHIUM_F(keypair_c) +#define dilithium_keypair_from_seed_c DILITHIUM_F(keypair_from_seed_c) #define dilithium_sign_c DILITHIUM_F(sign_c) #define dilithium_sign_ctx_c DILITHIUM_F(sign_ctx_c) #define dilithium_sign_init_c DILITHIUM_F(sign_init_c) @@ -85,6 +89,10 @@ #define dilithium_verify_update_c DILITHIUM_F(verify_update_c) #define dilithium_verify_final_c DILITHIUM_F(verify_final_c) +#define dilithium_keypair_tester DILITHIUM_F(keypair_tester) +#define dilithium_siggen_tester DILITHIUM_F(siggen_tester) +#define dilithium_sigver_tester DILITHIUM_F(sigver_tester) + #define ntt DILITHIUM_F(ntt) #define invntt_tomont DILITHIUM_F(invntt_tomont) #define poly_chknorm DILITHIUM_F(poly_chknorm) @@ -152,6 +160,8 @@ #define decompose_avx DILITHIUM_F(decompose_avx) #define make_hint_avx DILITHIUM_F(make_hint_avx) #define use_hint_avx DILITHIUM_F(use_hint_avx) +#define dilithium_keypair_avx2 DILITHIUM_F(keypair_avx2) +#define dilithium_keypair_from_seed_avx2 DILITHIUM_F(keypair_from_seed_avx2) #define dilithium_sign_avx2 DILITHIUM_F(sign_avx2) #define dilithium_sign_ctx_avx2 DILITHIUM_F(sign_ctx_avx2) #define dilithium_sign_init_avx2 DILITHIUM_F(sign_init_avx2) @@ -179,6 +189,9 @@ DILITHIUM_F(poly_pointwise_montgomery_armv8) #define polyvecl_pointwise_acc_montgomery_armv8 \ DILITHIUM_F(polyvecl_pointwise_acc_montgomery_armv8) +#define dilithium_keypair_armv8 DILITHIUM_F(keypair_armv8) +#define dilithium_keypair_from_seed_armv8 \ + DILITHIUM_F(keypair_from_seed_armv8) #define dilithium_sign_armv8 DILITHIUM_F(sign_armv8) #define dilithium_sign_ctx_armv8 DILITHIUM_F(sign_ctx_armv8) #define dilithium_sign_init_armv8 DILITHIUM_F(sign_init_armv8) @@ -200,6 +213,9 @@ #define poly_uniform_armv7 DILITHIUM_F(poly_uniform_armv7) #define armv7_poly_reduce_asm DILITHIUM_F(armv7_poly_reduce_asm) #define armv7_rej_uniform_asm DILITHIUM_F(armv7_rej_uniform_asm) +#define dilithium_keypair_armv7 DILITHIUM_F(keypair_armv7) +#define dilithium_keypair_from_seed_armv7 \ + DILITHIUM_F(keypair_from_seed_armv7) #define dilithium_sign_armv7 DILITHIUM_F(sign_armv7) #define dilithium_sign_ctx_armv7 DILITHIUM_F(sign_ctx_armv7) #define dilithium_sign_init_armv7 DILITHIUM_F(sign_init_armv7) @@ -212,6 +228,9 @@ #define dilithium_verify_final_armv7 DILITHIUM_F(verify_final_armv7) /* RISCV 64 ASM Implementation */ +#define dilithium_keypair_riscv64 DILITHIUM_F(keypair_riscv64) +#define dilithium_keypair_from_seed_riscv64 \ + DILITHIUM_F(keypair_from_seed_riscv64) #define dilithium_sign_riscv64 DILITHIUM_F(sign_riscv64) #define dilithium_sign_ctx_riscv64 DILITHIUM_F(sign_ctx_riscv64) #define dilithium_sign_init_riscv64 DILITHIUM_F(sign_init_riscv64) @@ -234,6 +253,9 @@ #define dilithium_poly_reduce_rv64im DILITHIUM_F(poly_reduce_rv64im) /* RISCV 64 RVV Implementation */ +#define dilithium_keypair_riscv64_rvv DILITHIUM_F(keypair_riscv64_rvv) +#define dilithium_keypair_from_seed_riscv64_rvv \ + DILITHIUM_F(keypair_from_seed_riscv64_rvv) #define dilithium_sign_riscv64_rvv DILITHIUM_F(sign_riscv64_rvv) #define dilithium_sign_ctx_riscv64_rvv DILITHIUM_F(sign_ctx_riscv64_rvv) #define dilithium_sign_init_riscv64_rvv DILITHIUM_F(sign_init_riscv64_rvv)
