/** * @file test_decaf.cxx * @author Mike Hamburg * * @copyright * Copyright (c) 2015 Cryptography Research, Inc. \n * Released under the MIT License. See LICENSE.txt for license information. * * @brief C++ tests, because that's easier. */ #include #include #include #include #include using namespace decaf; static bool passing = true; static const long NTESTS = 10000; #include "ristretto_vectors.inc.cxx" class Test { public: bool passing_now; Test(const char *test) { passing_now = true; printf("%s...", test); if (strlen(test) < 27) printf("%*s",int(27-strlen(test)),""); fflush(stdout); } ~Test() { if (std::uncaught_exception()) { fail(); printf(" due to uncaught exception.\n"); } if (passing_now) printf("[PASS]\n"); } void fail() { if (!passing_now) return; passing_now = passing = false; printf("[FAIL]\n"); } }; static uint64_t leint(const SecureBuffer &xx) { uint64_t out = 0; for (unsigned int i=0; i struct Tests { typedef typename Group::Scalar Scalar; typedef typename Group::Point Point; typedef typename Group::DhLadder DhLadder; typedef typename Group::Precomputed Precomputed; static void print(const char *name, const Scalar &x) { unsigned char buffer[Scalar::SER_BYTES]; x.serialize_into(buffer); printf(" %s = 0x", name); for (int i=sizeof(buffer)-1; i>=0; i--) { printf("%02x", buffer[i]); } printf("\n"); } static void hexprint(const char *name, const SecureBuffer &buffer) { printf(" %s = 0x", name); for (int i=buffer.size()-1; i>=0; i--) { printf("%02x", buffer[i]); } printf("\n"); } static void print(const char *name, const Point &x) { unsigned char buffer[Point::SER_BYTES]; x.serialize_into(buffer); printf(" %s = 0x", name); for (int i=Point::SER_BYTES-1; i>=0; i--) { printf("%02x", buffer[i]); } printf("\n"); } static bool arith_check( Test &test, const Scalar &x, const Scalar &y, const Scalar &z, const Scalar &l, const Scalar &r, const char *name ) { if (l == r) return true; test.fail(); printf(" %s", name); print("x", x); print("y", y); print("z", z); print("lhs", l); print("rhs", r); return false; } static bool point_check( Test &test, const Point &p, const Point &q, const Point &R, const Scalar &x, const Scalar &y, const Point &l, const Point &r, const char *name ) { bool good = l==r; if (!p.validate()) { good = false; printf(" p invalid\n"); } if (!q.validate()) { good = false; printf(" q invalid\n"); } if (!r.validate()) { good = false; printf(" r invalid\n"); } if (!l.validate()) { good = false; printf(" l invalid\n"); } if (good) return true; test.fail(); printf(" %s", name); print("x", x); print("y", y); print("p", p); print("q", q); print("r", R); print("lhs", r); print("rhs", l); return false; } static void test_arithmetic() { SpongeRng rng(Block("test_arithmetic"),SpongeRng::DETERMINISTIC); Test test("Arithmetic"); Scalar x(0),y(0),z(0); arith_check(test,x,y,z,INT_MAX,(decaf_word_t)INT_MAX,"cast from max"); arith_check(test,x,y,z,INT_MIN,-Scalar(1+(decaf_word_t)INT_MAX),"cast from min"); for (int i=0; i Point::HASH_BYTES) memcpy(&(*alts[j])[Point::HASH_BYTES], &b1[Point::HASH_BYTES], len-Point::HASH_BYTES); if (len > Point::HASH_BYTES) memcpy(&(*alts2[j])[Point::HASH_BYTES], &b1[Point::HASH_BYTES], len-Point::HASH_BYTES); successes[j] = decaf_successful( s.invert_elligator(*alts[j], j)); successes2[j] = decaf_successful(ss.invert_elligator(*alts2[j],j)); if (successes[j] != successes2[j] || (successes[j] && successes2[j] && *alts[j] != *alts2[j]) ) { test.fail(); printf(" Unscalable Elligator inversion: i=%d, hint=%d, s=%d,%d\n",i,j, -int(successes[j]),-int(successes2[j])); hexprint("x",b1); hexprint("X",*alts[j]); hexprint("X",*alts2[j]); } if (successes[j]) { good = good || (b1 == *alts[j]); for (int k=0; k b3(rng), b4(b3); t = Point::from_hash(b3); for (unsigned j=0; j<256; j+=2<<((Group::bits()-1)%8)) { b4[Point::HASH_BYTES-1] = b3[Point::HASH_BYTES-1] ^ j; Point u = Point::from_hash(b4); point_check(test,t,t,t,0,0,t,u,"elligator twiddle high bits"); } } } static void test_ec() { SpongeRng rng(Block("test_ec"),SpongeRng::DETERMINISTIC); Test test("EC"); Point id = Point::identity(), base = Point::base(); point_check(test,id,id,id,0,0,Point::from_hash(""),id,"fh0"); unsigned char enc[Point::SER_BYTES] = {0}; if (Group::FIELD_MODULUS_TYPE == 3) { /* When p == 3 mod 4, the QNR is -1, so u*1^2 = -1 also produces the * identity. */ point_check(test,id,id,id,0,0,Point::from_hash("\x01"),id,"fh1"); } point_check(test,id,id,id,0,0,Point(FixedBlock(enc)),id,"decode [0]"); try { enc[0] = 1; Point f((FixedBlock(enc))); test.fail(); printf(" Allowed deserialize of [1]: %d", f==id); } catch (CryptoException&) { /* ok */ } if (sqrt_minus_one.size()) { try { Point f(sqrt_minus_one); test.fail(); printf(" Allowed deserialize of [i]: %d", f==id); } catch (CryptoException&) { /* ok */ } } if (minus_sqrt_minus_one.size()) { try { Point f(minus_sqrt_minus_one); test.fail(); printf(" Allowed deserialize of [-i]: %d", f==id); } catch (CryptoException&) { /* ok */ } } for (int i=0; i base, out; FixedArrayBuffer s1(rng); decaf_error_t e = DhLadder::shared_secret_noexcept(out,base,s1); if (e != DECAF_FAILURE) { test.fail(); printf(" Multiply by 0 didn't give an error\n"); } if (!out.contents_equal(base)) { test.fail(); printf(" Multiply by 0 didn't give 0\n"); } } for (int i=0; i base(rng); FixedArrayBuffer s1(rng), s2(rng); SecureBuffer p1 = DhLadder::shared_secret(base,s1); SecureBuffer p2 = DhLadder::shared_secret(base,s2); SecureBuffer ss1 = DhLadder::shared_secret(p2,s1); SecureBuffer ss2 = DhLadder::shared_secret(p1,s2); if (!memeq(ss1,ss2)) { test.fail(); printf(" Shared secrets disagree on iteration %d.\n",i); } p1 = DhLadder::shared_secret(DhLadder::base_point(),s1); p2 = DhLadder::derive_public_key(s1); if (!memeq(p1,p2)) { test.fail(); printf(" Public keys disagree on iteration %d.\n Ladder public key: ",i); for (unsigned j=0; j::PrivateKey priv(eddsa_sk[t]); SecureBuffer eddsa_pk2 = priv.pub().serialize(); if (!memeq(SecureBuffer(eddsa_pk[t]), eddsa_pk2)) { test.fail(); printf(" EdDSA PK vectors #%d disagree.", t); printf("\n Correct: "); for (unsigned i=0; i::PrivateKeyPh priv2(eddsa_sk[t]); sig = priv2.sign_with_prehash(eddsa_message[t],eddsa_context[t]); } else { sig = priv.sign(eddsa_message[t],eddsa_context[t]); } if (!memeq(SecureBuffer(eddsa_sig[t]),sig)) { test.fail(); printf(" EdDSA sig vectors #%d disagree.", t); printf("\n Correct: "); for (unsigned i=0; i::PublicKey pub(eddsa_pk[t]); if (eddsa_prehashed[t]) { pub.verify_with_prehash(eddsa_sig[t], eddsa_message[t], eddsa_context[t]); } else { pub.verify(eddsa_sig[t], eddsa_message[t], eddsa_context[t]); } verified = true; } catch(CryptoException&) { verified = false; } if (verified != eddsa_verify_should_succeed[t]) { test.fail(); printf(" EdDSA Verify vector #%d disagree: verify %s but should %s\n", t, verified ? "passed" : "failed", eddsa_verify_should_succeed[t] ? "pass" : "fail"); } } /* X25519/X448 */ for (int i=0; i(rfc7748_1)))) { test.fail(); printf(" Test vectors disagree at 1."); } } else if (i==1000-1) { if (!memeq(k,SecureBuffer(FixedBlock(rfc7748_1000)))) { test.fail(); printf(" Test vectors disagree at 1000."); } } else if (i==1000000-1) { if (!memeq(k,SecureBuffer(FixedBlock(rfc7748_1000000)))) { test.fail(); printf(" Test vectors disagree at 1000000."); } } } } static void test_eddsa() { Test test("EdDSA"); SpongeRng rng(Block("test_eddsa"),SpongeRng::DETERMINISTIC); int lg_scalar = Group::bits(); for (int cof = Group::REMOVED_COFACTOR; cof>1; cof>>=1) { lg_scalar--; } typename Group::Scalar more_than_size = 1; for (int i=0; i::PrivateKey priv(rng); typename EdDSA::PublicKey pub(priv); SecureBuffer message(i); rng.read(message); SecureBuffer context(i%256); rng.read(context); SecureBuffer sig = priv.sign(message,context); try { pub.verify(sig,message,context); } catch(CryptoException&) { test.fail(); printf(" Signature validation failed on sig %d\n", i); } try { sig[(i/8) % sig.size()] ^= 1<<(i%8); pub.verify(sig,message,context); test.fail(); printf(" Signature validation passed incorrectly on corrupted sig %d\n", i); } catch(CryptoException&) {} sig[(i/8) % sig.size()] ^= 1<<(i%8); try { const int size = EdDSA::PublicKey::SER_BYTES; uint8_t ser[size]; pub.serialize_into(ser); ser[(i/8) % size] ^= 1<<(i%8); typename EdDSA::PublicKey pub2((FixedBlock(ser))); pub2.verify(sig,message,context); test.fail(); printf(" Signature validation passed incorrectly on corrupted pubkey %d\n", i); } catch(CryptoException&) {} if (message.size() > 0) { try { message[(i/8) % message.size()] ^= 1<<(i%8); pub.verify(sig,message,context); test.fail(); printf(" Signature validation passed incorrectly on corrupted message %d\n", i); } catch(CryptoException&) {} message[(i/8) % message.size()] ^= 1<<(i%8); } if (context.size() > 0) { try { context[(i/8) % context.size()] ^= 1<<(i%8); pub.verify(sig,message,context); test.fail(); printf(" Signature validation passed incorrectly on corrupted message %d\n", i); } catch(CryptoException&) {} context[(i/8) % context.size()] ^= 1<<(i%8); } // Construct sig which is numerically equal but improper const int scalarbytes = Group::Scalar::SER_BYTES; uint8_t *scalarpart = &sig[EdDSA::PublicKey::SER_BYTES]; typename Group::Scalar sig_r = FixedBlock(scalarpart); memcpy(scalarpart, (-sig_r).serialize().data(), scalarbytes); try { pub.verify(sig,message,context); test.fail(); printf(" Signature validation passed incorrectly on negated sig %d\n", i); } catch(CryptoException&) {} sig_r -= more_than_size; memcpy(scalarpart, sig_r.serialize().data(), scalarbytes); scalarpart[scalarbytes-1] += 1<<(lg_scalar%8); try { pub.verify(sig,message,context); test.fail(); printf(" Signature validation passed incorrectly on improper sig %d\n", i); } catch(CryptoException&) {} /* Test encode_like and torque */ Point p(rng); SecureBuffer p1 = p.mul_by_ratio_and_encode_like_eddsa(); SecureBuffer p2 = p.debugging_torque().mul_by_ratio_and_encode_like_eddsa(); if (!memeq(p1,p2)) { test.fail(); printf(" Torque and encode like EdDSA failed\n"); } SecureBuffer p3 = p.mul_by_ratio_and_encode_like_ladder(); SecureBuffer p4 = p.debugging_torque().mul_by_ratio_and_encode_like_ladder(); if (!memeq(p3,p4)) { test.fail(); printf(" Torque and encode like ladder failed\n"); } } } /* Thanks Johan Pascal */ static void test_convert_eddsa_to_x() { Test test("ECDH using EdDSA keys"); SpongeRng rng(Block("test_x_on_eddsa_key"),SpongeRng::DETERMINISTIC); for (int i=0; i::PrivateKey alice_priv(rng); typename EdDSA::PublicKey alice_pub(alice_priv); typename EdDSA::PrivateKey bob_priv(rng); typename EdDSA::PublicKey bob_pub(bob_priv); /* convert them to ECDH format * check public key value by computing it from direct conversion and regeneration from converted private) */ SecureBuffer alice_priv_x = alice_priv.convert_to_x(); SecureBuffer alice_pub_x_conversion = alice_pub.convert_to_x(); SecureBuffer alice_pub_x_generated = DhLadder::derive_public_key(alice_priv_x); if (!memeq(alice_pub_x_conversion, alice_pub_x_generated)) { test.fail(); printf(" Ed2X Public key conversion and regeneration from converted private key differs.\n"); } SecureBuffer bob_priv_x = bob_priv.convert_to_x(); SecureBuffer bob_pub_x_conversion = bob_pub.convert_to_x(); SecureBuffer bob_pub_x_generated = DhLadder::derive_public_key(bob_priv_x); if (!memeq(bob_pub_x_conversion, bob_pub_x_generated)) { test.fail(); printf(" Ed2X Public key conversion and regeneration from converted private key differs.\n"); } /* compute shared secrets and check they match */ SecureBuffer alice_shared = DhLadder::shared_secret(bob_pub_x_conversion, alice_priv_x); SecureBuffer bob_shared = DhLadder::shared_secret(alice_pub_x_conversion, bob_priv_x); if (!memeq(alice_shared, bob_shared)) { test.fail(); printf(" ECDH shared secret mismatch.\n"); } } } static void test_dalek_vectors() { Test test("Test vectors from Dalek"); Point p = Point::base(), q; for (unsigned i=0; i::count; i++) { if (!decaf_memeq(q.serialize().data(),base_multiples::values[i],Point::SER_BYTES)) { test.fail(); printf(" Failed test vector for %d * base point.\n", i); } q += p; } for (unsigned i=0; i::count; i++) { Point r = Point::from_hash(FixedBlock(elligator_examples::inputs[i])); Point s = Point(FixedBlock(elligator_examples::outputs[i])); point_check(test,r,r,r,0,0,r,s,"elligator test vector"); } } static void run() { printf("Testing %s:\n",Group::name()); test_arithmetic(); test_elligator(); test_ec(); test_eddsa(); test_convert_eddsa_to_x(); test_cfrg_crypto(); test_cfrg_vectors(); test_dalek_vectors(); printf("\n"); } }; /* template struct Tests */ template static void test_xof() { /* TODO: more testing of XOFs */ Test test("XOF"); SpongeRng rng(Block("test_xof"),SpongeRng::DETERMINISTIC); FixedArrayBuffer<1024> a,b,c; rng.read(c); T s1, s2; unsigned i; for (i=0; i >(); test_xof >(); printf("\n"); run_for_all_curves(); if (passing) printf("Passed all tests.\n"); return passing ? 0 : 1; }