diff --git a/soup/Soup.vcxproj b/soup/Soup.vcxproj index cf07177a..43e5ddeb 100644 --- a/soup/Soup.vcxproj +++ b/soup/Soup.vcxproj @@ -801,7 +801,6 @@ - diff --git a/soup/Soup.vcxproj.filters b/soup/Soup.vcxproj.filters index a66bd7b4..a0e4a3bf 100644 --- a/soup/Soup.vcxproj.filters +++ b/soup/Soup.vcxproj.filters @@ -1244,9 +1244,6 @@ crypto - - crypto - util diff --git a/soup/aes.cpp b/soup/aes.cpp index 7ad6d2bd..9351f8e9 100644 --- a/soup/aes.cpp +++ b/soup/aes.cpp @@ -1,7 +1,6 @@ #include "aes.hpp" #include // memcpy -#include #include "base.hpp" @@ -17,7 +16,6 @@ #endif #include "Endian.hpp" -#include "plusaes.hpp" /* Original source: https://github.com/SergeyBel/AES @@ -44,6 +42,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +// https://github.com/kkAyataka/plusaes was also used a reference for AES-GCM + #if SOUP_X86 #define IS_AES_INTRIN_AVAILBLE CpuInfo::get().supportsAESNI() #else @@ -435,7 +435,7 @@ NAMESPACE_SOUP } } - void aes::gcmEncrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, uint8_t tag[16]) SOUP_EXCAL + void aes::gcmEncrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, uint8_t tag[16]) noexcept { const auto Nr = getNrFromKeyLen(key_len); alignas(16) uint8_t roundKeys[240]; @@ -459,7 +459,7 @@ NAMESPACE_SOUP calcGcmTag(tag, data, data_len, aadata, aadata_len, roundKeys, Nr, h, j0); } - bool aes::gcmDecrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, const uint8_t tag[16]) SOUP_EXCAL + bool aes::gcmDecrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, const uint8_t tag[16]) noexcept { const auto Nr = getNrFromKeyLen(key_len); alignas(16) uint8_t roundKeys[240]; @@ -1017,22 +1017,22 @@ NAMESPACE_SOUP } } - void aes::calcGcmTag(uint8_t tag[16], uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t roundKeys[16], const int Nr, const uint8_t h[16], const uint8_t j0[16]) SOUP_EXCAL + void aes::calcGcmTag(uint8_t tag[16], uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t roundKeys[16], const int Nr, const uint8_t h[16], const uint8_t j0[16]) noexcept { - const auto lenC = data_len * 8; - const auto lenA = aadata_len * 8; - const std::size_t u = 128 * plusaes::detail::gcm::ceil(lenC / 128.0) - lenC; - const std::size_t v = 128 * plusaes::detail::gcm::ceil(lenA / 128.0) - lenA; - - std::vector ghash_in; - ghash_in.reserve((aadata_len + v / 8) + (data_len + u / 8) + 8 + 8); - plusaes::detail::gcm::push_back(ghash_in, aadata, aadata_len); - plusaes::detail::gcm::push_back_zero_bits(ghash_in, v); - plusaes::detail::gcm::push_back(ghash_in, data, data_len); - plusaes::detail::gcm::push_back_zero_bits(ghash_in, u); - plusaes::detail::gcm::push_back(ghash_in, std::bitset<64>(lenA)); - plusaes::detail::gcm::push_back(ghash_in, std::bitset<64>(lenC)); - ghash(tag, h, ghash_in.data(), ghash_in.size()); + const uint64_t lenC = data_len * 8; + const uint64_t lenA = aadata_len * 8; + auto u = (128 * static_cast(std::ceil(lenC / 128.0) + 0.5) - lenC) / 8; + auto v = (128 * static_cast(std::ceil(lenA / 128.0) + 0.5) - lenA) / 8; + uint8_t z[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + GhashState state(tag, h); + state.append(aadata, aadata_len); + while (v--) { state.appendByte(0); } + state.append(data, data_len); + while (u--) { state.appendByte(0); } + *reinterpret_cast(z) = Endianness::invert(lenA); state.append(z, 8); static_assert(ENDIAN_NATIVE == ENDIAN_LITTLE); + *reinterpret_cast(z) = Endianness::invert(lenC); state.append(z, 8); static_assert(ENDIAN_NATIVE == ENDIAN_LITTLE); + gctr(tag, 16, roundKeys, Nr, j0); } diff --git a/soup/aes.hpp b/soup/aes.hpp index 5dd20086..4ca75a79 100644 --- a/soup/aes.hpp +++ b/soup/aes.hpp @@ -23,8 +23,8 @@ NAMESPACE_SOUP static void cfbDecrypt(uint8_t* data, size_t data_len, const uint8_t* key, size_t key_len, const uint8_t iv[16]) noexcept; static void ecbEncrypt(uint8_t* data, size_t data_len, const uint8_t* key, size_t key_len) noexcept; static void ecbDecrypt(uint8_t* data, size_t data_len, const uint8_t* key, size_t key_len) noexcept; - static void gcmEncrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, uint8_t tag[16]) SOUP_EXCAL; - static bool gcmDecrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, const uint8_t tag[16]) SOUP_EXCAL; + static void gcmEncrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, uint8_t tag[16]) noexcept; + static bool gcmDecrypt(uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t* key, size_t key_len, const uint8_t* iv, size_t iv_len, const uint8_t tag[16]) noexcept; static void expandKey(uint8_t w[240], const uint8_t* key, size_t key_len) noexcept; @@ -58,7 +58,7 @@ NAMESPACE_SOUP static void calcJ0(uint8_t j0[16], const uint8_t h[16], const uint8_t* iv, size_t iv_len) noexcept; static void inc32(uint8_t block[16]) noexcept; static void gctr(uint8_t* data, size_t data_len, const uint8_t roundKeys[240], const int Nr, const uint8_t icb[8]) noexcept; - static void calcGcmTag(uint8_t tag[16], uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t roundKeys[16], const int Nr, const uint8_t h[16], const uint8_t j0[16]) SOUP_EXCAL; + static void calcGcmTag(uint8_t tag[16], uint8_t* data, size_t data_len, const uint8_t* aadata, size_t aadata_len, const uint8_t roundKeys[16], const int Nr, const uint8_t h[16], const uint8_t j0[16]) noexcept; struct GhashState { diff --git a/soup/plusaes.hpp b/soup/plusaes.hpp deleted file mode 100644 index b531052f..00000000 --- a/soup/plusaes.hpp +++ /dev/null @@ -1,1313 +0,0 @@ -// Source: https://github.com/Sainan/plusaes/tree/workaround32 - -// Copyright (C) 2015 kkAyataka -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) - -#ifndef PLUSAES_HPP__ -#define PLUSAES_HPP__ - -#include -#include -#include -#include -#include -#include - -/** @defgroup Base - * @{ */ - -/** Version number of the plusaes. - * 0x01020304 -> 1.2.3.4 */ -#define PLUSAES_VERSION 0x01000000 - -/** @} */ - -/** AES cipher APIs */ -namespace plusaes { -namespace detail { - -const int kWordSize = 4; -typedef unsigned int Word; - -const int kBlockSize = 4; -/** @private */ -struct State { - Word w[4]; - Word & operator[](const int index) { - return w[index]; - } - const Word & operator[](const int index) const { - return w[index]; - } -}; - -const int kStateSize = 16; // Word * BlockSize -typedef State RoundKey; -typedef std::vector RoundKeys; - -inline void add_round_key(const RoundKey &key, State &state) { - for (int i = 0; i < kBlockSize; ++i) { - state[i] ^= key[i]; - } -} - -const unsigned char kSbox[] = { - 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 -}; - -const unsigned char kInvSbox[] = { - 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, - 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, - 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, - 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, - 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, - 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, - 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, - 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, - 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, - 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, - 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, - 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, - 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, - 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, - 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, - 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d -}; - -inline Word sub_word(const Word w) { - return kSbox[(w >> 0) & 0xFF] << 0 | - kSbox[(w >> 8) & 0xFF] << 8 | - kSbox[(w >> 16) & 0xFF] << 16 | - kSbox[(w >> 24) & 0xFF] << 24; -} - -inline Word inv_sub_word(const Word w) { - return kInvSbox[(w >> 0) & 0xFF] << 0 | - kInvSbox[(w >> 8) & 0xFF] << 8 | - kInvSbox[(w >> 16) & 0xFF] << 16 | - kInvSbox[(w >> 24) & 0xFF] << 24; -} - -inline void sub_bytes(State &state) { - for (int i = 0; i < kBlockSize; ++i) { - state[i] = sub_word(state[i]); - } -} - -inline void inv_sub_bytes(State &state) { - for (int i = 0; i < kBlockSize; ++i) { - state[i] = inv_sub_word(state[i]); - } -} - -inline void shift_rows(State &state) { - const State ori = { state[0], state[1], state[2], state[3] }; - for (int r = 1; r < kWordSize; ++r) { - const Word m2 = 0xFF << (r * 8); - const Word m1 = ~m2; - for (int c = 0; c < kBlockSize; ++c) { - state[c] = (state[c] & m1) | (ori[(c + r) % kBlockSize] & m2); - } - } -} - -inline void inv_shift_rows(State &state) { - const State ori = { state[0], state[1], state[2], state[3] }; - for (int r = 1; r < kWordSize; ++r) { - const Word m2 = 0xFF << (r * 8); - const Word m1 = ~m2; - for (int c = 0; c < kBlockSize; ++c) { - state[c] = (state[c] & m1) | (ori[(c + kBlockSize - r) % kWordSize] & m2); - } - } -} - -inline unsigned char mul2(const unsigned char b) { - unsigned char m2 = b << 1; - if (b & 0x80) { - m2 ^= 0x011B; - } - - return m2; -} - -inline unsigned char mul(const unsigned char b, const unsigned char m) { - unsigned char v = 0; - unsigned char t = b; - for (int i = 0; i < 8; ++i) { // 8-bits - if ((m >> i) & 0x01) { - v ^= t; - } - - t = mul2(t); - } - - return v; -} - -inline void mix_columns(State &state) { - for (int i = 0; i < kBlockSize; ++i) { - const unsigned char v0_1 = (state[i] >> 0) & 0xFF; - const unsigned char v1_1 = (state[i] >> 8) & 0xFF; - const unsigned char v2_1 = (state[i] >> 16) & 0xFF; - const unsigned char v3_1 = (state[i] >> 24) & 0xFF; - - const unsigned char v0_2 = mul2(v0_1); - const unsigned char v1_2 = mul2(v1_1); - const unsigned char v2_2 = mul2(v2_1); - const unsigned char v3_2 = mul2(v3_1); - - const unsigned char v0_3 = v0_2 ^ v0_1; - const unsigned char v1_3 = v1_2 ^ v1_1; - const unsigned char v2_3 = v2_2 ^ v2_1; - const unsigned char v3_3 = v3_2 ^ v3_1; - - state[i] = - (v0_2 ^ v1_3 ^ v2_1 ^ v3_1) << 0 | - (v0_1 ^ v1_2 ^ v2_3 ^ v3_1) << 8 | - (v0_1 ^ v1_1 ^ v2_2 ^ v3_3) << 16 | - (v0_3 ^ v1_1 ^ v2_1 ^ v3_2) << 24; - } -} - -inline void inv_mix_columns(State &state) { - for (int i = 0; i < kBlockSize; ++i) { - const unsigned char v0 = (state[i] >> 0) & 0xFF; - const unsigned char v1 = (state[i] >> 8) & 0xFF; - const unsigned char v2 = (state[i] >> 16) & 0xFF; - const unsigned char v3 = (state[i] >> 24) & 0xFF; - - state[i] = - (mul(v0, 0x0E) ^ mul(v1, 0x0B) ^ mul(v2, 0x0D) ^ mul(v3, 0x09)) << 0 | - (mul(v0, 0x09) ^ mul(v1, 0x0E) ^ mul(v2, 0x0B) ^ mul(v3, 0x0D)) << 8 | - (mul(v0, 0x0D) ^ mul(v1, 0x09) ^ mul(v2, 0x0E) ^ mul(v3, 0x0B)) << 16 | - (mul(v0, 0x0B) ^ mul(v1, 0x0D) ^ mul(v2, 0x09) ^ mul(v3, 0x0E)) << 24; - } -} - -inline Word rot_word(const Word v) { - return ((v >> 8) & 0x00FFFFFF) | ((v & 0xFF) << 24); -} - -/** - * @private - * @throws std::invalid_argument - */ -inline unsigned int get_round_count(const int key_size) { - switch (key_size) { - case 16: - return 10; - case 24: - return 12; - case 32: - return 14; - default: - SOUP_THROW(std::invalid_argument("Invalid key size")); - } -} - -/** - * @private - * @throws std::invalid_argument - */ -inline RoundKeys expand_key(const unsigned char *key, const int key_size) { - if (key_size != 16 && key_size != 24 && key_size != 32) { - SOUP_THROW(std::invalid_argument("Invalid key size")); - } - - const Word rcon[] = { - 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, - 0x20, 0x40, 0x80, 0x1b, 0x36 - }; - - const int nb = kBlockSize; - const int nk = key_size / nb; - const int nr = get_round_count(key_size); - - std::vector w(nb * (nr + 1)); - for (int i = 0; i < nk; ++ i) { - memcpy(&w[i], key + (i * kWordSize), kWordSize); - } - - for (int i = nk; i < nb * (nr + 1); ++i) { - Word t = w[i - 1]; - if (i % nk == 0) { - t = sub_word(rot_word(t)) ^ rcon[i / nk]; - } - else if (nk > 6 && i % nk == 4) { - t = sub_word(t); - } - - w[i] = t ^ w[i - nk]; - } - - RoundKeys keys(nr + 1); - memcpy(&keys[0], &w[0], w.size() * kWordSize); - - return keys; -} - -inline void copy_bytes_to_state(const unsigned char data[16], State &state) { - memcpy(&state[0], data + 0, kWordSize); - memcpy(&state[1], data + 4, kWordSize); - memcpy(&state[2], data + 8, kWordSize); - memcpy(&state[3], data + 12, kWordSize); -} - -inline void copy_state_to_bytes(const State &state, unsigned char buf[16]) { - memcpy(buf + 0, &state[0], kWordSize); - memcpy(buf + 4, &state[1], kWordSize); - memcpy(buf + 8, &state[2], kWordSize); - memcpy(buf + 12, &state[3], kWordSize); -} - -inline void xor_data(unsigned char data[kStateSize], const unsigned char v[kStateSize]) { - for (int i = 0; i < kStateSize; ++i) { - data[i] ^= v[i]; - } -} - -/** increment counter (128-bit int) by 1 */ -inline void incr_counter(unsigned char counter[kStateSize]) { - unsigned n = kStateSize, c = 1; - do { - --n; - c += counter[n]; - counter[n] = c; - c >>= 8; - } while (n); -} - -inline void encrypt_state(const RoundKeys &rkeys, const unsigned char data[16], unsigned char encrypted[16]) { - State s; - copy_bytes_to_state(data, s); - - add_round_key(rkeys[0], s); - - for (unsigned int i = 1; i < rkeys.size() - 1; ++i) { - sub_bytes(s); - shift_rows(s); - mix_columns(s); - add_round_key(rkeys[i], s); - } - - sub_bytes(s); - shift_rows(s); - add_round_key(rkeys.back(), s); - - copy_state_to_bytes(s, encrypted); -} - -inline void decrypt_state(const RoundKeys &rkeys, const unsigned char data[16], unsigned char decrypted[16]) { - State s; - copy_bytes_to_state(data, s); - - add_round_key(rkeys.back(), s); - inv_shift_rows(s); - inv_sub_bytes(s); - - for (std::size_t i = rkeys.size() - 2; i > 0; --i) { - add_round_key(rkeys[i], s); - inv_mix_columns(s); - inv_shift_rows(s); - inv_sub_bytes(s); - } - - add_round_key(rkeys[0], s); - - copy_state_to_bytes(s, decrypted); -} - -template -std::vector key_from_string(const char (*key_str)[KeyLen]) { - std::vector key(KeyLen - 1); - memcpy(&key[0], *key_str, KeyLen - 1); - return key; -} - -inline bool is_valid_key_size(const std::size_t key_size) { - if (key_size != 16 && key_size != 24 && key_size != 32) { - return false; - } - else { - return true; - } -} - - -namespace gcm { - -const int kBlockBitSize = 128; -const int kBlockByteSize = kBlockBitSize / 8; - -/** - * @private - * GCM operation unit as bit. - * This library handles 128 bit little endian bit array. - * e.g. 0^127 || 1 == "000...0001" (bit string) == 1 - */ -typedef std::bitset bitset128; - -/** - * @private - * GCM operation unit. - * Little endian byte array - * - * If bitset128 is 1: 0^127 || 1 == "000...0001" (bit string) == 1 - * byte array is 0x00, 0x00, 0x00 ... 0x01 (low -> high). - * Byte array is NOT 0x01, 0x00 ... 0x00. - * - * This library handles GCM bit string in two ways. - * One is an array of bitset, which is a little endian 128-bit array's array. - * - * <- first byte - * bitset128 || bitset128 || bitset128... - * - * The other one is a byte array. - * <- first byte - * byte || byte || byte... - */ -class Block { -public: - Block() noexcept { - init_v(0, 0); - } - - Block(const unsigned char * bytes, const unsigned long bytes_size) noexcept { - init_v(bytes, bytes_size); - } - - Block(const std::vector & bytes) noexcept { - init_v(&bytes[0], bytes.size()); - } - - Block(const std::bitset<128> & bits) noexcept; // implementation below - - inline unsigned char * data() noexcept { - return v_; - } - - inline const unsigned char* data() const noexcept { - return v_; - } - - inline std::bitset<128> to_bits() const noexcept { - std::bitset<128> bits; - for (int i = 0; i < 16; ++i) { - bits <<= 8; - bits |= v_[i]; - } - - return bits; - } - - inline Block operator^(const Block & b) const noexcept { - Block r; - for (int i = 0; i < 16; ++i) { - r.data()[i] = data()[i] ^ b.data()[i]; - } - return r; - } - -private: - unsigned char v_[16]; - - inline void init_v(const unsigned char * bytes, const std::size_t bytes_size) noexcept { - memset(v_, 0, sizeof(v_)); - - const std::size_t cs = (std::min)(bytes_size, static_cast(16)); - for (std::size_t i = 0; i < cs; ++i) { - v_[i] = bytes[i]; - } - } -}; - -// Workaround for clang optimization in 32-bit build via Visual Studio producing incorrect results (https://github.com/kkAyataka/plusaes/issues/43) -#if defined(__clang__) && defined(_WIN32) && !defined(_WIN64) -#pragma optimize("", off) -#endif -inline Block::Block(const std::bitset<128> & bits) noexcept -{ - init_v(0, 0); - const std::bitset<128> mask(0xFF); // 1 byte mask - for (std::size_t i = 0; i < 16; ++i) { - v_[15 - i] = static_cast(((bits >> (i * 8)) & mask).to_ulong()); - } -} -#if defined(__clang__) && defined(_WIN32) && !defined(_WIN64) -#pragma optimize("", on) -#endif - -template -unsigned long ceil(const T v) { - return static_cast(std::ceil(v) + 0.5); -} - -template -std::bitset operator||(const std::bitset &v1, const std::bitset &v2) { - std::bitset ret(v1.to_string() + v2.to_string()); - return ret; -} - -template -std::bitset lsb(const std::bitset &X) { - std::bitset r; - for (std::size_t i = 0; i < S; ++i) { - r[i] = X[i]; - } - return r; -} - -template -std::bitset msb(const std::bitset &X) { - std::bitset r; - for (std::size_t i = 0; i < S; ++i) { - r[S - 1 - i] = X[X.size() - 1 - i]; - } - return r; -} - -template -std::bitset inc32(const std::bitset X) { - const std::size_t S = 32; - - const auto a = msb(X); - const std::bitset b((lsb(X).to_ulong() + 1)); // % (2^32); - // lsb<32> is low 32-bit value - // Spec.'s "mod 2^S" is not necessary when S is 32 (inc32). - // ...and 2^32 is over 32-bit integer. - - return a || b; -} - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wself-assign" -#endif // __clang__ - -/** Algorithm 1 @private */ -inline Block mul_blocks(const Block X, const Block Y) noexcept { - const bitset128 R = (std::bitset<8>("11100001") || std::bitset<120>()); - - bitset128 X_bits = X.to_bits(); - bitset128 Z; - bitset128 V = Y.to_bits(); - for (int i = 127; i >= 0; --i) { - // Z - if (X_bits[i] == false) { - Z = Z; - } - else { - Z = Z ^ V; - } - - // V - if (V[0] == false) { - V = V >> 1; - } - else { - V = (V >> 1) ^ R; - } - } - - return Z; -} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif // __clang__ - -/** Algorithm 2 @private */ -inline Block ghash(const Block & H, const std::vector & X) noexcept { - const std::size_t m = X.size() / kBlockByteSize; - Block Ym; - for (std::size_t i = 0; i < m; ++i) { - const Block Xi(&X[i * kBlockByteSize], kBlockByteSize); - Ym = mul_blocks((Ym ^ Xi), H); - } - - return Ym; -} - -template -std::bitset make_bitset(const unsigned char * bytes, const std::size_t bytes_size) { - std::bitset bits; - for (auto i = 0u; i < bytes_size; ++i) { - bits <<= 8; - bits |= bytes[i]; - } - return bits; -} - -/** Algorithm 3 @private */ -inline std::vector gctr(const detail::RoundKeys & rkeys, const Block & ICB, const unsigned char * X, const std::size_t X_size){ - if (!X || X_size == 0) { - return std::vector(); - } - else { - const unsigned long n = ceil(X_size * 8.0 / kBlockBitSize); - std::vector Y(X_size); - - Block CB; - for (std::size_t i = 0; i < n; ++i) { - // CB - if (i == 0) { // first - CB = ICB; - } - else { - CB = inc32(CB.to_bits()); - } - - // CIPH - Block eCB; - encrypt_state(rkeys, CB.data(), eCB.data()); - - // Y - int op_size = 0; - if (i < n - 1) { - op_size = kBlockByteSize; - } - else { // last - op_size = (X_size % kBlockByteSize) ? (X_size % kBlockByteSize) : kBlockByteSize; - } - const Block Yi = Block(X + i * kBlockBitSize / 8, op_size) ^ eCB; - memcpy(&Y[i * kBlockByteSize], Yi.data(), op_size); - } - - return Y; - } -} - -inline void push_back(std::vector & bytes, const unsigned char * data, const std::size_t data_size) { - bytes.insert(bytes.end(), data, data + data_size); -} - -inline void push_back(std::vector & bytes, const std::bitset<64> & bits) { - const std::bitset<64> mask(0xFF); // 1 byte mask - for (std::size_t i = 0; i < 8; ++i) { - bytes.push_back(static_cast(((bits >> ((7 - i) * 8)) & mask).to_ulong())); - } -} - -inline void push_back_zero_bits(std::vector& bytes, const std::size_t zero_bits_size) { - const std::vector zero_bytes(zero_bits_size / 8); - bytes.insert(bytes.end(), zero_bytes.begin(), zero_bytes.end()); -} - -inline Block calc_H(const RoundKeys & rkeys) { - std::vector H_raw(gcm::kBlockByteSize); - encrypt_state(rkeys, &H_raw[0], &H_raw[0]); - return gcm::Block(H_raw); -} - -inline Block calc_J0(const Block & H, const unsigned char * iv, const std::size_t iv_size) { - if (iv_size == 12) { - const std::bitset<96> iv_bits = gcm::make_bitset<96>(iv, iv_size); - return iv_bits || std::bitset<31>() || std::bitset<1>(1); - } - else { - const auto len_iv = iv_size * 8; - const auto s = 128 * gcm::ceil(len_iv / 128.0) - len_iv; - std::vector ghash_in; - gcm::push_back(ghash_in, iv, iv_size); - gcm::push_back_zero_bits(ghash_in, s + 64); - gcm::push_back(ghash_in, std::bitset<64>(len_iv)); - - return gcm::ghash(H, ghash_in); - } -} - -inline void calc_gcm_tag( - const unsigned char * data, - const std::size_t data_size, - const unsigned char * aadata, - const std::size_t aadata_size, - const unsigned char * key, - const std::size_t key_size, - const unsigned char * iv, - const std::size_t iv_size, - unsigned char * tag, - const std::size_t tag_size -) { - const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); - const gcm::Block H = gcm::calc_H(rkeys); - const gcm::Block J0 = gcm::calc_J0(H, iv, iv_size); - - const auto lenC = data_size * 8; - const auto lenA = aadata_size * 8; - const std::size_t u = 128 * gcm::ceil(lenC / 128.0) - lenC; - const std::size_t v = 128 * gcm::ceil(lenA / 128.0) - lenA; - - std::vector ghash_in; - ghash_in.reserve((aadata_size + v / 8) + (data_size + u / 8) + 8 + 8); - gcm::push_back(ghash_in, aadata, aadata_size); - gcm::push_back_zero_bits(ghash_in, v); - gcm::push_back(ghash_in, data, data_size); - gcm::push_back_zero_bits(ghash_in, u); - gcm::push_back(ghash_in, std::bitset<64>(lenA)); - gcm::push_back(ghash_in, std::bitset<64>(lenC)); - const gcm::Block S = gcm::ghash(H, ghash_in); - const std::vector T = gcm::gctr(rkeys, J0, S.data(), gcm::kBlockByteSize); - - // return - memcpy(tag, &T[0], (std::min)(tag_size, static_cast(16))); -} - -/** Algorithm 4 and 5 @private */ -inline void crypt_gcm( - const unsigned char* data, - const std::size_t data_size, - const unsigned char* key, - const std::size_t key_size, - const unsigned char* iv, - const std::size_t iv_size, - unsigned char* crypted -) { - const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); - const gcm::Block H = gcm::calc_H(rkeys); - const gcm::Block J0 = gcm::calc_J0(H, iv, iv_size); - - const std::vector C = gcm::gctr(rkeys, gcm::inc32(J0.to_bits()), data, data_size); - - if (crypted) { - memcpy(crypted, &C[0], data_size); - } -} - -} // namespce detail::gcm - -} // namespace detail - -/** @defgroup Base Base - * Base definitions and convenient functions - * @{ */ - -/** Version number of plusaes. */ -inline unsigned int version() { - return PLUSAES_VERSION; -} - -/** Create 128-bit key from string. */ -inline std::vector key_from_string(const char (*key_str)[17]) { - return detail::key_from_string<17>(key_str); -} - -/** Create 192-bit key from string. */ -inline std::vector key_from_string(const char (*key_str)[25]) { - return detail::key_from_string<25>(key_str); -} - -/** Create 256-bit key from string. */ -inline std::vector key_from_string(const char (*key_str)[33]) { - return detail::key_from_string<33>(key_str); -} - -/** Calculates encrypted data size when padding is enabled. */ -inline unsigned long get_padded_encrypted_size(const unsigned long data_size) { - return data_size + detail::kStateSize - (data_size % detail::kStateSize); -} - -/** Error code */ -typedef enum { - kErrorOk = 0, - kErrorInvalidDataSize = 1, - kErrorInvalidKeySize, - kErrorInvalidBufferSize, - kErrorInvalidKey, - kErrorDeprecated, // kErrorInvalidNonceSize - kErrorInvalidIvSize, - kErrorInvalidTagSize, - kErrorInvalidTag -} Error; - -/** @} */ - -namespace detail { - -inline Error check_encrypt_cond( - const unsigned long data_size, - const unsigned long key_size, - const unsigned long encrypted_size, - const bool pads) { - // check data size - if (!pads && (data_size % kStateSize != 0)) { - return kErrorInvalidDataSize; - } - - // check key size - if (!detail::is_valid_key_size(key_size)) { - return kErrorInvalidKeySize; - } - - // check encrypted buffer size - if (pads) { - const unsigned long required_size = get_padded_encrypted_size(data_size); - if (encrypted_size < required_size) { - return kErrorInvalidBufferSize; - } - } - else { - if (encrypted_size < data_size) { - return kErrorInvalidBufferSize; - } - } - return kErrorOk; -} - -inline Error check_decrypt_cond( - const unsigned long data_size, - const unsigned long key_size, - const unsigned long decrypted_size, - const unsigned long * padded_size - ) { - // check data size - if (data_size % 16 != 0) { - return kErrorInvalidDataSize; - } - - // check key size - if (!detail::is_valid_key_size(key_size)) { - return kErrorInvalidKeySize; - } - - // check decrypted buffer size - if (!padded_size) { - if (decrypted_size < data_size) { - return kErrorInvalidBufferSize; - } - } - else { - if (decrypted_size < (data_size - kStateSize)) { - return kErrorInvalidBufferSize; - } - } - - return kErrorOk; -} - -inline bool check_padding(const unsigned long padding, const unsigned char data[kStateSize]) { - if (padding > kStateSize) { - return false; - } - - for (unsigned long i = 0; i < padding; ++i) { - if (data[kStateSize - 1 - i] != padding) { - return false; - } - } - - return true; -} - -inline Error check_gcm_cond( - const std::size_t key_size, - const std::size_t iv_size, - const std::size_t tag_size -) { - // check key size - if (!detail::is_valid_key_size(key_size)) { - return kErrorInvalidKeySize; - } - - if (iv_size < 1) { - return kErrorInvalidIvSize; - } - - // check tag size - if ((tag_size < 12 || 16 < tag_size) && - (tag_size != 8) && - (tag_size != 4)) { - return kErrorInvalidTagSize; - } - - return kErrorOk; -} - -} // namespace detail - -/** @defgroup ECB ECB - * ECB mode functions - * @{ */ - -/** - * Encrypts data with ECB mode. - * @param [in] data Data. - * @param [in] data_size Data size. - * If the pads is false, data size must be multiple of 16. - * @param [in] key key bytes. The key length must be 16 (128-bit), 24 (192-bit) or 32 (256-bit). - * @param [in] key_size key size. - * @param [out] encrypted Encrypted data buffer. - * @param [in] encrypted_size Encrypted data buffer size. - * @param [in] pads If this value is true, encrypted data is padded by PKCS. - * Encrypted data size must be multiple of 16. - * If the pads is true, encrypted data is padded with PKCS. - * So the data is multiple of 16, encrypted data size needs additonal 16 bytes. - * @since 1.0.0 - */ -inline Error encrypt_ecb( - const unsigned char * data, - const unsigned long data_size, - const unsigned char * key, - const unsigned long key_size, - unsigned char *encrypted, - const unsigned long encrypted_size, - const bool pads - ) { - const Error e = detail::check_encrypt_cond(data_size, key_size, encrypted_size, pads); - if (e != kErrorOk) { - return e; - } - - const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); - - const unsigned long bc = data_size / detail::kStateSize; - for (unsigned long i = 0; i < bc; ++i) { - detail::encrypt_state(rkeys, data + (i * detail::kStateSize), encrypted + (i * detail::kStateSize)); - } - - if (pads) { - const int rem = data_size % detail::kStateSize; - const char pad_v = detail::kStateSize - rem; - - std::vector ib(detail::kStateSize, pad_v), ob(detail::kStateSize); - memcpy(&ib[0], data + data_size - rem, rem); - - detail::encrypt_state(rkeys, &ib[0], &ob[0]); - memcpy(encrypted + (data_size - rem), &ob[0], detail::kStateSize); - } - - return kErrorOk; -} - -/** - * Decrypts data with ECB mode. - * @param [in] data Data bytes. - * @param [in] data_size Data size. - * @param [in] key Key bytes. - * @param [in] key_size Key size. - * @param [out] decrypted Decrypted data buffer. - * @param [in] decrypted_size Decrypted data buffer size. - * @param [out] padded_size If this value is NULL, this function does not remove padding. - * If this value is not NULL, this function removes padding by PKCS - * and returns padded size using padded_size. - * @since 1.0.0 - */ -inline Error decrypt_ecb( - const unsigned char * data, - const unsigned long data_size, - const unsigned char * key, - const unsigned long key_size, - unsigned char * decrypted, - const unsigned long decrypted_size, - unsigned long * padded_size - ) { - const Error e = detail::check_decrypt_cond(data_size, key_size, decrypted_size, padded_size); - if (e != kErrorOk) { - return e; - } - - const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); - - const unsigned long bc = data_size / detail::kStateSize - 1; - for (unsigned long i = 0; i < bc; ++i) { - detail::decrypt_state(rkeys, data + (i * detail::kStateSize), decrypted + (i * detail::kStateSize)); - } - - unsigned char last[detail::kStateSize] = {}; - detail::decrypt_state(rkeys, data + (bc * detail::kStateSize), last); - - if (padded_size) { - *padded_size = last[detail::kStateSize - 1]; - const unsigned long cs = detail::kStateSize - *padded_size; - - if (!detail::check_padding(*padded_size, last)) { - return kErrorInvalidKey; - } - else if (decrypted_size >= (bc * detail::kStateSize) + cs) { - memcpy(decrypted + (bc * detail::kStateSize), last, cs); - } - else { - return kErrorInvalidBufferSize; - } - } - else { - memcpy(decrypted + (bc * detail::kStateSize), last, sizeof(last)); - } - - return kErrorOk; -} - -/** @} */ - -/** @defgroup CBC CBC - * CBC mode functions - * @{ */ - -/** - * Encrypt data with CBC mode. - * @param [in] data Data. - * @param [in] data_size Data size. - * If the pads is false, data size must be multiple of 16. - * @param [in] key key bytes. The key length must be 16 (128-bit), 24 (192-bit) or 32 (256-bit). - * @param [in] key_size key size. - * @param [in] iv Initialize vector. - * @param [out] encrypted Encrypted data buffer. - * @param [in] encrypted_size Encrypted data buffer size. - * @param [in] pads If this value is true, encrypted data is padded by PKCS. - * Encrypted data size must be multiple of 16. - * If the pads is true, encrypted data is padded with PKCS. - * So the data is multiple of 16, encrypted data size needs additonal 16 bytes. - * @since 1.0.0 - */ -inline Error encrypt_cbc( - const unsigned char * data, - const unsigned long data_size, - const unsigned char * key, - const unsigned long key_size, - const unsigned char (* iv)[16], - unsigned char * encrypted, - const unsigned long encrypted_size, - const bool pads - ) { - const Error e = detail::check_encrypt_cond(data_size, key_size, encrypted_size, pads); - if (e != kErrorOk) { - return e; - } - - const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); - - unsigned char s[detail::kStateSize] = {}; // encrypting data - - // calculate padding value - const bool ge16 = (data_size >= detail::kStateSize); - const int rem = data_size % detail::kStateSize; - const unsigned char pad_v = detail::kStateSize - rem; - - // encrypt 1st state - if (ge16) { - memcpy(s, data, detail::kStateSize); - } - else { - memset(s, pad_v, detail::kStateSize); - memcpy(s, data, data_size); - } - if (iv) { - detail::xor_data(s, *iv); - } - detail::encrypt_state(rkeys, s, encrypted); - - // encrypt mid - const unsigned long bc = data_size / detail::kStateSize; - for (unsigned long i = 1; i < bc; ++i) { - const long offset = i * detail::kStateSize; - memcpy(s, data + offset, detail::kStateSize); - detail::xor_data(s, encrypted + offset - detail::kStateSize); - - detail::encrypt_state(rkeys, s, encrypted + offset); - } - - // enctypt last - if (pads && ge16) { - std::vector ib(detail::kStateSize, pad_v), ob(detail::kStateSize); - memcpy(&ib[0], data + data_size - rem, rem); - - detail::xor_data(&ib[0], encrypted + (bc - 1) * detail::kStateSize); - - detail::encrypt_state(rkeys, &ib[0], &ob[0]); - memcpy(encrypted + (data_size - rem), &ob[0], detail::kStateSize); - } - - return kErrorOk; -} - -/** - * Decrypt data with CBC mode. - * @param [in] data Data bytes. - * @param [in] data_size Data size. - * @param [in] key Key bytes. - * @param [in] key_size Key size. - * @param [in] iv Initialize vector. - * @param [out] decrypted Decrypted data buffer. - * @param [in] decrypted_size Decrypted data buffer size. - * @param [out] padded_size If this value is NULL, this function does not remove padding. - * If this value is not NULL, this function removes padding by PKCS - * and returns padded size using padded_size. - * @since 1.0.0 - */ -inline Error decrypt_cbc( - const unsigned char * data, - const unsigned long data_size, - const unsigned char * key, - const unsigned long key_size, - const unsigned char (* iv)[16], - unsigned char * decrypted, - const unsigned long decrypted_size, - unsigned long * padded_size - ) { - const Error e = detail::check_decrypt_cond(data_size, key_size, decrypted_size, padded_size); - if (e != kErrorOk) { - return e; - } - - const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); - - // decrypt 1st state - detail::decrypt_state(rkeys, data, decrypted); - if (iv) { - detail::xor_data(decrypted, *iv); - } - - // decrypt mid - const unsigned long bc = data_size / detail::kStateSize - 1; - for (unsigned long i = 1; i < bc; ++i) { - const long offset = i * detail::kStateSize; - detail::decrypt_state(rkeys, data + offset, decrypted + offset); - detail::xor_data(decrypted + offset, data + offset - detail::kStateSize); - } - - // decrypt last - unsigned char last[detail::kStateSize] = {}; - if (data_size > detail::kStateSize) { - detail::decrypt_state(rkeys, data + (bc * detail::kStateSize), last); - detail::xor_data(last, data + (bc * detail::kStateSize - detail::kStateSize)); - } - else { - memcpy(last, decrypted, data_size); - memset(decrypted, 0, decrypted_size); - } - - if (padded_size) { - *padded_size = last[detail::kStateSize - 1]; - const unsigned long cs = detail::kStateSize - *padded_size; - - if (!detail::check_padding(*padded_size, last)) { - return kErrorInvalidKey; - } - else if (decrypted_size >= (bc * detail::kStateSize) + cs) { - memcpy(decrypted + (bc * detail::kStateSize), last, cs); - } - else { - return kErrorInvalidBufferSize; - } - } - else { - memcpy(decrypted + (bc * detail::kStateSize), last, sizeof(last)); - } - - return kErrorOk; -} - -/** @} */ - -/** @defgroup GCM GCM - * GCM mode functions - * @{ */ - -/** - * Encrypts data with GCM mode and gets an authentication tag. - * - * You can specify iv size and tag size. - * But usually you should use the other overloaded function whose iv and tag size is fixed. - * - * @returns kErrorOk - * @returns kErrorInvalidKeySize - * @returns kErrorInvalidIvSize - * @returns kErrorInvalidTagSize - */ -inline Error encrypt_gcm( - unsigned char * data, - const std::size_t data_size, - const unsigned char * aadata, - const std::size_t aadata_size, - const unsigned char * key, - const std::size_t key_size, - const unsigned char * iv, - const std::size_t iv_size, - unsigned char * tag, - const std::size_t tag_size -) { - const Error err = detail::check_gcm_cond(key_size, iv_size, tag_size); - if (err != kErrorOk) { - return err; - } - - detail::gcm::crypt_gcm(data, data_size, key, key_size, iv, iv_size, data); - detail::gcm::calc_gcm_tag(data, data_size, aadata, aadata_size, key, key_size, iv, iv_size, tag, tag_size); - - return kErrorOk; -} - -/** - * Encrypts data with GCM mode and gets an authentication tag. - * - * @param [in,out] data Input data and output buffer. - * This buffer is replaced with encrypted data. - * @param [in] data_size data size - * @param [in] aadata Additional Authenticated data - * @param [in] aadata_size aadata size - * @param [in] key Cipher key - * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32 (256-bit). - * @param [in] iv Initialization vector - * @param [out] tag Calculated authentication tag data - * - * @returns kErrorOk - * @returns kErrorInvalidKeySize - */ -inline Error encrypt_gcm( - unsigned char * data, - const std::size_t data_size, - const unsigned char * aadata, - const std::size_t aadata_size, - const unsigned char * key, - const std::size_t key_size, - const unsigned char (*iv)[12], - unsigned char (*tag)[16] -) { - return encrypt_gcm(data, data_size, aadata, aadata_size, key, key_size, *iv, 12, *tag, 16); -} - -/** - * Decrypts data with GCM mode and checks an authentication tag. - * - * You can specify iv size and tag size. - * But usually you should use the other overloaded function whose iv and tag size is fixed. - * - * @returns kErrorOk - * @returns kErrorInvalidKeySize - * @returns kErrorInvalidIvSize - * @returns kErrorInvalidTagSize - * @returns kErrorInvalidTag - */ -inline Error decrypt_gcm( - unsigned char * data, - const std::size_t data_size, - const unsigned char * aadata, - const std::size_t aadata_size, - const unsigned char * key, - const std::size_t key_size, - const unsigned char * iv, - const std::size_t iv_size, - const unsigned char * tag, - const std::size_t tag_size -) { - const Error err = detail::check_gcm_cond(key_size, iv_size, tag_size); - if (err != kErrorOk) { - return err; - } - - unsigned char * C = data; - const auto C_size = data_size; - unsigned char tagd[16] = {}; - detail::gcm::calc_gcm_tag(C, C_size, aadata, aadata_size, key, key_size, iv, iv_size, tagd, 16); - - if (memcmp(tag, tagd, tag_size) != 0) { - return kErrorInvalidTag; - } - else { - detail::gcm::crypt_gcm(C, C_size, key, key_size, iv, iv_size, C); - - return kErrorOk; - } -} - -/** - * Decrypts data with GCM mode and checks an authentication tag. - * - * @param [in,out] data Input data and output buffer. - * This buffer is replaced with decrypted data. - * @param [in] data_size data size - * @param [in] aadata Additional Authenticated data - * @param [in] aadata_size aadata size - * @param [in] key Cipher key - * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32 (256-bit). - * @param [in] iv Initialization vector - * @param [in] tag Authentication tag data - * - * @returns kErrorOk - * @returns kErrorInvalidKeySize - * @returns kErrorInvalidTag - */ -inline Error decrypt_gcm( - unsigned char * data, - const std::size_t data_size, - const unsigned char * aadata, - const std::size_t aadata_size, - const unsigned char * key, - const std::size_t key_size, - const unsigned char (*iv)[12], - const unsigned char (*tag)[16] -) { - return decrypt_gcm(data, data_size, aadata, aadata_size, key, key_size, *iv, 12, *tag, 16); -} - -/** @} */ - -/** @defgroup CTR CTR - * CTR mode function - * @{ */ - -/** - * Encrypts or decrypt data in-place with CTR mode. - * - * @param [in,out] data Input data and output buffer. - * This buffer is replaced with encrypted / decrypted data. - * @param [in] data_size Data size. - * @param [in] key Cipher key - * @param [in] key_size Cipher key size. This value must be 16 (128-bit), 24 (192-bit), or 32 (256-bit). - * @param [in] nonce Nonce of the counter initialization. - * - * @returns kErrorOk - * @returns kErrorInvalidKeySize - * @since 1.0.0 - */ -inline Error crypt_ctr( - unsigned char * data, - const std::size_t data_size, - const unsigned char * key, - const std::size_t key_size, - const unsigned char (*nonce)[16] -) { - if (!detail::is_valid_key_size(key_size)) return kErrorInvalidKeySize; - const detail::RoundKeys rkeys = detail::expand_key(key, static_cast(key_size)); - - unsigned long pos = 0; - unsigned long blkpos = detail::kStateSize; - unsigned char blk[detail::kStateSize] = {}; - unsigned char counter[detail::kStateSize] = {}; - memcpy(counter, nonce, 16); - - while (pos < data_size) { - if (blkpos == detail::kStateSize) { - detail::encrypt_state(rkeys, counter, blk); - detail::incr_counter(counter); - blkpos = 0; - } - data[pos++] ^= blk[blkpos++]; - } - - return kErrorOk; -} - -/** @} */ - -} // namespace plusaes - -#endif // PLUSAES_HPP__