Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

AES128-CTR replacement #3638

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
257 changes: 256 additions & 1 deletion libdevcrypto/AES.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,264 @@

using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;

namespace
{

// This code is modified version of https://github.com/kokke/tiny-AES128-C.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mention that it was released under the "unlicense", roughly equivalent to a release to the public domain.


constexpr int Nb = 4; ///< Number of columns in a AES state.
constexpr int Nk = 4; ///< The number of 32 bit words in a key.
constexpr int c_keySize = 16; ///< Key length in bytes (128 bit).
constexpr int Nr = 10; ///< The number of rounds in AES Cipher.

using state_t = byte[4][4];

/// The lookup-tables.
const byte sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};

// The round constant word array, Rcon[i], contains the values given by
// x to th e power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
// Note that i starts at 1, not 0).
const byte Rcon[255] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
};

/// Produces Nb(Nr+1) round keys. The round keys are used in each round
/// to decrypt the states.
void keyExpansion(byte* roundKeys, byte const* key)
{
int i;
byte tempa[4]; // Used for the column/row operations

// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
roundKeys[(i * 4) + 0] = key[(i * 4) + 0];
roundKeys[(i * 4) + 1] = key[(i * 4) + 1];
roundKeys[(i * 4) + 2] = key[(i * 4) + 2];
roundKeys[(i * 4) + 3] = key[(i * 4) + 3];
}

// All other round keys are found from the previous round keys.
for(; (i < (Nb * (Nr + 1))); ++i)
{
for (int j = 0; j < 4; ++j)
{
tempa[j] = roundKeys[(i-1) * 4 + j];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i is bounded by Nb * (Nr + 1) and j is bounded by 4 - is there any easier way to (perhaps in an automated way) check that this does not go out of bounds with respect to roundKeys?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hoped I will not have to decrypt this indexing :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but this just looks like dynamite to me. Buffers passed as plain pointers with sizes resulting from formulas including weird constants, it is just too easy to confuse two constants here, and we don't really know how much this library is used, tested, etc.

}
if (i % Nk == 0)
{
// This function rotates the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]

// Function RotWord()
{
auto k = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = k;
}

// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.

// Function Subword()
{
tempa[0] = sbox[tempa[0]];
tempa[1] = sbox[tempa[1]];
tempa[2] = sbox[tempa[2]];
tempa[3] = sbox[tempa[3]];
}

tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
roundKeys[i * 4 + 0] = roundKeys[(i - Nk) * 4 + 0] ^ tempa[0];
roundKeys[i * 4 + 1] = roundKeys[(i - Nk) * 4 + 1] ^ tempa[1];
roundKeys[i * 4 + 2] = roundKeys[(i - Nk) * 4 + 2] ^ tempa[2];
roundKeys[i * 4 + 3] = roundKeys[(i - Nk) * 4 + 3] ^ tempa[3];
}
}

/// Adds the round key to state.
/// The round key is added to the state by an XOR function.
void addRoundKey(state_t* state, byte const* roundKeys, int round)
{
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
(*state)[i][j] ^= roundKeys[round * Nb * 4 + i * Nb + j];
}

/// Substitutes the values in the
/// state matrix with values in an S-box.
void subBytes(state_t* state)
{
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
(*state)[j][i] = sbox[(*state)[j][i]];
}

/// Shifts the rows in the state to the left.
/// Each row is shifted with different offset.
/// Offset = Row number. So the first row is not shifted.
void shiftRows(state_t* state)
{
byte temp;

// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;

// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;

temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;

// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}

inline byte xtime(byte x)
{
return static_cast<byte>(x<<1) ^ (((x>>7) & 1) * 0x1b);
}

// MixColumns function mixes the columns of the state matrix
void mixColumns(state_t* state)
{
byte Tmp,Tm,t;
for(auto i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}

// Cipher is the main function that encrypts the PlainText.
void cipher(state_t* state, byte const* roundKeys)
{
// Add the First round key to the state before starting the rounds.
addRoundKey(state, roundKeys, 0);

// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr-1 rounds are executed in the loop below.
for (int round = 1; round < Nr; ++round)
{
subBytes(state);
shiftRows(state);
mixColumns(state);
addRoundKey(state, roundKeys, round);
}

// The last round is given below.
// The MixColumns function is not here in the last round.
subBytes(state);
shiftRows(state);
addRoundKey(state, roundKeys, Nr);
}

void AES128_CTR_process_buffer(byte* output, const byte* input, size_t length, const byte* key, const byte* iv)
{
state_t state;
byte roundKeys[176]; // The array that stores the round keys.
byte ctr[c_keySize];

std::copy(iv, iv + c_keySize, ctr);
keyExpansion(roundKeys, key);

for (size_t i = 0; i < length; ++i)
{
if ((i & 0x0000000F) == 0)
{
std::copy(std::begin(ctr), std::end(ctr), &state[0][0]);
cipher(&state, roundKeys);

for (int j = c_keySize - 1; j >= 0; --j)
{
++(ctr[j]);

if (ctr[j])
break;
}
}

output[i] = (input[i]) ^ ((&state[0][0])[i & 0x0000000F]);
}
}

}

bytes dev::encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain)
{
if (_k.size() != 16)
return {};

bytes ret(_plain.size());
AES128_CTR_process_buffer(ret.data(), _plain.data(), _plain.size(), _k.data(), _iv.data());
return ret;
}

