summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorstelar7 <dudedbz@gmail.com>2022-04-07 00:16:32 +0200
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2022-04-08 14:02:02 +0430
commitc2379912227bb6b64cf2ef2a252f87e452c9e8d4 (patch)
treea05d9099c6488dfc39d11a1a39c0e6e729f435da /Userland
parente109b967a18711dbf884e6d380598225a3b5901b (diff)
downloadserenity-c2379912227bb6b64cf2ef2a252f87e452c9e8d4.zip
LibCrypto: Add Poly1305
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibCrypto/Authentication/Poly1305.cpp226
-rw-r--r--Userland/Libraries/LibCrypto/Authentication/Poly1305.h34
-rw-r--r--Userland/Libraries/LibCrypto/CMakeLists.txt1
3 files changed, 261 insertions, 0 deletions
diff --git a/Userland/Libraries/LibCrypto/Authentication/Poly1305.cpp b/Userland/Libraries/LibCrypto/Authentication/Poly1305.cpp
new file mode 100644
index 0000000000..02ba66acb5
--- /dev/null
+++ b/Userland/Libraries/LibCrypto/Authentication/Poly1305.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2022, stelar7 <dudedbz@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <AK/ByteReader.h>
+#include <AK/Endian.h>
+#include <LibCrypto/Authentication/Poly1305.h>
+
+namespace Crypto::Authentication {
+
+Poly1305::Poly1305(ReadonlyBytes key)
+{
+ for (size_t i = 0; i < 16; i += 4) {
+ m_state.r[i / 4] = AK::convert_between_host_and_little_endian(ByteReader::load32(key.offset(i)));
+ }
+
+ // r[3], r[7], r[11], and r[15] are required to have their top four bits clear (be smaller than 16)
+ // r[4], r[8], and r[12] are required to have their bottom two bits clear (be divisible by 4)
+ m_state.r[0] &= 0x0FFFFFFF;
+ m_state.r[1] &= 0x0FFFFFFC;
+ m_state.r[2] &= 0x0FFFFFFC;
+ m_state.r[3] &= 0x0FFFFFFC;
+
+ for (size_t i = 16; i < 32; i += 4) {
+ m_state.s[(i - 16) / 4] = AK::convert_between_host_and_little_endian(ByteReader::load32(key.offset(i)));
+ }
+}
+
+void Poly1305::update(ReadonlyBytes message)
+{
+ size_t offset = 0;
+ while (offset < message.size()) {
+ u32 n = min(message.size() - offset, 16 - m_state.block_count);
+ memcpy(m_state.blocks + m_state.block_count, message.offset_pointer(offset), n);
+ m_state.block_count += n;
+ offset += n;
+
+ if (m_state.block_count == 16) {
+ process_block();
+ m_state.block_count = 0;
+ }
+ }
+}
+
+void Poly1305::process_block()
+{
+ u32 a[5];
+ u8 n = m_state.block_count;
+
+ // Add one bit beyond the number of octets. For a 16-byte block,
+ // this is equivalent to adding 2^128 to the number. For the shorter
+ // block, it can be 2^120, 2^112, or any power of two that is evenly
+ // divisible by 8, all the way down to 2^8.
+ m_state.blocks[n++] = 0x01;
+
+ // If the block is not 17 bytes long (the last block), pad it with zeros.
+ // This is meaningless if you are treating the blocks as numbers.
+ while (n < 17) {
+ m_state.blocks[n++] = 0x00;
+ }
+
+ // Read the block as a little-endian number.
+ for (size_t i = 0; i < 16; i += 4) {
+ a[i / 4] = AK::convert_between_host_and_little_endian(ByteReader::load32(m_state.blocks + i));
+ }
+ a[4] = m_state.blocks[16];
+
+ // Add this number to the accumulator.
+ m_state.a[0] += a[0];
+ m_state.a[1] += a[1];
+ m_state.a[2] += a[2];
+ m_state.a[3] += a[3];
+ m_state.a[4] += a[4];
+
+ // Carry
+ m_state.a[1] += m_state.a[0] >> 32;
+ m_state.a[2] += m_state.a[1] >> 32;
+ m_state.a[3] += m_state.a[2] >> 32;
+ m_state.a[4] += m_state.a[3] >> 32;
+
+ // Only consider the least significant bits
+ a[0] = m_state.a[0] & 0xFFFFFFFF;
+ a[1] = m_state.a[1] & 0xFFFFFFFF;
+ a[2] = m_state.a[2] & 0xFFFFFFFF;
+ a[3] = m_state.a[3] & 0xFFFFFFFF;
+ a[4] = m_state.a[4] & 0xFFFFFFFF;
+
+ // Multiply by r
+ m_state.a[0] = (u64)a[0] * m_state.r[0];
+ m_state.a[1] = (u64)a[0] * m_state.r[1] + (u64)a[1] * m_state.r[0];
+ m_state.a[2] = (u64)a[0] * m_state.r[2] + (u64)a[1] * m_state.r[1] + (u64)a[2] * m_state.r[0];
+ m_state.a[3] = (u64)a[0] * m_state.r[3] + (u64)a[1] * m_state.r[2] + (u64)a[2] * m_state.r[1] + (u64)a[3] * m_state.r[0];
+ m_state.a[4] = (u64)a[1] * m_state.r[3] + (u64)a[2] * m_state.r[2] + (u64)a[3] * m_state.r[1] + (u64)a[4] * m_state.r[0];
+ m_state.a[5] = (u64)a[2] * m_state.r[3] + (u64)a[3] * m_state.r[2] + (u64)a[4] * m_state.r[1];
+ m_state.a[6] = (u64)a[3] * m_state.r[3] + (u64)a[4] * m_state.r[2];
+ m_state.a[7] = (u64)a[4] * m_state.r[3];
+
+ // Carry
+ m_state.a[1] += m_state.a[0] >> 32;
+ m_state.a[2] += m_state.a[1] >> 32;
+ m_state.a[3] += m_state.a[2] >> 32;
+ m_state.a[4] += m_state.a[3] >> 32;
+ m_state.a[5] += m_state.a[4] >> 32;
+ m_state.a[6] += m_state.a[5] >> 32;
+ m_state.a[7] += m_state.a[6] >> 32;
+
+ // Save the high part of the accumulator
+ a[0] = m_state.a[4] & 0xFFFFFFFC;
+ a[1] = m_state.a[5] & 0xFFFFFFFF;
+ a[2] = m_state.a[6] & 0xFFFFFFFF;
+ a[3] = m_state.a[7] & 0xFFFFFFFF;
+
+ // Only consider the least significant bits
+ m_state.a[0] &= 0xFFFFFFFF;
+ m_state.a[1] &= 0xFFFFFFFF;
+ m_state.a[2] &= 0xFFFFFFFF;
+ m_state.a[3] &= 0xFFFFFFFF;
+ m_state.a[4] &= 0x00000003;
+
+ // Fast modular reduction (first pass)
+ m_state.a[0] += a[0];
+ m_state.a[0] += (a[0] >> 2) | (a[1] << 30);
+ m_state.a[1] += a[1];
+ m_state.a[1] += (a[1] >> 2) | (a[2] << 30);
+ m_state.a[2] += a[2];
+ m_state.a[2] += (a[2] >> 2) | (a[3] << 30);
+ m_state.a[3] += a[3];
+ m_state.a[3] += (a[3] >> 2);
+
+ // Carry
+ m_state.a[1] += m_state.a[0] >> 32;
+ m_state.a[2] += m_state.a[1] >> 32;
+ m_state.a[3] += m_state.a[2] >> 32;
+ m_state.a[4] += m_state.a[3] >> 32;
+
+ // Save the high part of the accumulator
+ a[0] = m_state.a[4] & 0xFFFFFFFC;
+
+ // Only consider the least significant bits
+ m_state.a[0] &= 0xFFFFFFFF;
+ m_state.a[1] &= 0xFFFFFFFF;
+ m_state.a[2] &= 0xFFFFFFFF;
+ m_state.a[3] &= 0xFFFFFFFF;
+ m_state.a[4] &= 0x00000003;
+
+ // Fast modular reduction (second pass)
+ m_state.a[0] += a[0];
+ m_state.a[0] += a[0] >> 2;
+
+ // Carry
+ m_state.a[1] += m_state.a[0] >> 32;
+ m_state.a[2] += m_state.a[1] >> 32;
+ m_state.a[3] += m_state.a[2] >> 32;
+ m_state.a[4] += m_state.a[3] >> 32;
+
+ // Only consider the least significant bits
+ m_state.a[0] &= 0xFFFFFFFF;
+ m_state.a[1] &= 0xFFFFFFFF;
+ m_state.a[2] &= 0xFFFFFFFF;
+ m_state.a[3] &= 0xFFFFFFFF;
+ m_state.a[4] &= 0x00000003;
+}
+
+ErrorOr<ByteBuffer> Poly1305::digest()
+{
+ if (m_state.block_count != 0)
+ process_block();
+
+ u32 b[4];
+
+ // Save the accumulator
+ b[0] = m_state.a[0] & 0xFFFFFFFF;
+ b[1] = m_state.a[1] & 0xFFFFFFFF;
+ b[2] = m_state.a[2] & 0xFFFFFFFF;
+ b[3] = m_state.a[3] & 0xFFFFFFFF;
+
+ // Compute a + 5
+ m_state.a[0] += 5;
+
+ // Carry
+ m_state.a[1] += m_state.a[0] >> 32;
+ m_state.a[2] += m_state.a[1] >> 32;
+ m_state.a[3] += m_state.a[2] >> 32;
+ m_state.a[4] += m_state.a[3] >> 32;
+
+ // Select mask based on (a + 5) >= 2^130
+ u32 mask = ((m_state.a[4] & 0x04) >> 2) - 1;
+
+ // Select based on mask
+ m_state.a[0] = (m_state.a[0] & ~mask) | (b[0] & mask);
+ m_state.a[1] = (m_state.a[1] & ~mask) | (b[1] & mask);
+ m_state.a[2] = (m_state.a[2] & ~mask) | (b[2] & mask);
+ m_state.a[3] = (m_state.a[3] & ~mask) | (b[3] & mask);
+
+ // Finally, the value of the secret key "s" is added to the accumulator,
+ // and the 128 least significant bits are serialized in little-endian
+ // order to form the tag.
+ m_state.a[0] += m_state.s[0];
+ m_state.a[1] += m_state.s[1];
+ m_state.a[2] += m_state.s[2];
+ m_state.a[3] += m_state.s[3];
+
+ // Carry
+ m_state.a[1] += m_state.a[0] >> 32;
+ m_state.a[2] += m_state.a[1] >> 32;
+ m_state.a[3] += m_state.a[2] >> 32;
+ m_state.a[4] += m_state.a[3] >> 32;
+
+ // Only consider the least significant bits
+ b[0] = m_state.a[0] & 0xFFFFFFFF;
+ b[1] = m_state.a[1] & 0xFFFFFFFF;
+ b[2] = m_state.a[2] & 0xFFFFFFFF;
+ b[3] = m_state.a[3] & 0xFFFFFFFF;
+
+ ByteBuffer output = TRY(ByteBuffer::create_uninitialized(16));
+
+ for (auto i = 0; i < 4; i++) {
+ ByteReader::store(output.offset_pointer(i * 4), AK::convert_between_host_and_little_endian(b[i]));
+ }
+
+ return output;
+}
+
+}
diff --git a/Userland/Libraries/LibCrypto/Authentication/Poly1305.h b/Userland/Libraries/LibCrypto/Authentication/Poly1305.h
new file mode 100644
index 0000000000..b4c921dc18
--- /dev/null
+++ b/Userland/Libraries/LibCrypto/Authentication/Poly1305.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022, stelar7 <dudedbz@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/ByteBuffer.h>
+
+namespace Crypto::Authentication {
+
+struct State {
+ u32 r[4] {};
+ u32 s[4] {};
+ u64 a[8] {};
+ u8 blocks[17] {};
+ u8 block_count {};
+};
+
+class Poly1305 {
+
+public:
+ explicit Poly1305(ReadonlyBytes key);
+ void update(ReadonlyBytes message);
+ ErrorOr<ByteBuffer> digest();
+
+private:
+ void process_block();
+
+ State m_state;
+};
+
+}
diff --git a/Userland/Libraries/LibCrypto/CMakeLists.txt b/Userland/Libraries/LibCrypto/CMakeLists.txt
index a7d707bf40..a62e63db81 100644
--- a/Userland/Libraries/LibCrypto/CMakeLists.txt
+++ b/Userland/Libraries/LibCrypto/CMakeLists.txt
@@ -5,6 +5,7 @@ set(SOURCES
ASN1/DER.cpp
ASN1/PEM.cpp
Authentication/GHash.cpp
+ Authentication/Poly1305.cpp
BigInt/Algorithms/BitwiseOperations.cpp
BigInt/Algorithms/Division.cpp
BigInt/Algorithms/GCD.cpp