/src/serenity/Userland/Libraries/LibCrypto/Cipher/Mode/CTR.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/StringBuilder.h> |
10 | | #include <AK/StringView.h> |
11 | | #include <LibCrypto/Cipher/Mode/Mode.h> |
12 | | |
13 | | #ifndef KERNEL |
14 | | # include <AK/ByteString.h> |
15 | | #endif |
16 | | |
17 | | namespace Crypto::Cipher { |
18 | | |
19 | | /* |
20 | | * Heads up: CTR is a *family* of modes, because the "counter" function is |
21 | | * implementation-defined. This makes interoperability a pain in the neurons. |
22 | | * Here are several contradicting(!) interpretations: |
23 | | * |
24 | | * "The counter can be *any function* which produces a sequence which is |
25 | | * guaranteed not to repeat for a long time, although an actual increment-by-one |
26 | | * counter is the simplest and most popular." |
27 | | * The illustrations show that first increment should happen *after* the first |
28 | | * round. I call this variant BIGINT_INCR_0. |
29 | | * The AESAVS goes a step further and requires only that "counters" do not |
30 | | * repeat, leaving the method of counting completely open. |
31 | | * See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR) |
32 | | * See: https://csrc.nist.gov/csrc/media/projects/cryptographic-algorithm-validation-program/documents/aes/aesavs.pdf |
33 | | * |
34 | | * BIGINT_INCR_0 is the behavior of the OpenSSL command "openssl enc -aes-128-ctr", |
35 | | * and the behavior of CRYPTO_ctr128_encrypt(). OpenSSL is not alone in the |
36 | | * assumption that BIGINT_INCR_0 is all there is; even some NIST |
37 | | * specification/survey(?) doesn't consider counting any other way. |
38 | | * See: https://github.com/openssl/openssl/blob/33388b44b67145af2181b1e9528c381c8ea0d1b6/crypto/modes/ctr128.c#L71 |
39 | | * See: http://www.cryptogrium.com/aes-ctr.html |
40 | | * See: https://web.archive.org/web/20150226072817/http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ctr/ctr-spec.pdf |
41 | | * |
42 | | * "[T]he successive counter blocks are derived by applying an incrementing |
43 | | * function." |
44 | | * It defines a *family* of functions called "Standard Incrementing Function" |
45 | | * which only increment the lower-m bits, for some number 0<m<=blocksize. |
46 | | * The included test vectors suggest that the first increment should happen |
47 | | * *after* the first round. I call this INT32_INCR_0, or in general INTm_INCR_0. |
48 | | * This in particular is the behavior of CRYPTO_ctr128_encrypt_ctr32() in OpenSSL. |
49 | | * See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf |
50 | | * See: https://github.com/openssl/openssl/blob/33388b44b67145af2181b1e9528c381c8ea0d1b6/crypto/modes/ctr128.c#L147 |
51 | | * |
52 | | * The python package "cryptography" and RFC 3686 (which appears among the |
53 | | * first online search results when searching for "AES CTR 128 test vector") |
54 | | * share a peculiar interpretation of CTR mode: the counter is incremented *before* |
55 | | * the first round. RFC 3686 does not consider any other interpretation. I call |
56 | | * this variant BIGINT_INCR_1. |
57 | | * See: https://tools.ietf.org/html/rfc3686.html#section-6 |
58 | | * See: https://cryptography.io/en/latest/development/test-vectors/#symmetric-ciphers |
59 | | * |
60 | | * And finally, because the method is left open, a different increment could be |
61 | | * used, for example little endian, or host endian, or mixed endian. Or any crazy |
62 | | * LSFR with sufficiently large period. That is the reason for the constant part |
63 | | * "INCR" in the previous counters. |
64 | | * |
65 | | * Due to this plethora of mutually-incompatible counters, |
66 | | * the method of counting should be a template parameter. |
67 | | * This currently implements BIGINT_INCR_0, which means perfect |
68 | | * interoperability with openssl. The test vectors from RFC 3686 just need to be |
69 | | * incremented by 1. |
70 | | * TODO: Implement other counters? |
71 | | */ |
72 | | |
73 | | struct IncrementInplace { |
74 | | void operator()(Bytes& in) const |
75 | 0 | { |
76 | 0 | for (size_t i = in.size(); i > 0;) { |
77 | 0 | --i; |
78 | 0 | if (in[i] == (u8)-1) { |
79 | 0 | in[i] = 0; |
80 | 0 | } else { |
81 | 0 | in[i]++; |
82 | 0 | break; |
83 | 0 | } |
84 | 0 | } |
85 | 0 | } |
86 | | }; |
87 | | |
88 | | template<typename T, typename IncrementFunctionType = IncrementInplace> |
89 | | class CTR : public Mode<T> { |
90 | | public: |
91 | | constexpr static size_t IVSizeInBits = 128; |
92 | | |
93 | | virtual ~CTR() = default; |
94 | | |
95 | | // Must intercept `Intent`, because AES must always be set to |
96 | | // Encryption, even when decrypting AES-CTR. |
97 | | // TODO: How to deal with ciphers that take different arguments? |
98 | | // FIXME: Add back the default intent parameter once clang-11 is the default in GitHub Actions. |
99 | | // Once added back, remove the parameter where it's constructed in get_random_bytes in Kernel/Security/Random.h. |
100 | | template<typename KeyType, typename... Args> |
101 | | explicit constexpr CTR(KeyType const& user_key, size_t key_bits, Intent, Args... args) |
102 | | : Mode<T>(user_key, key_bits, Intent::Encryption, args...) |
103 | | { |
104 | | } |
105 | | |
106 | | #ifndef KERNEL |
107 | | virtual ByteString class_name() const override |
108 | | { |
109 | | StringBuilder builder; |
110 | | builder.append(this->cipher().class_name()); |
111 | | builder.append("_CTR"sv); |
112 | | return builder.to_byte_string(); |
113 | | } |
114 | | #endif |
115 | | |
116 | | virtual size_t IV_length() const override |
117 | | { |
118 | | return IVSizeInBits / 8; |
119 | | } |
120 | | |
121 | | virtual void encrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}, Bytes* ivec_out = nullptr) override |
122 | | { |
123 | | // Our interpretation of "ivec" is what AES-CTR |
124 | | // would define as nonce + IV + 4 zero bytes. |
125 | | this->encrypt_or_stream(&in, out, ivec, ivec_out); |
126 | | } |
127 | | |
128 | | void key_stream(Bytes& out, Bytes const& ivec = {}, Bytes* ivec_out = nullptr) |
129 | | { |
130 | | this->encrypt_or_stream(nullptr, out, ivec, ivec_out); |
131 | | } |
132 | | |
133 | | virtual void decrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}) override |
134 | | { |
135 | | // XOR (and thus CTR) is the most symmetric mode. |
136 | | this->encrypt(in, out, ivec); |
137 | | } |
138 | | |
139 | | private: |
140 | | u8 m_ivec_storage[IVSizeInBits / 8]; |
141 | | typename T::BlockType m_cipher_block {}; |
142 | | |
143 | | protected: |
144 | | constexpr static IncrementFunctionType increment {}; |
145 | | |
146 | | void encrypt_or_stream(ReadonlyBytes const* in, Bytes& out, ReadonlyBytes ivec, Bytes* ivec_out = nullptr) |
147 | | { |
148 | | size_t length; |
149 | | if (in) { |
150 | | VERIFY(in->size() <= out.size()); |
151 | | length = in->size(); |
152 | | if (length == 0) |
153 | | return; |
154 | | } else { |
155 | | length = out.size(); |
156 | | } |
157 | | |
158 | | auto& cipher = this->cipher(); |
159 | | |
160 | | // FIXME: We should have two of these encrypt/decrypt functions that |
161 | | // we SFINAE out based on whether the Cipher mode needs an ivec |
162 | | VERIFY(!ivec.is_empty()); |
163 | | VERIFY(ivec.size() >= IV_length()); |
164 | | |
165 | | m_cipher_block.set_padding_mode(cipher.padding_mode()); |
166 | | |
167 | | __builtin_memcpy(m_ivec_storage, ivec.data(), IV_length()); |
168 | | Bytes iv { m_ivec_storage, IV_length() }; |
169 | | |
170 | | size_t offset { 0 }; |
171 | | auto block_size = cipher.block_size(); |
172 | | |
173 | | while (length > 0) { |
174 | | m_cipher_block.overwrite(iv.slice(0, block_size)); |
175 | | |
176 | | cipher.encrypt_block(m_cipher_block, m_cipher_block); |
177 | | if (in) { |
178 | | m_cipher_block.apply_initialization_vector(in->slice(offset)); |
179 | | } |
180 | | auto write_size = min(block_size, length); |
181 | | |
182 | | VERIFY(offset + write_size <= out.size()); |
183 | | __builtin_memcpy(out.offset(offset), m_cipher_block.bytes().data(), write_size); |
184 | | |
185 | | increment(iv); |
186 | | length -= write_size; |
187 | | offset += write_size; |
188 | | } |
189 | | |
190 | | if (ivec_out) |
191 | | __builtin_memcpy(ivec_out->data(), iv.data(), min(ivec_out->size(), IV_length())); |
192 | | } |
193 | | }; |
194 | | |
195 | | } |