Coverage Report

Created: 2026-02-16 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}