]> git.codecow.com Git - Monocypher.git/commitdiff
added random self-consistency tests
authorLoup Vaillant <loup@loup-vaillant.fr>
Tue, 4 Jul 2017 20:13:53 +0000 (22:13 +0200)
committerLoup Vaillant <loup@loup-vaillant.fr>
Tue, 4 Jul 2017 20:44:08 +0000 (22:44 +0200)
.gitignore
makefile
test.sh
tests/properties.c [new file with mode: 0644]
tests/sodium.c
tests/vectors.c [moved from tests/test.c with 82% similarity]

index 682c054273ad90fe9ffe96d8ec88eb6cdab4fc47..8614aa6199a6f6c41c63fbfef2c59f8775889ec5 100644 (file)
@@ -5,7 +5,8 @@
 *.sav
 bin/*
 frama-c/*
-test
+properties
+vectors
 sodium
 donna
 rename_*
index 2faecacff614771774c2e3b43faec10c5fee4ae8..6737d57099cabd9e65ef17e2a19aaaba4bb11b49 100644 (file)
--- a/makefile
+++ b/makefile
@@ -1,5 +1,5 @@
 CC=gcc
-CFLAGS= -I src -O2 -Wall -Wextra -std=c11 -pedantic
+CFLAGS= -I src -O2 -Wall -Wextra -std=c11 -pedantic -g
 
 #-fno-stack-protector
 
@@ -7,14 +7,15 @@ CFLAGS= -I src -O2 -Wall -Wextra -std=c11 -pedantic
 # disable implicit rules
 .SUFFIXES:
 
-all: test sodium donna
+all: vectors properties sodium donna
 
 clean:
        rm -rf bin frama-c
        rm -f src/*.gch src/rename_*
-       rm -f test sodium donna
+       rm -f vectors properties sodium donna
 
-TEST_DEPS=tests/test.c bin/monocypher.o bin/sha512.o
+TEST_DEPS=tests/vectors.c bin/monocypher.o bin/sha512.o
+PROP_DEPS=tests/properties.c bin/classic_monocypher.o
 GEN_HEADERS=bin/argon2i.h      \
             bin/blake2b.h      \
             bin/blake2b_easy.h \
@@ -29,7 +30,7 @@ GEN_HEADERS=bin/argon2i.h      \
             bin/x_chacha20.h
 
 # Test suite based on test vectors
-test: $(TEST_DEPS) $(GEN_HEADERS)
+vectors: $(TEST_DEPS) $(GEN_HEADERS)
        $(CC) $(CFLAGS) -I bin -o $@ $(TEST_DEPS)
 
 bin/vector: tests/vector_to_header.c
@@ -40,6 +41,10 @@ bin/%.h: tests/vectors/% bin/vector
        @echo generate $@
        @bin/vector $$(basename $<) <$< >$@
 
+# Property based tests (consistency)
+properties: $(PROP_DEPS) $(GEN_HEADERS)
+       $(CC) $(CFLAGS) -I bin -o $@ $(PROP_DEPS)
+
 # Test suite based on comparison with libsodium
 C_SODIUM_FLAGS=$$(pkg-config --cflags libsodium)
 LD_SODIUM_FLAGS=$$(pkg-config --libs libsodium)
@@ -88,7 +93,7 @@ rename_%.h: %.h
 frama-c: $(GEN_HEADERS)                    \
          src/monocypher.c src/monocypher.h \
          src/sha512.c src/sha512.h         \
-         tests/test.c
+         tests/vectors.c
        @echo copy sources to frama-c directory for analysis
        @mkdir -p frama-c
        @cp $^ frama-c
diff --git a/test.sh b/test.sh
index 8fae7527132d223188808cc1b55abd60d3688b2a..91f9220318036d27a9e65ca0069095a58d7c2886 100755 (executable)
--- a/test.sh
+++ b/test.sh
@@ -3,32 +3,43 @@
 echo
 echo "Build"
 echo "-----"
-make test sodium donna
+make vectors properties sodium donna
 
 echo
-echo "Independent tests (with vectors)"
-echo "--------------------------------"
-./test
+echo "Tests against vectors"
+echo "---------------------"
+./vectors
 retval_test=$?
 
 echo
-echo "Fuzz tests (compare with libsodium)"
-echo "-----------------------------------"
+echo "Random self-consistency tests"
+echo "-----------------------------"
+./properties
+retval_prop=$?
+
+echo
+echo "Random comparison tests with libsodium"
+echo "--------------------------------------"
 ./sodium
 retval_sodium=$?
+
 echo
-echo "Fuzz tests (compare with ed25519-donna)"
-echo "---------------------------------------"
+echo "Random comparison tests with ed25519-donna)"
+echo "-------------------------------------------"
 ./donna
 retval_donna=$?
 
 echo
 if [ "$retval_test"   -ne 0 ] ||\
+   [ "$retval_prop"   -ne 0 ] ||\
    [ "$retval_sodium" -ne 0 ] ||\
    [ "$retval_donna"  -ne 0 ]
 then
     echo "TESTS FAILED.  VERIFY IMPLEMENTATION.  REPORT BUG"
     echo "DO. NOT. USE."
 else
-    echo "All tests OK!"
+    echo "-------------------"
+    echo "-- All tests OK! --"
+    echo "-------------------"
+    echo ""
 fi
diff --git a/tests/properties.c b/tests/properties.c
new file mode 100644 (file)
index 0000000..910f8f3
--- /dev/null
@@ -0,0 +1,207 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "monocypher.h"
+#include "sha512.h"
+
+#define FOR(i, start, end) for (size_t (i) = (start); (i) < (end); (i)++)
+typedef uint8_t   u8;
+typedef uint32_t u32;
+typedef  int32_t i32;
+typedef  int64_t i64;
+typedef uint64_t u64;
+
+// Deterministic "random" number generator, so we can make "random", yet
+// reproducible tests.  To change the random stream, change the seed.
+void random(u8 *stream, size_t size)
+{
+    static crypto_chacha_ctx ctx;
+    static int is_init = 0;
+    if (!is_init) {
+        static const u8 seed[32] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+                                    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+        crypto_chacha20_init(&ctx, seed, seed);
+        is_init = 1;
+    }
+    crypto_chacha20_stream(&ctx, stream, size);
+}
+
+static u32 load32_le(const u8 s[4])
+{
+    return (u32)s[0]
+        | ((u32)s[1] <<  8)
+        | ((u32)s[2] << 16)
+        | ((u32)s[3] << 24);
+}
+
+// Random number between 0 and max.
+// Unbalanced if max is not a power of 2.
+u32 rand_mod(u32 max)
+{
+    u8 n[4];
+    random(n, 4);
+    return load32_le(n) % max;
+}
+
+// Tests that encrypting in chunks yields the same result than
+// encrypting all at once.
+static int chacha20()
+{
+    static const size_t block_size = 64;             // Chacha Block size
+    static const size_t input_size = block_size * 4; // total input size
+    static const size_t c_max_size = block_size * 2; // maximum chunk size
+    int status = 0;
+    FOR (i, 0, 1000) {
+        size_t offset = 0;
+        // outputs
+        u8 output_chunk[input_size];
+        u8 output_whole[input_size];
+        // inputs
+        u8 input       [input_size];  random(input, input_size);
+        u8 key         [32];          random(key  , 32);
+        u8 nonce       [8];           random(nonce, 8);
+
+        // Encrypt in chunks
+        crypto_chacha_ctx ctx;
+        crypto_chacha20_init(&ctx, key, nonce);
+        while (1) {
+            size_t chunk_size = rand_mod(c_max_size);
+            if (offset + chunk_size > input_size) { break; }
+            u8 *out = output_chunk + offset;
+            u8 *in  = input        + offset;
+            crypto_chacha20_encrypt(&ctx, out, in, chunk_size);
+            offset += chunk_size;
+        }
+        // Encrypt all at once
+        crypto_chacha20_init(&ctx, key, nonce);
+        crypto_chacha20_encrypt(&ctx, output_whole, input, offset);
+
+        // Compare the results (must be the same)
+        status |= crypto_memcmp(output_chunk, output_whole, offset);
+    }
+    printf("%s: Chacha20\n", status != 0 ? "FAILED" : "OK");
+    return status;
+}
+
+// Tests that authenticating bit by bit yields the same mac than
+// authenticating all at once
+static int poly1305()
+{
+    static const size_t block_size = 16;             // poly1305 block size
+    static const size_t input_size = block_size * 4; // total input size
+    static const size_t c_max_size = block_size * 2; // maximum chunk size
+    int status = 0;
+    FOR (i, 0, 1000) {
+        size_t offset = 0;
+        // outputs
+        u8 mac_chunk[16];
+        u8 mac_whole[16];
+        // inputs
+        u8 input[input_size];  random(input, input_size);
+        u8 key  [32];          random(key  , 32);
+
+        // Authenticate bit by bit
+        crypto_poly1305_ctx ctx;
+        crypto_poly1305_init(&ctx, key);
+        while (1) {
+            size_t chunk_size = rand_mod(c_max_size);
+            if (offset + chunk_size > input_size) { break; }
+            crypto_poly1305_update(&ctx, input + offset, chunk_size);
+            offset += chunk_size;
+        }
+        crypto_poly1305_final(&ctx, mac_chunk);
+
+        // Authenticate all at once
+        crypto_poly1305_auth(mac_whole, input, offset, key);
+
+        // Compare the results (must be the same)
+        status |= crypto_memcmp(mac_chunk, mac_whole, 16);
+    }
+    printf("%s: Poly1305\n", status != 0 ? "FAILED" : "OK");
+    return status;
+}
+
+// Tests that hashing bit by bit yields the same hash than hashing all
+// at once.  Note: I figured we didn't need to test keyed mode, or
+// different hash sizes, again.  This test sticks to the simplified
+// interface.
+static int blake2b()
+{
+    static const size_t block_size = 128;            // Blake2b block size
+    static const size_t input_size = block_size * 4; // total input size
+    static const size_t c_max_size = block_size * 2; // maximum chunk size
+    int status = 0;
+    FOR (i, 0, 1000) {
+        size_t offset = 0;
+        // outputs
+        u8 hash_chunk[64];
+        u8 hash_whole[64];
+        // inputs
+        u8 input[input_size];  random(input, input_size);
+
+        // Authenticate bit by bit
+        crypto_blake2b_ctx ctx;
+        crypto_blake2b_init(&ctx);
+        while (1) {
+            size_t chunk_size = rand_mod(c_max_size);
+            if (offset + chunk_size > input_size) { break; }
+            crypto_blake2b_update(&ctx, input + offset, chunk_size);
+            offset += chunk_size;
+        }
+        crypto_blake2b_final(&ctx, hash_chunk);
+
+        // Authenticate all at once
+        crypto_blake2b(hash_whole, input, offset);
+
+        // Compare the results (must be the same)
+        status |= crypto_memcmp(hash_chunk, hash_whole, 64);
+    }
+    printf("%s: Blake2b\n", status != 0 ? "FAILED" : "OK");
+    return status;
+}
+
+static int aead()
+{
+    int status = 0;
+    FOR (i, 0, 1000) {
+        u8 key      [32];  random(key      , 32);
+        u8 nonce    [24];  random(nonce    , 24);
+        u8 ad       [ 4];  random(ad       ,  4);
+        u8 plaintext[ 8];  random(plaintext,  8);
+        u8 box[24], box2[24];
+        u8 out[8];
+        // AEAD roundtrip
+        crypto_aead_lock(box, box+16, key, nonce, ad, 4, plaintext, 8);
+        status |= crypto_aead_unlock(out, key, nonce, box, ad, 4, box+16, 8);
+        status |= crypto_memcmp(plaintext, out, 8);
+        box[0]++;
+        status |= !crypto_aead_unlock(out, key, nonce, box, ad, 4, box+16, 8);
+
+        // Authenticated roundtrip (easy interface)
+        // Make and accept message
+        crypto_lock(box, box + 16, key, nonce, plaintext, 8);
+        status |= crypto_unlock(out, key, nonce, box, box + 16, 8);
+        // Make sure decrypted text and original text are the same
+        status |= crypto_memcmp(plaintext, out, 8);
+        // Make and reject forgery
+        box[0]++;
+        status |= !crypto_unlock(out, key, nonce, box, box + 16, 8);
+        box[0]--; // undo forgery
+
+        // Same result for both interfaces
+        crypto_aead_lock(box2, box2 + 16, key, nonce, 0, 0, plaintext, 8);
+        status |= crypto_memcmp(box, box2, 24);
+    }
+    printf("%s: aead\n", status != 0 ? "FAILED" : "OK");
+    return status;
+}
+
+int main(void)
+{
+    int status = 0;
+    status |= chacha20();
+    status |= poly1305();
+    status |= blake2b();
+    status |= aead();
+    return status;
+}
index bcf080c91fd1d35c871166ee573baa8f9ed48474..332154f2cb2de69c82a6be9308df21fd4226647b 100644 (file)
@@ -6,13 +6,7 @@
 #include <sodium.h>
 
 #define FOR(i, start, end) for (size_t (i) = (start); (i) < (end); (i)++)
-#define sv static void
-typedef  int8_t   i8;
-typedef uint8_t   u8;
-typedef uint32_t u32;
-typedef  int32_t i32;
-typedef  int64_t i64;
-typedef uint64_t u64;
+typedef uint8_t u8;
 
 // Deterministic "random" number generator, so we can make "random", yet
 // reproducible tests.  To change the random stream, change the seed.
@@ -20,7 +14,7 @@ void random(u8 *stream, u8 size)
 {
     static rename_chacha_ctx ctx;
     static int is_init = 0;
-    if (!is_init){
+    if (!is_init) {
         static const u8 seed[32] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
                                     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
         rename_chacha20_init(&ctx, seed, seed);
similarity index 82%
rename from tests/test.c
rename to tests/vectors.c
index 21fa9e082167108785cf17b150b02a40fe7ccd62..33f563af631ea2cf3f1c8f06492b238e2d300e0e 100644 (file)
@@ -253,47 +253,9 @@ sv key_exchange(const vector in[], vector *out)
     crypto_key_exchange(out->buf, secret_key->buf, public_key->buf);
 }
 
-static int test_aead()
-{
-    u8 key[32]      = { 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7,
-                        0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7 };
-    u8 nonce[24]    = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-                        1, 1, 1, 1, 1, 1, 1, 1 };
-    u8 ad       [4] = { 3, 2, 1, 0 };
-    u8 plaintext[8] = { 7, 6, 5, 4, 3, 2, 1, 0 };
-    u8 box[24], box2[24];
-    u8 out[8];
-    int status = 0;
-    // AEAD roundtrip
-    crypto_aead_lock(box, box+16, key, nonce, ad, 4, plaintext, 8);
-    status |= crypto_aead_unlock(out, key, nonce, box, ad, 4, box+16, 8);
-    status |= crypto_memcmp(plaintext, out, 8);
-    box[0]++;
-    status |= !crypto_aead_unlock(out, key, nonce, box, ad, 4, box+16, 8);
-    printf("%s: aead (detached)\n", status != 0 ? "FAILED" : "OK");
-
-    // Authenticated roundtrip (easy interface)
-    crypto_lock(box, box + 16, key, nonce, plaintext, 8);       // make message
-    status |= crypto_unlock(out, key, nonce, box, box + 16, 8); // accept message
-    status |= crypto_memcmp(plaintext, out, 8);                 // roundtrip
-    box[0]++;                                                   // make forgery
-    status |= !crypto_unlock(out, key, nonce, box, box + 16, 8);// reject forgery
-    printf("%s: aead (simplified)\n", status != 0 ? "FAILED" : "OK");
-    box[0]--; // undo forgery
-
-    // Same result for both interfaces
-    crypto_aead_lock(box2, box2 + 16, key, nonce, 0, 0, plaintext, 8);
-    status |= crypto_memcmp(box, box2, 24);
-    printf("%s: aead (compared)\n", status != 0 ? "FAILED" : "OK");
-
-    return status;
-}
-
 int main(void)
 {
     int status = 0;
-    /* status |= generic_test(equal, "tests/vectors/test_equal"  , 2); */
-    /* status |= generic_test(diff , "tests/vectors/test_diff"   , 2); */
     status |= TEST(chacha20    , 2);
     status |= TEST(h_chacha20  , 2);
     status |= TEST(x_chacha20  , 2);
@@ -307,6 +269,5 @@ int main(void)
     status |= TEST(ed25519_key , 1);
     status |= TEST(ed25519_sign, 3);
     status |= test_x25519();
-    status |= test_aead();
     return status;
 }