diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-04-27 21:58:04 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-05-02 12:24:10 +0200 |
commit | 899ca245aeb7da5f5a741edf796ef34a4c02ec7f (patch) | |
tree | 2e7501431a7b59cd463c0a0985b817c82b526a68 /Libraries/LibCrypto/Cipher/Mode | |
parent | cf5941c972e8a09ad6c9e2296892742c27fafe23 (diff) | |
download | serenity-899ca245aeb7da5f5a741edf796ef34a4c02ec7f.zip |
LibCrypto: Implement Cipher and AES_CBC
Also adds a test program to userland
Diffstat (limited to 'Libraries/LibCrypto/Cipher/Mode')
-rw-r--r-- | Libraries/LibCrypto/Cipher/Mode/CBC.h | 114 | ||||
-rw-r--r-- | Libraries/LibCrypto/Cipher/Mode/Mode.h | 97 |
2 files changed, 211 insertions, 0 deletions
diff --git a/Libraries/LibCrypto/Cipher/Mode/CBC.h b/Libraries/LibCrypto/Cipher/Mode/CBC.h new file mode 100644 index 0000000000..8aa688b23f --- /dev/null +++ b/Libraries/LibCrypto/Cipher/Mode/CBC.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020, Ali Mohammad Pur <ali.mpfard@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <LibCrypto/Cipher/Mode/Mode.h> + +namespace Crypto { + +template <typename T> +class CBC : public Mode<T> { +public: + template <typename... Args> + explicit constexpr CBC<T>(Args... args) + : Mode<T>(args...) + { + } + + virtual Optional<ByteBuffer> encrypt(const ByteBuffer& in, ByteBuffer& out, Optional<ByteBuffer> ivec = {}) override + { + auto length = in.size(); + if (length == 0) + return {}; + + auto& cipher = this->cipher(); + + // FIXME: We should have two of these encrypt/decrypt functions that + // we SFINAE out based on whether the Cipher mode needs an ivec + ASSERT(ivec.has_value()); + const auto* iv = ivec.value().data(); + + typename T::BlockType block { cipher.padding_mode() }; + size_t offset { 0 }; + auto block_size = cipher.block_size(); + + while (length >= block_size) { + block.overwrite(in.slice_view(offset, block_size)); + block.apply_initialization_vector(iv); + cipher.encrypt_block(block, block); + out.overwrite(offset, block.get().data(), block_size); + iv = out.offset_pointer(offset); + length -= block_size; + offset += block_size; + } + + if (length > 0) { + block.overwrite(in.slice_view(offset, length)); + block.apply_initialization_vector(iv); + cipher.encrypt_block(block, block); + out.overwrite(offset, block.get().data(), block_size); + iv = out.offset_pointer(offset); + } + + return ByteBuffer::copy(iv, block_size); + } + virtual void decrypt(const ByteBuffer& in, ByteBuffer& out, Optional<ByteBuffer> ivec = {}) override + { + auto length = in.size(); + if (length == 0) + return; + + auto& cipher = this->cipher(); + + ASSERT(ivec.has_value()); + const auto* iv = ivec.value().data(); + + auto block_size = cipher.block_size(); + + // if the data is not aligned, it's not correct encrypted data + // FIXME (ponder): Should we simply decrypt as much as we can? + ASSERT(length % block_size == 0); + + typename T::BlockType block { cipher.padding_mode() }; + size_t offset { 0 }; + + while (length > 0) { + auto* slice = in.offset_pointer(offset); + block.overwrite(slice, block_size); + cipher.decrypt_block(block, block); + block.apply_initialization_vector(iv); + auto decrypted = block.get(); + out.overwrite(offset, decrypted.data(), decrypted.size()); + iv = slice; + length -= block_size; + offset += block_size; + } + this->prune_padding(out); + } +}; + +} diff --git a/Libraries/LibCrypto/Cipher/Mode/Mode.h b/Libraries/LibCrypto/Cipher/Mode/Mode.h new file mode 100644 index 0000000000..178ea7d0fa --- /dev/null +++ b/Libraries/LibCrypto/Cipher/Mode/Mode.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020, Ali Mohammad Pur <ali.mpfard@gmail.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/ByteBuffer.h> +#include <LibCrypto/Cipher/Cipher.h> + +namespace Crypto { + +template <typename T> +class Mode { +public: + // FIXME: Somehow communicate that encrypt returns the last initialization vector (if the mode supports it) + virtual Optional<ByteBuffer> encrypt(const ByteBuffer& in, ByteBuffer& out, Optional<ByteBuffer> ivec = {}) = 0; + virtual void decrypt(const ByteBuffer& in, ByteBuffer& out, Optional<ByteBuffer> ivec = {}) = 0; + + const T& cipher() const { return m_cipher; } + + ByteBuffer create_aligned_buffer(size_t input_size) const + { + size_t remainder = (input_size + T::block_size()) % T::block_size(); + if (remainder == 0) + return ByteBuffer::create_uninitialized(input_size); + else + return ByteBuffer::create_uninitialized(input_size + T::block_size() - remainder); + } + +protected: + T& cipher() { return m_cipher; } + + virtual void prune_padding(ByteBuffer& data) + { + auto size = data.size(); + switch (m_cipher.padding_mode()) { + case PaddingMode::CMS: { + auto maybe_padding_length = data[size - 1]; + if (maybe_padding_length >= T::block_size()) { + // cannot be padding (the entire block cannot be padding) + return; + } + for (auto i = maybe_padding_length; i > 0; --i) { + if (data[size - i] != maybe_padding_length) { + // not padding, part of data + return; + } + } + data.trim(size - maybe_padding_length); + break; + } + case PaddingMode::Null: { + while (data[size - 1] == 0) + --size; + data.trim(size); + break; + } + default: + // FIXME: support other padding modes + ASSERT_NOT_REACHED(); + break; + } + } + + // FIXME: Somehow add a reference version of this + template <typename... Args> + Mode(Args... args) + : m_cipher(args...) + { + } + +private: + T m_cipher; +}; +} |