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)
This commit is contained in:
parent
83dc1dc99c
commit
91752ab7ed
@ -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=libdashconsensus.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 = libdashconsensus.la
|
||||
LIBBITCOINCONSENSUS=libdashconsensus.la
|
||||
else
|
||||
LIBBITCOINCONSENSUS=
|
||||
endif
|
||||
lib_LTLIBRARIES = $(LIBBITCOINCONSENSUS)
|
||||
|
||||
bin_PROGRAMS =
|
||||
TESTS =
|
||||
@ -257,8 +256,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 = \
|
||||
@ -289,6 +286,8 @@ libbitcoin_wallet_a_SOURCES = \
|
||||
crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) $(PIC_FLAGS)
|
||||
crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) $(PIC_FLAGS)
|
||||
crypto_libbitcoin_crypto_a_SOURCES = \
|
||||
crypto/aes.cpp \
|
||||
crypto/aes.h \
|
||||
crypto/common.h \
|
||||
crypto/hmac_sha256.cpp \
|
||||
crypto/hmac_sha256.h \
|
||||
@ -440,21 +439,15 @@ dashd_LDADD = \
|
||||
$(LIBBITCOIN_COMMON) \
|
||||
$(LIBUNIVALUE) \
|
||||
$(LIBBITCOIN_UTIL) \
|
||||
$(LIBBITCOIN_WALLET) \
|
||||
$(LIBBITCOIN_ZMQ) \
|
||||
$(LIBBITCOIN_CONSENSUS) \
|
||||
$(LIBBITCOIN_CRYPTO) \
|
||||
$(LIBLEVELDB) \
|
||||
$(LIBMEMENV) \
|
||||
$(LIBSECP256K1)
|
||||
|
||||
if ENABLE_ZMQ
|
||||
dashd_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
|
||||
endif
|
||||
|
||||
if ENABLE_WALLET
|
||||
dashd_LDADD += libbitcoin_wallet.a
|
||||
endif
|
||||
|
||||
dashd_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
|
||||
dashd_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
|
||||
|
||||
# dash-cli binary #
|
||||
dash_cli_SOURCES = dash-cli.cpp
|
||||
@ -512,6 +505,12 @@ libdashconsensus_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
|
||||
@ -529,7 +528,7 @@ CLEANFILES += zmq/*.gcda zmq/*.gcno
|
||||
|
||||
DISTCLEANFILES = obj/build.h
|
||||
|
||||
EXTRA_DIST = leveldb
|
||||
EXTRA_DIST = leveldb $(CTAES_DIST)
|
||||
|
||||
clean-local:
|
||||
-$(MAKE) -C leveldb clean
|
||||
|
@ -102,6 +102,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
|
||||
|
||||
|
217
src/crypto/aes.cpp
Normal file
217
src/crypto/aes.cpp
Normal file
@ -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);
|
||||
}
|
118
src/crypto/aes.h
Normal file
118
src/crypto/aes.h
Normal file
@ -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
|
21
src/crypto/ctaes/COPYING
Normal file
21
src/crypto/ctaes/COPYING
Normal file
@ -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.
|
41
src/crypto/ctaes/README.md
Normal file
41
src/crypto/ctaes/README.md
Normal file
@ -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
|
170
src/crypto/ctaes/bench.c
Normal file
170
src/crypto/ctaes/bench.c
Normal file
@ -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;
|
||||
}
|
556
src/crypto/ctaes/ctaes.c
Normal file
556
src/crypto/ctaes/ctaes.c
Normal file
@ -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;
|
||||
}
|
||||
}
|
41
src/crypto/ctaes/ctaes.h
Normal file
41
src/crypto/ctaes/ctaes.h
Normal file
@ -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
|
110
src/crypto/ctaes/test.c
Normal file
110
src/crypto/ctaes/test.c
Normal file
@ -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);
|
||||
}
|
@ -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,7 +17,7 @@
|
||||
|
||||
#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)
|
||||
@ -65,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++) {
|
||||
@ -250,6 +372,73 @@ 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_CASE(pbkdf2_hmac_sha512_test) {
|
||||
// test vectors from
|
||||
// https://github.com/trezor/trezor-crypto/blob/87c920a7e747f7ed40b6ae841327868ab914435b/tests.c#L1936-L1957
|
||||
|
@ -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"
|
||||
@ -14,6 +16,33 @@
|
||||
#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)
|
||||
{
|
||||
if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
|
||||
@ -21,8 +50,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 +65,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,65 +75,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);
|
||||
// n + AES_BLOCKSIZE bytes
|
||||
vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE);
|
||||
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
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);
|
||||
|
||||
if (!ctx) return false;
|
||||
|
||||
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);
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
|
||||
if (!fOk) return false;
|
||||
|
||||
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);
|
||||
vchPlaintext.resize(nLen);
|
||||
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
|
||||
if (!ctx) return false;
|
||||
|
||||
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);
|
||||
|
||||
EVP_CIPHER_CTX_free(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;
|
||||
}
|
||||
|
||||
@ -113,8 +115,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);
|
||||
@ -164,8 +166,8 @@ bool EncryptAES256(const SecureString& sKey, const SecureString& sPlaintext, con
|
||||
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));
|
||||
|
@ -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()
|
||||
|
230
src/wallet/test/crypto_tests.cpp
Normal file
230
src/wallet/test/crypto_tests.cpp
Normal file
@ -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_dash.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()
|
Loading…
Reference in New Issue
Block a user