// 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) 2020, Mike Pechkin and 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 2017-2020 by Mike Pechkin and 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 "monocypher.h" #include "monocypher-ed25519.h" #include "utils.h" #include "tis-ci-vectors.h" #include #include #include static void chacha20(vector_reader *reader) { vector key = next_input(reader); vector nonce = next_input(reader); vector plain = next_input(reader); u64 ctr = load64_le(next_input(reader).buf); vector out = next_output(reader); u64 nb_blocks = plain.size / 64 + (plain.size % 64 != 0); u64 new_ctr = crypto_chacha20_djb(out.buf, plain.buf, plain.size, key.buf, nonce.buf, ctr); if (new_ctr - ctr != nb_blocks) { printf("FAILURE: Chacha20 returned counter not correct: "); } } static void ietf_chacha20(vector_reader *reader) { vector key = next_input(reader); vector nonce = next_input(reader); vector plain = next_input(reader); u64 ctr = load64_le(next_input(reader).buf); vector out = next_output(reader); u32 nb_blocks = (u32)(plain.size / 64 + (plain.size % 64 != 0)); u32 new_ctr = crypto_chacha20_ietf(out.buf, plain.buf, plain.size, key.buf, nonce.buf, (u32)ctr); if (new_ctr - ctr != nb_blocks) { printf("FAILURE: IETF Chacha20 returned counter not correct: "); } } static void hchacha20(vector_reader *reader) { vector key = next_input(reader); vector nonce = next_input(reader); vector out = next_output(reader); crypto_chacha20_h(out.buf, key.buf, nonce.buf); } static void xchacha20(vector_reader *reader) { vector key = next_input(reader); vector nonce = next_input(reader); vector plain = next_input(reader); u64 ctr = load64_le(next_input(reader).buf); vector out = next_output(reader); u64 nb_blocks = plain.size / 64 + (plain.size % 64 != 0); u64 new_ctr = crypto_chacha20_x(out.buf, plain.buf, plain.size, key.buf, nonce.buf, ctr); if (new_ctr - ctr != nb_blocks) { printf("FAILURE: XChacha20 returned counter not correct: "); } } static void poly1305(vector_reader *reader) { vector key = next_input(reader); vector msg = next_input(reader); vector out = next_output(reader); crypto_poly1305(out.buf, msg.buf, msg.size, key.buf); } static void aead_ietf(vector_reader *reader) { vector key = next_input(reader); vector nonce = next_input(reader); vector ad = next_input(reader); vector text = next_input(reader); vector out = next_output(reader); crypto_aead_lock(out.buf + 16, out.buf, key.buf, nonce.buf, ad.buf, ad.size, text.buf, text.size); } static void blake2b(vector_reader *reader) { vector msg = next_input(reader); vector key = next_input(reader); vector out = next_output(reader); crypto_blake2b_keyed(out.buf, out.size, key.buf, key.size, msg.buf, msg.size); } static void sha512(vector_reader *reader) { vector in = next_input(reader); vector out = next_output(reader); crypto_sha512(out.buf, in.buf, in.size); } static void sha512_hmac(vector_reader *reader) { vector key = next_input(reader); vector msg = next_input(reader); vector out = next_output(reader); crypto_sha512_hmac(out.buf, key.buf, key.size, msg.buf, msg.size); } static void sha512_hkdf(vector_reader *reader) { vector ikm = next_input(reader); vector salt = next_input(reader); vector info = next_input(reader); vector okm = next_output(reader); crypto_sha512_hkdf(okm .buf, okm .size, ikm .buf, ikm .size, salt.buf, salt.size, info.buf, info.size); } static void argon2(vector_reader *reader) { crypto_argon2_config config; config.algorithm = load32_le(next_input(reader).buf); config.nb_blocks = load32_le(next_input(reader).buf); config.nb_passes = load32_le(next_input(reader).buf); config.nb_lanes = load32_le(next_input(reader).buf); vector pass = next_input(reader); vector salt = next_input(reader); vector key = next_input(reader); vector ad = next_input(reader); vector out = next_output(reader); void *work_area = alloc(config.nb_blocks * 1024); crypto_argon2_inputs inputs; inputs.pass = pass.buf; inputs.salt = salt.buf; inputs.pass_size = (u32)pass.size; inputs.salt_size = (u32)salt.size; crypto_argon2_extras extras; extras.key = key.buf; extras.ad = ad.buf; extras.key_size = (u32)key.size; extras.ad_size = (u32)ad.size; crypto_argon2(out.buf, (u32)out.size, work_area, config, inputs, extras); free(work_area); } static void x25519(vector_reader *reader) { vector scalar = next_input(reader); vector point = next_input(reader); vector out = next_output(reader); crypto_x25519(out.buf, scalar.buf, point.buf); } static void edDSA(vector_reader *reader) { vector secret_k = next_input(reader); vector public_k = next_input(reader); vector msg = next_input(reader); vector out = next_output(reader); u8 fat_secret_key[64]; memcpy(fat_secret_key , secret_k.buf, 32); memcpy(fat_secret_key + 32, public_k.buf, 32); crypto_eddsa_sign(out.buf, fat_secret_key, msg.buf, msg.size); } static void ed_25519(vector_reader *reader) { vector secret_k = next_input(reader); vector public_k = next_input(reader); vector msg = next_input(reader); vector out = next_output(reader); u8 fat_secret_key[64]; memcpy(fat_secret_key , secret_k.buf, 32); memcpy(fat_secret_key + 32, public_k.buf, 32); crypto_ed25519_sign(out.buf, fat_secret_key, msg.buf, msg.size); } static void ed_25519_check(vector_reader *reader) { vector public_k = next_input(reader); vector msg = next_input(reader); vector sig = next_input(reader); vector out = next_output(reader); out.buf[0] = (u8)crypto_ed25519_check(sig.buf, public_k.buf, msg.buf, msg.size); } static void elligator_dir(vector_reader *reader) { vector in = next_input(reader); vector out = next_output(reader); crypto_elligator_map(out.buf, in.buf); } static void elligator_inv(vector_reader *reader) { vector point = next_input(reader); u8 tweak = next_input(reader).buf[0]; u8 failure = next_input(reader).buf[0]; vector out = next_output(reader); int check = crypto_elligator_rev(out.buf, point.buf, tweak); ASSERT((u8)check == failure); if (check) { out.buf[0] = 0; } } //@ ensures \result == 0; static int p_eddsa_x25519() { RANDOM_INPUT(e_seed, 32); u8 secret [64]; u8 e_public1[32]; crypto_eddsa_key_pair(secret, e_public1, e_seed); u8 x_private[64]; crypto_blake2b(x_private, 64, secret, 32); u8 x_public1[32]; crypto_eddsa_to_x25519 (x_public1, e_public1); u8 x_public2[32]; crypto_x25519_public_key(x_public2, x_private); ASSERT_EQUAL(x_public1, x_public2, 32); u8 e_public2[32]; crypto_x25519_to_eddsa (e_public2, x_public1); ASSERT((e_public2[31] & 0x80) == 0); // x coordinate always positive e_public1[31] &= 0x7f; // y coordinate back to original ASSERT_EQUAL(e_public1, e_public2, 32); return 0; } //@ ensures \result == 0; static int p_dirty() { int status = 0; RANDOM_INPUT(sk1, 32); sk1[0] |= 1; // make sure it's dirty u8 skc [32]; memcpy(skc, sk1, 32); skc[0] &= 248; // make sure it's clean u8 pks [32]; crypto_x25519_dirty_small(pks , sk1); u8 pksc[32]; crypto_x25519_dirty_small(pksc, skc); u8 pkf [32]; crypto_x25519_dirty_fast (pkf , sk1); u8 pkfc[32]; crypto_x25519_dirty_fast (pkfc, skc); u8 pk1 [32]; crypto_x25519_public_key (pk1 , sk1); // Both dirty functions behave the same status |= memcmp(pks, pkf, 32); // Dirty functions behave cleanly if we clear the 3 msb first status |= memcmp(pksc, pk1, 32); status |= memcmp(pkfc, pk1, 32); printf("%s: x25519 dirty\n", status != 0 ? "FAILED" : "OK"); return status; } //@ ensures \result == 0; static int p_x25519_inverse() { int status = 0; RANDOM_INPUT(b, 32); u8 base[32]; // random point (cofactor is cleared). crypto_x25519_public_key(base, b); // check round trip RANDOM_INPUT(sk, 32); u8 pk [32]; u8 blind[32]; crypto_x25519(pk, sk, base); crypto_x25519_inverse(blind, sk, pk); status |= memcmp(blind, base, 32); // check cofactor clearing // (Multiplying by a low order point yields zero u8 low_order[32] = { 0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57, }; u8 zero[32] = {0}; crypto_x25519_inverse(blind, sk, low_order); status |= memcmp(blind, zero, 32); printf("%s: x25519_inverse\n", status != 0 ? "FAILED" : "OK"); return status; } //@ ensures \result == 0; static int p_verify(size_t size, int (*compare)(const u8*, const u8*)) { int status = 0; u8 a[64]; // size <= 64 u8 b[64]; // size <= 64 FOR (i, 0, 2) { FOR (j, 0, 2) { // Set every byte to the chosen value, then compare FOR (k, 0, size) { a[k] = (u8)i; b[k] = (u8)j; } int cmp = compare(a, b); status |= (i == j ? cmp : ~cmp); // Set only two bytes to the chosen value, then compare FOR (k, 0, size / 2) { FOR (l, 0, size) { a[l] = 0; b[l] = 0; } a[k] = (u8)i; a[k + size/2 - 1] = (u8)i; b[k] = (u8)j; b[k + size/2 - 1] = (u8)j; cmp = compare(a, b); status |= (i == j ? cmp : ~cmp); } } } printf("%s: crypto_verify%zu\n", status != 0 ? "FAILED" : "OK", size); return status; } //@ ensures \result == 0; static int p_verify16(){ return p_verify(16, crypto_verify16); } //@ ensures \result == 0; static int p_verify32(){ return p_verify(32, crypto_verify32); } //@ ensures \result == 0; static int p_verify64(){ return p_verify(64, crypto_verify64); } #define TEST(name) \ int v_##name() { \ return vector_test(name, #name, nb_##name##_vectors, name##_vectors); \ } //@ ensures \result == 0; TEST(chacha20) //@ ensures \result == 0; TEST(ietf_chacha20) //@ ensures \result == 0; TEST(hchacha20) //@ ensures \result == 0; TEST(xchacha20) //@ ensures \result == 0; TEST(poly1305) //@ ensures \result == 0; TEST(aead_ietf) //@ ensures \result == 0; TEST(blake2b) //@ ensures \result == 0; TEST(sha512) //@ ensures \result == 0; TEST(sha512_hmac) //@ ensures \result == 0; TEST(sha512_hkdf) //@ ensures \result == 0; TEST(argon2) //@ ensures \result == 0; TEST(x25519) //@ ensures \result == 0; TEST(edDSA) //@ ensures \result == 0; TEST(ed_25519) //@ ensures \result == 0; TEST(ed_25519_check) //@ ensures \result == 0; TEST(elligator_dir) //@ ensures \result == 0; TEST(elligator_inv) //@ ensures \result == 0; int main(void) { ASSERT(v_chacha20 () == 0); ASSERT(v_ietf_chacha20 () == 0); ASSERT(v_hchacha20 () == 0); ASSERT(v_xchacha20 () == 0); ASSERT(v_poly1305 () == 0); ASSERT(v_aead_ietf () == 0); ASSERT(v_blake2b () == 0); ASSERT(v_sha512 () == 0); ASSERT(v_sha512_hmac () == 0); ASSERT(v_sha512_hkdf () == 0); ASSERT(v_argon2 () == 0); ASSERT(v_x25519 () == 0); ASSERT(v_edDSA () == 0); ASSERT(v_ed_25519 () == 0); ASSERT(v_ed_25519_check() == 0); ASSERT(v_elligator_dir () == 0); ASSERT(v_elligator_inv () == 0); ASSERT(p_eddsa_x25519 () == 0); ASSERT(p_dirty () == 0); ASSERT(p_x25519_inverse() == 0); ASSERT(p_verify16 () == 0); ASSERT(p_verify32 () == 0); ASSERT(p_verify64 () == 0); return 0; }