/src/serenity/Userland/Libraries/LibCrypto/Cipher/Mode/CBC.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, Ali Mohammad Pur <mpfard@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 | | template<typename T> |
20 | | class CBC : public Mode<T> { |
21 | | public: |
22 | | constexpr static size_t IVSizeInBits = 128; |
23 | | |
24 | 0 | virtual ~CBC() = default; |
25 | | template<typename... Args> |
26 | | explicit constexpr CBC(Args... args) |
27 | 0 | : Mode<T>(args...) |
28 | 0 | { |
29 | 0 | } Unexecuted instantiation: Crypto::Cipher::CBC<Crypto::Cipher::AESCipher>::CBC<AK::Detail::ByteBuffer<32ul>, int, Crypto::Cipher::Intent, Crypto::Cipher::PaddingMode>(AK::Detail::ByteBuffer<32ul>, int, Crypto::Cipher::Intent, Crypto::Cipher::PaddingMode) Unexecuted instantiation: Crypto::Cipher::CBC<Crypto::Cipher::AESCipher>::CBC<AK::Span<unsigned char const>, int, Crypto::Cipher::Intent, Crypto::Cipher::PaddingMode>(AK::Span<unsigned char const>, int, Crypto::Cipher::Intent, Crypto::Cipher::PaddingMode) Unexecuted instantiation: Crypto::Cipher::CBC<Crypto::Cipher::AESCipher>::CBC<AK::Detail::ByteBuffer<32ul>, unsigned long, Crypto::Cipher::Intent, Crypto::Cipher::PaddingMode>(AK::Detail::ByteBuffer<32ul>, unsigned long, Crypto::Cipher::Intent, Crypto::Cipher::PaddingMode) |
30 | | |
31 | | #ifndef KERNEL |
32 | | virtual ByteString class_name() const override |
33 | 0 | { |
34 | 0 | StringBuilder builder; |
35 | 0 | builder.append(this->cipher().class_name()); |
36 | 0 | builder.append("_CBC"sv); |
37 | 0 | return builder.to_byte_string(); |
38 | 0 | } |
39 | | #endif |
40 | | |
41 | | virtual size_t IV_length() const override |
42 | 0 | { |
43 | 0 | return IVSizeInBits / 8; |
44 | 0 | } |
45 | | |
46 | | virtual void encrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}, Bytes* ivec_out = nullptr) override |
47 | 0 | { |
48 | 0 | auto length = in.size(); |
49 | 0 | if (length == 0) |
50 | 0 | return; |
51 | | |
52 | 0 | auto& cipher = this->cipher(); |
53 | | |
54 | | // FIXME: We should have two of these encrypt/decrypt functions that |
55 | | // we SFINAE out based on whether the Cipher mode needs an ivec |
56 | 0 | VERIFY(!ivec.is_empty()); |
57 | 0 | ReadonlyBytes iv = ivec; |
58 | |
|
59 | 0 | m_cipher_block.set_padding_mode(cipher.padding_mode()); |
60 | 0 | size_t offset { 0 }; |
61 | 0 | auto block_size = cipher.block_size(); |
62 | |
|
63 | 0 | while (length >= block_size) { |
64 | 0 | m_cipher_block.overwrite(in.slice(offset, block_size)); |
65 | 0 | m_cipher_block.apply_initialization_vector(iv); |
66 | 0 | cipher.encrypt_block(m_cipher_block, m_cipher_block); |
67 | 0 | VERIFY(offset + block_size <= out.size()); |
68 | 0 | __builtin_memcpy(out.offset(offset), m_cipher_block.bytes().data(), block_size); |
69 | 0 | iv = out.slice(offset); |
70 | 0 | length -= block_size; |
71 | 0 | offset += block_size; |
72 | 0 | } |
73 | | |
74 | 0 | if (length > 0) { |
75 | 0 | m_cipher_block.overwrite(in.slice(offset, length)); |
76 | 0 | m_cipher_block.apply_initialization_vector(iv); |
77 | 0 | cipher.encrypt_block(m_cipher_block, m_cipher_block); |
78 | 0 | VERIFY(offset + block_size <= out.size()); |
79 | 0 | __builtin_memcpy(out.offset(offset), m_cipher_block.bytes().data(), block_size); |
80 | 0 | iv = out.slice(offset); |
81 | 0 | } |
82 | | |
83 | 0 | if (ivec_out) |
84 | 0 | __builtin_memcpy(ivec_out->data(), iv.data(), min(IV_length(), ivec_out->size())); |
85 | 0 | } |
86 | | |
87 | | virtual void decrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}) override |
88 | 0 | { |
89 | 0 | auto length = in.size(); |
90 | 0 | if (length == 0) |
91 | 0 | return; |
92 | | |
93 | 0 | auto& cipher = this->cipher(); |
94 | |
|
95 | 0 | VERIFY(!ivec.is_empty()); |
96 | 0 | ReadonlyBytes iv = ivec; |
97 | |
|
98 | 0 | auto block_size = cipher.block_size(); |
99 | | |
100 | | // if the data is not aligned, it's not correct encrypted data |
101 | | // FIXME (ponder): Should we simply decrypt as much as we can? |
102 | 0 | VERIFY(length % block_size == 0); |
103 | | |
104 | 0 | m_cipher_block.set_padding_mode(cipher.padding_mode()); |
105 | 0 | size_t offset { 0 }; |
106 | |
|
107 | 0 | while (length > 0) { |
108 | 0 | auto slice = in.slice(offset); |
109 | 0 | m_cipher_block.overwrite(slice.data(), block_size); |
110 | 0 | cipher.decrypt_block(m_cipher_block, m_cipher_block); |
111 | 0 | m_cipher_block.apply_initialization_vector(iv); |
112 | 0 | auto decrypted = m_cipher_block.bytes(); |
113 | 0 | VERIFY(offset + decrypted.size() <= out.size()); |
114 | 0 | __builtin_memcpy(out.offset(offset), decrypted.data(), decrypted.size()); |
115 | 0 | iv = slice; |
116 | 0 | length -= block_size; |
117 | 0 | offset += block_size; |
118 | 0 | } |
119 | 0 | out = out.slice(0, offset); |
120 | 0 | this->prune_padding(out); |
121 | 0 | } |
122 | | |
123 | | private: |
124 | | typename T::BlockType m_cipher_block {}; |
125 | | }; |
126 | | |
127 | | } |