Prechádzať zdrojové kódy

LibCrypto: Add CTR cipher mode

Kernel: Changed fortuna implementation to use CTR mode instead of
manually implementing a counter.
Peter Elliott 5 rokov pred
rodič
commit
2e8cfe5435

+ 13 - 26
Kernel/Random.h

@@ -31,8 +31,8 @@
 #include <AK/ByteBuffer.h>
 #include <AK/Types.h>
 #include <Kernel/StdLib.h>
-#include <LibCrypto/Cipher/Cipher.h>
 #include <LibCrypto/Cipher/AES.h>
+#include <LibCrypto/Cipher/Cipher.h>
 #include <LibCrypto/Hash/SHA2.h>
 
 namespace Kernel {
@@ -48,31 +48,30 @@ public:
     using HashType = HashT;
     using DigestType = HashT::DigestType;
 
+    FortunaPRNG()
+        : m_counter(ByteBuffer::create_zeroed(BlockType::block_size()))
+    {
+    }
+
     void get_random_bytes(u8* buffer, size_t n)
     {
         if (m_p0_len >= reseed_threshold) {
             this->reseed();
         }
 
-        ASSERT(m_counter != 0);
+        ASSERT(m_reseed_number > 0);
 
         // FIXME: More than 2^20 bytes cannot be generated without refreshing the key.
         ASSERT(n < (1 << 20));
 
-        CipherType cipher(m_key, KeySize);
-
-        size_t block_size = CipherType::BlockSizeInBits / 8;
+        typename CipherType::CTRMode cipher(m_key, KeySize);
 
-        for (size_t i = 0; i < n; i += block_size) {
-            this->generate_block(cipher, &buffer[i], min(block_size, n - i));
-        }
+        auto wrapped_buffer = ByteBuffer::wrap(buffer, n);
+        m_counter = cipher.key_stream(wrapped_buffer, m_counter).value();
 
         // Extract a new key from the prng stream.
 
-        for (size_t i = 0; i < KeySize/8; i += block_size) {
-            this->generate_block(cipher, &(m_key[i]), min(block_size, KeySize - i));
-        }
-
+        m_counter = cipher.key_stream(m_key, m_counter).value();
     }
 
     template<typename T>
@@ -86,17 +85,6 @@ public:
     }
 
 private:
-    void generate_block(CipherType cipher, u8* buffer, size_t size)
-    {
-
-        BlockType input((u8*)&m_counter, sizeof(m_counter));
-        BlockType output;
-        cipher.encrypt_block(input, output);
-        m_counter++;
-
-        memcpy(buffer, output.get().data(), size);
-    }
-
     void reseed()
     {
         HashType new_key;
@@ -111,12 +99,11 @@ private:
         m_key = ByteBuffer::copy(digest.immutable_data(),
             digest.data_length());
 
-        m_counter++;
         m_reseed_number++;
         m_p0_len = 0;
     }
 
-    size_t m_counter { 0 };
+    ByteBuffer m_counter;
     size_t m_reseed_number { 0 };
     size_t m_p0_len { 0 };
     ByteBuffer m_key;
@@ -125,12 +112,12 @@ private:
 
 class KernelRng : public FortunaPRNG<Crypto::Cipher::AESCipher, Crypto::Hash::SHA256, 256> {
     AK_MAKE_ETERNAL;
+
 public:
     static KernelRng& the();
 
 private:
     KernelRng();
-
 };
 
 // NOTE: These API's are primarily about expressing intent/needs in the calling code.

+ 2 - 0
Libraries/LibCrypto/Cipher/AES.h

@@ -30,6 +30,7 @@
 #include <AK/Vector.h>
 #include <LibCrypto/Cipher/Cipher.h>
 #include <LibCrypto/Cipher/Mode/CBC.h>
+#include <LibCrypto/Cipher/Mode/CTR.h>
 
 namespace Crypto {
 namespace Cipher {
@@ -110,6 +111,7 @@ private:
 class AESCipher final : public Cipher<AESCipherKey, AESCipherBlock> {
 public:
     using CBCMode = CBC<AESCipher>;
+    using CTRMode = CTR<AESCipher>;
 
     constexpr static size_t BlockSizeInBits = BlockType::BlockSizeInBits;
 

+ 138 - 0
Libraries/LibCrypto/Cipher/Mode/CTR.h

@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2020, Peter Elliott <pelliott@ualberta.ca>
+ * 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/String.h>
+#include <AK/StringBuilder.h>
+#include <AK/StringView.h>
+#include <LibCrypto/Cipher/Mode/Mode.h>
+
+namespace Crypto {
+namespace Cipher {
+
+template<typename T>
+class CTR : public Mode<T> {
+public:
+    constexpr static size_t IVSizeInBits = 128;
+
+    virtual ~CTR() { }
+
+    template<typename... Args>
+    explicit constexpr CTR<T>(Args... args)
+        : Mode<T>(args...)
+    {
+    }
+
+    virtual String class_name() const override
+    {
+        StringBuilder builder;
+        builder.append(this->cipher().class_name());
+        builder.append("_CTR");
+        return builder.build();
+    }
+
+    virtual size_t IV_length() const override { return IVSizeInBits / 8; }
+
+    virtual Optional<ByteBuffer> encrypt(const ByteBuffer& in, ByteBuffer& out, Optional<ByteBuffer> ivec = {}) override
+    {
+        return this->encrypt_or_stream(&in, out, ivec);
+    }
+
+    Optional<ByteBuffer> key_stream(ByteBuffer& out, Optional<ByteBuffer> ivec = {})
+    {
+        return this->encrypt_or_stream(nullptr, out, ivec);
+    }
+
+    virtual void decrypt(const ByteBuffer& in, ByteBuffer& out, Optional<ByteBuffer> ivec = {}) override
+    {
+        (void)in;
+        (void)out;
+        (void)ivec;
+        // FIXME: Implement CTR decryption when it is needed.
+    }
+
+private:
+    static ByteBuffer increment(const ByteBuffer& in)
+    {
+        ByteBuffer new_buffer(in);
+        size_t* num_view = (size_t*)new_buffer.data();
+
+        for (size_t i = 0; i < in.size() / sizeof(size_t); ++i) {
+            if (num_view[i] == (size_t)-1) {
+                num_view[i] = 0;
+            } else {
+                num_view[i]++;
+                break;
+            }
+        }
+        return new_buffer;
+    }
+
+    Optional<ByteBuffer> encrypt_or_stream(const ByteBuffer* in, ByteBuffer& out, Optional<ByteBuffer> ivec)
+    {
+        size_t length;
+        if (in) {
+            ASSERT(in->size() <= out.size());
+            length = in->size();
+            if (length == 0)
+                return {};
+        } else {
+            length = out.size();
+        }
+
+        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());
+        auto iv = ivec.value();
+
+        typename T::BlockType block { cipher.padding_mode() };
+        size_t offset { 0 };
+        auto block_size = cipher.block_size();
+
+        while (length > 0) {
+            block.overwrite(iv.slice_view(0, block_size));
+
+            cipher.encrypt_block(block, block);
+            if (in) {
+                block.apply_initialization_vector(in->data() + offset);
+            }
+            auto write_size = min(block_size, length);
+            out.overwrite(offset, block.get().data(), write_size);
+
+            iv = increment(iv);
+            length -= write_size;
+            offset += write_size;
+        }
+
+        return iv;
+    }
+};
+
+}
+}