Skip to content

Commit 2e2f0d7

Browse files
authored
Merge pull request #255 from cppalliance/ecb
Implement ECB mode with new block cipher
2 parents 6a4d72c + 3d17296 commit 2e2f0d7

11 files changed

+503
-46
lines changed

include/boost/crypt2/aes/cipher_mode.hpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,24 @@
55
#ifndef BOOST_CIPHER_MODE_HPP
66
#define BOOST_CIPHER_MODE_HPP
77

8-
namespace boost::crypt::aes {
8+
#include <boost/crypt2/detail/compat.hpp>
99

10-
enum class cipher_mode
11-
{
10+
namespace boost::crypt {
11+
12+
enum class aes_cipher_mode {
1213
ecb, // Electronic Codebook
14+
ctr, // Counter
1315
};
1416

15-
} // namespace boost::crypt::aes
17+
template <aes_cipher_mode c>
18+
class aes128;
19+
20+
template <aes_cipher_mode c>
21+
class aes192;
22+
23+
template <aes_cipher_mode c>
24+
class aes256;
25+
26+
} // namespace boost::crypt
1627

1728
#endif //BOOST_CIPHER_MODE_HPP
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
//
5+
// See: https://datatracker.ietf.org/doc/html/rfc5652#section-6.3
6+
7+
#ifndef BOOST_CRYPT2_AES_DETAIL_PKCS7_HPP
8+
#define BOOST_CRYPT2_AES_DETAIL_PKCS7_HPP
9+
10+
#include <boost/crypt2/detail/config.hpp>
11+
#include <boost/crypt2/detail/compat.hpp>
12+
13+
namespace boost::crypt::aes_detail {
14+
15+
template <compat::size_t Extent>
16+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto pkcs7(
17+
compat::span<const compat::byte, Extent> message,
18+
compat::array<compat::byte, 16>& padded_message) noexcept -> void
19+
{
20+
const auto pad_num {static_cast<compat::byte>(16U - message.size())};
21+
22+
auto message_begin {message.begin()};
23+
auto message_end {message.end()};
24+
auto padded_message_begin {padded_message.begin()};
25+
26+
while (message_begin != message_end)
27+
{
28+
*padded_message_begin++ = *message_begin++;
29+
}
30+
31+
const auto padded_message_end {padded_message.end()};
32+
while (padded_message_begin != padded_message_end)
33+
{
34+
*padded_message_begin++ = pad_num;
35+
}
36+
}
37+
38+
} // namespace boost::crypt::aes_detail
39+
40+
#endif //BOOST_CRYPT2_AES_DETAIL_PKCS7_HPP

include/boost/crypt2/aes/ecb.hpp

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_CRYPT2_AES_ECB_HPP
6+
#define BOOST_CRYPT2_AES_ECB_HPP
7+
8+
#include <boost/crypt2/aes/cipher_mode.hpp>
9+
#include <boost/crypt2/aes/detail/cipher.hpp>
10+
#include <boost/crypt2/detail/config.hpp>
11+
#include <boost/crypt2/detail/compat.hpp>
12+
#include <boost/crypt2/detail/concepts.hpp>
13+
#include <boost/crypt2/detail/clear_mem.hpp>
14+
#include <boost/crypt2/detail/assert.hpp>
15+
#include <boost/crypt2/state.hpp>
16+
17+
namespace boost::crypt {
18+
19+
namespace aes_detail {
20+
21+
template <compat::size_t Nr>
22+
class ecb_impl {
23+
private:
24+
25+
static constexpr compat::size_t key_length_bytes {Nr == 10 ? 16 :
26+
Nr == 12 ? 24 :
27+
Nr == 14 ? 32 : 0};
28+
29+
static constexpr compat::size_t block_length_bytes {16U};
30+
31+
static_assert(key_length_bytes != 0, "Invalid key length");
32+
33+
cipher<Nr> block_cipher;
34+
35+
bool initialized {false};
36+
37+
public:
38+
39+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR ecb_impl() noexcept = default;
40+
41+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR ~ecb_impl() noexcept = default;
42+
43+
template <compat::size_t Extent>
44+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto init(compat::span<const compat::byte, Extent> key) noexcept -> state;
45+
46+
template <concepts::sized_range SizedRange>
47+
BOOST_CRYPT_GPU_ENABLED auto init(SizedRange&& key) noexcept -> state;
48+
49+
template <compat::size_t Extent>
50+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto
51+
encrypt_no_padding(compat::span<compat::byte, Extent> message) noexcept -> state;
52+
53+
template <concepts::sized_range SizedRange>
54+
BOOST_CRYPT_GPU_ENABLED auto encrypt_no_padding(SizedRange&& message) noexcept -> state;
55+
56+
template <compat::size_t Extent>
57+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto
58+
decrypt_no_padding(compat::span<compat::byte, Extent> ciphertext) noexcept -> state;
59+
60+
template <concepts::sized_range SizedRange>
61+
BOOST_CRYPT_GPU_ENABLED auto decrypt_no_padding(SizedRange&& ciphertext) noexcept -> state;
62+
};
63+
64+
template <compat::size_t Nr>
65+
template <compat::size_t Extent>
66+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto ecb_impl<Nr>::init(
67+
compat::span<const compat::byte, Extent> key) noexcept -> state
68+
{
69+
static_assert(Extent >= key_length_bytes, "Invalid key length");
70+
71+
if (key.size() < key_length_bytes)
72+
{
73+
return state::insufficient_key_length;
74+
}
75+
76+
const auto fixed_key {key.template first<key_length_bytes>()};
77+
BOOST_CRYPT_ASSERT(fixed_key.size_bytes() == key_length_bytes);
78+
79+
block_cipher.init(fixed_key);
80+
81+
initialized = true;
82+
83+
return state::success;
84+
}
85+
86+
template <compat::size_t Nr>
87+
template <concepts::sized_range SizedRange>
88+
BOOST_CRYPT_GPU_ENABLED auto ecb_impl<Nr>::init(SizedRange&& key) noexcept -> state
89+
{
90+
const auto key_span {compat::make_span(key)};
91+
if (key_span.size_bytes() < key_length_bytes)
92+
{
93+
return state::insufficient_key_length;
94+
}
95+
96+
const auto byte_key_span {compat::as_bytes(key_span)};
97+
const auto fixed_key {byte_key_span.template first<key_length_bytes>()};
98+
99+
block_cipher.init(fixed_key);
100+
101+
initialized = true;
102+
103+
return state::success;
104+
}
105+
106+
template <compat::size_t Nr>
107+
template <compat::size_t Extent>
108+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto ecb_impl<Nr>::encrypt_no_padding(
109+
compat::span<compat::byte, Extent> message) noexcept -> state
110+
{
111+
static_assert(Extent == compat::dynamic_extent || Extent % block_length_bytes == 0, "Invalid ciphertext length");
112+
113+
if (message.size() % block_length_bytes != 0 && !message.empty())
114+
{
115+
return state::incorrect_message_length;
116+
}
117+
if (!initialized)
118+
{
119+
return state::uninitialized;
120+
}
121+
122+
auto message_begin {message.begin()};
123+
const auto message_end {message.end()};
124+
while (message_begin != message_end)
125+
{
126+
auto fixed_span {compat::span<compat::byte, block_length_bytes>(message_begin, message_begin + block_length_bytes)};
127+
block_cipher.block_cipher(fixed_span);
128+
message_begin += block_length_bytes;
129+
}
130+
131+
return state::success;
132+
}
133+
134+
template <compat::size_t Nr>
135+
template <concepts::sized_range SizedRange>
136+
BOOST_CRYPT_GPU_ENABLED auto ecb_impl<Nr>::encrypt_no_padding(
137+
SizedRange&& message) noexcept -> state
138+
{
139+
auto message_span {compat::make_span(message)};
140+
return encrypt_no_padding(compat::as_writable_bytes(message_span));
141+
}
142+
143+
template <compat::size_t Nr>
144+
template <compat::size_t Extent>
145+
BOOST_CRYPT_GPU_ENABLED_CONSTEXPR auto ecb_impl<Nr>::decrypt_no_padding(
146+
compat::span<compat::byte, Extent> ciphertext) noexcept -> state
147+
{
148+
static_assert(Extent == compat::dynamic_extent || Extent % block_length_bytes == 0, "Invalid ciphertext length");
149+
150+
if (ciphertext.size() % block_length_bytes != 0 && !ciphertext.empty())
151+
{
152+
return state::incorrect_message_length;
153+
}
154+
if (!initialized)
155+
{
156+
return state::uninitialized;
157+
}
158+
159+
auto ciphertext_begin {ciphertext.begin()};
160+
const auto ciphertext_end {ciphertext.end()};
161+
while (ciphertext_begin != ciphertext_end)
162+
{
163+
auto fixed_span {compat::span<compat::byte, block_length_bytes>(ciphertext_begin, ciphertext_begin + block_length_bytes)};
164+
block_cipher.inverse_block_cipher(fixed_span);
165+
ciphertext_begin += block_length_bytes;
166+
}
167+
168+
return state::success;
169+
}
170+
171+
template <compat::size_t Nr>
172+
template <concepts::sized_range SizedRange>
173+
BOOST_CRYPT_GPU_ENABLED auto ecb_impl<Nr>::decrypt_no_padding(
174+
SizedRange&& ciphertext) noexcept -> state
175+
{
176+
auto ciphertext_span {compat::make_span(ciphertext)};
177+
return decrypt_no_padding(compat::as_writable_bytes(ciphertext_span));
178+
}
179+
180+
} // namespace aes_detail
181+
182+
template <>
183+
class aes128<aes_cipher_mode::ecb> : public aes_detail::ecb_impl<10> {};
184+
185+
template <>
186+
class aes192<aes_cipher_mode::ecb> : public aes_detail::ecb_impl<12> {};
187+
188+
template <>
189+
class aes256<aes_cipher_mode::ecb> : public aes_detail::ecb_impl<14> {};
190+
191+
} // namespace boost::crypt
192+
193+
#endif // BOOST_CRYPT2_AES_ECB_HPP

include/boost/crypt2/state.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ BOOST_CRYPT_EXPORT enum class state
2121
requested_too_many_bits, // 2^19 bits is all that's allowed per request
2222
insufficient_key_length, // The key is not of proscribed length
2323
insufficient_output_length, // The output will not fit in the provided container
24+
incorrect_message_length, // In non-padded AES modes the message must be a multiple of 128 bits
2425
state_error // added more input after get_digest without re-init
2526
};
2627

