From: Loup Vaillant Date: Thu, 1 Dec 2022 15:27:08 +0000 (+0100) Subject: Remove EdDSA incremental & custom hash API X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=77812533af12acb83843fbe29d210eddbb8fa127;p=Monocypher.git Remove EdDSA incremental & custom hash API The incremental and custom hash API was too complex and too niche to justify itself. I'm removing them in favour of a more flexible approach: giving the basic building blocks necessary to implement EdDSA manually. Those building blocks comprise 5 specialised functions: - crypto_eddsa_trim_scalar: turn 32 random bytes into a proper scalar. - crypto_eddsa_reduce : reduces a 64 bytes number modulo L. - crypto_eddsa_mul_add : like MUL_ADD, except modulo L. - crypto_eddsa_scalarbase : multiplies a scalar by the base point. - crypto_eddsa_r_check : generates R independently for verification. These make it fairly easy to implement EdDSA (including Ed25519) in various ways, including the streaming and custom hash functions I just removed, replacing the deterministic nonce by a random one, or adding a random prefix to mitigate the energy side channel in some settings. I believe only minimal tweaks are required to implement the Edwards25519 half of RFC 8032 entirely (including the context and pre-hash variants), as well as XEdDSA (which should only require a single Montgomery to Edwards conversion function). This is a prototype, and the extensibility promises remain to be tested. Ideally that means implementing all the fancy extensions in a separate project, and _maybe_ include some of them in the optional files. Related to #227 --- diff --git a/doc/man/man3/advanced/crypto_check_final.3monocypher b/doc/man/man3/advanced/crypto_check_final.3monocypher deleted file mode 120000 index 08c255b..0000000 --- a/doc/man/man3/advanced/crypto_check_final.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/advanced/crypto_check_init.3monocypher b/doc/man/man3/advanced/crypto_check_init.3monocypher deleted file mode 120000 index 08c255b..0000000 --- a/doc/man/man3/advanced/crypto_check_init.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/advanced/crypto_check_init_custom_hash.3monocypher b/doc/man/man3/advanced/crypto_check_init_custom_hash.3monocypher deleted file mode 120000 index a4b73ad..0000000 --- a/doc/man/man3/advanced/crypto_check_init_custom_hash.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_sign_init_first_pass_custom_hash.3monocypher \ No newline at end of file diff --git a/doc/man/man3/advanced/crypto_check_update.3monocypher b/doc/man/man3/advanced/crypto_check_update.3monocypher deleted file mode 120000 index 08c255b..0000000 --- a/doc/man/man3/advanced/crypto_check_update.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/advanced/crypto_sign_final.3monocypher b/doc/man/man3/advanced/crypto_sign_final.3monocypher deleted file mode 120000 index 08c255b..0000000 --- a/doc/man/man3/advanced/crypto_sign_final.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/advanced/crypto_sign_init_first_pass.3monocypher b/doc/man/man3/advanced/crypto_sign_init_first_pass.3monocypher deleted file mode 100644 index cbaaf05..0000000 --- a/doc/man/man3/advanced/crypto_sign_init_first_pass.3monocypher +++ /dev/null @@ -1,318 +0,0 @@ -.\" This file is dual-licensed. Choose whichever you want. -.\" -.\" The first licence is a regular 2-clause BSD licence. The second licence -.\" is the CC-0 from Creative Commons. It is intended to release Monocypher -.\" to the public domain. The BSD licence serves as a fallback option. -.\" -.\" SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0 -.\" -.\" ---------------------------------------------------------------------------- -.\" -.\" Copyright (c) 2017-2021 Loup Vaillant -.\" Copyright (c) 2017-2018 Michael Savage -.\" Copyright (c) 2017, 2019-2022 Fabio Scotoni -.\" All rights reserved. -.\" -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions are -.\" met: -.\" -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the -.\" distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -.\" HOLDER OR CONTRIBUTORS 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 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" ---------------------------------------------------------------------------- -.\" -.\" Written in 2017-2022 by Loup Vaillant, Michael Savage and Fabio Scotoni -.\" -.\" To the extent possible under law, the author(s) have dedicated all copyright -.\" and related neighboring rights to this software to the public domain -.\" worldwide. This software is distributed without any warranty. -.\" -.\" You should have received a copy of the CC0 Public Domain Dedication along -.\" with this software. If not, see -.\" -.\" -.Dd February 13, 2022 -.Dt CRYPTO_SIGN_INIT_FIRST_PASS 3MONOCYPHER -.Os -.Sh NAME -.Nm crypto_sign_init_first_pass , -.Nm crypto_sign_update , -.Nm crypto_sign_final , -.Nm crypto_sign_init_second_pass , -.Nm crypto_check_init , -.Nm crypto_check_update , -.Nm crypto_check_final -.Nd incremental public key signatures -.Sh SYNOPSIS -.In monocypher.h -.Ft void -.Fo crypto_sign_init_first_pass -.Fa "crypto_sign_ctx *ctx" -.Fa "const uint8_t secret_key[32]" -.Fa "const uint8_t public_key[32]" -.Fc -.Ft void -.Fo crypto_sign_update -.Fa "crypto_sign_ctx *ctx" -.Fa "const uint8_t *message" -.Fa "size_t message_size" -.Fc -.Ft void -.Fo crypto_sign_final -.Fa "crypto_sign_ctx *ctx" -.Fa "uint8_t signature[64]" -.Fc -.Ft void -.Fo crypto_sign_init_second_pass -.Fa "crypto_sign_ctx *ctx" -.Fc -.Ft void -.Fo crypto_check_init -.Fa "crypto_check_ctx *ctx" -.Fa "const uint8_t signature[64]" -.Fa "const uint8_t public_key[32]" -.Fc -.Ft void -.Fo crypto_check_update -.Fa "crypto_check_ctx *ctx" -.Fa "const uint8_t *message" -.Fa "size_t message_size" -.Fc -.Ft int -.Fo crypto_check_final -.Fa "crypto_check_ctx *ctx" -.Fc -.Sh DESCRIPTION -These functions are variants of -.Xr crypto_sign 3monocypher -and -.Xr crypto_check 3monocypher . -Prefer those simpler functions if possible. -.Pp -The arguments are the same as those described in -.Xr crypto_sign 3monocypher . -.Pp -This incremental interface can be used to sign or verify messages too -large to fit in a single buffer. -The arguments are the same as the direct interface described in -.Xr crypto_sign 3monocypher . -.Pp -The direct and incremental interface produce and accept the same -signatures. -.Pp -Signing is done in two passes. -This requires five steps: -.Bl -bullet -.It -Initialisation of the first pass with -.Fn crypto_sign_init_first_pass . -The public key is optional and will be recomputed if not provided. -This recomputation doubles the execution time for short messages. -.It -The first pass proper, with -.Fn crypto_sign_update . -.Sy Under no circumstances must you forget the first pass . -Forgetting to call -.Fn crypto_sign_update -will appear to work in that it produces valid signatures -but also -loses all security because attackers may now recover the secret key. -.It -Initialisation of the second pass with -.Fn crypto_sign_init_second_pass . -.It -The second pass proper, with -.Fn crypto_sign_update . -The same update function is used for both passes. -.It -Signature generation with -.Fn crypto_sign_final . -This also wipes the context. -.El -.Pp -Verification requires three steps: -.Bl -bullet -.It -Initialisation with -.Fn crypto_check_init . -.It -Update with -.Fn crypto_check_update . -.It -Signature verification with -.Fn crypto_check_final . -.El -.Sh RETURN VALUES -.Fn crypto_sign_init_first_pass , -.Fn crypto_sign_init_second_pass , -.Fn crypto_sign_update , -.Fn crypto_sign_final , -.Fn crypto_check_init , -and -.Fn crypto_check_update -return nothing. -.Pp -.Fn crypto_check_final -returns 0 for legitimate messages and -1 for forgeries. -.Sh EXAMPLES -Sign a message: -.Bd -literal -offset indent -uint8_t sk [ 32]; /* Secret key */ -const uint8_t pk [ 32]; /* Public key (optional) */ -const uint8_t message [500]; /* Message to sign */ -uint8_t signature[ 64]; /* Output signature */ -crypto_sign_ctx ctx; -arc4random_buf(sk, 32); -crypto_sign_public_key(pk, sk); -crypto_sign_init_first_pass((crypto_sign_ctx_abstract*)&ctx, sk, pk); -/* Wipe the secret key if no longer needed */ -crypto_wipe(sk, 32); -for (size_t i = 0; i < 500; i += 100) { - crypto_sign_update((crypto_sign_ctx_abstract*)&ctx, message + i, 100); -} -crypto_sign_init_second_pass((crypto_sign_ctx_abstract*)&ctx); -for (size_t i = 0; i < 500; i += 100) { - crypto_sign_update((crypto_sign_ctx_abstract*)&ctx, message + i, 100); -} -crypto_sign_final((crypto_sign_ctx_abstract*)&ctx, signature); -.Ed -.Pp -Check the above: -.Bd -literal -offset indent -const uint8_t pk [ 32]; /* Public key */ -const uint8_t message [500]; /* Message to sign */ -const uint8_t signature[ 64]; /* Signature to check */ -crypto_check_ctx ctx; -crypto_check_init((crypto_sign_ctx_abstract*)&ctx, signature, pk); -for (size_t i = 0; i < 500; i += 100) { - crypto_check_update((crypto_sign_ctx_abstract*)&ctx, message + i, 100); -} -if (crypto_check_final((crypto_sign_ctx_abstract*)&ctx)) { - /* Message is corrupted, abort processing */ -} else { - /* Message is genuine */ -} -.Ed -.Pp -This interface can be used to mitigate attacks that leverage power -analysis and fault injection (glitching) \(en both of which require -physical access and appropriate equipment. -We inject additional randomness (at least 32 bytes) and -enough all-zero padding to fill the hash function's block size -(128 bytes for both BLAKE2b and SHA-512). -Note that -.Fn crypto_sign_init_first_pass -already fills 32 bytes, -so randomness and padding must fill 32 bytes -.Em less -than the block -size (96 bytes for BLAKE2b and SHA-512). -Access to a cryptographically secure pseudo-random generator is a -requirement for effective side-channel mitigation. -Signing a message with increased power-related side-channel mitigations: -.Bd -literal -offset indent -const uint8_t message [ 500]; /* Message to sign */ -uint8_t sk [ 32]; /* Secret key */ -const uint8_t pk [ 32]; /* Public key (optional) */ -uint8_t signature[ 64]; /* Output signature */ -uint8_t buf [128-32] = {0}; /* Mitigation buffer */ -crypto_sign_ctx ctx; -crypto_sign_ctx_abstract *actx = (crypto_sign_ctx_abstract *)&ctx; - -arc4random_buf(sk, 32); -crypto_sign_public_key(pk, sk); - -arc4random_buf(buf, 32); -/* The rest of buf MUST be zeroes. */ - -crypto_sign_init_first_pass(actx, sk, pk); -crypto_sign_update (actx, buf, sizeof(buf)); -crypto_sign_update (actx, message, 500); - -crypto_sign_init_second_pass(actx); -crypto_sign_update (actx, message, 500); -crypto_sign_final (actx, signature); - -crypto_wipe(buf, 32); -/* Wipe the secret key if no longer needed */ -crypto_wipe(sk, 32); -.Ed -.Sh SEE ALSO -.Xr crypto_blake2b 3monocypher , -.Xr crypto_x25519 3monocypher , -.Xr crypto_lock 3monocypher , -.Xr crypto_sign 3monocypher , -.Xr crypto_wipe 3monocypher , -.Xr intro 3monocypher -.Sh STANDARDS -These functions implement PureEdDSA with Curve25519 and BLAKE2b, as -described in RFC 8032. -This is the same as Ed25519, with BLAKE2b instead of SHA-512. -.Pp -The example for side-channel mitigation follows the methodology outlined -in I-D.draft-mattsson-cfrg-det-sigs-with-noise-02. -.Sh HISTORY -The -.Fn crypto_sign_init_first_pass , -.Fn crypto_sign_update , -.Fn crypto_sign_final , -.Fn crypto_sign_init_second_pass , -.Fn crypto_check_init , -.Fn crypto_check_update , -and -.Fn crypto_check_final -functions first appeared in Monocypher 1.1.0. -.Pp -Starting with Monocypher 2.0.5, modified signatures abusing the inherent -signature malleability property of EdDSA now cause a non-zero return -value of -.Fn crypto_check_final ; -in prior versions, such signatures would be accepted. -.Pp -.Sy A critical security vulnerability -that caused all-zero signatures to be accepted was introduced in -Monocypher 0.3; -it was fixed in Monocypher 1.1.1 and 2.0.4. -.Sh SECURITY CONSIDERATIONS -Messages are not verified until the call to -.Fn crypto_check_final . -Messages may be stored before they are verified, but they cannot be -.Em trusted . -Processing untrusted messages increases the attack surface of the -system. -Doing so securely is hard. -Do not process messages before calling -.Fn crypto_check_final . -.Pp -When signing messages, the security considerations documented in -.Xr crypto_sign 3monocypher -also apply. -If power-related side-channels are part of your threat model, -note that there may still be other power-related side-channels (such as -if the CPU leaks information when an operation overflows a register) -that must be considered. -.Sh IMPLEMENTATION DETAILS -EdDSA signatures require two passes that cannot be performed in -parallel. -There are ways around this limitation, but they all lower security in -some way. -For this reason, Monocypher does not support them. diff --git a/doc/man/man3/advanced/crypto_sign_init_first_pass_custom_hash.3monocypher b/doc/man/man3/advanced/crypto_sign_init_first_pass_custom_hash.3monocypher deleted file mode 100644 index f1f7920..0000000 --- a/doc/man/man3/advanced/crypto_sign_init_first_pass_custom_hash.3monocypher +++ /dev/null @@ -1,296 +0,0 @@ -.\" This file is dual-licensed. Choose whichever you want. -.\" -.\" The first licence is a regular 2-clause BSD licence. The second licence -.\" is the CC-0 from Creative Commons. It is intended to release Monocypher -.\" to the public domain. The BSD licence serves as a fallback option. -.\" -.\" SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0 -.\" -.\" ---------------------------------------------------------------------------- -.\" -.\" Copyright (c) 2019-2020 Fabio Scotoni -.\" All rights reserved. -.\" -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions are -.\" met: -.\" -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the -.\" distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -.\" HOLDER OR CONTRIBUTORS 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 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" ---------------------------------------------------------------------------- -.\" -.\" Written in 2019-2020 by Fabio Scotoni -.\" -.\" To the extent possible under law, the author(s) have dedicated all copyright -.\" and related neighboring rights to this software to the public domain -.\" worldwide. This software is distributed without any warranty. -.\" -.\" You should have received a copy of the CC0 Public Domain Dedication along -.\" with this software. If not, see -.\" -.\" -.Dd December 28, 2019 -.Dt CRYPTO_SIGN_INIT_FIRST_PASS_CUSTOM_HASH 3MONOCYPHER -.Os -.Sh NAME -.Nm crypto_sign_init_first_pass_custom_hash , -.Nm crypto_sign_public_key_custom_hash , -.Nm crypto_check_init_custom_hash -.Nd public key signatures with custom hash functions -.Sh SYNOPSIS -.In monocypher.h -.Ft void -.Fo crypto_sign_init_first_pass_custom_hash -.Fa "crypto_sign_ctx_abstract *ctx" -.Fa "const uint8_t secret_key[32]" -.Fa "const uint8_t public_key[32]" -.Fa "const crypto_sign_vtable *hash" -.Fc -.Ft void -.Fo crypto_sign_public_key_custom_hash -.Fa "uint8_t public_key[32]" -.Fa "const uint8_t secret_key[32]" -.Fa "const crypto_sign_vtable *hash" -.Fc -.Ft void -.Fo crypto_check_init_custom_hash -.Fa "crypto_sign_ctx_abstract *ctx" -.Fa "const uint8_t signature[64]" -.Fa "const uint8_t public_key[32]" -.Fa "const crypto_sign_vtable *hash" -.Fc -.Sh DESCRIPTION -These functions are variants of the -.Xr crypto_sign_init_first_pass 3monocypher -family of functions. -They provide the ability to replace the EdDSA hash function with any -user-provided hash function. -.Pp -.Sy This is a highly advanced feature . -Interoperability of public key signatures -with other cryptographic libraries can normally be achieved by using -.Xr crypto_ed25519_sign 3monocypher -or -.Xr crypto_ed25519_sign_init_first_pass 3monocypher -already. -This interface is exposed only for completeness and to handle special -situations -(e.g. to use the hash function of the future winner of the NIST -lightweight crypto competition on a device with highly constrained -resources or taking advantage of hardware support for cryptographic -hash functions). -Whenever possible, these functions should be avoided. -.Pp -To make a custom hash algorithm available for use with these functions, -a -.Vt crypto_sign_vtable -structure must be provided. -It is defined as: -.Bd -literal -typedef struct { - void (*hash)(uint8_t hash[64], const uint8_t *message, - size_t message_size); - void (*init )(void *ctx); - void (*update)(void *ctx, const uint8_t *message, - size_t message_size); - void (*final )(void *ctx, uint8_t hash[64]); - size_t ctx_size; -} crypto_sign_vtable; -.Ed -.Pp -The context argument to the functions shall be referred to as -.Dq outer signing context . -The outer signing context must contain a -.Vt crypto_sign_ctx_abstract -as -.Em its first member . -Other than that, the outer signing context may be defined freely. -Logically, it is required to contain some kind of hash context as well; -otherwise it cannot work as a custom hash function. -.Pp -Because the calling code cannot know the real type of the outer signing -context, -it is cast to -.Vt void * -when calling the hash functions in the vtable, -but the -.Fa ctx -argument to the functions in the vtable is always guaranteed to be the -outer signing context. -.Pp -The hash functions must not fail. -If they somehow can fail, -they have no way to propagate its error status, -and thus the only ways to handle errors -are to either assume an error never occurs (if reasonable) -or to induce a crash in the code when an error occurs. -.Pp -The fields of -.Vt crypto_sign_vtable -are: -.Bl -tag -width Ds -.It Fa hash -Function that computes a 64-byte hash for a given message -and writes the computed hash to -.Fa hash . -The output length -.Em must -be exactly 64 bytes. -This will normally be constructed using the functions that provide the -.Fa init , -.Fa update , -and -.Fa final -members. -.It Fa init -Function that initialises the hash context of an outer signing context. -.It Fa update -Function that updates the hash context of an outer signing context. -It must be able to handle message sizes of at least 32 bytes. -.It Fa final -Function that finalises the hash context of an outer signing context -and writes the computed hash to -.Fa hash . -The output length -.Em must -be exactly 64 bytes. -This function should wipe the hash context with -.Xr crypto_wipe 3monocypher -if it contains pointers to objects outside the outer signing context. -Monocypher takes care of wiping the outer signing context. -.It Fa ctx_size -The size of the outer signing context as determined by -.Fn sizeof . -.El -.Pp -The functions indicated in the -.Vt crypto_sign_vtable -must be thread-safe if any of Monocypher's signing functions are -accessed from multiple threads. -.Pp -After calling -.Fn crypto_sign_init_first_pass_custom_hash -or -.Fn crypto_check_init_custom_hash , -the -.Xr crypto_sign_update 3monocypher , -.Xr crypto_sign_final 3monocypher , -.Xr crypto_sign_init_second_pass 3monocypher , -.Xr crypto_check_update 3monocypher , -and -.Xr crypto_check_final 3monocypher -functions can be used as usual. -They will call into the hash vtable as required. -The same security considerations and semantics apply. -.Sh RETURN VALUES -These functions return nothing. -.Sh EXAMPLES -Defining and using a custom implementation of SHA-512 and crudely -checking its results against -.Xr crypto_ed25519_sign 3monocypher : -.Bd -literal -offset indent -struct outer_ctx { - crypto_sign_ctx_abstract sctx; - SHA2_CTX hash_ctx; -}; - -static void -my_hash(uint8_t hash[64], const uint8_t *msg, size_t len) -{ - SHA2_CTX ctx; - SHA512Init(&ctx); - SHA512Update(&ctx, msg, len); - SHA512Final(hash, &ctx); -} - -static void -my_init(void *ctx) -{ - struct outer_ctx *octx = (struct outer_ctx *)ctx; - SHA512Init(&octx->hash_ctx); -} - -static void -my_update(void *ctx, const uint8_t *msg, size_t len) -{ - struct outer_ctx *octx = (struct outer_ctx *)ctx; - SHA512Update(&octx->hash_ctx, msg, len); -} - -static void -my_final(void *ctx, uint8_t *hash) -{ - struct outer_ctx *octx = (struct outer_ctx *)ctx; - SHA512Final(hash, &octx->hash_ctx); -} - -static const crypto_sign_vtable my_vtable = { - my_hash, - my_init, - my_update, - my_final, - sizeof(struct outer_ctx) -}; - -int -main(void) -{ - uint8_t theirs[64], mine[64]; - uint8_t sk[32] = {0x01, 0x02, 0x03, 0x04}; - const uint8_t msg[] = { - 0x00, 0x01, 0x02, 0x04 - }; - - crypto_ed25519_sign(theirs, sk, NULL, msg, sizeof(msg)); - - struct outer_ctx ctx; - crypto_sign_ctx_abstract *actx = (crypto_sign_ctx_abstract*)&ctx; - crypto_sign_init_first_pass_custom_hash(actx, - sk, NULL, &my_vtable); - crypto_wipe(sk, sizeof(sk)); - crypto_sign_update( actx, msg, sizeof(msg)); - crypto_sign_init_second_pass(actx); - crypto_sign_update( actx, msg, sizeof(msg)); - crypto_sign_final( actx, mine); - - if (crypto_verify64(theirs, mine) != 0) { - fprintf(stderr, "theirs != mine\en"); - return 1; - } - puts("ok"); - return 0; -} -.Ed -.Sh SEE ALSO -.Xr crypto_blake2b 3monocypher , -.Xr crypto_sha512 3monocypher , -.Xr crypto_sign 3monocypher , -.Xr crypto_sign_init_first_pass 3monocypher , -.Xr crypto_wipe 3monocypher , -.Xr intro 3monocypher -.Sh HISTORY -The -.Fn crypto_sign_init_first_pass_custom_hash , -.Fn crypto_sign_public_key_custom_hash , -and -.Fn crypto_check_init_first_pass_custom_hash -functions first appeared in Monocypher 3.0.0. diff --git a/doc/man/man3/advanced/crypto_sign_init_second_pass.3monocypher b/doc/man/man3/advanced/crypto_sign_init_second_pass.3monocypher deleted file mode 120000 index 08c255b..0000000 --- a/doc/man/man3/advanced/crypto_sign_init_second_pass.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/advanced/crypto_sign_public_key_custom_hash.3monocypher b/doc/man/man3/advanced/crypto_sign_public_key_custom_hash.3monocypher deleted file mode 120000 index a4b73ad..0000000 --- a/doc/man/man3/advanced/crypto_sign_public_key_custom_hash.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_sign_init_first_pass_custom_hash.3monocypher \ No newline at end of file diff --git a/doc/man/man3/advanced/crypto_sign_update.3monocypher b/doc/man/man3/advanced/crypto_sign_update.3monocypher deleted file mode 120000 index 08c255b..0000000 --- a/doc/man/man3/advanced/crypto_sign_update.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/intro.3monocypher b/doc/man/man3/intro.3monocypher index e6d4d19..6e8c219 100644 --- a/doc/man/man3/intro.3monocypher +++ b/doc/man/man3/intro.3monocypher @@ -114,11 +114,6 @@ and implement EdDSA, with Curve25519 and BLAKE2b. This is the same as the more famous Ed25519, with SHA-512 replaced by the faster and more secure BLAKE2b. -.Pp -For highly specialised needs, -it is possible to use a custom hash function with EdDSA; -see -.Xr crypto_sign_init_first_pass_custom_hash 3monocypher . .Ss Constant time comparison .Xr crypto_verify16 3monocypher , .Xr crypto_verify32 3monocypher , @@ -153,10 +148,6 @@ and .Xr crypto_chacha20 3monocypher , .Xr crypto_chacha20_ctr 3monocypher , .Xr crypto_check 3monocypher , -.Xr crypto_check_final 3monocypher , -.Xr crypto_check_init 3monocypher , -.Xr crypto_check_init_custom_hash 3monocypher , -.Xr crypto_check_update 3monocypher , .Xr crypto_curve_to_hidden 3monocypher , .Xr crypto_from_eddsa_private 3monocypher , .Xr crypto_from_eddsa_public 3monocypher , @@ -172,13 +163,7 @@ and .Xr crypto_poly1305_init 3monocypher , .Xr crypto_poly1305_update 3monocypher , .Xr crypto_sign 3monocypher , -.Xr crypto_sign_final 3monocypher , -.Xr crypto_sign_init_first_pass 3monocypher , -.Xr crypto_sign_init_first_pass_custom_hash 3monocypher , -.Xr crypto_sign_init_second_pass 3monocypher , .Xr crypto_sign_public_key 3monocypher , -.Xr crypto_sign_public_key_custom_hash 3monocypher , -.Xr crypto_sign_update 3monocypher , .Xr crypto_unlock 3monocypher , .Xr crypto_unlock_aead 3monocypher , .Xr crypto_verify16 3monocypher , @@ -196,14 +181,8 @@ and .Xr crypto_from_ed25519_private 3monocypher , .Xr crypto_from_ed25519_public 3monocypher , .Xr crypto_ed25519_check 3monocypher , -.Xr crypto_ed25519_check_init 3monocypher , -.Xr crypto_ed25519_check_update 3monocypher , -.Xr crypto_ed25519_check_final 3monocypher , .Xr crypto_ed25519_public_key 3monocypher , .Xr crypto_ed25519_sign 3monocypher , -.Xr crypto_ed25519_sign_init_first_pass 3monocypher , -.Xr crypto_ed25519_sign_init_second_pass 3monocypher , -.Xr crypto_ed25519_sign_final 3monocypher , .Xr crypto_hmac_sha512 3monocypher , .Xr crypto_hmac_sha512_init 3monocypher , .Xr crypto_hmac_sha512_update 3monocypher , diff --git a/doc/man/man3/optional/crypto_ed25519_check_final.3monocypher b/doc/man/man3/optional/crypto_ed25519_check_final.3monocypher deleted file mode 120000 index a2ccd03..0000000 --- a/doc/man/man3/optional/crypto_ed25519_check_final.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_ed25519_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/optional/crypto_ed25519_check_init.3monocypher b/doc/man/man3/optional/crypto_ed25519_check_init.3monocypher deleted file mode 120000 index a2ccd03..0000000 --- a/doc/man/man3/optional/crypto_ed25519_check_init.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_ed25519_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/optional/crypto_ed25519_check_update.3monocypher b/doc/man/man3/optional/crypto_ed25519_check_update.3monocypher deleted file mode 120000 index a2ccd03..0000000 --- a/doc/man/man3/optional/crypto_ed25519_check_update.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_ed25519_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/optional/crypto_ed25519_sign_final.3monocypher b/doc/man/man3/optional/crypto_ed25519_sign_final.3monocypher deleted file mode 120000 index a2ccd03..0000000 --- a/doc/man/man3/optional/crypto_ed25519_sign_final.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_ed25519_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/optional/crypto_ed25519_sign_init_first_pass.3monocypher b/doc/man/man3/optional/crypto_ed25519_sign_init_first_pass.3monocypher deleted file mode 100644 index 61fd649..0000000 --- a/doc/man/man3/optional/crypto_ed25519_sign_init_first_pass.3monocypher +++ /dev/null @@ -1,157 +0,0 @@ -.\" This file is dual-licensed. Choose whichever you want. -.\" -.\" The first licence is a regular 2-clause BSD licence. The second licence -.\" is the CC-0 from Creative Commons. It is intended to release Monocypher -.\" to the public domain. The BSD licence serves as a fallback option. -.\" -.\" SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0 -.\" -.\" ---------------------------------------------------------------------------- -.\" -.\" Copyright (c) 2019-2020, 2022 Fabio Scotoni -.\" All rights reserved. -.\" -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions are -.\" met: -.\" -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the -.\" distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -.\" "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -.\" HOLDER OR CONTRIBUTORS 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 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -.\" -.\" ---------------------------------------------------------------------------- -.\" -.\" Written in 2019-2020 and 2022 by Fabio Scotoni -.\" -.\" To the extent possible under law, the author(s) have dedicated all copyright -.\" and related neighboring rights to this software to the public domain -.\" worldwide. This software is distributed without any warranty. -.\" -.\" You should have received a copy of the CC0 Public Domain Dedication along -.\" with this software. If not, see -.\" -.\" -.Dd February 13, 2022 -.Dt CRYPTO_ED25519_SIGN_INIT_FIRST_PASS 3MONOCYPHER -.Os -.Sh NAME -.Nm crypto_ed25519_sign_init_first_pass , -.Nm crypto_ed25519_sign_update , -.Nm crypto_ed25519_sign_final , -.Nm crypto_ed25519_sign_init_second_pass , -.Nm crypto_ed25519_check_init , -.Nm crypto_ed25519_check_update , -.Nm crypto_ed25519_check_final -.Nd incremental public key signatures -.Sh SYNOPSIS -.In monocypher-ed25519.h -.Ft void -.Fo crypto_ed25519_sign_init_first_pass -.Fa "crypto_ed25519_sign_ctx *ctx" -.Fa "const uint8_t secret_key[32]" -.Fa "const uint8_t public_key[32]" -.Fc -.Ft void -.Fo crypto_ed25519_sign_update -.Fa "crypto_ed25519_sign_ctx *ctx" -.Fa "const uint8_t *message" -.Fa "size_t message_size" -.Fc -.Ft void -.Fo crypto_ed25519_sign_final -.Fa "crypto_ed25519_sign_ctx *ctx" -.Fa "uint8_t signature[64]" -.Fc -.Ft void -.Fo crypto_ed25519_sign_init_second_pass -.Fa "crypto_ed25519_sign_ctx *ctx" -.Fc -.Ft void -.Fo crypto_ed25519_check_init -.Fa "crypto_ed25519_check_ctx *ctx" -.Fa "const uint8_t signature[64]" -.Fa "const uint8_t public_key[32]" -.Fc -.Ft void -.Fo crypto_ed25519_check_update -.Fa "crypto_ed25519_check_ctx *ctx" -.Fa "const uint8_t *message" -.Fa "size_t message_size" -.Fc -.Ft int -.Fo crypto_ed25519_check_final -.Fa "crypto_ed25519_check_ctx *ctx" -.Fc -.Sh DESCRIPTION -These functions are variants of -.Xr crypto_ed25519_sign 3monocypher -and -.Xr crypto_ed25519_check 3monocypher . -Prefer those simpler functions if possible. -.Pp -These functions provide Ed25519 public key signatures and verification -with SHA-512 as the underlying hash function. -They are interoperable with other Ed25519 implementations. -If you have no interoperability requirements, prefer -.Xr crypto_sign 3monocypher . -.Pp -The arguments, security considerations, and semantics are the same as -those described in -.Xr crypto_sign_init_first_pass 3monocypher -and -.Xr crypto_sign 3monocypher . -.Sh RETURN VALUES -.Fn crypto_ed25519_sign_init_first_pass , -.Fn crypto_ed25519_sign_init_second_pass , -.Fn crypto_ed25519_sign_update , -.Fn crypto_ed25519_sign_final , -.Fn crypto_ed25519_check_init , -and -.Fn crypto_ed25519_check_update -return nothing. -.Pp -.Fn crypto_ed25519_check_final -returns 0 for legitimate messages and -1 for forgeries. -.Sh SEE ALSO -.Xr crypto_blake2b 3monocypher , -.Xr crypto_x25519 3monocypher , -.Xr crypto_lock 3monocypher , -.Xr crypto_ed25519_sign 3monocypher , -.Xr crypto_sign 3monocypher , -.Xr crypto_sign_init_first_pass 3monocypher , -.Xr crypto_sha512 3monocypher , -.Xr crypto_wipe 3monocypher , -.Xr intro 3monocypher -.Sh STANDARDS -These functions implement Ed25519 as described in RFC 8032. -.Sh HISTORY -The -.Fn crypto_ed25519_sign_init_first_pass , -.Fn crypto_ed25519_sign_update , -.Fn crypto_ed25519_sign_final , -.Fn crypto_ed25519_sign_init_second_pass , -.Fn crypto_ed25519_check_init , -.Fn crypto_ed25519_check_update , -and -.Fn crypto_ed25519_check_final -functions first appeared in Monocypher 3.0.0. -They replace recompilation of Monocypher with the -.Dv ED25519_SHA512 -preprocessor definition. diff --git a/doc/man/man3/optional/crypto_ed25519_sign_init_second_pass.3monocypher b/doc/man/man3/optional/crypto_ed25519_sign_init_second_pass.3monocypher deleted file mode 120000 index a2ccd03..0000000 --- a/doc/man/man3/optional/crypto_ed25519_sign_init_second_pass.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_ed25519_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/doc/man/man3/optional/crypto_ed25519_sign_update.3monocypher b/doc/man/man3/optional/crypto_ed25519_sign_update.3monocypher deleted file mode 120000 index a2ccd03..0000000 --- a/doc/man/man3/optional/crypto_ed25519_sign_update.3monocypher +++ /dev/null @@ -1 +0,0 @@ -crypto_ed25519_sign_init_first_pass.3monocypher \ No newline at end of file diff --git a/src/monocypher.c b/src/monocypher.c index 1b7d827..2869c38 100644 --- a/src/monocypher.c +++ b/src/monocypher.c @@ -637,23 +637,6 @@ void crypto_blake2b(u8 hash[64], const u8 *message, size_t message_size) crypto_blake2b_general(hash, 64, 0, 0, message, message_size); } -static void blake2b_vtable_init(void *ctx) { - crypto_blake2b_init(&((crypto_sign_ctx*)ctx)->hash); -} -static void blake2b_vtable_update(void *ctx, const u8 *m, size_t s) { - crypto_blake2b_update(&((crypto_sign_ctx*)ctx)->hash, m, s); -} -static void blake2b_vtable_final(void *ctx, u8 *h) { - crypto_blake2b_final(&((crypto_sign_ctx*)ctx)->hash, h); -} -const crypto_sign_vtable crypto_blake2b_vtable = { - crypto_blake2b, - blake2b_vtable_init, - blake2b_vtable_update, - blake2b_vtable_final, - sizeof(crypto_sign_ctx), -}; - //////////////// /// Argon2 i /// //////////////// @@ -1517,11 +1500,12 @@ static void fe_invert(fe out, const fe x) } // trim a scalar for scalar multiplication -static void trim_scalar(u8 scalar[32]) +void crypto_eddsa_trim_scalar(u8 out[32], const u8 in[32]) { - scalar[ 0] &= 248; - scalar[31] &= 127; - scalar[31] |= 64; + COPY(out, in, 32); + out[ 0] &= 248; + out[31] &= 127; + out[31] |= 64; } // get bit from scalar at position i @@ -1599,8 +1583,7 @@ void crypto_x25519(u8 raw_shared_secret[32], { // restrict the possible scalar values u8 e[32]; - COPY(e, your_secret_key, 32); - trim_scalar(e); + crypto_eddsa_trim_scalar(e, your_secret_key); scalarmult(raw_shared_secret, e, their_public_key, 255); WIPE_BUFFER(e); } @@ -1706,16 +1689,17 @@ static void mod_l(u8 reduced[32], const u32 x[16]) WIPE_BUFFER(xr); } -static void reduce(u8 r[64]) +void crypto_eddsa_reduce(u8 reduced[32], const u8 expanded[64]) { u32 x[16]; - load32_le_buf(x, r, 16); - mod_l(r, x); + load32_le_buf(x, expanded, 16); + mod_l(reduced, x); WIPE_BUFFER(x); } // r = (a * b) + c -static void mul_add(u8 r[32], const u8 a[32], const u8 b[32], const u8 c[32]) +void crypto_eddsa_mul_add(u8 r[32], + const u8 a[32], const u8 b[32], const u8 c[32]) { u32 A[8]; load32_le_buf(A, a, 8); u32 B[8]; load32_le_buf(B, b, 8); @@ -2195,7 +2179,7 @@ static void ge_scalarmult_base(ge *p, const u8 scalar[32]) // All bits set form: 1 means 1, 0 means -1 u8 s_scalar[32]; - mul_add(s_scalar, scalar, half_mod_L, half_ones); + crypto_eddsa_mul_add(s_scalar, scalar, half_mod_L, half_ones); // Double and add ladder fe tmp_a, tmp_b; // temporaries for addition @@ -2226,41 +2210,66 @@ static void ge_scalarmult_base(ge *p, const u8 scalar[32]) WIPE_BUFFER(s_scalar); } -void crypto_sign_public_key_custom_hash(u8 public_key[32], - const u8 secret_key[32], - const crypto_sign_vtable *hash) +void crypto_eddsa_scalarbase(u8 point[32], const u8 scalar[32]) +{ + ge P; + ge_scalarmult_base(&P, scalar); + ge_tobytes(point, &P); + WIPE_CTX(&P); +} + +int crypto_eddsa_r_check(u8 r_check[32], const u8 public_key[32], + const u8 h_ram[32], const u8 s[32]) { - u8 a[64]; - hash->hash(a, secret_key, 32); - trim_scalar(a); ge A; - ge_scalarmult_base(&A, a); - ge_tobytes(public_key, &A); - WIPE_BUFFER(a); - WIPE_CTX(&A); + u32 s32[8]; // s (different encoding) + load32_le_buf(s32, s, 8); + + if (ge_frombytes_neg_vartime(&A, public_key) || // A = -pk + is_above_l(s32)) { // prevent s malleability + return -1; + } + ge_double_scalarmult_vartime(&A, h_ram, s); // A = [s]B - [h_ram]pk + ge_tobytes(r_check, &A); // r_check = A + return 0; } void crypto_sign_public_key(u8 public_key[32], const u8 secret_key[32]) { - crypto_sign_public_key_custom_hash(public_key, secret_key, - &crypto_blake2b_vtable); + u8 a[64]; + crypto_blake2b(a, secret_key, 32); + crypto_eddsa_trim_scalar(a, a); + crypto_eddsa_scalarbase(public_key, a); } -void crypto_sign_init_first_pass_custom_hash(crypto_sign_ctx_abstract *ctx, - const u8 secret_key[32], - const u8 public_key[32], - const crypto_sign_vtable *hash) +static void hash_reduce(u8 h[32], + const u8 *a, size_t a_size, + const u8 *b, size_t b_size, + const u8 *c, size_t c_size) { - ctx->hash = hash; // set vtable - u8 *a = ctx->buf; - u8 *prefix = ctx->buf + 32; - ctx->hash->hash(a, secret_key, 32); - trim_scalar(a); + u8 hash[64]; + crypto_blake2b_ctx ctx; + crypto_blake2b_init (&ctx); + crypto_blake2b_update(&ctx, a, a_size); + crypto_blake2b_update(&ctx, b, b_size); + crypto_blake2b_update(&ctx, c, c_size); + crypto_blake2b_final (&ctx, hash); + crypto_eddsa_reduce(h, hash); +} +void crypto_sign(u8 signature[64], + const u8 secret_key[32], + const u8 public_key[32], + const u8 *message, size_t message_size) +{ + u8 a[64]; + crypto_blake2b(a, secret_key, 32); + crypto_eddsa_trim_scalar(a, a); + u8 pk[32]; // not secret, not wiped if (public_key == 0) { - crypto_sign_public_key_custom_hash(ctx->pk, secret_key, ctx->hash); + crypto_eddsa_scalarbase(pk, a); } else { - COPY(ctx->pk, public_key, 32); + COPY(pk, public_key, 32); } // Deterministic part of EdDSA: Construct a nonce by hashing the message @@ -2268,125 +2277,34 @@ void crypto_sign_init_first_pass_custom_hash(crypto_sign_ctx_abstract *ctx, // An actual random number would work just fine, and would save us // the trouble of hashing the message twice. If we did that // however, the user could fuck it up and reuse the nonce. - ctx->hash->init (ctx); - ctx->hash->update(ctx, prefix , 32); -} + u8 r[32]; + hash_reduce(r, a + 32, 32, message, message_size, 0, 0); -void crypto_sign_init_first_pass(crypto_sign_ctx_abstract *ctx, - const u8 secret_key[32], - const u8 public_key[32]) -{ - crypto_sign_init_first_pass_custom_hash(ctx, secret_key, public_key, - &crypto_blake2b_vtable); -} + // First half of the signature R = [r]B + u8 R[32]; // Not secret, not wiped + crypto_eddsa_scalarbase(R, r); -void crypto_sign_update(crypto_sign_ctx_abstract *ctx, - const u8 *msg, size_t msg_size) -{ - ctx->hash->update(ctx, msg, msg_size); -} + // Second half of the signature + u8 h_ram[32]; + hash_reduce(h_ram, R, 32, pk, 32, message, message_size); + COPY(signature, R, 32); + crypto_eddsa_mul_add(signature + 32, h_ram, a, r); // s = h_ram * a + r -void crypto_sign_init_second_pass(crypto_sign_ctx_abstract *ctx) -{ - u8 *r = ctx->buf + 32; - u8 *half_sig = ctx->buf + 64; - ctx->hash->final(ctx, r); - reduce(r); - - // first half of the signature = "random" nonce times the base point - ge R; - ge_scalarmult_base(&R, r); - ge_tobytes(half_sig, &R); - WIPE_CTX(&R); - - // Hash R, the public key, and the message together. - // It cannot be done in parallel with the first hash. - ctx->hash->init (ctx); - ctx->hash->update(ctx, half_sig, 32); - ctx->hash->update(ctx, ctx->pk , 32); -} - -void crypto_sign_final(crypto_sign_ctx_abstract *ctx, u8 signature[64]) -{ - u8 *a = ctx->buf; - u8 *r = ctx->buf + 32; - u8 *half_sig = ctx->buf + 64; - u8 h_ram[64]; - ctx->hash->final(ctx, h_ram); - reduce(h_ram); - COPY(signature, half_sig, 32); - mul_add(signature + 32, h_ram, a, r); // s = h_ram * a + r + WIPE_BUFFER(a); + WIPE_BUFFER(r); WIPE_BUFFER(h_ram); - crypto_wipe(ctx, ctx->hash->ctx_size); } -void crypto_sign(u8 signature[64], - const u8 secret_key[32], - const u8 public_key[32], +int crypto_check(const u8 signature[64], const u8 public_key[32], const u8 *message, size_t message_size) { - crypto_sign_ctx ctx; - crypto_sign_ctx_abstract *actx = (crypto_sign_ctx_abstract*)&ctx; - crypto_sign_init_first_pass (actx, secret_key, public_key); - crypto_sign_update (actx, message, message_size); - crypto_sign_init_second_pass(actx); - crypto_sign_update (actx, message, message_size); - crypto_sign_final (actx, signature); -} - -void crypto_check_init_custom_hash(crypto_check_ctx_abstract *ctx, - const u8 signature[64], - const u8 public_key[32], - const crypto_sign_vtable *hash) -{ - ctx->hash = hash; // set vtable - COPY(ctx->buf, signature , 64); - COPY(ctx->pk , public_key, 32); - ctx->hash->init (ctx); - ctx->hash->update(ctx, signature , 32); - ctx->hash->update(ctx, public_key, 32); -} - -void crypto_check_init(crypto_check_ctx_abstract *ctx, const u8 signature[64], - const u8 public_key[32]) -{ - crypto_check_init_custom_hash(ctx, signature, public_key, - &crypto_blake2b_vtable); -} - -void crypto_check_update(crypto_check_ctx_abstract *ctx, - const u8 *msg, size_t msg_size) -{ - ctx->hash->update(ctx, msg, msg_size); -} - -int crypto_check_final(crypto_check_ctx_abstract *ctx) -{ - u8 *s = ctx->buf + 32; // s - u8 h_ram[64]; - u32 s32[8]; // s (different encoding) - ge A; - - ctx->hash->final(ctx, h_ram); - reduce(h_ram); - load32_le_buf(s32, s, 8); - if (ge_frombytes_neg_vartime(&A, ctx->pk) || // A = -pk - is_above_l(s32)) { // prevent s malleability + u8 h_ram [32]; + u8 r_check[32]; + hash_reduce(h_ram, signature, 32, public_key, 32, message, message_size); + if (crypto_eddsa_r_check(r_check, public_key, h_ram, signature + 32)) { return -1; } - ge_double_scalarmult_vartime(&A, h_ram, s); // A = [s]B - [h_ram]pk - ge_tobytes(ctx->pk, &A); // R_check = A - return crypto_verify32(ctx->buf, ctx->pk); // R == R_check ? OK : fail -} - -int crypto_check(const u8 signature[64], const u8 public_key[32], - const u8 *message, size_t message_size) -{ - crypto_check_ctx ctx; - crypto_check_ctx_abstract *actx = (crypto_check_ctx_abstract*)&ctx; - crypto_check_init (actx, signature, public_key); - crypto_check_update(actx, message, message_size); - return crypto_check_final(actx); + return crypto_verify32(r_check, signature); // R == R_check ? OK : fail } /////////////////////// @@ -2501,8 +2419,7 @@ void crypto_x25519_dirty_small(u8 public_key[32], const u8 secret_key[32]) }; // separate the main factor & the cofactor of the scalar u8 scalar[32]; - COPY(scalar, secret_key, 32); - trim_scalar(scalar); + crypto_eddsa_trim_scalar(scalar, secret_key); // Separate the main factor and the cofactor // @@ -2572,8 +2489,7 @@ void crypto_x25519_dirty_fast(u8 public_key[32], const u8 secret_key[32]) // Compute clean scalar multiplication u8 scalar[32]; ge pk; - COPY(scalar, secret_key, 32); - trim_scalar(scalar); + crypto_eddsa_trim_scalar(scalar, secret_key); ge_scalarmult_base(&pk, scalar); // Compute low order point @@ -2856,8 +2772,7 @@ void crypto_x25519_inverse(u8 blind_salt [32], const u8 private_key[32], 0xfffffffe, 0xffffffff, 0xffffffff, 0x0fffffff,}; u8 scalar[32]; - COPY(scalar, private_key, 32); - trim_scalar(scalar); + crypto_eddsa_trim_scalar(scalar, private_key); // Convert the scalar in Montgomery form // m_scl = scalar * 2^256 (modulo L) diff --git a/src/monocypher.h b/src/monocypher.h index f8fd070..1fa9bce 100644 --- a/src/monocypher.h +++ b/src/monocypher.h @@ -67,17 +67,6 @@ extern "C" { /// Type definitions /// //////////////////////// -// Vtable for EdDSA with a custom hash. -// Instantiate it to define a custom hash. -// Its size, contents, and layout, are part of the public API. -typedef struct { - void (*hash)(uint8_t hash[64], const uint8_t *message, size_t message_size); - void (*init )(void *ctx); - void (*update)(void *ctx, const uint8_t *message, size_t message_size); - void (*final )(void *ctx, uint8_t hash[64]); - size_t ctx_size; -} crypto_sign_vtable; - // Do not rely on the size or contents of any of the types below, // they may change without notice. @@ -99,20 +88,6 @@ typedef struct { size_t hash_size; } crypto_blake2b_ctx; -// Signatures (EdDSA) -typedef struct { - const crypto_sign_vtable *hash; - uint8_t buf[96]; - uint8_t pk [32]; -} crypto_sign_ctx_abstract; -typedef crypto_sign_ctx_abstract crypto_check_ctx_abstract; - -typedef struct { - crypto_sign_ctx_abstract ctx; - crypto_blake2b_ctx hash; -} crypto_sign_ctx; -typedef crypto_sign_ctx crypto_check_ctx; - //////////////////////////// /// High level interface /// //////////////////////////// @@ -180,9 +155,6 @@ void crypto_blake2b_final (crypto_blake2b_ctx *ctx, uint8_t *hash); void crypto_blake2b_general_init(crypto_blake2b_ctx *ctx, size_t hash_size, const uint8_t *key, size_t key_size); -// vtable for signatures -extern const crypto_sign_vtable crypto_blake2b_vtable; - // Password key derivation (Argon2 i) // ---------------------------------- @@ -319,6 +291,19 @@ void crypto_x25519_inverse(uint8_t blind_salt [32], const uint8_t private_key[32], const uint8_t curve_point[32]); +// EdDSA +// ----- +void crypto_eddsa_trim_scalar(uint8_t out[32], const uint8_t in[32]); +void crypto_eddsa_reduce(uint8_t reduced[32], const uint8_t expanded[64]); +void crypto_eddsa_mul_add(uint8_t r[32], + const uint8_t a[32], + const uint8_t b[32], + const uint8_t c[32]); +void crypto_eddsa_scalarbase(uint8_t point[32], const uint8_t scalar[32]); +int crypto_eddsa_r_check(uint8_t r_check[32], + const uint8_t public_key[32], + const uint8_t h_ram[32], + const uint8_t s[32]); // EdDSA to X25519 // --------------- @@ -326,44 +311,6 @@ void crypto_from_eddsa_private(uint8_t x25519[32], const uint8_t eddsa[32]); void crypto_from_eddsa_public (uint8_t x25519[32], const uint8_t eddsa[32]); -// EdDSA -- Incremental interface -// ------------------------------ - -// Signing (2 passes) -// Make sure the two passes hash the same message, -// else you might reveal the private key. -void crypto_sign_init_first_pass(crypto_sign_ctx_abstract *ctx, - const uint8_t secret_key[32], - const uint8_t public_key[32]); -void crypto_sign_update(crypto_sign_ctx_abstract *ctx, - const uint8_t *message, size_t message_size); -void crypto_sign_init_second_pass(crypto_sign_ctx_abstract *ctx); -// use crypto_sign_update() again. -void crypto_sign_final(crypto_sign_ctx_abstract *ctx, uint8_t signature[64]); - -// Verification (1 pass) -// Make sure you don't use (parts of) the message -// before you're done checking it. -void crypto_check_init (crypto_check_ctx_abstract *ctx, - const uint8_t signature[64], - const uint8_t public_key[32]); -void crypto_check_update(crypto_check_ctx_abstract *ctx, - const uint8_t *message, size_t message_size); -int crypto_check_final (crypto_check_ctx_abstract *ctx); - -// Custom hash interface -void crypto_sign_public_key_custom_hash(uint8_t public_key[32], - const uint8_t secret_key[32], - const crypto_sign_vtable *hash); -void crypto_sign_init_first_pass_custom_hash(crypto_sign_ctx_abstract *ctx, - const uint8_t secret_key[32], - const uint8_t public_key[32], - const crypto_sign_vtable *hash); -void crypto_check_init_custom_hash(crypto_check_ctx_abstract *ctx, - const uint8_t signature[64], - const uint8_t public_key[32], - const crypto_sign_vtable *hash); - // Elligator 2 // ----------- diff --git a/src/optional/monocypher-ed25519.c b/src/optional/monocypher-ed25519.c index 446553b..7230e47 100644 --- a/src/optional/monocypher-ed25519.c +++ b/src/optional/monocypher-ed25519.c @@ -283,23 +283,6 @@ void crypto_sha512(u8 hash[64], const u8 *message, size_t message_size) crypto_sha512_final (&ctx, hash); } -static void sha512_vtable_init(void *ctx) { - crypto_sha512_init(&((crypto_sign_ed25519_ctx*)ctx)->hash); -} -static void sha512_vtable_update(void *ctx, const u8 *m, size_t s) { - crypto_sha512_update(&((crypto_sign_ed25519_ctx*)ctx)->hash, m, s); -} -static void sha512_vtable_final(void *ctx, u8 *h) { - crypto_sha512_final(&((crypto_sign_ed25519_ctx*)ctx)->hash, h); -} -const crypto_sign_vtable crypto_sha512_vtable = { - crypto_sha512, - sha512_vtable_init, - sha512_vtable_update, - sha512_vtable_final, - sizeof(crypto_sign_ed25519_ctx), -}; - //////////////////// /// HMAC SHA 512 /// //////////////////// @@ -355,28 +338,27 @@ void crypto_hmac_sha512(u8 hmac[64], const u8 *key, size_t key_size, /////////////// /// Ed25519 /// /////////////// - -void crypto_ed25519_public_key(u8 public_key[32], - const u8 secret_key[32]) +void crypto_ed25519_public_key(u8 public_key[32], const u8 secret_key[32]) { - crypto_sign_public_key_custom_hash(public_key, secret_key, - &crypto_sha512_vtable); -} - -void crypto_ed25519_sign_init_first_pass(crypto_sign_ctx_abstract *ctx, - const u8 secret_key[32], - const u8 public_key[32]) -{ - crypto_sign_init_first_pass_custom_hash(ctx, secret_key, public_key, - &crypto_sha512_vtable); + u8 a[64]; + crypto_sha512(a, secret_key, 32); + crypto_eddsa_trim_scalar(a, a); + crypto_eddsa_scalarbase(public_key, a); } -void crypto_ed25519_check_init(crypto_check_ctx_abstract *ctx, - const u8 signature[64], - const u8 public_key[32]) +static void hash_reduce(u8 h[32], + const u8 *a, size_t a_size, + const u8 *b, size_t b_size, + const u8 *c, size_t c_size) { - crypto_check_init_custom_hash(ctx, signature, public_key, - &crypto_sha512_vtable); + u8 hash[64]; + crypto_sha512_ctx ctx; + crypto_sha512_init (&ctx); + crypto_sha512_update(&ctx, a, a_size); + crypto_sha512_update(&ctx, b, b_size); + crypto_sha512_update(&ctx, c, c_size); + crypto_sha512_final (&ctx, hash); + crypto_eddsa_reduce(h, hash); } void crypto_ed25519_sign(u8 signature [64], @@ -384,24 +366,50 @@ void crypto_ed25519_sign(u8 signature [64], const u8 public_key[32], const u8 *message, size_t message_size) { - crypto_sign_ed25519_ctx ctx; - crypto_sign_ctx_abstract *actx = (crypto_sign_ctx_abstract*)&ctx; - crypto_ed25519_sign_init_first_pass (actx, secret_key, public_key); - crypto_ed25519_sign_update (actx, message, message_size); - crypto_ed25519_sign_init_second_pass(actx); - crypto_ed25519_sign_update (actx, message, message_size); - crypto_ed25519_sign_final (actx, signature); + u8 a[64]; + crypto_sha512(a, secret_key, 32); + crypto_eddsa_trim_scalar(a, a); + u8 pk[32]; // not secret, not wiped + if (public_key == 0) { + crypto_eddsa_scalarbase(pk, a); + } else { + COPY(pk, public_key, 32); + } + + // Deterministic part of EdDSA: Construct a nonce by hashing the message + // instead of generating a random number. + // An actual random number would work just fine, and would save us + // the trouble of hashing the message twice. If we did that + // however, the user could fuck it up and reuse the nonce. + u8 r[32]; + hash_reduce(r, a + 32, 32, message, message_size, 0, 0); + + // First half of the signature R = [r]B + u8 R[32]; // Not secret, not wiped + crypto_eddsa_scalarbase(R, r); + + // Second half of the signature + u8 h_ram[32]; + hash_reduce(h_ram, R, 32, pk, 32, message, message_size); + COPY(signature, R, 32); + crypto_eddsa_mul_add(signature + 32, h_ram, a, r); // s = h_ram * a + r + + WIPE_BUFFER(a); + WIPE_BUFFER(r); + WIPE_BUFFER(h_ram); } int crypto_ed25519_check(const u8 signature [64], const u8 public_key[32], const u8 *message, size_t message_size) { - crypto_check_ed25519_ctx ctx; - crypto_check_ctx_abstract *actx = (crypto_check_ctx_abstract*)&ctx; - crypto_ed25519_check_init (actx, signature, public_key); - crypto_ed25519_check_update(actx, message, message_size); - return crypto_ed25519_check_final(actx); + u8 h_ram [32]; + u8 r_check[32]; + hash_reduce(h_ram, signature, 32, public_key, 32, message, message_size); + if (crypto_eddsa_r_check(r_check, public_key, h_ram, signature + 32)) { + return -1; + } + return crypto_verify32(r_check, signature); // R == R_check ? OK : fail } void crypto_from_ed25519_private(u8 x25519[32], const u8 eddsa[32]) diff --git a/src/optional/monocypher-ed25519.h b/src/optional/monocypher-ed25519.h index 07ef9be..8ee14e1 100644 --- a/src/optional/monocypher-ed25519.h +++ b/src/optional/monocypher-ed25519.h @@ -80,11 +80,6 @@ typedef struct { crypto_sha512_ctx ctx; } crypto_hmac_sha512_ctx; -typedef struct { - crypto_sign_ctx_abstract ctx; - crypto_sha512_ctx hash; -} crypto_sign_ed25519_ctx; -typedef crypto_sign_ed25519_ctx crypto_check_ed25519_ctx; // SHA 512 // ------- @@ -95,9 +90,6 @@ void crypto_sha512_final (crypto_sha512_ctx *ctx, uint8_t hash[64]); void crypto_sha512(uint8_t hash[64], const uint8_t *message, size_t message_size); -// vtable for signatures -extern const crypto_sign_vtable crypto_sha512_vtable; - // HMAC SHA 512 // ------------ @@ -114,11 +106,9 @@ void crypto_hmac_sha512(uint8_t hmac[64], // Ed25519 // ------- -// Generate public key void crypto_ed25519_public_key(uint8_t public_key[32], const uint8_t secret_key[32]); -// Direct interface void crypto_ed25519_sign(uint8_t signature [64], const uint8_t secret_key[32], const uint8_t public_key[32], // optional, may be 0 @@ -127,21 +117,6 @@ int crypto_ed25519_check(const uint8_t signature [64], const uint8_t public_key[32], const uint8_t *message, size_t message_size); -// Incremental interface -void crypto_ed25519_sign_init_first_pass(crypto_sign_ctx_abstract *ctx, - const uint8_t secret_key[32], - const uint8_t public_key[32]); -#define crypto_ed25519_sign_update crypto_sign_update -#define crypto_ed25519_sign_init_second_pass crypto_sign_init_second_pass -// use crypto_ed25519_sign_update() again. -#define crypto_ed25519_sign_final crypto_sign_final - -void crypto_ed25519_check_init(crypto_check_ctx_abstract *ctx, - const uint8_t signature[64], - const uint8_t public_key[32]); -#define crypto_ed25519_check_update crypto_check_update -#define crypto_ed25519_check_final crypto_check_final - void crypto_from_ed25519_private(uint8_t x25519[32], const uint8_t eddsa[32]); #define crypto_from_ed25519_public crypto_from_eddsa_public diff --git a/tests/test.c b/tests/test.c index 3d4d7a1..4606c88 100644 --- a/tests/test.c +++ b/tests/test.c @@ -635,7 +635,7 @@ static int p_hmac_sha512() return status; } -// Tests that the input and output buffers of crypto_sha_512 can overlap. +// Tests that the input and output buffers of HMAC can overlap. static int p_hmac_sha512_overlap() { #undef INPUT_SIZE @@ -812,41 +812,6 @@ static int p_eddsa_overlap() return status; } -static int p_eddsa_incremental() -{ - int status = 0; - FOR (i, 0, MESSAGE_SIZE) { - RANDOM_INPUT(msg, MESSAGE_SIZE); - RANDOM_INPUT(sk, 32); - u8 pk [32]; crypto_sign_public_key(pk, sk); - u8 sig_mono[64]; crypto_sign(sig_mono, sk, pk, msg, MESSAGE_SIZE); - u8 sig_incr[64]; - { - crypto_sign_ctx ctx; - crypto_sign_ctx_abstract *actx = (crypto_sign_ctx_abstract*)&ctx; - crypto_sign_init_first_pass (actx, sk, pk); - crypto_sign_update (actx, msg , i); - crypto_sign_update (actx, msg+i, MESSAGE_SIZE-i); - crypto_sign_init_second_pass(actx); - crypto_sign_update (actx, msg , i); - crypto_sign_update (actx, msg+i, MESSAGE_SIZE-i); - crypto_sign_final (actx, sig_incr); - } - status |= memcmp(sig_mono, sig_incr, 64); - status |= crypto_check(sig_mono, pk, msg, MESSAGE_SIZE); - { - crypto_check_ctx ctx; - crypto_check_ctx_abstract *actx = (crypto_check_ctx_abstract*)&ctx; - crypto_check_init (actx, sig_incr, pk); - crypto_check_update(actx, msg , i); - crypto_check_update(actx, msg+i, MESSAGE_SIZE-i); - status |= crypto_check_final(actx); - } - } - printf("%s: EdDSA (incremental)\n", status != 0 ? "FAILED" : "OK"); - return status; -} - static int p_aead() { int status = 0; @@ -1182,7 +1147,6 @@ int main(int argc, char *argv[]) status |= p_eddsa_roundtrip(); status |= p_eddsa_random(); status |= p_eddsa_overlap(); - status |= p_eddsa_incremental(); status |= p_aead(); status |= p_elligator_direct_msb(); status |= p_elligator_direct_overlap();