/** * @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++ benchmarks, because that's easier. */ #include #include #include #include #include #include #include #include #include #include #include using namespace decaf; static __inline__ void __attribute__((unused)) ignore_result ( int result ) { (void)result; } static double now(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + tv.tv_usec/1000000.0; } // RDTSC from the chacha code #ifndef __has_builtin #define __has_builtin(X) 0 #endif #if defined(__clang__) && __has_builtin(__builtin_readcyclecounter) #define rdtsc __builtin_readcyclecounter #else static inline uint64_t rdtsc(void) { # if defined(__x86_64__) uint32_t lobits, hibits; __asm__ __volatile__ ("rdtsc" : "=a"(lobits), "=d"(hibits)); return (lobits | ((uint64_t)(hibits) << 32)); # elif defined(__i386__) uint64_t __value; __asm__ __volatile__ ("rdtsc" : "=A"(__value)); return __value; # else return 0; # endif } #endif static void printSI(double x, const char *unit, const char *spacer = " ") { const char *small[] = {" ","m","ยต","n","p"}; const char *big[] = {" ","k","M","G","T"}; if (x < 1) { unsigned di=0; for (di=0; di= 1000; di++) { x /= 1000.0; } printf("%6.2f%s%s%s", x, spacer, big[di], unit); } } class Benchmark { static const int NTESTS = 20, NSAMPLES=50, DISCARD=2; static double totalCy, totalS; public: int i, j, ntests, nsamples; double begin; uint64_t tsc_begin; std::vector times; std::vector cycles; Benchmark(const char *s, double factor = 1) { printf("%s:", s); if (strlen(s) < 25) printf("%*s",int(25-strlen(s)),""); fflush(stdout); i = j = 0; ntests = NTESTS * factor; nsamples = NSAMPLES; begin = now(); tsc_begin = rdtsc(); times = std::vector(NSAMPLES); cycles = std::vector(NSAMPLES); } ~Benchmark() { double tsc = 0; double t = 0; std::sort(times.begin(), times.end()); std::sort(cycles.begin(), cycles.end()); for (int k=DISCARD; k= ntests) { uint64_t tsc = rdtsc() - tsc_begin; double t = now() - begin; begin += t; tsc_begin += tsc; assert(j >= 0 && j < nsamples); cycles[j] = tsc; times[j] = t; j++; i = 0; } return j < nsamples; } static void calib() { if (totalS && totalCy) { const char *s = "Cycle calibration"; printf("%s:", s); if (strlen(s) < 25) printf("%*s",int(25-strlen(s)),""); printSI(totalCy / totalS, "Hz"); printf("\n"); } } }; double Benchmark::totalCy = 0, Benchmark::totalS = 0; template struct Benches { typedef typename Group::Scalar Scalar; typedef typename Group::Point Point; typedef typename Group::Precomputed Precomputed; static void cfrg() { SpongeRng rng(Block("bench_cfrg_crypto"),SpongeRng::DETERMINISTIC); FixedArrayBuffer base(rng); FixedArrayBuffer s1(rng); for (Benchmark b("RFC 7748 keygen"); b.iter(); ) { Group::DhLadder::derive_public_key(s1); } for (Benchmark b("RFC 7748 shared secret"); b.iter(); ) { Group::DhLadder::shared_secret(base,s1); } FixedArrayBuffer::PrivateKey::SER_BYTES> e1(rng); typename EdDSA::PublicKey pub((NOINIT())); typename EdDSA::PrivateKey priv((NOINIT())); SecureBuffer sig; for (Benchmark b("EdDSA keygen"); b.iter(); ) { priv = e1; } for (Benchmark b("EdDSA sign"); b.iter(); ) { sig = priv.sign(Block(NULL,0)); } pub = priv; for (Benchmark b("EdDSA verify"); b.iter(); ) { pub.verify(sig,Block(NULL,0)); } } static void macro() { printf("\nMacro-benchmarks for %s:\n", Group::name()); printf("CFRG crypto benchmarks:\n"); cfrg(); } static void micro() { SpongeRng rng(Block("per-curve-benchmarks"),SpongeRng::DETERMINISTIC); Precomputed pBase; Point p,q; Scalar s(1),t(2); SecureBuffer ep, ep2(Point::SER_BYTES*2); printf("\nMicro-benchmarks for %s:\n", Group::name()); for (Benchmark b("Scalar add", 1000); b.iter(); ) { s+=t; } for (Benchmark b("Scalar times", 100); b.iter(); ) { s*=t; } for (Benchmark b("Scalar inv", 1); b.iter(); ) { s.inverse(); } for (Benchmark b("Point add", 100); b.iter(); ) { p += q; } for (Benchmark b("Point double", 100); b.iter(); ) { p.double_in_place(); } for (Benchmark b("Point scalarmul"); b.iter(); ) { p * s; } for (Benchmark b("Point encode"); b.iter(); ) { ep = p.serialize(); } for (Benchmark b("Point decode"); b.iter(); ) { p = Point(ep); } for (Benchmark b("Point create/destroy"); b.iter(); ) { Point r; } for (Benchmark b("Point hash nonuniform"); b.iter(); ) { Point::from_hash(ep); } for (Benchmark b("Point hash uniform"); b.iter(); ) { Point::from_hash(ep2); } for (Benchmark b("Point unhash nonuniform"); b.iter(); ) { ignore_result(p.invert_elligator(ep,0)); } for (Benchmark b("Point unhash uniform"); b.iter(); ) { ignore_result(p.invert_elligator(ep2,0)); } for (Benchmark b("Point steg"); b.iter(); ) { p.steg_encode(rng); } for (Benchmark b("Point double scalarmul"); b.iter(); ) { Point::double_scalarmul(p,s,q,t); } for (Benchmark b("Point dual scalarmul"); b.iter(); ) { p.dual_scalarmul(p,q,s,t); } for (Benchmark b("Point precmp scalarmul"); b.iter(); ) { pBase * s; } for (Benchmark b("Point double scalarmul_v"); b.iter(); ) { s = Scalar(rng); t = Scalar(rng); p.non_secret_combo_with_base(s,t); } } }; /* template struct Benches */ template struct Macro { static void run() { Benches::macro(); } }; template struct Micro { static void run() { Benches::micro(); } }; int main(int argc, char **argv) { bool micro = false; if (argc >= 2 && !strcmp(argv[1], "--micro")) micro = true; SpongeRng rng(Block("micro-benchmarks"),SpongeRng::DETERMINISTIC); if (micro) { printf("\nMicro-benchmarks:\n"); SHAKE<128> shake1; SHAKE<256> shake2; SHA3<512> sha5; SHA512 sha2; unsigned char b1024[1024] = {1}; for (Benchmark b("SHAKE128 1kiB", 30); b.iter(); ) { shake1 += Buffer(b1024,1024); } for (Benchmark b("SHAKE256 1kiB", 30); b.iter(); ) { shake2 += Buffer(b1024,1024); } for (Benchmark b("SHA3-512 1kiB", 30); b.iter(); ) { sha5 += Buffer(b1024,1024); } for (Benchmark b("SHA512 1kiB", 30); b.iter(); ) { sha2 += Buffer(b1024,1024); } run_for_all_curves(); } run_for_all_curves(); printf("\n"); Benchmark::calib(); printf("\n"); return 0; }