aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2016-06-01 18:22:20 +0200
committerPieter Wuille <pieter.wuille@gmail.com>2016-06-01 18:22:34 +0200
commitb89ef131147f71a96152a7b5c4374266cdf539b2 (patch)
tree5d1b3beb20160d13d4cf285765e1751d88d3d5dc
parent01d8359983e2f77b5118fede3ffa947072c666c8 (diff)
parent723779c6504453cfb5ccdacf864e7e2f09bb6c32 (diff)
downloadbitcoin-b89ef131147f71a96152a7b5c4374266cdf539b2.tar.xz
Merge #7689: Replace OpenSSL AES with ctaes-based version
723779c build: Enumerate ctaes rather than globbing (Cory Fields) 34ed64a crypter: add tests for crypter (Cory Fields) 0a36b9a crypter: shuffle Makefile so that crypto can be used by the wallet (Cory Fields) 976f9ec crypter: add a BytesToKey clone to replace the use of openssl (Cory Fields) 9049cde crypter: hook up the new aes cbc classes (Cory Fields) fb96831 crypter: constify encrypt/decrypt (Cory Fields) 1c391a5 crypter: fix the stored initialization vector size (Cory Fields) daa3841 crypto: add aes cbc tests (Cory Fields) 27a212d crypto: add AES 128/256 CBC classes (Cory Fields) 6bec172 Add ctaes-based constant time AES implementation (Pieter Wuille) a545127 Squashed 'src/crypto/ctaes/' content from commit cd3c3ac (Pieter Wuille)
-rw-r--r--src/Makefile.am65
-rw-r--r--src/Makefile.test.include1
-rw-r--r--src/crypto/aes.cpp217
-rw-r--r--src/crypto/aes.h118
-rw-r--r--src/crypto/ctaes/COPYING21
-rw-r--r--src/crypto/ctaes/README.md41
-rw-r--r--src/crypto/ctaes/bench.c170
-rw-r--r--src/crypto/ctaes/ctaes.c556
-rw-r--r--src/crypto/ctaes/ctaes.h41
-rw-r--r--src/crypto/ctaes/test.c110
-rw-r--r--src/test/crypto_tests.cpp191
-rw-r--r--src/wallet/crypter.cpp92
-rw-r--r--src/wallet/crypter.h15
-rw-r--r--src/wallet/test/crypto_tests.cpp230
14 files changed, 1790 insertions, 78 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index f630ad4aa1..ea49efe92d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -15,13 +15,12 @@ LIBUNIVALUE = $(UNIVALUE_LIBS)
endif
BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config
-BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
+BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS)
LIBBITCOIN_SERVER=libbitcoin_server.a
-LIBBITCOIN_WALLET=libbitcoin_wallet.a
LIBBITCOIN_COMMON=libbitcoin_common.a
LIBBITCOIN_CONSENSUS=libbitcoin_consensus.a
LIBBITCOIN_CLI=libbitcoin_cli.a
@@ -30,32 +29,32 @@ LIBBITCOIN_CRYPTO=crypto/libbitcoin_crypto.a
LIBBITCOINQT=qt/libbitcoinqt.a
LIBSECP256K1=secp256k1/libsecp256k1.la
+if ENABLE_ZMQ
+LIBBITCOIN_ZMQ=libbitcoin_zmq.a
+endif
+if BUILD_BITCOIN_LIBS
+LIBBITCOINCONSENSUS=libbitcoinconsensus.la
+endif
+if ENABLE_WALLET
+LIBBITCOIN_WALLET=libbitcoin_wallet.a
+endif
+
$(LIBSECP256K1): $(wildcard secp256k1/src/*) $(wildcard secp256k1/include/*)
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C $(@D) $(@F)
# Make is not made aware of per-object dependencies to avoid limiting building parallelization
# But to build the less dependent modules first, we manually select their order here:
EXTRA_LIBRARIES += \
- crypto/libbitcoin_crypto.a \
- libbitcoin_util.a \
- libbitcoin_common.a \
- libbitcoin_consensus.a \
- libbitcoin_server.a \
- libbitcoin_cli.a
-if ENABLE_WALLET
-BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
-EXTRA_LIBRARIES += libbitcoin_wallet.a
-endif
-if ENABLE_ZMQ
-EXTRA_LIBRARIES += libbitcoin_zmq.a
-endif
+ $(LIBBITCOIN_CRYPTO) \
+ $(LIBBITCOIN_UTIL) \
+ $(LIBBITCOIN_COMMON) \
+ $(LIBBITCOIN_CONSENSUS) \
+ $(LIBBITCOIN_SERVER) \
+ $(LIBBITCOIN_CLI) \
+ $(LIBBITCOIN_WALLET) \
+ $(LIBBITCOIN_ZMQ)
-if BUILD_BITCOIN_LIBS
-lib_LTLIBRARIES = libbitcoinconsensus.la
-LIBBITCOINCONSENSUS=libbitcoinconsensus.la
-else
-LIBBITCOINCONSENSUS=
-endif
+lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
bin_PROGRAMS =
TESTS =
@@ -196,8 +195,6 @@ libbitcoin_server_a_SOURCES = \
$(BITCOIN_CORE_H)
if ENABLE_ZMQ
-LIBBITCOIN_ZMQ=libbitcoin_zmq.a
-
libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
libbitcoin_zmq_a_SOURCES = \
@@ -225,6 +222,8 @@ libbitcoin_wallet_a_SOURCES = \
crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES)
crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
crypto_libbitcoin_crypto_a_SOURCES = \
+ crypto/aes.cpp \
+ crypto/aes.h \
crypto/common.h \
crypto/hmac_sha256.cpp \
crypto/hmac_sha256.h \
@@ -345,21 +344,15 @@ bitcoind_LDADD = \
$(LIBBITCOIN_COMMON) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_UTIL) \
+ $(LIBBITCOIN_WALLET) \
+ $(LIBBITCOIN_ZMQ) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBLEVELDB) \
$(LIBMEMENV) \
$(LIBSECP256K1)
-if ENABLE_ZMQ
-bitcoind_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
-endif
-
-if ENABLE_WALLET
-bitcoind_LDADD += libbitcoin_wallet.a
-endif
-
-bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
+bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
@@ -418,6 +411,12 @@ libbitcoinconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
endif
#
+CTAES_DIST = crypto/ctaes/bench.c
+CTAES_DIST += crypto/ctaes/ctaes.c
+CTAES_DIST += crypto/ctaes/ctaes.h
+CTAES_DIST += crypto/ctaes/README.md
+CTAES_DIST += crypto/ctaes/test.c
+
CLEANFILES = leveldb/libleveldb.a leveldb/libmemenv.a
CLEANFILES += $(EXTRA_LIBRARIES)
CLEANFILES += *.gcda *.gcno
@@ -435,7 +434,7 @@ CLEANFILES += zmq/*.gcda zmq/*.gcno
DISTCLEANFILES = obj/build.h
-EXTRA_DIST = leveldb
+EXTRA_DIST = leveldb $(CTAES_DIST)
clean-local:
-$(MAKE) -C leveldb clean
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index 897a7dd4a2..77cf1001e1 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -95,6 +95,7 @@ BITCOIN_TESTS += \
wallet/test/wallet_test_fixture.h \
wallet/test/accounting_tests.cpp \
wallet/test/wallet_tests.cpp \
+ wallet/test/crypto_tests.cpp \
wallet/test/rpc_wallet_tests.cpp
endif
diff --git a/src/crypto/aes.cpp b/src/crypto/aes.cpp
new file mode 100644
index 0000000000..1d469d0fb4
--- /dev/null
+++ b/src/crypto/aes.cpp
@@ -0,0 +1,217 @@
+// Copyright (c) 2016 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "aes.h"
+#include "crypto/common.h"
+
+#include <assert.h>
+#include <string.h>
+
+extern "C" {
+#include "crypto/ctaes/ctaes.c"
+}
+
+AES128Encrypt::AES128Encrypt(const unsigned char key[16])
+{
+ AES128_init(&ctx, key);
+}
+
+AES128Encrypt::~AES128Encrypt()
+{
+ memset(&ctx, 0, sizeof(ctx));
+}
+
+void AES128Encrypt::Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const
+{
+ AES128_encrypt(&ctx, 1, ciphertext, plaintext);
+}
+
+AES128Decrypt::AES128Decrypt(const unsigned char key[16])
+{
+ AES128_init(&ctx, key);
+}
+
+AES128Decrypt::~AES128Decrypt()
+{
+ memset(&ctx, 0, sizeof(ctx));
+}
+
+void AES128Decrypt::Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const
+{
+ AES128_decrypt(&ctx, 1, plaintext, ciphertext);
+}
+
+AES256Encrypt::AES256Encrypt(const unsigned char key[32])
+{
+ AES256_init(&ctx, key);
+}
+
+AES256Encrypt::~AES256Encrypt()
+{
+ memset(&ctx, 0, sizeof(ctx));
+}
+
+void AES256Encrypt::Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const
+{
+ AES256_encrypt(&ctx, 1, ciphertext, plaintext);
+}
+
+AES256Decrypt::AES256Decrypt(const unsigned char key[32])
+{
+ AES256_init(&ctx, key);
+}
+
+AES256Decrypt::~AES256Decrypt()
+{
+ memset(&ctx, 0, sizeof(ctx));
+}
+
+void AES256Decrypt::Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const
+{
+ AES256_decrypt(&ctx, 1, plaintext, ciphertext);
+}
+
+
+template <typename T>
+static int CBCEncrypt(const T& enc, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out)
+{
+ int written = 0;
+ int padsize = size % AES_BLOCKSIZE;
+ unsigned char mixed[AES_BLOCKSIZE];
+
+ if (!data || !size || !out)
+ return 0;
+
+ if (!pad && padsize != 0)
+ return 0;
+
+ memcpy(mixed, iv, AES_BLOCKSIZE);
+
+ // Write all but the last block
+ while (written + AES_BLOCKSIZE <= size) {
+ for (int i = 0; i != AES_BLOCKSIZE; i++)
+ mixed[i] ^= *data++;
+ enc.Encrypt(out + written, mixed);
+ memcpy(mixed, out + written, AES_BLOCKSIZE);
+ written += AES_BLOCKSIZE;
+ }
+ if (pad) {
+ // For all that remains, pad each byte with the value of the remaining
+ // space. If there is none, pad by a full block.
+ for (int i = 0; i != padsize; i++)
+ mixed[i] ^= *data++;
+ for (int i = padsize; i != AES_BLOCKSIZE; i++)
+ mixed[i] ^= AES_BLOCKSIZE - padsize;
+ enc.Encrypt(out + written, mixed);
+ written += AES_BLOCKSIZE;
+ }
+ return written;
+}
+
+template <typename T>
+static int CBCDecrypt(const T& dec, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out)
+{
+ unsigned char padsize = 0;
+ int written = 0;
+ bool fail = false;
+ const unsigned char* prev = iv;
+
+ if (!data || !size || !out)
+ return 0;
+
+ if (size % AES_BLOCKSIZE != 0)
+ return 0;
+
+ // Decrypt all data. Padding will be checked in the output.
+ while (written != size) {
+ dec.Decrypt(out, data + written);
+ for (int i = 0; i != AES_BLOCKSIZE; i++)
+ *out++ ^= prev[i];
+ prev = data + written;
+ written += AES_BLOCKSIZE;
+ }
+
+ // When decrypting padding, attempt to run in constant-time
+ if (pad) {
+ // If used, padding size is the value of the last decrypted byte. For
+ // it to be valid, It must be between 1 and AES_BLOCKSIZE.
+ padsize = *--out;
+ fail = !padsize | (padsize > AES_BLOCKSIZE);
+
+ // If not well-formed, treat it as though there's no padding.
+ padsize *= !fail;
+
+ // All padding must equal the last byte otherwise it's not well-formed
+ for (int i = AES_BLOCKSIZE; i != 0; i--)
+ fail |= ((i > AES_BLOCKSIZE - padsize) & (*out-- != padsize));
+
+ written -= padsize;
+ }
+ return written * !fail;
+}
+
+AES256CBCEncrypt::AES256CBCEncrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
+ : enc(key), pad(padIn)
+{
+ memcpy(iv, ivIn, AES_BLOCKSIZE);
+}
+
+int AES256CBCEncrypt::Encrypt(const unsigned char* data, int size, unsigned char* out) const
+{
+ return CBCEncrypt(enc, iv, data, size, pad, out);
+}
+
+AES256CBCEncrypt::~AES256CBCEncrypt()
+{
+ memset(iv, 0, sizeof(iv));
+}
+
+AES256CBCDecrypt::AES256CBCDecrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
+ : dec(key), pad(padIn)
+{
+ memcpy(iv, ivIn, AES_BLOCKSIZE);
+}
+
+
+int AES256CBCDecrypt::Decrypt(const unsigned char* data, int size, unsigned char* out) const
+{
+ return CBCDecrypt(dec, iv, data, size, pad, out);
+}
+
+AES256CBCDecrypt::~AES256CBCDecrypt()
+{
+ memset(iv, 0, sizeof(iv));
+}
+
+AES128CBCEncrypt::AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
+ : enc(key), pad(padIn)
+{
+ memcpy(iv, ivIn, AES_BLOCKSIZE);
+}
+
+AES128CBCEncrypt::~AES128CBCEncrypt()
+{
+ memset(iv, 0, AES_BLOCKSIZE);
+}
+
+int AES128CBCEncrypt::Encrypt(const unsigned char* data, int size, unsigned char* out) const
+{
+ return CBCEncrypt(enc, iv, data, size, pad, out);
+}
+
+AES128CBCDecrypt::AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn)
+ : dec(key), pad(padIn)
+{
+ memcpy(iv, ivIn, AES_BLOCKSIZE);
+}
+
+AES128CBCDecrypt::~AES128CBCDecrypt()
+{
+ memset(iv, 0, AES_BLOCKSIZE);
+}
+
+int AES128CBCDecrypt::Decrypt(const unsigned char* data, int size, unsigned char* out) const
+{
+ return CBCDecrypt(dec, iv, data, size, pad, out);
+}
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
new file mode 100644
index 0000000000..8cae357c12
--- /dev/null
+++ b/src/crypto/aes.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2015 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+//
+// C++ wrapper around ctaes, a constant-time AES implementation
+
+#ifndef BITCOIN_CRYPTO_AES_H
+#define BITCOIN_CRYPTO_AES_H
+
+extern "C" {
+#include "crypto/ctaes/ctaes.h"
+}
+
+static const int AES_BLOCKSIZE = 16;
+static const int AES128_KEYSIZE = 16;
+static const int AES256_KEYSIZE = 32;
+
+/** An encryption class for AES-128. */
+class AES128Encrypt
+{
+private:
+ AES128_ctx ctx;
+
+public:
+ AES128Encrypt(const unsigned char key[16]);
+ ~AES128Encrypt();
+ void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const;
+};
+
+/** A decryption class for AES-128. */
+class AES128Decrypt
+{
+private:
+ AES128_ctx ctx;
+
+public:
+ AES128Decrypt(const unsigned char key[16]);
+ ~AES128Decrypt();
+ void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const;
+};
+
+/** An encryption class for AES-256. */
+class AES256Encrypt
+{
+private:
+ AES256_ctx ctx;
+
+public:
+ AES256Encrypt(const unsigned char key[32]);
+ ~AES256Encrypt();
+ void Encrypt(unsigned char ciphertext[16], const unsigned char plaintext[16]) const;
+};
+
+/** A decryption class for AES-256. */
+class AES256Decrypt
+{
+private:
+ AES256_ctx ctx;
+
+public:
+ AES256Decrypt(const unsigned char key[32]);
+ ~AES256Decrypt();
+ void Decrypt(unsigned char plaintext[16], const unsigned char ciphertext[16]) const;
+};
+
+class AES256CBCEncrypt
+{
+public:
+ AES256CBCEncrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
+ ~AES256CBCEncrypt();
+ int Encrypt(const unsigned char* data, int size, unsigned char* out) const;
+
+private:
+ const AES256Encrypt enc;
+ const bool pad;
+ unsigned char iv[AES_BLOCKSIZE];
+};
+
+class AES256CBCDecrypt
+{
+public:
+ AES256CBCDecrypt(const unsigned char key[AES256_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
+ ~AES256CBCDecrypt();
+ int Decrypt(const unsigned char* data, int size, unsigned char* out) const;
+
+private:
+ const AES256Decrypt dec;
+ const bool pad;
+ unsigned char iv[AES_BLOCKSIZE];
+};
+
+class AES128CBCEncrypt
+{
+public:
+ AES128CBCEncrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
+ ~AES128CBCEncrypt();
+ int Encrypt(const unsigned char* data, int size, unsigned char* out) const;
+
+private:
+ const AES128Encrypt enc;
+ const bool pad;
+ unsigned char iv[AES_BLOCKSIZE];
+};
+
+class AES128CBCDecrypt
+{
+public:
+ AES128CBCDecrypt(const unsigned char key[AES128_KEYSIZE], const unsigned char ivIn[AES_BLOCKSIZE], bool padIn);
+ ~AES128CBCDecrypt();
+ int Decrypt(const unsigned char* data, int size, unsigned char* out) const;
+
+private:
+ const AES128Decrypt dec;
+ const bool pad;
+ unsigned char iv[AES_BLOCKSIZE];
+};
+
+#endif // BITCOIN_CRYPTO_AES_H
diff --git a/src/crypto/ctaes/COPYING b/src/crypto/ctaes/COPYING
new file mode 100644
index 0000000000..415b202a2a
--- /dev/null
+++ b/src/crypto/ctaes/COPYING
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Pieter Wuille
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/crypto/ctaes/README.md b/src/crypto/ctaes/README.md
new file mode 100644
index 0000000000..0e7fe17751
--- /dev/null
+++ b/src/crypto/ctaes/README.md
@@ -0,0 +1,41 @@
+ctaes
+=====
+
+Simple C module for constant-time AES encryption and decryption.
+
+Features:
+* Simple, pure C code without any dependencies.
+* No tables or data-dependent branches whatsoever, but using bit sliced approach from https://eprint.iacr.org/2009/129.pdf.
+* Very small object code: slightly over 4k of executable code when compiled with -Os.
+* Slower than implementations based on precomputed tables or specialized instructions, but can do ~15 MB/s on modern CPUs.
+
+Performance
+-----------
+
+Compiled with GCC 5.3.1 with -O3, on an Intel(R) Core(TM) i7-4800MQ CPU, numbers in CPU cycles:
+
+| Algorithm | Key schedule | Encryption per byte | Decryption per byte |
+| --------- | ------------:| -------------------:| -------------------:|
+| AES-128 | 2.8k | 154 | 161 |
+| AES-192 | 3.1k | 169 | 181 |
+| AES-256 | 4.0k | 191 | 203 |
+
+Build steps
+-----------
+
+Object code:
+
+ $ gcc -O3 ctaes.c -c -o ctaes.o
+
+Tests:
+
+ $ gcc -O3 ctaes.c test.c -o test
+
+Benchmark:
+
+ $ gcc -O3 ctaes.c bench.c -o bench
+
+Review
+------
+
+Results of a formal review of the code can be found in http://bitcoin.sipa.be/ctaes/review.zip
diff --git a/src/crypto/ctaes/bench.c b/src/crypto/ctaes/bench.c
new file mode 100644
index 0000000000..a86df496c8
--- /dev/null
+++ b/src/crypto/ctaes/bench.c
@@ -0,0 +1,170 @@
+#include <stdio.h>
+#include <math.h>
+#include "sys/time.h"
+
+#include "ctaes.h"
+
+static double gettimedouble(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_usec * 0.000001 + tv.tv_sec;
+}
+
+static void print_number(double x) {
+ double y = x;
+ int c = 0;
+ if (y < 0.0) {
+ y = -y;
+ }
+ while (y < 100.0) {
+ y *= 10.0;
+ c++;
+ }
+ printf("%.*f", c, x);
+}
+
+static void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) {
+ int i;
+ double min = HUGE_VAL;
+ double sum = 0.0;
+ double max = 0.0;
+ for (i = 0; i < count; i++) {
+ double begin, total;
+ if (setup != NULL) {
+ setup(data);
+ }
+ begin = gettimedouble();
+ benchmark(data);
+ total = gettimedouble() - begin;
+ if (teardown != NULL) {
+ teardown(data);
+ }
+ if (total < min) {
+ min = total;
+ }
+ if (total > max) {
+ max = total;
+ }
+ sum += total;
+ }
+ printf("%s: min ", name);
+ print_number(min * 1000000000.0 / iter);
+ printf("ns / avg ");
+ print_number((sum / count) * 1000000000.0 / iter);
+ printf("ns / max ");
+ print_number(max * 1000000000.0 / iter);
+ printf("ns\n");
+}
+
+static void bench_AES128_init(void* data) {
+ AES128_ctx* ctx = (AES128_ctx*)data;
+ int i;
+ for (i = 0; i < 50000; i++) {
+ AES128_init(ctx, (unsigned char*)ctx);
+ }
+}
+
+static void bench_AES128_encrypt_setup(void* data) {
+ AES128_ctx* ctx = (AES128_ctx*)data;
+ static const unsigned char key[16] = {0};
+ AES128_init(ctx, key);
+}
+
+static void bench_AES128_encrypt(void* data) {
+ const AES128_ctx* ctx = (const AES128_ctx*)data;
+ unsigned char scratch[16] = {0};
+ int i;
+ for (i = 0; i < 4000000 / 16; i++) {
+ AES128_encrypt(ctx, 1, scratch, scratch);
+ }
+}
+
+static void bench_AES128_decrypt(void* data) {
+ const AES128_ctx* ctx = (const AES128_ctx*)data;
+ unsigned char scratch[16] = {0};
+ int i;
+ for (i = 0; i < 4000000 / 16; i++) {
+ AES128_decrypt(ctx, 1, scratch, scratch);
+ }
+}
+
+static void bench_AES192_init(void* data) {
+ AES192_ctx* ctx = (AES192_ctx*)data;
+ int i;
+ for (i = 0; i < 50000; i++) {
+ AES192_init(ctx, (unsigned char*)ctx);
+ }
+}
+
+static void bench_AES192_encrypt_setup(void* data) {
+ AES192_ctx* ctx = (AES192_ctx*)data;
+ static const unsigned char key[16] = {0};
+ AES192_init(ctx, key);
+}
+
+static void bench_AES192_encrypt(void* data) {
+ const AES192_ctx* ctx = (const AES192_ctx*)data;
+ unsigned char scratch[16] = {0};
+ int i;
+ for (i = 0; i < 4000000 / 16; i++) {
+ AES192_encrypt(ctx, 1, scratch, scratch);
+ }
+}
+
+static void bench_AES192_decrypt(void* data) {
+ const AES192_ctx* ctx = (const AES192_ctx*)data;
+ unsigned char scratch[16] = {0};
+ int i;
+ for (i = 0; i < 4000000 / 16; i++) {
+ AES192_decrypt(ctx, 1, scratch, scratch);
+ }
+}
+
+static void bench_AES256_init(void* data) {
+ AES256_ctx* ctx = (AES256_ctx*)data;
+ int i;
+ for (i = 0; i < 50000; i++) {
+ AES256_init(ctx, (unsigned char*)ctx);
+ }
+}
+
+
+static void bench_AES256_encrypt_setup(void* data) {
+ AES256_ctx* ctx = (AES256_ctx*)data;
+ static const unsigned char key[16] = {0};
+ AES256_init(ctx, key);
+}
+
+static void bench_AES256_encrypt(void* data) {
+ const AES256_ctx* ctx = (const AES256_ctx*)data;
+ unsigned char scratch[16] = {0};
+ int i;
+ for (i = 0; i < 4000000 / 16; i++) {
+ AES256_encrypt(ctx, 1, scratch, scratch);
+ }
+}
+
+static void bench_AES256_decrypt(void* data) {
+ const AES256_ctx* ctx = (const AES256_ctx*)data;
+ unsigned char scratch[16] = {0};
+ int i;
+ for (i = 0; i < 4000000 / 16; i++) {
+ AES256_decrypt(ctx, 1, scratch, scratch);
+ }
+}
+
+int main(void) {
+ AES128_ctx ctx128;
+ AES192_ctx ctx192;
+ AES256_ctx ctx256;
+ run_benchmark("aes128_init", bench_AES128_init, NULL, NULL, &ctx128, 20, 50000);
+ run_benchmark("aes128_encrypt_byte", bench_AES128_encrypt, bench_AES128_encrypt_setup, NULL, &ctx128, 20, 4000000);
+ run_benchmark("aes128_decrypt_byte", bench_AES128_decrypt, bench_AES128_encrypt_setup, NULL, &ctx128, 20, 4000000);
+ run_benchmark("aes192_init", bench_AES192_init, NULL, NULL, &ctx192, 20, 50000);
+ run_benchmark("aes192_encrypt_byte", bench_AES192_encrypt, bench_AES192_encrypt_setup, NULL, &ctx192, 20, 4000000);
+ run_benchmark("aes192_decrypt_byte", bench_AES192_decrypt, bench_AES192_encrypt_setup, NULL, &ctx192, 20, 4000000);
+ run_benchmark("aes256_init", bench_AES256_init, NULL, NULL, &ctx256, 20, 50000);
+ run_benchmark("aes256_encrypt_byte", bench_AES256_encrypt, bench_AES256_encrypt_setup, NULL, &ctx256, 20, 4000000);
+ run_benchmark("aes256_decrypt_byte", bench_AES256_decrypt, bench_AES256_encrypt_setup, NULL, &ctx256, 20, 4000000);
+ return 0;
+}
diff --git a/src/crypto/ctaes/ctaes.c b/src/crypto/ctaes/ctaes.c
new file mode 100644
index 0000000000..2389fc0bb2
--- /dev/null
+++ b/src/crypto/ctaes/ctaes.c
@@ -0,0 +1,556 @@
+ /*********************************************************************
+ * Copyright (c) 2016 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+/* Constant time, unoptimized, concise, plain C, AES implementation
+ * Based On:
+ * Emilia Kasper and Peter Schwabe, Faster and Timing-Attack Resistant AES-GCM
+ * http://www.iacr.org/archive/ches2009/57470001/57470001.pdf
+ * But using 8 16-bit integers representing a single AES state rather than 8 128-bit
+ * integers representing 8 AES states.
+ */
+
+#include "ctaes.h"
+
+/* Slice variable slice_i contains the i'th bit of the 16 state variables in this order:
+ * 0 1 2 3
+ * 4 5 6 7
+ * 8 9 10 11
+ * 12 13 14 15
+ */
+
+/** Convert a byte to sliced form, storing it corresponding to given row and column in s */
+static void LoadByte(AES_state* s, unsigned char byte, int r, int c) {
+ int i;
+ for (i = 0; i < 8; i++) {
+ s->slice[i] |= (byte & 1) << (r * 4 + c);
+ byte >>= 1;
+ }
+}
+
+/** Load 16 bytes of data into 8 sliced integers */
+static void LoadBytes(AES_state *s, const unsigned char* data16) {
+ int c;
+ for (c = 0; c < 4; c++) {
+ int r;
+ for (r = 0; r < 4; r++) {
+ LoadByte(s, *(data16++), r, c);
+ }
+ }
+}
+
+/** Convert 8 sliced integers into 16 bytes of data */
+static void SaveBytes(unsigned char* data16, const AES_state *s) {
+ int c;
+ for (c = 0; c < 4; c++) {
+ int r;
+ for (r = 0; r < 4; r++) {
+ int b;
+ uint8_t v = 0;
+ for (b = 0; b < 8; b++) {
+ v |= ((s->slice[b] >> (r * 4 + c)) & 1) << b;
+ }
+ *(data16++) = v;
+ }
+ }
+}
+
+/* S-box implementation based on the gate logic from:
+ * Joan Boyar and Rene Peralta, A depth-16 circuit for the AES S-box.
+ * https://eprint.iacr.org/2011/332.pdf
+*/
+static void SubBytes(AES_state *s, int inv) {
+ /* Load the bit slices */
+ uint16_t U0 = s->slice[7], U1 = s->slice[6], U2 = s->slice[5], U3 = s->slice[4];
+ uint16_t U4 = s->slice[3], U5 = s->slice[2], U6 = s->slice[1], U7 = s->slice[0];
+
+ uint16_t T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16;
+ uint16_t T17, T18, T19, T20, T21, T22, T23, T24, T25, T26, T27, D;
+ uint16_t M1, M6, M11, M13, M15, M20, M21, M22, M23, M25, M37, M38, M39, M40;
+ uint16_t M41, M42, M43, M44, M45, M46, M47, M48, M49, M50, M51, M52, M53, M54;
+ uint16_t M55, M56, M57, M58, M59, M60, M61, M62, M63;
+
+ if (inv) {
+ uint16_t R5, R13, R17, R18, R19;
+ /* Undo linear postprocessing */
+ T23 = U0 ^ U3;
+ T22 = ~(U1 ^ U3);
+ T2 = ~(U0 ^ U1);
+ T1 = U3 ^ U4;
+ T24 = ~(U4 ^ U7);
+ R5 = U6 ^ U7;
+ T8 = ~(U1 ^ T23);
+ T19 = T22 ^ R5;
+ T9 = ~(U7 ^ T1);
+ T10 = T2 ^ T24;
+ T13 = T2 ^ R5;
+ T3 = T1 ^ R5;
+ T25 = ~(U2 ^ T1);
+ R13 = U1 ^ U6;
+ T17 = ~(U2 ^ T19);
+ T20 = T24 ^ R13;
+ T4 = U4 ^ T8;
+ R17 = ~(U2 ^ U5);
+ R18 = ~(U5 ^ U6);
+ R19 = ~(U2 ^ U4);
+ D = U0 ^ R17;
+ T6 = T22 ^ R17;
+ T16 = R13 ^ R19;
+ T27 = T1 ^ R18;
+ T15 = T10 ^ T27;
+ T14 = T10 ^ R18;
+ T26 = T3 ^ T16;
+ } else {
+ /* Linear preprocessing. */
+ T1 = U0 ^ U3;
+ T2 = U0 ^ U5;
+ T3 = U0 ^ U6;
+ T4 = U3 ^ U5;
+ T5 = U4 ^ U6;
+ T6 = T1 ^ T5;
+ T7 = U1 ^ U2;
+ T8 = U7 ^ T6;
+ T9 = U7 ^ T7;
+ T10 = T6 ^ T7;
+ T11 = U1 ^ U5;
+ T12 = U2 ^ U5;
+ T13 = T3 ^ T4;
+ T14 = T6 ^ T11;
+ T15 = T5 ^ T11;
+ T16 = T5 ^ T12;
+ T17 = T9 ^ T16;
+ T18 = U3 ^ U7;
+ T19 = T7 ^ T18;
+ T20 = T1 ^ T19;
+ T21 = U6 ^ U7;
+ T22 = T7 ^ T21;
+ T23 = T2 ^ T22;
+ T24 = T2 ^ T10;
+ T25 = T20 ^ T17;
+ T26 = T3 ^ T16;
+ T27 = T1 ^ T12;
+ D = U7;
+ }
+
+ /* Non-linear transformation (identical to the code in SubBytes) */
+ M1 = T13 & T6;
+ M6 = T3 & T16;
+ M11 = T1 & T15;
+ M13 = (T4 & T27) ^ M11;
+ M15 = (T2 & T10) ^ M11;
+ M20 = T14 ^ M1 ^ (T23 & T8) ^ M13;
+ M21 = (T19 & D) ^ M1 ^ T24 ^ M15;
+ M22 = T26 ^ M6 ^ (T22 & T9) ^ M13;
+ M23 = (T20 & T17) ^ M6 ^ M15 ^ T25;
+ M25 = M22 & M20;
+ M37 = M21 ^ ((M20 ^ M21) & (M23 ^ M25));
+ M38 = M20 ^ M25 ^ (M21 | (M20 & M23));
+ M39 = M23 ^ ((M22 ^ M23) & (M21 ^ M25));
+ M40 = M22 ^ M25 ^ (M23 | (M21 & M22));
+ M41 = M38 ^ M40;
+ M42 = M37 ^ M39;
+ M43 = M37 ^ M38;
+ M44 = M39 ^ M40;
+ M45 = M42 ^ M41;
+ M46 = M44 & T6;
+ M47 = M40 & T8;
+ M48 = M39 & D;
+ M49 = M43 & T16;
+ M50 = M38 & T9;
+ M51 = M37 & T17;
+ M52 = M42 & T15;
+ M53 = M45 & T27;
+ M54 = M41 & T10;
+ M55 = M44 & T13;
+ M56 = M40 & T23;
+ M57 = M39 & T19;
+ M58 = M43 & T3;
+ M59 = M38 & T22;
+ M60 = M37 & T20;
+ M61 = M42 & T1;
+ M62 = M45 & T4;
+ M63 = M41 & T2;
+
+ if (inv){
+ /* Undo linear preprocessing */
+ uint16_t P0 = M52 ^ M61;
+ uint16_t P1 = M58 ^ M59;
+ uint16_t P2 = M54 ^ M62;
+ uint16_t P3 = M47 ^ M50;
+ uint16_t P4 = M48 ^ M56;
+ uint16_t P5 = M46 ^ M51;
+ uint16_t P6 = M49 ^ M60;
+ uint16_t P7 = P0 ^ P1;
+ uint16_t P8 = M50 ^ M53;
+ uint16_t P9 = M55 ^ M63;
+ uint16_t P10 = M57 ^ P4;
+ uint16_t P11 = P0 ^ P3;
+ uint16_t P12 = M46 ^ M48;
+ uint16_t P13 = M49 ^ M51;
+ uint16_t P14 = M49 ^ M62;
+ uint16_t P15 = M54 ^ M59;
+ uint16_t P16 = M57 ^ M61;
+ uint16_t P17 = M58 ^ P2;
+ uint16_t P18 = M63 ^ P5;
+ uint16_t P19 = P2 ^ P3;
+ uint16_t P20 = P4 ^ P6;
+ uint16_t P22 = P2 ^ P7;
+ uint16_t P23 = P7 ^ P8;
+ uint16_t P24 = P5 ^ P7;
+ uint16_t P25 = P6 ^ P10;
+ uint16_t P26 = P9 ^ P11;
+ uint16_t P27 = P10 ^ P18;
+ uint16_t P28 = P11 ^ P25;
+ uint16_t P29 = P15 ^ P20;
+ s->slice[7] = P13 ^ P22;
+ s->slice[6] = P26 ^ P29;
+ s->slice[5] = P17 ^ P28;
+ s->slice[4] = P12 ^ P22;
+ s->slice[3] = P23 ^ P27;
+ s->slice[2] = P19 ^ P24;
+ s->slice[1] = P14 ^ P23;
+ s->slice[0] = P9 ^ P16;
+ } else {
+ /* Linear postprocessing */
+ uint16_t L0 = M61 ^ M62;
+ uint16_t L1 = M50 ^ M56;
+ uint16_t L2 = M46 ^ M48;
+ uint16_t L3 = M47 ^ M55;
+ uint16_t L4 = M54 ^ M58;
+ uint16_t L5 = M49 ^ M61;
+ uint16_t L6 = M62 ^ L5;
+ uint16_t L7 = M46 ^ L3;
+ uint16_t L8 = M51 ^ M59;
+ uint16_t L9 = M52 ^ M53;
+ uint16_t L10 = M53 ^ L4;
+ uint16_t L11 = M60 ^ L2;
+ uint16_t L12 = M48 ^ M51;
+ uint16_t L13 = M50 ^ L0;
+ uint16_t L14 = M52 ^ M61;
+ uint16_t L15 = M55 ^ L1;
+ uint16_t L16 = M56 ^ L0;
+ uint16_t L17 = M57 ^ L1;
+ uint16_t L18 = M58 ^ L8;
+ uint16_t L19 = M63 ^ L4;
+ uint16_t L20 = L0 ^ L1;
+ uint16_t L21 = L1 ^ L7;
+ uint16_t L22 = L3 ^ L12;
+ uint16_t L23 = L18 ^ L2;
+ uint16_t L24 = L15 ^ L9;
+ uint16_t L25 = L6 ^ L10;
+ uint16_t L26 = L7 ^ L9;
+ uint16_t L27 = L8 ^ L10;
+ uint16_t L28 = L11 ^ L14;
+ uint16_t L29 = L11 ^ L17;
+ s->slice[7] = L6 ^ L24;
+ s->slice[6] = ~(L16 ^ L26);
+ s->slice[5] = ~(L19 ^ L28);
+ s->slice[4] = L6 ^ L21;
+ s->slice[3] = L20 ^ L22;
+ s->slice[2] = L25 ^ L29;
+ s->slice[1] = ~(L13 ^ L27);
+ s->slice[0] = ~(L6 ^ L23);
+ }
+}
+
+#define BIT_RANGE(from,to) (((1 << ((to) - (from))) - 1) << (from))
+
+#define BIT_RANGE_LEFT(x,from,to,shift) (((x) & BIT_RANGE((from), (to))) << (shift))
+#define BIT_RANGE_RIGHT(x,from,to,shift) (((x) & BIT_RANGE((from), (to))) >> (shift))
+
+static void ShiftRows(AES_state* s) {
+ int i;
+ for (i = 0; i < 8; i++) {
+ uint16_t v = s->slice[i];
+ s->slice[i] =
+ (v & BIT_RANGE(0, 4)) |
+ BIT_RANGE_LEFT(v, 4, 5, 3) | BIT_RANGE_RIGHT(v, 5, 8, 1) |
+ BIT_RANGE_LEFT(v, 8, 10, 2) | BIT_RANGE_RIGHT(v, 10, 12, 2) |
+ BIT_RANGE_LEFT(v, 12, 15, 1) | BIT_RANGE_RIGHT(v, 15, 16, 3);
+ }
+}
+
+static void InvShiftRows(AES_state* s) {
+ int i;
+ for (i = 0; i < 8; i++) {
+ uint16_t v = s->slice[i];
+ s->slice[i] =
+ (v & BIT_RANGE(0, 4)) |
+ BIT_RANGE_LEFT(v, 4, 7, 1) | BIT_RANGE_RIGHT(v, 7, 8, 3) |
+ BIT_RANGE_LEFT(v, 8, 10, 2) | BIT_RANGE_RIGHT(v, 10, 12, 2) |
+ BIT_RANGE_LEFT(v, 12, 13, 3) | BIT_RANGE_RIGHT(v, 13, 16, 1);
+ }
+}
+
+#define ROT(x,b) (((x) >> ((b) * 4)) | ((x) << ((4-(b)) * 4)))
+
+static void MixColumns(AES_state* s, int inv) {
+ /* The MixColumns transform treats the bytes of the columns of the state as
+ * coefficients of a 3rd degree polynomial over GF(2^8) and multiplies them
+ * by the fixed polynomial a(x) = {03}x^3 + {01}x^2 + {01}x + {02}, modulo
+ * x^4 + {01}.
+ *
+ * In the inverse transform, we multiply by the inverse of a(x),
+ * a^-1(x) = {0b}x^3 + {0d}x^2 + {09}x + {0e}. This is equal to
+ * a(x) * ({04}x^2 + {05}), so we can reuse the forward transform's code
+ * (found in OpenSSL's bsaes-x86_64.pl, attributed to Jussi Kivilinna)
+ *
+ * In the bitsliced representation, a multiplication of every column by x
+ * mod x^4 + 1 is simply a right rotation.
+ */
+
+ /* Shared for both directions is a multiplication by a(x), which can be
+ * rewritten as (x^3 + x^2 + x) + {02}*(x^3 + {01}).
+ *
+ * First compute s into the s? variables, (x^3 + {01}) * s into the s?_01
+ * variables and (x^3 + x^2 + x)*s into the s?_123 variables.
+ */
+ uint16_t s0 = s->slice[0], s1 = s->slice[1], s2 = s->slice[2], s3 = s->slice[3];
+ uint16_t s4 = s->slice[4], s5 = s->slice[5], s6 = s->slice[6], s7 = s->slice[7];
+ uint16_t s0_01 = s0 ^ ROT(s0, 1), s0_123 = ROT(s0_01, 1) ^ ROT(s0, 3);
+ uint16_t s1_01 = s1 ^ ROT(s1, 1), s1_123 = ROT(s1_01, 1) ^ ROT(s1, 3);
+ uint16_t s2_01 = s2 ^ ROT(s2, 1), s2_123 = ROT(s2_01, 1) ^ ROT(s2, 3);
+ uint16_t s3_01 = s3 ^ ROT(s3, 1), s3_123 = ROT(s3_01, 1) ^ ROT(s3, 3);
+ uint16_t s4_01 = s4 ^ ROT(s4, 1), s4_123 = ROT(s4_01, 1) ^ ROT(s4, 3);
+ uint16_t s5_01 = s5 ^ ROT(s5, 1), s5_123 = ROT(s5_01, 1) ^ ROT(s5, 3);
+ uint16_t s6_01 = s6 ^ ROT(s6, 1), s6_123 = ROT(s6_01, 1) ^ ROT(s6, 3);
+ uint16_t s7_01 = s7 ^ ROT(s7, 1), s7_123 = ROT(s7_01, 1) ^ ROT(s7, 3);
+ /* Now compute s = s?_123 + {02} * s?_01. */
+ s->slice[0] = s7_01 ^ s0_123;
+ s->slice[1] = s7_01 ^ s0_01 ^ s1_123;
+ s->slice[2] = s1_01 ^ s2_123;
+ s->slice[3] = s7_01 ^ s2_01 ^ s3_123;
+ s->slice[4] = s7_01 ^ s3_01 ^ s4_123;
+ s->slice[5] = s4_01 ^ s5_123;
+ s->slice[6] = s5_01 ^ s6_123;
+ s->slice[7] = s6_01 ^ s7_123;
+ if (inv) {
+ /* In the reverse direction, we further need to multiply by
+ * {04}x^2 + {05}, which can be written as {04} * (x^2 + {01}) + {01}.
+ *
+ * First compute (x^2 + {01}) * s into the t?_02 variables: */
+ uint16_t t0_02 = s->slice[0] ^ ROT(s->slice[0], 2);
+ uint16_t t1_02 = s->slice[1] ^ ROT(s->slice[1], 2);
+ uint16_t t2_02 = s->slice[2] ^ ROT(s->slice[2], 2);
+ uint16_t t3_02 = s->slice[3] ^ ROT(s->slice[3], 2);
+ uint16_t t4_02 = s->slice[4] ^ ROT(s->slice[4], 2);
+ uint16_t t5_02 = s->slice[5] ^ ROT(s->slice[5], 2);
+ uint16_t t6_02 = s->slice[6] ^ ROT(s->slice[6], 2);
+ uint16_t t7_02 = s->slice[7] ^ ROT(s->slice[7], 2);
+ /* And then update s += {04} * t?_02 */
+ s->slice[0] ^= t6_02;
+ s->slice[1] ^= t6_02 ^ t7_02;
+ s->slice[2] ^= t0_02 ^ t7_02;
+ s->slice[3] ^= t1_02 ^ t6_02;
+ s->slice[4] ^= t2_02 ^ t6_02 ^ t7_02;
+ s->slice[5] ^= t3_02 ^ t7_02;
+ s->slice[6] ^= t4_02;
+ s->slice[7] ^= t5_02;
+ }
+}
+
+static void AddRoundKey(AES_state* s, const AES_state* round) {
+ int b;
+ for (b = 0; b < 8; b++) {
+ s->slice[b] ^= round->slice[b];
+ }
+}
+
+/** column_0(s) = column_c(a) */
+static void GetOneColumn(AES_state* s, const AES_state* a, int c) {
+ int b;
+ for (b = 0; b < 8; b++) {
+ s->slice[b] = (a->slice[b] >> c) & 0x1111;
+ }
+}
+
+/** column_c1(r) |= (column_0(s) ^= column_c2(a)) */
+static void KeySetupColumnMix(AES_state* s, AES_state* r, const AES_state* a, int c1, int c2) {
+ int b;
+ for (b = 0; b < 8; b++) {
+ r->slice[b] |= ((s->slice[b] ^= ((a->slice[b] >> c2) & 0x1111)) & 0x1111) << c1;
+ }
+}
+
+/** Rotate the rows in s one position upwards, and xor in r */
+static void KeySetupTransform(AES_state* s, const AES_state* r) {
+ int b;
+ for (b = 0; b < 8; b++) {
+ s->slice[b] = ((s->slice[b] >> 4) | (s->slice[b] << 12)) ^ r->slice[b];
+ }
+}
+
+/* Multiply the cells in s by x, as polynomials over GF(2) mod x^8 + x^4 + x^3 + x + 1 */
+static void MultX(AES_state* s) {
+ uint16_t top = s->slice[7];
+ s->slice[7] = s->slice[6];
+ s->slice[6] = s->slice[5];
+ s->slice[5] = s->slice[4];
+ s->slice[4] = s->slice[3] ^ top;
+ s->slice[3] = s->slice[2] ^ top;
+ s->slice[2] = s->slice[1];
+ s->slice[1] = s->slice[0] ^ top;
+ s->slice[0] = top;
+}
+
+/** Expand the cipher key into the key schedule.
+ *
+ * state must be a pointer to an array of size nrounds + 1.
+ * key must be a pointer to 4 * nkeywords bytes.
+ *
+ * AES128 uses nkeywords = 4, nrounds = 10
+ * AES192 uses nkeywords = 6, nrounds = 12
+ * AES256 uses nkeywords = 8, nrounds = 14
+ */
+static void AES_setup(AES_state* rounds, const uint8_t* key, int nkeywords, int nrounds)
+{
+ int i;
+
+ /* The one-byte round constant */
+ AES_state rcon = {{1,0,0,0,0,0,0,0}};
+ /* The number of the word being generated, modulo nkeywords */
+ int pos = 0;
+ /* The column representing the word currently being processed */
+ AES_state column;
+
+ for (i = 0; i < nrounds + 1; i++) {
+ int b;
+ for (b = 0; b < 8; b++) {
+ rounds[i].slice[b] = 0;
+ }
+ }
+
+ /* The first nkeywords round columns are just taken from the key directly. */
+ for (i = 0; i < nkeywords; i++) {
+ int r;
+ for (r = 0; r < 4; r++) {
+ LoadByte(&rounds[i >> 2], *(key++), r, i & 3);
+ }
+ }
+
+ GetOneColumn(&column, &rounds[(nkeywords - 1) >> 2], (nkeywords - 1) & 3);
+
+ for (i = nkeywords; i < 4 * (nrounds + 1); i++) {
+ /* Transform column */
+ if (pos == 0) {
+ SubBytes(&column, 0);
+ KeySetupTransform(&column, &rcon);
+ MultX(&rcon);
+ } else if (nkeywords > 6 && pos == 4) {
+ SubBytes(&column, 0);
+ }
+ if (++pos == nkeywords) pos = 0;
+ KeySetupColumnMix(&column, &rounds[i >> 2], &rounds[(i - nkeywords) >> 2], i & 3, (i - nkeywords) & 3);
+ }
+}
+
+static void AES_encrypt(const AES_state* rounds, int nrounds, unsigned char* cipher16, const unsigned char* plain16) {
+ AES_state s = {{0}};
+ int round;
+
+ LoadBytes(&s, plain16);
+ AddRoundKey(&s, rounds++);
+
+ for (round = 1; round < nrounds; round++) {
+ SubBytes(&s, 0);
+ ShiftRows(&s);
+ MixColumns(&s, 0);
+ AddRoundKey(&s, rounds++);
+ }
+
+ SubBytes(&s, 0);
+ ShiftRows(&s);
+ AddRoundKey(&s, rounds);
+
+ SaveBytes(cipher16, &s);
+}
+
+static void AES_decrypt(const AES_state* rounds, int nrounds, unsigned char* plain16, const unsigned char* cipher16) {
+ /* Most AES decryption implementations use the alternate scheme
+ * (the Equivalent Inverse Cipher), which looks more like encryption, but
+ * needs different round constants. We can't reuse any code here anyway, so
+ * don't bother. */
+ AES_state s = {{0}};
+ int round;
+
+ rounds += nrounds;
+
+ LoadBytes(&s, cipher16);
+ AddRoundKey(&s, rounds--);
+
+ for (round = 1; round < nrounds; round++) {
+ InvShiftRows(&s);
+ SubBytes(&s, 1);
+ AddRoundKey(&s, rounds--);
+ MixColumns(&s, 1);
+ }
+
+ InvShiftRows(&s);
+ SubBytes(&s, 1);
+ AddRoundKey(&s, rounds);
+
+ SaveBytes(plain16, &s);
+}
+
+void AES128_init(AES128_ctx* ctx, const unsigned char* key16) {
+ AES_setup(ctx->rk, key16, 4, 10);
+}
+
+void AES128_encrypt(const AES128_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16) {
+ while (blocks--) {
+ AES_encrypt(ctx->rk, 10, cipher16, plain16);
+ cipher16 += 16;
+ plain16 += 16;
+ }
+}
+
+void AES128_decrypt(const AES128_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16) {
+ while (blocks--) {
+ AES_decrypt(ctx->rk, 10, plain16, cipher16);
+ cipher16 += 16;
+ plain16 += 16;
+ }
+}
+
+void AES192_init(AES192_ctx* ctx, const unsigned char* key24) {
+ AES_setup(ctx->rk, key24, 6, 12);
+}
+
+void AES192_encrypt(const AES192_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16) {
+ while (blocks--) {
+ AES_encrypt(ctx->rk, 12, cipher16, plain16);
+ cipher16 += 16;
+ plain16 += 16;
+ }
+
+}
+
+void AES192_decrypt(const AES192_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16) {
+ while (blocks--) {
+ AES_decrypt(ctx->rk, 12, plain16, cipher16);
+ cipher16 += 16;
+ plain16 += 16;
+ }
+}
+
+void AES256_init(AES256_ctx* ctx, const unsigned char* key32) {
+ AES_setup(ctx->rk, key32, 8, 14);
+}
+
+void AES256_encrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16) {
+ while (blocks--) {
+ AES_encrypt(ctx->rk, 14, cipher16, plain16);
+ cipher16 += 16;
+ plain16 += 16;
+ }
+}
+
+void AES256_decrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16) {
+ while (blocks--) {
+ AES_decrypt(ctx->rk, 14, plain16, cipher16);
+ cipher16 += 16;
+ plain16 += 16;
+ }
+}
diff --git a/src/crypto/ctaes/ctaes.h b/src/crypto/ctaes/ctaes.h
new file mode 100644
index 0000000000..2f0af04216
--- /dev/null
+++ b/src/crypto/ctaes/ctaes.h
@@ -0,0 +1,41 @@
+ /*********************************************************************
+ * Copyright (c) 2016 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#ifndef _CTAES_H_
+#define _CTAES_H_ 1
+
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct {
+ uint16_t slice[8];
+} AES_state;
+
+typedef struct {
+ AES_state rk[11];
+} AES128_ctx;
+
+typedef struct {
+ AES_state rk[13];
+} AES192_ctx;
+
+typedef struct {
+ AES_state rk[15];
+} AES256_ctx;
+
+void AES128_init(AES128_ctx* ctx, const unsigned char* key16);
+void AES128_encrypt(const AES128_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16);
+void AES128_decrypt(const AES128_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16);
+
+void AES192_init(AES192_ctx* ctx, const unsigned char* key24);
+void AES192_encrypt(const AES192_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16);
+void AES192_decrypt(const AES192_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16);
+
+void AES256_init(AES256_ctx* ctx, const unsigned char* key32);
+void AES256_encrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* cipher16, const unsigned char* plain16);
+void AES256_decrypt(const AES256_ctx* ctx, size_t blocks, unsigned char* plain16, const unsigned char* cipher16);
+
+#endif
diff --git a/src/crypto/ctaes/test.c b/src/crypto/ctaes/test.c
new file mode 100644
index 0000000000..fce1696acd
--- /dev/null
+++ b/src/crypto/ctaes/test.c
@@ -0,0 +1,110 @@
+ /*********************************************************************
+ * Copyright (c) 2016 Pieter Wuille *
+ * Distributed under the MIT software license, see the accompanying *
+ * file COPYING or http://www.opensource.org/licenses/mit-license.php.*
+ **********************************************************************/
+
+#include "ctaes.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+typedef struct {
+ int keysize;
+ const char* key;
+ const char* plain;
+ const char* cipher;
+} ctaes_test;
+
+static const ctaes_test ctaes_tests[] = {
+ /* AES test vectors from FIPS 197. */
+ {128, "000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a"},
+ {192, "000102030405060708090a0b0c0d0e0f1011121314151617", "00112233445566778899aabbccddeeff", "dda97ca4864cdfe06eaf70a0ec0d7191"},
+ {256, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089"},
+
+ /* AES-ECB test vectors from NIST sp800-38a. */
+ {128, "2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97"},
+ {128, "2b7e151628aed2a6abf7158809cf4f3c", "ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf"},
+ {128, "2b7e151628aed2a6abf7158809cf4f3c", "30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688"},
+ {128, "2b7e151628aed2a6abf7158809cf4f3c", "f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4"},
+ {192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "6bc1bee22e409f96e93d7e117393172a", "bd334f1d6e45f25ff712a214571fa5cc"},
+ {192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "ae2d8a571e03ac9c9eb76fac45af8e51", "974104846d0ad3ad7734ecb3ecee4eef"},
+ {192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "30c81c46a35ce411e5fbc1191a0a52ef", "ef7afd2270e2e60adce0ba2face6444e"},
+ {192, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "f69f2445df4f9b17ad2b417be66c3710", "9a4b41ba738d6c72fb16691603c18e0e"},
+ {256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8"},
+ {256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "ae2d8a571e03ac9c9eb76fac45af8e51", "591ccb10d410ed26dc5ba74a31362870"},
+ {256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "30c81c46a35ce411e5fbc1191a0a52ef", "b6ed21b99ca6f4f9f153e7b1beafed1d"},
+ {256, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "f69f2445df4f9b17ad2b417be66c3710", "23304b7a39f9f3ff067d8d8f9e24ecc7"}
+};
+
+static void from_hex(unsigned char* data, int len, const char* hex) {
+ int p;
+ for (p = 0; p < len; p++) {
+ int v = 0;
+ int n;
+ for (n = 0; n < 2; n++) {
+ assert((*hex >= '0' && *hex <= '9') || (*hex >= 'a' && *hex <= 'f'));
+ if (*hex >= '0' && *hex <= '9') {
+ v |= (*hex - '0') << (4 * (1 - n));
+ } else {
+ v |= (*hex - 'a' + 10) << (4 * (1 - n));
+ }
+ hex++;
+ }
+ *(data++) = v;
+ }
+ assert(*hex == 0);
+}
+
+int main(void) {
+ int i;
+ int fail = 0;
+ for (i = 0; i < sizeof(ctaes_tests) / sizeof(ctaes_tests[0]); i++) {
+ unsigned char key[32], plain[16], cipher[16], ciphered[16], deciphered[16];
+ const ctaes_test* test = &ctaes_tests[i];
+ assert(test->keysize == 128 || test->keysize == 192 || test->keysize == 256);
+ from_hex(plain, 16, test->plain);
+ from_hex(cipher, 16, test->cipher);
+ switch (test->keysize) {
+ case 128: {
+ AES128_ctx ctx;
+ from_hex(key, 16, test->key);
+ AES128_init(&ctx, key);
+ AES128_encrypt(&ctx, 1, ciphered, plain);
+ AES128_decrypt(&ctx, 1, deciphered, cipher);
+ break;
+ }
+ case 192: {
+ AES192_ctx ctx;
+ from_hex(key, 24, test->key);
+ AES192_init(&ctx, key);
+ AES192_encrypt(&ctx, 1, ciphered, plain);
+ AES192_decrypt(&ctx, 1, deciphered, cipher);
+ break;
+ }
+ case 256: {
+ AES256_ctx ctx;
+ from_hex(key, 32, test->key);
+ AES256_init(&ctx, key);
+ AES256_encrypt(&ctx, 1, ciphered, plain);
+ AES256_decrypt(&ctx, 1, deciphered, cipher);
+ break;
+ }
+ }
+ if (memcmp(cipher, ciphered, 16)) {
+ fprintf(stderr, "E(key=\"%s\", plain=\"%s\") != \"%s\"\n", test->key, test->plain, test->cipher);
+ fail++;
+ }
+ if (memcmp(plain, deciphered, 16)) {
+ fprintf(stderr, "D(key=\"%s\", cipher=\"%s\") != \"%s\"\n", test->key, test->cipher, test->plain);
+ fail++;
+ }
+ }
+ if (fail == 0) {
+ fprintf(stderr, "All tests succesful\n");
+ } else {
+ fprintf(stderr, "%i tests failed\n", fail);
+ }
+ return (fail != 0);
+}
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 0b46d718d1..58a62ee022 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include "crypto/aes.h"
#include "crypto/ripemd160.h"
#include "crypto/sha1.h"
#include "crypto/sha256.h"
@@ -16,6 +17,8 @@
#include <boost/assign/list_of.hpp>
#include <boost/test/unit_test.hpp>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
BOOST_FIXTURE_TEST_SUITE(crypto_tests, BasicTestingSetup)
@@ -63,6 +66,127 @@ void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const s
TestVector(CHMAC_SHA512(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout));
}
+void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
+{
+ std::vector<unsigned char> key = ParseHex(hexkey);
+ std::vector<unsigned char> in = ParseHex(hexin);
+ std::vector<unsigned char> correctout = ParseHex(hexout);
+ std::vector<unsigned char> buf, buf2;
+
+ assert(key.size() == 16);
+ assert(in.size() == 16);
+ assert(correctout.size() == 16);
+ AES128Encrypt enc(&key[0]);
+ buf.resize(correctout.size());
+ buf2.resize(correctout.size());
+ enc.Encrypt(&buf[0], &in[0]);
+ BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout));
+ AES128Decrypt dec(&key[0]);
+ dec.Decrypt(&buf2[0], &buf[0]);
+ BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in));
+}
+
+void TestAES256(const std::string &hexkey, const std::string &hexin, const std::string &hexout)
+{
+ std::vector<unsigned char> key = ParseHex(hexkey);
+ std::vector<unsigned char> in = ParseHex(hexin);
+ std::vector<unsigned char> correctout = ParseHex(hexout);
+ std::vector<unsigned char> buf;
+
+ assert(key.size() == 32);
+ assert(in.size() == 16);
+ assert(correctout.size() == 16);
+ AES256Encrypt enc(&key[0]);
+ buf.resize(correctout.size());
+ enc.Encrypt(&buf[0], &in[0]);
+ BOOST_CHECK(buf == correctout);
+ AES256Decrypt dec(&key[0]);
+ dec.Decrypt(&buf[0], &buf[0]);
+ BOOST_CHECK(buf == in);
+}
+
+void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
+{
+ std::vector<unsigned char> key = ParseHex(hexkey);
+ std::vector<unsigned char> iv = ParseHex(hexiv);
+ std::vector<unsigned char> in = ParseHex(hexin);
+ std::vector<unsigned char> correctout = ParseHex(hexout);
+ std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE);
+
+ // Encrypt the plaintext and verify that it equals the cipher
+ AES128CBCEncrypt enc(&key[0], &iv[0], pad);
+ int size = enc.Encrypt(&in[0], in.size(), &realout[0]);
+ realout.resize(size);
+ BOOST_CHECK(realout.size() == correctout.size());
+ BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout);
+
+ // Decrypt the cipher and verify that it equals the plaintext
+ std::vector<unsigned char> decrypted(correctout.size());
+ AES128CBCDecrypt dec(&key[0], &iv[0], pad);
+ size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]);
+ decrypted.resize(size);
+ BOOST_CHECK(decrypted.size() == in.size());
+ BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin);
+
+ // Encrypt and re-decrypt substrings of the plaintext and verify that they equal each-other
+ for(std::vector<unsigned char>::iterator i(in.begin()); i != in.end(); ++i)
+ {
+ std::vector<unsigned char> sub(i, in.end());
+ std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE);
+ int size = enc.Encrypt(&sub[0], sub.size(), &subout[0]);
+ if (size != 0)
+ {
+ subout.resize(size);
+ std::vector<unsigned char> subdecrypted(subout.size());
+ size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]);
+ subdecrypted.resize(size);
+ BOOST_CHECK(decrypted.size() == in.size());
+ BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub));
+ }
+ }
+}
+
+void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad, const std::string &hexin, const std::string &hexout)
+{
+ std::vector<unsigned char> key = ParseHex(hexkey);
+ std::vector<unsigned char> iv = ParseHex(hexiv);
+ std::vector<unsigned char> in = ParseHex(hexin);
+ std::vector<unsigned char> correctout = ParseHex(hexout);
+ std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE);
+
+ // Encrypt the plaintext and verify that it equals the cipher
+ AES256CBCEncrypt enc(&key[0], &iv[0], pad);
+ int size = enc.Encrypt(&in[0], in.size(), &realout[0]);
+ realout.resize(size);
+ BOOST_CHECK(realout.size() == correctout.size());
+ BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout);
+
+ // Decrypt the cipher and verify that it equals the plaintext
+ std::vector<unsigned char> decrypted(correctout.size());
+ AES256CBCDecrypt dec(&key[0], &iv[0], pad);
+ size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]);
+ decrypted.resize(size);
+ BOOST_CHECK(decrypted.size() == in.size());
+ BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin);
+
+ // Encrypt and re-decrypt substrings of the plaintext and verify that they equal each-other
+ for(std::vector<unsigned char>::iterator i(in.begin()); i != in.end(); ++i)
+ {
+ std::vector<unsigned char> sub(i, in.end());
+ std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE);
+ int size = enc.Encrypt(&sub[0], sub.size(), &subout[0]);
+ if (size != 0)
+ {
+ subout.resize(size);
+ std::vector<unsigned char> subdecrypted(subout.size());
+ size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]);
+ subdecrypted.resize(size);
+ BOOST_CHECK(decrypted.size() == in.size());
+ BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub));
+ }
+ }
+}
+
std::string LongTestString(void) {
std::string ret;
for (int i=0; i<200000; i++) {
@@ -248,4 +372,71 @@ BOOST_AUTO_TEST_CASE(hmac_sha512_testvectors) {
"b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58");
}
+BOOST_AUTO_TEST_CASE(aes_testvectors) {
+ // AES test vectors from FIPS 197.
+ TestAES128("000102030405060708090a0b0c0d0e0f", "00112233445566778899aabbccddeeff", "69c4e0d86a7b0430d8cdb78070b4c55a");
+ TestAES256("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", "00112233445566778899aabbccddeeff", "8ea2b7ca516745bfeafc49904b496089");
+
+ // AES-ECB test vectors from NIST sp800-38a.
+ TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "6bc1bee22e409f96e93d7e117393172a", "3ad77bb40d7a3660a89ecaf32466ef97");
+ TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "ae2d8a571e03ac9c9eb76fac45af8e51", "f5d3d58503b9699de785895a96fdbaaf");
+ TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "30c81c46a35ce411e5fbc1191a0a52ef", "43b1cd7f598ece23881b00e3ed030688");
+ TestAES128("2b7e151628aed2a6abf7158809cf4f3c", "f69f2445df4f9b17ad2b417be66c3710", "7b0c785e27e8ad3f8223207104725dd4");
+ TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8");
+ TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "ae2d8a571e03ac9c9eb76fac45af8e51", "591ccb10d410ed26dc5ba74a31362870");
+ TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "30c81c46a35ce411e5fbc1191a0a52ef", "b6ed21b99ca6f4f9f153e7b1beafed1d");
+ TestAES256("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "f69f2445df4f9b17ad2b417be66c3710", "23304b7a39f9f3ff067d8d8f9e24ecc7");
+}
+
+BOOST_AUTO_TEST_CASE(aes_cbc_testvectors) {
+
+ // NIST AES CBC 128-bit encryption test-vectors
+ TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", false, \
+ "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d");
+ TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", false, \
+ "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b2");
+ TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", false, \
+ "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516");
+ TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", false, \
+ "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a7");
+
+ // The same vectors with padding enabled
+ TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "000102030405060708090A0B0C0D0E0F", true, \
+ "6bc1bee22e409f96e93d7e117393172a", "7649abac8119b246cee98e9b12e9197d8964e0b149c10b7b682e6e39aaeb731c");
+ TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "7649ABAC8119B246CEE98E9B12E9197D", true, \
+ "ae2d8a571e03ac9c9eb76fac45af8e51", "5086cb9b507219ee95db113a917678b255e21d7100b988ffec32feeafaf23538");
+ TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "5086cb9b507219ee95db113a917678b2", true, \
+ "30c81c46a35ce411e5fbc1191a0a52ef", "73bed6b8e3c1743b7116e69e22229516f6eccda327bf8e5ec43718b0039adceb");
+ TestAES128CBC("2b7e151628aed2a6abf7158809cf4f3c", "73bed6b8e3c1743b7116e69e22229516", true, \
+ "f69f2445df4f9b17ad2b417be66c3710", "3ff1caa1681fac09120eca307586e1a78cb82807230e1321d3fae00d18cc2012");
+
+ // NIST AES CBC 256-bit encryption test-vectors
+ TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
+ "000102030405060708090A0B0C0D0E0F", false, "6bc1bee22e409f96e93d7e117393172a", \
+ "f58c4c04d6e5f1ba779eabfb5f7bfbd6");
+ TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
+ "F58C4C04D6E5F1BA779EABFB5F7BFBD6", false, "ae2d8a571e03ac9c9eb76fac45af8e51", \
+ "9cfc4e967edb808d679f777bc6702c7d");
+ TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
+ "9CFC4E967EDB808D679F777BC6702C7D", false, "30c81c46a35ce411e5fbc1191a0a52ef",
+ "39f23369a9d9bacfa530e26304231461");
+ TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
+ "39F23369A9D9BACFA530E26304231461", false, "f69f2445df4f9b17ad2b417be66c3710", \
+ "b2eb05e2c39be9fcda6c19078c6a9d1b");
+
+ // The same vectors with padding enabled
+ TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
+ "000102030405060708090A0B0C0D0E0F", true, "6bc1bee22e409f96e93d7e117393172a", \
+ "f58c4c04d6e5f1ba779eabfb5f7bfbd6485a5c81519cf378fa36d42b8547edc0");
+ TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
+ "F58C4C04D6E5F1BA779EABFB5F7BFBD6", true, "ae2d8a571e03ac9c9eb76fac45af8e51", \
+ "9cfc4e967edb808d679f777bc6702c7d3a3aa5e0213db1a9901f9036cf5102d2");
+ TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
+ "9CFC4E967EDB808D679F777BC6702C7D", true, "30c81c46a35ce411e5fbc1191a0a52ef",
+ "39f23369a9d9bacfa530e263042314612f8da707643c90a6f732b3de1d3f5cee");
+ TestAES256CBC("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", \
+ "39F23369A9D9BACFA530E26304231461", true, "f69f2445df4f9b17ad2b417be66c3710", \
+ "b2eb05e2c39be9fcda6c19078c6a9d1b3f461796d6b0d6b2e0c2a72b4d80e644");
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index 95aa4c2593..190f8ecf2a 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -4,6 +4,8 @@
#include "crypter.h"
+#include "crypto/aes.h"
+#include "crypto/sha512.h"
#include "script/script.h"
#include "script/standard.h"
#include "util.h"
@@ -11,8 +13,33 @@
#include <string>
#include <vector>
#include <boost/foreach.hpp>
-#include <openssl/aes.h>
-#include <openssl/evp.h>
+
+int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const
+{
+ // This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc
+ // cipher and sha512 message digest. Because sha512's output size (64b) is
+ // greater than the aes256 block size (16b) + aes256 key size (32b),
+ // there's no need to process more than once (D_0).
+
+ if(!count || !key || !iv)
+ return 0;
+
+ unsigned char buf[CSHA512::OUTPUT_SIZE];
+ CSHA512 di;
+
+ di.Write((const unsigned char*)strKeyData.c_str(), strKeyData.size());
+ if(chSalt.size())
+ di.Write(&chSalt[0], chSalt.size());
+ di.Finalize(buf);
+
+ for(int i = 0; i != count - 1; i++)
+ di.Reset().Write(buf, sizeof(buf)).Finalize(buf);
+
+ memcpy(key, buf, WALLET_CRYPTO_KEY_SIZE);
+ memcpy(iv, buf + WALLET_CRYPTO_KEY_SIZE, WALLET_CRYPTO_IV_SIZE);
+ memory_cleanse(buf, sizeof(buf));
+ return WALLET_CRYPTO_KEY_SIZE;
+}
bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
{
@@ -21,8 +48,7 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v
int i = 0;
if (nDerivationMethod == 0)
- i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
- (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);
+ i = BytesToKeySHA512AES(chSalt, strKeyData, nRounds, chKey, chIV);
if (i != (int)WALLET_CRYPTO_KEY_SIZE)
{
@@ -37,7 +63,7 @@ bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::v
bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV)
{
- if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_KEY_SIZE)
+ if (chNewKey.size() != WALLET_CRYPTO_KEY_SIZE || chNewIV.size() != WALLET_CRYPTO_IV_SIZE)
return false;
memcpy(&chKey[0], &chNewKey[0], sizeof chKey);
@@ -47,57 +73,39 @@ bool CCrypter::SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigne
return true;
}
-bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext)
+bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) const
{
if (!fKeySet)
return false;
// max ciphertext len for a n bytes of plaintext is
- // n + AES_BLOCK_SIZE - 1 bytes
- int nLen = vchPlaintext.size();
- int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
- vchCiphertext = std::vector<unsigned char> (nCLen);
-
- EVP_CIPHER_CTX ctx;
+ // n + AES_BLOCKSIZE bytes
+ vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE);
- bool fOk = true;
-
- EVP_CIPHER_CTX_init(&ctx);
- if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
- if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
- if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
- EVP_CIPHER_CTX_cleanup(&ctx);
-
- if (!fOk) return false;
+ AES256CBCEncrypt enc(chKey, chIV, true);
+ size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]);
+ if(nLen < vchPlaintext.size())
+ return false;
+ vchCiphertext.resize(nLen);
- vchCiphertext.resize(nCLen + nFLen);
return true;
}
-bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext)
+bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext) const
{
if (!fKeySet)
return false;
// plaintext will always be equal to or lesser than length of ciphertext
int nLen = vchCiphertext.size();
- int nPLen = nLen, nFLen = 0;
-
- vchPlaintext = CKeyingMaterial(nPLen);
- EVP_CIPHER_CTX ctx;
+ vchPlaintext.resize(nLen);
- bool fOk = true;
-
- EVP_CIPHER_CTX_init(&ctx);
- if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
- if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
- if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
- EVP_CIPHER_CTX_cleanup(&ctx);
-
- if (!fOk) return false;
-
- vchPlaintext.resize(nPLen + nFLen);
+ AES256CBCDecrypt dec(chKey, chIV, true);
+ nLen = dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]);
+ if(nLen == 0)
+ return false;
+ vchPlaintext.resize(nLen);
return true;
}
@@ -105,8 +113,8 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM
static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext)
{
CCrypter cKeyCrypter;
- std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
- memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
+ std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
+ memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
return false;
return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext);
@@ -115,8 +123,8 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri
static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext)
{
CCrypter cKeyCrypter;
- std::vector<unsigned char> chIV(WALLET_CRYPTO_KEY_SIZE);
- memcpy(&chIV[0], &nIV, WALLET_CRYPTO_KEY_SIZE);
+ std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE);
+ memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE);
if(!cKeyCrypter.SetKey(vMasterKey, chIV))
return false;
return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext));
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index eb06a7866a..5d0a4a3305 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -13,6 +13,7 @@ class uint256;
const unsigned int WALLET_CRYPTO_KEY_SIZE = 32;
const unsigned int WALLET_CRYPTO_SALT_SIZE = 8;
+const unsigned int WALLET_CRYPTO_IV_SIZE = 16;
/**
* Private key encryption is done based on a CMasterKey,
@@ -66,18 +67,26 @@ public:
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial;
+namespace wallet_crypto
+{
+ class TestCrypter;
+}
+
/** Encryption/decryption context with key information */
class CCrypter
{
+friend class wallet_crypto::TestCrypter; // for test access to chKey/chIV
private:
unsigned char chKey[WALLET_CRYPTO_KEY_SIZE];
- unsigned char chIV[WALLET_CRYPTO_KEY_SIZE];
+ unsigned char chIV[WALLET_CRYPTO_IV_SIZE];
bool fKeySet;
+ int BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const;
+
public:
bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod);
- bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext);
- bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext);
+ bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext) const;
+ bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext) const;
bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV);
void CleanKey()
diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp
new file mode 100644
index 0000000000..05387f5f2b
--- /dev/null
+++ b/src/wallet/test/crypto_tests.cpp
@@ -0,0 +1,230 @@
+// Copyright (c) 2014 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "random.h"
+#include "utilstrencodings.h"
+#include "test/test_bitcoin.h"
+#include "wallet/crypter.h"
+
+#include <vector>
+
+#include <boost/test/unit_test.hpp>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+
+BOOST_FIXTURE_TEST_SUITE(wallet_crypto, BasicTestingSetup)
+
+bool OldSetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod, unsigned char* chKey, unsigned char* chIV)
+{
+ if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
+ return false;
+
+ int i = 0;
+ if (nDerivationMethod == 0)
+ i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(), &chSalt[0],
+ (unsigned char *)&strKeyData[0], strKeyData.size(), nRounds, chKey, chIV);
+
+ if (i != (int)WALLET_CRYPTO_KEY_SIZE)
+ {
+ memory_cleanse(chKey, sizeof(chKey));
+ memory_cleanse(chIV, sizeof(chIV));
+ return false;
+ }
+ return true;
+}
+
+bool OldEncrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext, const unsigned char chKey[32], const unsigned char chIV[16])
+{
+ // max ciphertext len for a n bytes of plaintext is
+ // n + AES_BLOCK_SIZE - 1 bytes
+ int nLen = vchPlaintext.size();
+ int nCLen = nLen + AES_BLOCK_SIZE, nFLen = 0;
+ vchCiphertext = std::vector<unsigned char> (nCLen);
+
+ EVP_CIPHER_CTX ctx;
+
+ bool fOk = true;
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (fOk) fOk = EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
+ if (fOk) fOk = EVP_EncryptUpdate(&ctx, &vchCiphertext[0], &nCLen, &vchPlaintext[0], nLen) != 0;
+ if (fOk) fOk = EVP_EncryptFinal_ex(&ctx, (&vchCiphertext[0]) + nCLen, &nFLen) != 0;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+
+ if (!fOk) return false;
+
+ vchCiphertext.resize(nCLen + nFLen);
+ return true;
+}
+
+bool OldDecrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext, const unsigned char chKey[32], const unsigned char chIV[16])
+{
+ // plaintext will always be equal to or lesser than length of ciphertext
+ int nLen = vchCiphertext.size();
+ int nPLen = nLen, nFLen = 0;
+
+ vchPlaintext = CKeyingMaterial(nPLen);
+
+ EVP_CIPHER_CTX ctx;
+
+ bool fOk = true;
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (fOk) fOk = EVP_DecryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, chKey, chIV) != 0;
+ if (fOk) fOk = EVP_DecryptUpdate(&ctx, &vchPlaintext[0], &nPLen, &vchCiphertext[0], nLen) != 0;
+ if (fOk) fOk = EVP_DecryptFinal_ex(&ctx, (&vchPlaintext[0]) + nPLen, &nFLen) != 0;
+ EVP_CIPHER_CTX_cleanup(&ctx);
+
+ if (!fOk) return false;
+
+ vchPlaintext.resize(nPLen + nFLen);
+ return true;
+}
+
+class TestCrypter
+{
+public:
+static void TestPassphraseSingle(const std::vector<unsigned char>& vchSalt, const SecureString& passphrase, uint32_t rounds,
+ const std::vector<unsigned char>& correctKey = std::vector<unsigned char>(),
+ const std::vector<unsigned char>& correctIV=std::vector<unsigned char>())
+{
+ unsigned char chKey[WALLET_CRYPTO_KEY_SIZE];
+ unsigned char chIV[WALLET_CRYPTO_IV_SIZE];
+
+ CCrypter crypt;
+ crypt.SetKeyFromPassphrase(passphrase, vchSalt, rounds, 0);
+
+ OldSetKeyFromPassphrase(passphrase, vchSalt, rounds, 0, chKey, chIV);
+
+ BOOST_CHECK_MESSAGE(memcmp(chKey, crypt.chKey, sizeof(chKey)) == 0, \
+ HexStr(chKey, chKey+sizeof(chKey)) + std::string(" != ") + HexStr(crypt.chKey, crypt.chKey + (sizeof crypt.chKey)));
+ BOOST_CHECK_MESSAGE(memcmp(chIV, crypt.chIV, sizeof(chIV)) == 0, \
+ HexStr(chIV, chIV+sizeof(chIV)) + std::string(" != ") + HexStr(crypt.chIV, crypt.chIV + (sizeof crypt.chIV)));
+
+ if(!correctKey.empty())
+ BOOST_CHECK_MESSAGE(memcmp(chKey, &correctKey[0], sizeof(chKey)) == 0, \
+ HexStr(chKey, chKey+sizeof(chKey)) + std::string(" != ") + HexStr(correctKey.begin(), correctKey.end()));
+ if(!correctIV.empty())
+ BOOST_CHECK_MESSAGE(memcmp(chIV, &correctIV[0], sizeof(chIV)) == 0,
+ HexStr(chIV, chIV+sizeof(chIV)) + std::string(" != ") + HexStr(correctIV.begin(), correctIV.end()));
+}
+
+static void TestPassphrase(const std::vector<unsigned char>& vchSalt, const SecureString& passphrase, uint32_t rounds,
+ const std::vector<unsigned char>& correctKey = std::vector<unsigned char>(),
+ const std::vector<unsigned char>& correctIV=std::vector<unsigned char>())
+{
+ TestPassphraseSingle(vchSalt, passphrase, rounds, correctKey, correctIV);
+ for(SecureString::const_iterator i(passphrase.begin()); i != passphrase.end(); ++i)
+ TestPassphraseSingle(vchSalt, SecureString(i, passphrase.end()), rounds);
+}
+
+
+static void TestDecrypt(const CCrypter& crypt, const std::vector<unsigned char>& vchCiphertext, \
+ const std::vector<unsigned char>& vchPlaintext = std::vector<unsigned char>())
+{
+ CKeyingMaterial vchDecrypted1;
+ CKeyingMaterial vchDecrypted2;
+ int result1, result2;
+ result1 = crypt.Decrypt(vchCiphertext, vchDecrypted1);
+ result2 = OldDecrypt(vchCiphertext, vchDecrypted2, crypt.chKey, crypt.chIV);
+ BOOST_CHECK(result1 == result2);
+
+ // These two should be equal. However, OpenSSL 1.0.1j introduced a change
+ // that would zero all padding except for the last byte for failed decrypts.
+ // This behavior was reverted for 1.0.1k.
+ if (vchDecrypted1 != vchDecrypted2 && vchDecrypted1.size() >= AES_BLOCK_SIZE && SSLeay() == 0x100010afL)
+ {
+ for(CKeyingMaterial::iterator it = vchDecrypted1.end() - AES_BLOCK_SIZE; it != vchDecrypted1.end() - 1; it++)
+ *it = 0;
+ }
+
+ BOOST_CHECK_MESSAGE(vchDecrypted1 == vchDecrypted2, HexStr(vchDecrypted1.begin(), vchDecrypted1.end()) + " != " + HexStr(vchDecrypted2.begin(), vchDecrypted2.end()));
+
+ if (vchPlaintext.size())
+ BOOST_CHECK(CKeyingMaterial(vchPlaintext.begin(), vchPlaintext.end()) == vchDecrypted2);
+}
+
+static void TestEncryptSingle(const CCrypter& crypt, const CKeyingMaterial& vchPlaintext,
+ const std::vector<unsigned char>& vchCiphertextCorrect = std::vector<unsigned char>())
+{
+ std::vector<unsigned char> vchCiphertext1;
+ std::vector<unsigned char> vchCiphertext2;
+ int result1 = crypt.Encrypt(vchPlaintext, vchCiphertext1);
+
+ int result2 = OldEncrypt(vchPlaintext, vchCiphertext2, crypt.chKey, crypt.chIV);
+ BOOST_CHECK(result1 == result2);
+ BOOST_CHECK(vchCiphertext1 == vchCiphertext2);
+
+ if (!vchCiphertextCorrect.empty())
+ BOOST_CHECK(vchCiphertext2 == vchCiphertextCorrect);
+
+ const std::vector<unsigned char> vchPlaintext2(vchPlaintext.begin(), vchPlaintext.end());
+
+ if(vchCiphertext1 == vchCiphertext2)
+ TestDecrypt(crypt, vchCiphertext1, vchPlaintext2);
+}
+
+static void TestEncrypt(const CCrypter& crypt, const std::vector<unsigned char>& vchPlaintextIn, \
+ const std::vector<unsigned char>& vchCiphertextCorrect = std::vector<unsigned char>())
+{
+ TestEncryptSingle(crypt, CKeyingMaterial(vchPlaintextIn.begin(), vchPlaintextIn.end()), vchCiphertextCorrect);
+ for(std::vector<unsigned char>::const_iterator i(vchPlaintextIn.begin()); i != vchPlaintextIn.end(); ++i)
+ TestEncryptSingle(crypt, CKeyingMaterial(i, vchPlaintextIn.end()));
+}
+
+};
+
+BOOST_AUTO_TEST_CASE(passphrase) {
+ // These are expensive.
+
+ TestCrypter::TestPassphrase(ParseHex("0000deadbeef0000"), "test", 25000, \
+ ParseHex("fc7aba077ad5f4c3a0988d8daa4810d0d4a0e3bcb53af662998898f33df0556a"), \
+ ParseHex("cf2f2691526dd1aa220896fb8bf7c369"));
+
+ std::string hash(GetRandHash().ToString());
+ std::vector<unsigned char> vchSalt(8);
+ GetRandBytes(&vchSalt[0], vchSalt.size());
+ uint32_t rounds = insecure_rand();
+ if (rounds > 30000)
+ rounds = 30000;
+ TestCrypter::TestPassphrase(vchSalt, SecureString(hash.begin(), hash.end()), rounds);
+}
+
+BOOST_AUTO_TEST_CASE(encrypt) {
+ std::vector<unsigned char> vchSalt = ParseHex("0000deadbeef0000");
+ BOOST_CHECK(vchSalt.size() == WALLET_CRYPTO_SALT_SIZE);
+ CCrypter crypt;
+ crypt.SetKeyFromPassphrase("passphrase", vchSalt, 25000, 0);
+ TestCrypter::TestEncrypt(crypt, ParseHex("22bcade09ac03ff6386914359cfe885cfeb5f77ff0d670f102f619687453b29d"));
+
+ for (int i = 0; i != 100; i++)
+ {
+ uint256 hash(GetRandHash());
+ TestCrypter::TestEncrypt(crypt, std::vector<unsigned char>(hash.begin(), hash.end()));
+ }
+
+}
+
+BOOST_AUTO_TEST_CASE(decrypt) {
+ std::vector<unsigned char> vchSalt = ParseHex("0000deadbeef0000");
+ BOOST_CHECK(vchSalt.size() == WALLET_CRYPTO_SALT_SIZE);
+ CCrypter crypt;
+ crypt.SetKeyFromPassphrase("passphrase", vchSalt, 25000, 0);
+
+ // Some corner cases the came up while testing
+ TestCrypter::TestDecrypt(crypt,ParseHex("795643ce39d736088367822cdc50535ec6f103715e3e48f4f3b1a60a08ef59ca"));
+ TestCrypter::TestDecrypt(crypt,ParseHex("de096f4a8f9bd97db012aa9d90d74de8cdea779c3ee8bc7633d8b5d6da703486"));
+ TestCrypter::TestDecrypt(crypt,ParseHex("32d0a8974e3afd9c6c3ebf4d66aa4e6419f8c173de25947f98cf8b7ace49449c"));
+ TestCrypter::TestDecrypt(crypt,ParseHex("e7c055cca2faa78cb9ac22c9357a90b4778ded9b2cc220a14cea49f931e596ea"));
+ TestCrypter::TestDecrypt(crypt,ParseHex("b88efddd668a6801d19516d6830da4ae9811988ccbaf40df8fbb72f3f4d335fd"));
+ TestCrypter::TestDecrypt(crypt,ParseHex("8cae76aa6a43694e961ebcb28c8ca8f8540b84153d72865e8561ddd93fa7bfa9"));
+
+ for (int i = 0; i != 100; i++)
+ {
+ uint256 hash(GetRandHash());
+ TestCrypter::TestDecrypt(crypt, std::vector<unsigned char>(hash.begin(), hash.end()));
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END()