bytesSec dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher)
{
if (_k.size() != 16)
return {};

bytesSec ret(_cipher.size());
AES128_CTR_process_buffer(ret.writable().data(), _cipher.data(), _cipher.size(), _k.data(), _iv.data());
return ret;
}

bytes dev::aesDecrypt(bytesConstRef _ivCipher, std::string const& _password, unsigned _rounds, bytesConstRef _salt)
{
bytes pw = asBytes(_password);
Expand Down
16 changes: 15 additions & 1 deletion libdevcrypto/AES.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,25 @@

#pragma once

#include "Common.h"
#include <libdevcore/FixedHash.h>

namespace dev
{

/// Encrypts payload with specified IV/ctr using AES128-CTR.
bytes encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain);

/// Decrypts payload with specified IV/ctr using AES128-CTR.
bytesSec decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher);

/// Encrypts payload with specified IV/ctr using AES128-CTR.
inline bytes encryptSymNoAuth(SecureFixedHash<16> const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }
inline bytes encryptSymNoAuth(SecureFixedHash<32> const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }

/// Decrypts payload with specified IV/ctr using AES128-CTR.
inline bytesSec decryptSymNoAuth(SecureFixedHash<16> const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }
inline bytesSec decryptSymNoAuth(SecureFixedHash<32> const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }

bytes aesDecrypt(bytesConstRef _cipher, std::string const& _password, unsigned _rounds = 2000, bytesConstRef _salt = bytesConstRef());

}
40 changes: 0 additions & 40 deletions libdevcrypto/Common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,46 +161,6 @@ std::pair<bytes, h128> dev::encryptSymNoAuth(SecureFixedHash<16> const& _k, byte
return make_pair(encryptSymNoAuth(_k, iv, _plain), iv);
}

bytes dev::encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain)
{
if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
return bytes();
SecByteBlock key(_k.data(), _k.size());
try
{
CTR_Mode<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), _iv.data());
bytes ret(_plain.size());
e.ProcessData(ret.data(), _plain.data(), _plain.size());
return ret;
}
catch (CryptoPP::Exception& _e)
{
cerr << _e.what() << endl;
return bytes();
}
}

bytesSec dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher)
{
if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32)
return bytesSec();
SecByteBlock key(_k.data(), _k.size());
try
{
CTR_Mode<AES>::Decryption d;
d.SetKeyWithIV(key, key.size(), _iv.data());
bytesSec ret(_cipher.size());
d.ProcessData(ret.writable().data(), _cipher.data(), _cipher.size());
return ret;
}
catch (CryptoPP::Exception& _e)
{
cerr << _e.what() << endl;
return bytesSec();
}
}

Public dev::recover(Signature const& _sig, h256 const& _message)
{
int v = _sig[64];
Expand Down
18 changes: 2 additions & 16 deletions libdevcrypto/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,26 +115,12 @@ bool decryptECIES(Secret const& _k, bytesConstRef _sharedMacData, bytesConstRef
/// Encrypts payload with random IV/ctr using AES128-CTR.
std::pair<bytes, h128> encryptSymNoAuth(SecureFixedHash<16> const& _k, bytesConstRef _plain);

/// Encrypts payload with specified IV/ctr using AES128-CTR.
bytes encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain);

/// Decrypts payload with specified IV/ctr using AES128-CTR.
bytesSec decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher);

/// Encrypts payload with specified IV/ctr using AES128-CTR.
inline bytes encryptSymNoAuth(SecureFixedHash<16> const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }
inline bytes encryptSymNoAuth(SecureFixedHash<32> const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); }

/// Decrypts payload with specified IV/ctr using AES128-CTR.
inline bytesSec decryptSymNoAuth(SecureFixedHash<16> const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }
inline bytesSec decryptSymNoAuth(SecureFixedHash<32> const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); }

/// Recovers Public key from signed message hash.
Public recover(Signature const& _sig, h256 const& _hash);

/// Returns siganture of message hash.
Signature sign(Secret const& _k, h256 const& _hash);

/// Verify signature.
bool verify(Public const& _k, Signature const& _s, h256 const& _hash);

Expand Down
1 change: 1 addition & 0 deletions libdevcrypto/CryptoPP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <libdevcore/Assertions.h>
#include <libdevcore/SHA3.h>
#include "ECDHE.h"
#include "AES.h"

static_assert(CRYPTOPP_VERSION == 570, "Wrong Crypto++ version");

Expand Down
2 changes: 2 additions & 0 deletions libdevcrypto/SecretStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <libdevcore/FileSystem.h>
#include <json_spirit/JsonSpiritHeaders.h>
#include <libdevcrypto/Exceptions.h>
#include "AES.h"

using namespace std;
using namespace dev;
namespace js = json_spirit;
Expand Down