From: Loup Vaillant Date: Mon, 30 Mar 2020 13:28:43 +0000 (+0200) Subject: Added constant time tests with Valgrind X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=2b41a16c7d368c9f5d7cbd0e4665e9f992948c32;p=Monocypher.git Added constant time tests with Valgrind The trick is to call Monocypher API with uninitialised buffers. If Valgrind complains about uninitialised something, that means an array index or a conditional jump depends on secret data. Note that crypto_check() is not tested: that's because it doesn't even try to be constant time. Note that a couple tested functions do have secret dependent conditional jumps. Those jumps however are just final checks, that just reveal success or failure (and those are revealed anyway, as part as the semantics of the function being tested). Note that optimisations are disabled for the compilation of `ctgrind.c` and the linking of `ctgrind.out`. This is an attempt to maximise Valgrind's findings. Also note that Valgrind seems to miss a secret dependent conditional jump (it finds only one where we should have 2). But that may just be Valgrind squashing the error report, instead of an actual miss. --- diff --git a/makefile b/makefile index 69d09d6..86048aa 100644 --- a/makefile +++ b/makefile @@ -67,7 +67,7 @@ endif .PHONY: all library static-library dynamic-library \ install install-doc pkg-config-libhydrogen \ - check test \ + check test ctgrind \ speed speed-sodium speed-tweetnacl speed-hydrogen speed-c25519 \ clean uninstall \ dist @@ -144,6 +144,9 @@ speed-c25519 : speed-c25519.out test test-legacy speed speed-sodium speed-tweetnacl speed-hydrogen speed-c25519: ./$< +ctgrind: ctgrind.out + valgrind ./ctgrind.out + # Monocypher libraries lib/libmonocypher.a: lib/monocypher.o $(LINK_ED25519) ar cr $@ $^ @@ -169,6 +172,7 @@ SPEED = tests/speed lib/utils.o :tests/utils.c lib/test.o :tests/test.c $(TEST_COMMON) tests/vectors.h lib/test-legacy.o :tests/test-legacy.c $(TEST_LEGACY) tests/vectors.h +lib/ctgrind.o :tests/ctgrind.c $(TEST_COMMON) lib/speed.o :$(SPEED)/speed.c $(TEST_COMMON) $(SPEED)/speed.h lib/speed-tweetnacl.o:$(SPEED)/speed-tweetnacl.c $(TEST_COMMON) $(SPEED)/speed.h lib/utils.o lib/test.o lib/test-legacy.o lib/speed.o: @@ -176,6 +180,11 @@ lib/utils.o lib/test.o lib/test-legacy.o lib/speed.o: $(CC) $(CFLAGS) \ -I src -I src/optional -I tests \ -fPIC -c -o $@ $< +lib/ctgrind.o: # suppress optimisations to maximise findings + @mkdir -p $(@D) + $(CC) $(CFLAGS) -O0 \ + -I src -I src/optional -I tests \ + -fPIC -c -o $@ $< lib/speed-tweetnacl.o: @mkdir -p $(@D) @@ -223,9 +232,12 @@ lib/speed-c25519.o:$(SPEED)/speed-c25519.c \ TEST_OBJ= lib/utils.o lib/monocypher.o test.out : lib/test.o $(TEST_OBJ) lib/monocypher-ed25519.o test-legacy.out: lib/test-legacy.o $(TEST_OBJ) lib/chacha20.o lib/aead-incr.o +ctgrind.out : lib/ctgrind.o $(TEST_OBJ) lib/monocypher-ed25519.o speed.out : lib/speed.o $(TEST_OBJ) lib/monocypher-ed25519.o test.out test-legacy.out speed.out: $(CC) $(CFLAGS) -I src -I src/optional -o $@ $^ +ctgrind.out: + $(CC) $(CFLAGS) -O0 -I src -I src/optional -o $@ $^ speed-sodium.out: lib/speed-sodium.o lib/utils.o $(CC) $(CFLAGS) -o $@ $^ \ `pkg-config --cflags libsodium` \ diff --git a/tests/ctgrind.c b/tests/ctgrind.c new file mode 100644 index 0000000..dba60b5 --- /dev/null +++ b/tests/ctgrind.c @@ -0,0 +1,309 @@ +// This file is dual-licensed. Choose whichever licence you want from +// the two licences listed below. +// +// 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-2020, Loup Vaillant +// 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 2020 by Loup Vaillant +// +// 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 +// + +#include +#include +#include +#include "monocypher.h" +#include "monocypher-ed25519.h" +#include "utils.h" + +static void verify16() +{ + u8 a[16]; + u8 b[16]; + crypto_verify16(a, b); +} + +static void verify32() +{ + u8 a[32]; + u8 b[32]; + crypto_verify32(a, b); +} + +static void verify64() +{ + u8 a[64]; + u8 b[64]; + crypto_verify64(a, b); +} + +static void wipe() +{ + FOR (i, 0, 128) { + u8 secret[128]; + crypto_wipe(secret, i); + } +} + +static void lock_aead() +{ + FOR(i, 0, 128) { + u8 mac [ 16]; + u8 cipher_text[128]; + u8 key [ 32]; + u8 nonce [ 24]; + u8 ad [128]; + u8 plain_text [128]; + crypto_lock_aead(mac, cipher_text, key, nonce, ad, i, plain_text, i); + } +} + +static void unlock_aead() +{ + FOR(i, 0, 128) { + u8 plain_text [128]; + u8 key [ 32]; + u8 nonce [ 24]; + u8 mac [ 16]; + u8 ad [128]; + u8 cipher_text[128]; + crypto_unlock_aead(plain_text, key, nonce, mac, ad, i, cipher_text, i); + } +} + +static void blake2b_general() +{ + FOR (i, 0, 256) { + u8 hash [ 64]; + u8 key [ 64]; + u8 message[256]; + crypto_blake2b_general(hash, 64, key, 0, message, i); + } + FOR (i, 0, 64) { + u8 hash [ 64]; + u8 key [ 64]; + u8 message[256]; + crypto_blake2b_general(hash, 64, key, i, message, 128); + } + FOR (i, 0, 64) { + u8 hash [ 64]; + u8 key [ 64]; + u8 message[256]; + crypto_blake2b_general(hash, i, key, 0, message, 0); + } +} + +static void argon2i_general() +{ + void *work_area = alloc(1024 * 600); + u8 hash [ 32]; + u8 password[ 16]; + u8 salt [ 16]; + u8 key [ 32]; + u8 ad [128]; + crypto_argon2i_general(hash, 32, work_area, 600, 3, + password, 16, salt, 16, key, 32, ad, 128); + free(work_area); +} + +static void key_exchange() +{ + u8 shared_key [32]; + u8 your_secret_key [32]; + u8 their_public_key[32]; + crypto_key_exchange(shared_key, your_secret_key, their_public_key); +} + +static void sign_public_key() +{ + u8 public_key[32]; + u8 secret_key[32]; + crypto_sign_public_key(public_key, secret_key); +} + +static void sign() +{ + u8 signature [64]; + u8 secret_key[32]; + u8 public_key[32]; + u8 message [64]; + crypto_sign(signature, secret_key, public_key, message, 64); +} + +static void from_eddsa_private() +{ + u8 x25519[32]; + u8 eddsa [32]; + crypto_from_eddsa_private(x25519, eddsa); +} +static void from_eddsa_public() +{ + u8 x25519[32]; + u8 eddsa [32]; + crypto_from_eddsa_public(x25519, eddsa); +} + +static void hidden_to_curve() +{ + u8 curve [32]; + u8 hidden[32]; + crypto_hidden_to_curve(curve, hidden); +} + +static void curve_to_hidden() +{ + u8 hidden[32]; + u8 curve [32]; + u8 tweak; // The compiler notices this one is used uninitialised + crypto_curve_to_hidden(hidden, curve, tweak); +} + +static void hidden_key_pair() +{ + u8 hidden [32]; + u8 secret_key[32]; + u8 seed [32]; + crypto_hidden_key_pair(hidden, secret_key,seed); +} + +static void h_chacha20() +{ + u8 out[32], key[32], in[16]; + crypto_hchacha20(out, key, in); +} + +static void chacha20() +{ + FOR (i, 0, 128) { + u8 cipher_text[128]; + u8 plain_text [128]; + u8 key [ 32]; + u8 nonce [ 8]; + crypto_chacha20(cipher_text, plain_text, i, key, nonce); + } +} +static void xchacha20() +{ + FOR (i, 0, 128) { + u8 cipher_text[128]; + u8 plain_text [128]; + u8 key [ 32]; + u8 nonce [ 24]; + crypto_xchacha20(cipher_text, plain_text, i, key, nonce); + } +} +static void ietf_chacha20() +{ + FOR (i, 0, 128) { + u8 cipher_text[128]; + u8 plain_text [128]; + u8 key [ 32]; + u8 nonce [ 12]; + crypto_ietf_chacha20(cipher_text, plain_text, i, key, nonce); + } +} + +static void poly1305() +{ + FOR (i, 0, 32) { + u8 mac [16]; + u8 message [32]; + u8 key [32]; + crypto_poly1305(mac, message, i, key); + } +} + +static void x25519_dirty_small() +{ + u8 pk[32]; + u8 sk[32]; + crypto_x25519_dirty_small(pk, sk); +} +static void x25519_dirty_fast() +{ + u8 pk[32]; + u8 sk[32]; + crypto_x25519_dirty_fast(pk, sk); +} + +static void x25519_inverse() +{ + u8 blind_salt [32]; + u8 private_key[32]; + u8 curve_point[32]; + crypto_x25519_inverse(blind_salt, private_key, curve_point); +} + + +#define RUN(f, s) printf("%s: crypto_"#f"\n", s); f() + +int main() +{ + RUN(verify16 , "constant time"); + RUN(verify32 , "constant time"); + RUN(verify64 , "constant time"); + RUN(wipe , "constant time"); + RUN(lock_aead , "constant time"); + RUN(unlock_aead , "1 conditional"); + RUN(blake2b_general , "constant time"); + RUN(argon2i_general , "constant time"); + RUN(key_exchange , "constant time"); + RUN(sign_public_key , "constant time"); + RUN(sign , "constant time"); + printf( "skipped : crypto_check.\n"); + RUN(from_eddsa_private, "constant time"); + RUN(from_eddsa_public , "constant time"); + RUN(hidden_to_curve , "constant time"); + RUN(curve_to_hidden , "1 conditional"); + RUN(hidden_key_pair , "1 conditional"); // shouldn't that be 2? + RUN(h_chacha20 , "constant time"); + RUN(chacha20 , "constant time"); + RUN(xchacha20 , "constant time"); + RUN(ietf_chacha20 , "constant time"); + RUN(poly1305 , "constant time"); + RUN(x25519_dirty_small, "constant time"); + RUN(x25519_dirty_fast , "constant time"); + RUN(x25519_inverse , "constant time"); + + return 0; +}