test/Jamfile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ run test_hash_drbg.cpp ;
8383

8484
run test_aes.cpp ;
8585

86+
run test_pkcs7.cpp ;
87+
8688
# NIST standard testing
8789
run test_nist_cavs_sha1_monte.cpp ;
8890
run test_nist_cavs_sha1_short_long.cpp ;
@@ -150,9 +152,9 @@ run test_nist_cavs_shake256_short_long.cpp ;
150152
run test_nist_cavs_shake256_monte.cpp ;
151153
run test_nist_cavs_shake256_variable_output.cpp ;
152154

153-
#run test_nist_cavs_aes128_kat_ecb.cpp ;
154-
#run test_nist_cavs_aes128_mmt_ecb.cpp ;
155-
#run test_nist_cavs_aes128_mct_ecb.cpp ;
155+
run test_nist_cavs_aes128_kat_ecb.cpp ;
156+
run test_nist_cavs_aes128_mmt_ecb.cpp ;
157+
run test_nist_cavs_aes128_mct_ecb.cpp ;
156158
#run test_nist_cavs_aes128_kat_cbc.cpp ;
157159
#run test_nist_cavs_aes128_mmt_cbc.cpp ;
158160
#run test_nist_cavs_aes128_mct_cbc.cpp ;

0 commit comments

Comments
 (0)