1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 14:37:45 +00:00

Libraries: Move to Userland/Libraries/

This commit is contained in:
Andreas Kling 2021-01-12 12:17:30 +01:00
parent dc28c07fa5
commit 13d7c09125
1857 changed files with 266 additions and 274 deletions

View file

@ -0,0 +1,112 @@
/*
* 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/Types.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace Crypto {
namespace ASN1 {
enum class Kind {
Eol,
Boolean,
Integer,
ShortInteger,
BitString,
OctetString,
Null,
ObjectIdentifier,
IA5String,
PrintableString,
Utf8String,
UTCTime,
Choice,
Sequence,
Set,
SetOf
};
static inline StringView kind_name(Kind kind)
{
switch (kind) {
case Kind::Eol:
return "EndOfList";
case Kind::Boolean:
return "Boolean";
case Kind::Integer:
return "Integer";
case Kind::ShortInteger:
return "ShortInteger";
case Kind::BitString:
return "BitString";
case Kind::OctetString:
return "OctetString";
case Kind::Null:
return "Null";
case Kind::ObjectIdentifier:
return "ObjectIdentifier";
case Kind::IA5String:
return "IA5String";
case Kind::PrintableString:
return "PrintableString";
case Kind::Utf8String:
return "UTF8String";
case Kind::UTCTime:
return "UTCTime";
case Kind::Choice:
return "Choice";
case Kind::Sequence:
return "Sequence";
case Kind::Set:
return "Set";
case Kind::SetOf:
return "SetOf";
}
return "InvalidKind";
}
struct List {
Kind kind;
void* data;
size_t size;
bool used;
List *prev, *next, *child, *parent;
};
static constexpr void set(List& list, Kind type, void* data, size_t size)
{
list.kind = type;
list.data = data;
list.size = size;
list.used = false;
}
}
}

View file

@ -0,0 +1,474 @@
/*
* 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/Types.h>
#include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace Crypto {
static bool der_decode_integer(const u8* in, size_t length, UnsignedBigInteger& number)
{
if (length < 3) {
dbgln("invalid header size");
return false;
}
size_t x { 0 };
// must start with 0x02
if ((in[x++] & 0x1f) != 0x02) {
dbgln("not an integer {} ({} follows)", in[x - 1], in[x]);
return false;
}
// decode length
size_t z = in[x++];
if ((x & 0x80) == 0) {
// overflow
if (x + z > length) {
dbgln("would overflow {} > {}", z + x, length);
return false;
}
number = UnsignedBigInteger::import_data(in + x, z);
return true;
} else {
// actual big integer
z &= 0x7f;
// overflow
if ((x + z) > length || z > 4 || z == 0) {
dbgln("would overflow {} > {}", z + x, length);
return false;
}
size_t y = 0;
while (z--) {
y = ((size_t)(in[x++])) | (y << 8);
}
// overflow
if (x + y > length) {
dbgln("would overflow {} > {}", y + x, length);
return false;
}
number = UnsignedBigInteger::import_data(in + x, y);
return true;
}
// see if it's negative
if (in[x] & 0x80) {
dbgln("negative bigint unsupported in der_decode_integer");
return false;
}
return true;
}
static bool der_length_integer(UnsignedBigInteger* num, size_t* out_length)
{
auto& bigint = *num;
size_t value_length = bigint.trimmed_length() * sizeof(u32);
auto length = value_length;
if (length == 0) {
++length;
} else {
// the number comes with a 0 padding to make it positive in 2's comp
// add that zero if the msb is 1, but only if we haven't padded it
// ourselves
auto ms2b = (u16)(bigint.words()[bigint.trimmed_length() - 1] >> 16);
if ((ms2b & 0xff00) == 0) {
if (!(((u8)ms2b) & 0x80))
--length;
} else if (ms2b & 0x8000) {
++length;
}
}
if (value_length < 128) {
++length;
} else {
++length;
while (value_length) {
++length;
value_length >>= 8;
}
}
// kind
++length;
*out_length = length;
return true;
}
constexpr static bool der_decode_object_identifier(const u8* in, size_t in_length, u8* words, u8* out_length)
{
if (in_length < 3)
return false; // invalid header
if (*out_length < 2)
return false; // need at least two words
size_t x { 0 };
if ((in[x++] & 0x1f) != 0x06) {
return false; // invalid header value
}
size_t length { 0 };
if (in[x] < 128) {
length = in[x++];
} else {
if ((in[x] < 0x81) | (in[x] > 0x82))
return false; // invalid header
size_t y = in[x++] & 0x7f;
while (y--)
length = (length << 8) | (size_t)in[x++];
}
if (length < 1 || length + x > in_length)
return false; // invalid length or overflow
size_t y { 0 }, t { 0 };
while (length--) {
t = (t << 7) | (in[x] & 0x7f);
if (!(in[x++] & 0x80)) {
if (y >= *out_length)
return false; // overflow
if (y == 0) {
words[0] = t / 40;
words[1] = t % 40;
y = 2;
} else {
words[y++] = t;
}
t = 0;
}
}
*out_length = y;
return true;
}
static constexpr size_t der_object_identifier_bits(size_t x)
{
x &= 0xffffffff;
size_t c { 0 };
while (x) {
++c;
x >>= 1;
}
return c;
}
constexpr static bool der_length_object_identifier(u8* words, size_t num_words, size_t* out_length)
{
if (num_words < 2)
return false;
if (words[0] > 3 || (words[0] < 2 && words[1] > 39))
return false;
size_t z { 0 };
size_t wordbuf = words[0] * 40 + words[1];
for (size_t y = 0; y < num_words; ++y) {
auto t = der_object_identifier_bits(wordbuf);
z = t / 7 + (!!(t % 7)) + (!!(wordbuf == 0));
if (y < num_words - 1)
wordbuf = words[y + 1];
}
if (z < 128) {
z += 2;
} else if (z < 256) {
z += 3;
} else {
z += 4;
}
*out_length = z;
return true;
}
constexpr static bool der_length_sequence(ASN1::List* list, size_t in_length, size_t* out_length)
{
size_t y { 0 }, x { 0 };
for (size_t i = 0; i < in_length; ++i) {
auto type = list[i].kind;
auto size = list[i].size;
auto data = list[i].data;
if (type == ASN1::Kind::Eol)
break;
switch (type) {
case ASN1::Kind::Integer:
if (!der_length_integer((UnsignedBigInteger*)data, &x)) {
return false;
}
y += x;
break;
case ASN1::Kind::ObjectIdentifier:
if (!der_length_object_identifier((u8*)data, size, &x)) {
return false;
}
y += x;
break;
case ASN1::Kind::Sequence:
if (!der_length_sequence((ASN1::List*)data, size, &x)) {
return false;
}
y += x;
break;
default:
dbgln("Unhandled Kind {}", ASN1::kind_name(type));
ASSERT_NOT_REACHED();
break;
}
}
if (y < 128) {
y += 2;
} else if (y < 256) {
y += 3;
} else if (y < 65536) {
y += 4;
} else if (y < 16777216ul) {
y += 5;
} else {
dbgln("invalid length {}", y);
return false;
}
*out_length = y;
return true;
}
static inline bool der_decode_sequence(const u8* in, size_t in_length, ASN1::List* list, size_t out_length, bool ordered = true)
{
if (in_length < 2) {
dbgln("header too small");
return false; // invalid header
}
size_t x { 0 };
if (in[x++] != 0x30) {
dbgln("not a sequence: {}", in[x - 1]);
return false; // not a sequence
}
size_t block_size { 0 };
size_t y { 0 };
if (in[x] < 128) {
block_size = in[x++];
} else if (in[x] & 0x80) {
if ((in[x] < 0x81) || (in[x] > 0x83)) {
dbgln("invalid length element {}", in[x]);
return false;
}
y = in[x++] & 0x7f;
if (x + y > in_length) {
dbgln("would overflow {} > {}", x + y, in_length);
return false; // overflow
}
block_size = 0;
while (y--)
block_size = (block_size << 8) | (size_t)in[x++];
}
// overflow
if (x + block_size > in_length) {
dbgln("would overflow {} > {}", x + block_size, in_length);
return false;
}
for (size_t i = 0; i < out_length; ++i)
list[i].used = false;
in_length = block_size;
for (size_t i = 0; i < out_length; ++i) {
size_t z = 0;
auto kind = list[i].kind;
auto size = list[i].size;
auto data = list[i].data;
if (!ordered && list[i].used) {
continue;
}
switch (kind) {
case ASN1::Kind::Integer:
z = in_length;
if (!der_decode_integer(in + x, z, *(UnsignedBigInteger*)data)) {
dbgln("could not decode an integer");
return false;
}
if (!der_length_integer((UnsignedBigInteger*)data, &z)) {
dbgln("could not figure out the length");
return false;
}
break;
case ASN1::Kind::ObjectIdentifier:
z = in_length;
if (!der_decode_object_identifier(in + x, z, (u8*)data, (u8*)&size)) {
if (!ordered)
continue;
return false;
}
list[i].size = size;
if (!der_length_object_identifier((u8*)data, size, &z)) {
return false;
}
break;
case ASN1::Kind::Sequence:
if ((in[x] & 0x3f) != 0x30) {
dbgln("Not a sequence: {}", (in[x] & 0x3f));
return false;
}
z = in_length;
if (!der_decode_sequence(in + x, z, (ASN1::List*)data, size)) {
if (!ordered)
continue;
return false;
}
if (!der_length_sequence((ASN1::List*)data, size, &z)) {
return false;
}
break;
default:
dbgln("Unhandled ASN1 kind {}", ASN1::kind_name(kind));
ASSERT_NOT_REACHED();
break;
}
x += z;
in_length -= z;
list[i].used = true;
if (!ordered)
i = -1;
}
for (size_t i = 0; i < out_length; ++i)
if (!list[i].used) {
dbgln("index {} was not read", i);
return false;
}
return true;
}
template<size_t element_count>
struct der_decode_sequence_many_base {
constexpr void set(size_t index, ASN1::Kind kind, size_t size, void* data)
{
ASN1::set(m_list[index], kind, data, size);
}
constexpr der_decode_sequence_many_base(const u8* in, size_t in_length)
: m_in(in)
, m_in_length(in_length)
{
}
ASN1::List* list() { return m_list; }
const u8* in() { return m_in; }
size_t in_length() { return m_in_length; }
protected:
ASN1::List m_list[element_count];
const u8* m_in;
size_t m_in_length;
};
template<size_t element_count>
struct der_decode_sequence_many : public der_decode_sequence_many_base<element_count> {
template<typename ElementType, typename... Args>
constexpr void construct(size_t index, ASN1::Kind kind, size_t size, ElementType data, Args... args)
{
der_decode_sequence_many_base<element_count>::set(index, kind, size, (void*)data);
construct(index + 1, args...);
}
constexpr void construct(size_t index)
{
ASSERT(index == element_count);
}
template<typename... Args>
constexpr der_decode_sequence_many(const u8* in, size_t in_length, Args... args)
: der_decode_sequence_many_base<element_count>(in, in_length)
{
construct(0, args...);
}
constexpr operator bool()
{
return der_decode_sequence(this->m_in, this->m_in_length, this->m_list, element_count);
}
};
// FIXME: Move these terrible constructs into their own place
constexpr static void decode_b64_block(const u8 in[4], u8 out[3])
{
out[0] = (u8)(in[0] << 2 | in[1] >> 4);
out[1] = (u8)(in[1] << 4 | in[2] >> 2);
out[2] = (u8)(((in[2] << 6) & 0xc0) | in[3]);
}
constexpr static char base64_chars[] { "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq" };
constexpr static size_t decode_b64(const u8* in_buffer, size_t in_length, ByteBuffer& out_buffer)
{
u8 in[4] { 0 }, out[3] { 0 }, v { 0 };
size_t i { 0 }, length { 0 };
size_t output_offset { 0 };
const u8* ptr = in_buffer;
while (ptr <= in_buffer + in_length) {
for (length = 0, i = 0; i < 4 && (ptr <= in_buffer + in_length); ++i) {
v = 0;
while ((ptr <= in_buffer + in_length) && !v) {
v = ptr[0];
++ptr;
v = (u8)((v < 43 || v > 122) ? 0 : base64_chars[v - 43]);
if (v)
v = (u8)(v == '$' ? 0 : v - 61);
}
if (ptr <= in_buffer + in_length) {
++length;
if (v)
in[i] = v - 1;
} else {
in[i] = 0;
}
}
if (length) {
decode_b64_block(in, out);
out_buffer.overwrite(output_offset, out, length - 1);
output_offset += length - 1;
}
}
return output_offset;
}
}

View file

@ -0,0 +1,75 @@
/*
* 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/Span.h>
#include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/ASN1/DER.h>
namespace Crypto {
static ByteBuffer decode_pem(ReadonlyBytes data_in, size_t cert_index = 0)
{
size_t i { 0 };
size_t start_at { 0 };
size_t idx { 0 };
size_t input_length = data_in.size();
auto alloc_len = input_length / 4 * 3;
auto output = ByteBuffer::create_uninitialized(alloc_len);
for (i = 0; i < input_length; i++) {
if ((data_in[i] == '\n') || (data_in[i] == '\r'))
continue;
if (data_in[i] != '-') {
// Read entire line.
while ((i < input_length) && (data_in[i] != '\n'))
i++;
continue;
}
if (data_in[i] == '-') {
auto end_idx = i;
// Read until end of line.
while ((i < input_length) && (data_in[i] != '\n'))
i++;
if (start_at) {
if (cert_index > 0) {
cert_index--;
start_at = 0;
} else {
idx = decode_b64(data_in.offset(start_at), end_idx - start_at, output);
break;
}
} else
start_at = i + 1;
}
}
return output.slice(0, idx);
}
}

View file

@ -0,0 +1,148 @@
/*
* 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.
*/
#include <AK/MemoryStream.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <LibCrypto/Authentication/GHash.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace {
static u32 to_u32(const u8* b)
{
return AK::convert_between_host_and_big_endian(*(const u32*)b);
}
static void to_u8s(u8* b, const u32* w)
{
for (auto i = 0; i < 4; ++i) {
auto& e = *((u32*)(b + i * 4));
e = AK::convert_between_host_and_big_endian(w[i]);
}
}
}
namespace Crypto {
namespace Authentication {
GHash::TagType GHash::process(ReadonlyBytes aad, ReadonlyBytes cipher)
{
u32 tag[4] { 0, 0, 0, 0 };
auto transform_one = [&](auto& buf) {
size_t i = 0;
for (; i < buf.size(); i += 16) {
if (i + 16 <= buf.size()) {
for (auto j = 0; j < 4; ++j) {
tag[j] ^= to_u32(buf.offset(i + j * 4));
}
galois_multiply(tag, m_key, tag);
}
}
if (i > buf.size()) {
static u8 buffer[16];
Bytes buffer_bytes { buffer, 16 };
OutputMemoryStream stream { buffer_bytes };
stream.write(buf.slice(i - 16));
stream.fill_to_end(0);
for (auto j = 0; j < 4; ++j) {
tag[j] ^= to_u32(buffer_bytes.offset(j * 4));
}
galois_multiply(tag, m_key, tag);
}
};
transform_one(aad);
transform_one(cipher);
auto aad_bits = 8 * (u64)aad.size();
auto cipher_bits = 8 * (u64)cipher.size();
auto high = [](u64 value) -> u32 { return value >> 32; };
auto low = [](u64 value) -> u32 { return value & 0xffffffff; };
#ifdef GHASH_PROCESS_DEBUG
dbg() << "AAD bits: " << high(aad_bits) << " : " << low(aad_bits);
dbg() << "Cipher bits: " << high(cipher_bits) << " : " << low(cipher_bits);
dbg() << "Tag bits: " << tag[0] << " : " << tag[1] << " : " << tag[2] << " : " << tag[3];
#endif
tag[0] ^= high(aad_bits);
tag[1] ^= low(aad_bits);
tag[2] ^= high(cipher_bits);
tag[3] ^= low(cipher_bits);
#ifdef GHASH_PROCESS_DEBUG
dbg() << "Tag bits: " << tag[0] << " : " << tag[1] << " : " << tag[2] << " : " << tag[3];
#endif
galois_multiply(tag, m_key, tag);
TagType digest;
to_u8s(digest.data, tag);
return digest;
}
/// Galois Field multiplication using <x^127 + x^7 + x^2 + x + 1>.
/// Note that x, y, and z are strictly BE.
void galois_multiply(u32 (&z)[4], const u32 (&_x)[4], const u32 (&_y)[4])
{
u32 x[4] { _x[0], _x[1], _x[2], _x[3] };
u32 y[4] { _y[0], _y[1], _y[2], _y[3] };
__builtin_memset(z, 0, sizeof(z));
for (ssize_t i = 127; i > -1; --i) {
if ((y[3 - (i / 32)] >> (i % 32)) & 1) {
z[0] ^= x[0];
z[1] ^= x[1];
z[2] ^= x[2];
z[3] ^= x[3];
}
auto a0 = x[0] & 1;
x[0] >>= 1;
auto a1 = x[1] & 1;
x[1] >>= 1;
x[1] |= a0 << 31;
auto a2 = x[2] & 1;
x[2] >>= 1;
x[2] |= a1 << 31;
auto a3 = x[3] & 1;
x[3] >>= 1;
x[3] |= a2 << 31;
if (a3)
x[0] ^= 0xe1000000;
}
}
}
}

View file

@ -0,0 +1,76 @@
/*
* 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/String.h>
#include <AK/Types.h>
#include <LibCrypto/Hash/HashFunction.h>
namespace Crypto {
namespace Authentication {
void galois_multiply(u32 (&z)[4], const u32 (&x)[4], const u32 (&y)[4]);
struct GHashDigest {
constexpr static size_t Size = 16;
u8 data[Size];
const u8* immutable_data() const { return data; }
size_t data_length() { return Size; }
};
class GHash final {
public:
using TagType = GHashDigest;
template<size_t N>
explicit GHash(const char (&key)[N])
: GHash({ key, N })
{
}
explicit GHash(const ReadonlyBytes& key)
{
for (size_t i = 0; i < 16; i += 4)
m_key[i / 4] = AK::convert_between_host_and_big_endian(*(const u32*)(key.offset(i)));
}
constexpr static size_t digest_size() { return TagType::Size; }
String class_name() const { return "GHash"; }
TagType process(ReadonlyBytes aad, ReadonlyBytes cipher);
private:
inline void transform(ReadonlyBytes, ReadonlyBytes);
u32 m_key[4];
};
}
}

View file

@ -0,0 +1,138 @@
/*
* 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 <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <AK/Types.h>
#include <AK/Vector.h>
constexpr static auto IPAD = 0x36;
constexpr static auto OPAD = 0x5c;
namespace Crypto {
namespace Authentication {
template<typename HashT>
class HMAC {
public:
using HashType = HashT;
using TagType = typename HashType::DigestType;
size_t digest_size() const { return m_inner_hasher.digest_size(); }
template<typename KeyBufferType, typename... Args>
HMAC(KeyBufferType key, Args... args)
: m_inner_hasher(args...)
, m_outer_hasher(args...)
{
derive_key(key);
reset();
}
TagType process(const u8* message, size_t length)
{
reset();
update(message, length);
return digest();
}
void update(const u8* message, size_t length)
{
m_inner_hasher.update(message, length);
}
TagType process(ReadonlyBytes span) { return process(span.data(), span.size()); }
TagType process(const StringView& string) { return process((const u8*)string.characters_without_null_termination(), string.length()); }
void update(ReadonlyBytes span) { return update(span.data(), span.size()); }
void update(const StringView& string) { return update((const u8*)string.characters_without_null_termination(), string.length()); }
TagType digest()
{
m_outer_hasher.update(m_inner_hasher.digest().immutable_data(), m_inner_hasher.digest_size());
auto result = m_outer_hasher.digest();
reset();
return result;
}
void reset()
{
m_inner_hasher.reset();
m_outer_hasher.reset();
m_inner_hasher.update(m_key_data, m_inner_hasher.block_size());
m_outer_hasher.update(m_key_data + m_inner_hasher.block_size(), m_outer_hasher.block_size());
}
String class_name() const
{
StringBuilder builder;
builder.append("HMAC-");
builder.append(m_inner_hasher.class_name());
return builder.build();
}
private:
void derive_key(const u8* key, size_t length)
{
auto block_size = m_inner_hasher.block_size();
u8 v_key[block_size];
__builtin_memset(v_key, 0, block_size);
auto key_buffer = Bytes { v_key, block_size };
// m_key_data is zero'd, so copying the data in
// the first few bytes leaves the rest zero, which
// is exactly what we want (zero padding)
if (length > block_size) {
m_inner_hasher.update(key, length);
auto digest = m_inner_hasher.digest();
// FIXME: should we check if the hash function creates more data than its block size?
key_buffer.overwrite(0, digest.immutable_data(), m_inner_hasher.digest_size());
} else {
key_buffer.overwrite(0, key, length);
}
// fill out the inner and outer padded keys
auto* i_key = m_key_data;
auto* o_key = m_key_data + block_size;
for (size_t i = 0; i < block_size; ++i) {
auto key_byte = key_buffer[i];
i_key[i] = key_byte ^ IPAD;
o_key[i] = key_byte ^ OPAD;
}
}
void derive_key(ReadonlyBytes key) { derive_key(key.data(), key.size()); }
void derive_key(const StringView& key) { derive_key(key.bytes()); }
HashType m_inner_hasher, m_outer_hasher;
u8 m_key_data[2048];
};
}
}

View file

@ -0,0 +1,272 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* 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.
*/
#include "SignedBigInteger.h"
#include <AK/StringBuilder.h>
namespace Crypto {
SignedBigInteger SignedBigInteger::import_data(const u8* ptr, size_t length)
{
bool sign = *ptr;
auto unsigned_data = UnsignedBigInteger::import_data(ptr + 1, length - 1);
return { move(unsigned_data), sign };
}
size_t SignedBigInteger::export_data(Bytes data, bool remove_leading_zeros) const
{
// FIXME: Support this:
// m <0XX> -> m <XX> (if remove_leading_zeros)
ASSERT(!remove_leading_zeros);
data[0] = m_sign;
auto bytes_view = data.slice(1, data.size() - 1);
return m_unsigned_data.export_data(bytes_view, remove_leading_zeros) + 1;
}
SignedBigInteger SignedBigInteger::from_base10(StringView str)
{
bool sign = false;
if (str.length() > 1) {
auto maybe_sign = str[0];
if (maybe_sign == '-') {
str = str.substring_view(1, str.length() - 1);
sign = true;
}
if (maybe_sign == '+')
str = str.substring_view(1, str.length() - 1);
}
auto unsigned_data = UnsignedBigInteger::from_base10(str);
return { move(unsigned_data), sign };
}
String SignedBigInteger::to_base10() const
{
StringBuilder builder;
if (m_sign)
builder.append('-');
builder.append(m_unsigned_data.to_base10());
return builder.to_string();
}
FLATTEN SignedBigInteger SignedBigInteger::plus(const SignedBigInteger& other) const
{
// If both are of the same sign, just add the unsigned data and return.
if (m_sign == other.m_sign)
return { other.m_unsigned_data.plus(m_unsigned_data), m_sign };
// One value is signed while the other is not.
return m_sign ? other.minus(this->m_unsigned_data) : minus(other.m_unsigned_data);
}
FLATTEN SignedBigInteger SignedBigInteger::minus(const SignedBigInteger& other) const
{
// If the signs are different, convert the op to an addition.
if (m_sign != other.m_sign) {
// -x - y = - (x + y)
// x - -y = (x + y)
SignedBigInteger result { other.m_unsigned_data.plus(this->m_unsigned_data) };
if (m_sign)
result.negate();
return result;
}
if (!m_sign) {
// Both operands are positive.
// x - y = - (y - x)
if (m_unsigned_data < other.m_unsigned_data) {
// The result will be negative.
return { other.m_unsigned_data.minus(m_unsigned_data), true };
}
// The result will be either zero, or positive.
return SignedBigInteger { m_unsigned_data.minus(other.m_unsigned_data) };
}
// Both operands are negative.
// -x - -y = y - x
if (m_unsigned_data < other.m_unsigned_data) {
// The result will be positive.
return SignedBigInteger { other.m_unsigned_data.minus(m_unsigned_data), true };
}
// The result will be either zero, or negative.
// y - x = - (x - y)
return SignedBigInteger { m_unsigned_data.minus(other.m_unsigned_data) };
}
FLATTEN SignedBigInteger SignedBigInteger::plus(const UnsignedBigInteger& other) const
{
if (m_sign) {
if (other < m_unsigned_data)
return { m_unsigned_data.minus(other), true };
return { other.minus(m_unsigned_data), false };
}
return { m_unsigned_data.plus(other), false };
}
FLATTEN SignedBigInteger SignedBigInteger::minus(const UnsignedBigInteger& other) const
{
if (m_sign)
return { m_unsigned_data.plus(m_unsigned_data), true };
if (other < m_unsigned_data)
return { m_unsigned_data.minus(other), false };
return { other.minus(m_unsigned_data), true };
}
FLATTEN SignedBigInteger SignedBigInteger::bitwise_or(const UnsignedBigInteger& other) const
{
return { unsigned_value().bitwise_or(other), m_sign };
}
FLATTEN SignedBigInteger SignedBigInteger::bitwise_and(const UnsignedBigInteger& other) const
{
return { unsigned_value().bitwise_and(other), false };
}
FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(const UnsignedBigInteger& other) const
{
return { unsigned_value().bitwise_xor(other), m_sign };
}
FLATTEN SignedBigInteger SignedBigInteger::bitwise_not() const
{
return { unsigned_value().bitwise_not(), !m_sign };
}
FLATTEN SignedBigInteger SignedBigInteger::bitwise_or(const SignedBigInteger& other) const
{
auto result = bitwise_or(other.unsigned_value());
// The sign bit will have to be OR'd manually.
if (other.is_negative())
result.negate();
return result;
}
FLATTEN SignedBigInteger SignedBigInteger::bitwise_and(const SignedBigInteger& other) const
{
auto result = bitwise_and(other.unsigned_value());
// The sign bit will have to be AND'd manually.
result.m_sign = is_negative() || other.is_negative();
return result;
}
FLATTEN SignedBigInteger SignedBigInteger::bitwise_xor(const SignedBigInteger& other) const
{
auto result = bitwise_xor(other.unsigned_value());
// The sign bit will have to be XOR'd manually.
result.m_sign = is_negative() ^ other.is_negative();
return result;
}
bool SignedBigInteger::operator==(const UnsignedBigInteger& other) const
{
if (m_sign)
return false;
return m_unsigned_data == other;
}
bool SignedBigInteger::operator!=(const UnsignedBigInteger& other) const
{
if (m_sign)
return true;
return m_unsigned_data != other;
}
bool SignedBigInteger::operator<(const UnsignedBigInteger& other) const
{
if (m_sign)
return true;
return m_unsigned_data < other;
}
FLATTEN SignedBigInteger SignedBigInteger::shift_left(size_t num_bits) const
{
return SignedBigInteger { m_unsigned_data.shift_left(num_bits), m_sign };
}
FLATTEN SignedBigInteger SignedBigInteger::multiplied_by(const SignedBigInteger& other) const
{
bool result_sign = m_sign ^ other.m_sign;
return { m_unsigned_data.multiplied_by(other.m_unsigned_data), result_sign };
}
FLATTEN SignedDivisionResult SignedBigInteger::divided_by(const SignedBigInteger& divisor) const
{
// Aa / Bb -> (A^B)q, Ar
bool result_sign = m_sign ^ divisor.m_sign;
auto unsigned_division_result = m_unsigned_data.divided_by(divisor.m_unsigned_data);
return {
{ move(unsigned_division_result.quotient), result_sign },
{ move(unsigned_division_result.remainder), m_sign }
};
}
void SignedBigInteger::set_bit_inplace(size_t bit_index)
{
m_unsigned_data.set_bit_inplace(bit_index);
}
bool SignedBigInteger::operator==(const SignedBigInteger& other) const
{
if (is_invalid() != other.is_invalid())
return false;
if (m_unsigned_data == 0 && other.m_unsigned_data == 0)
return true;
return m_sign == other.m_sign && m_unsigned_data == other.m_unsigned_data;
}
bool SignedBigInteger::operator!=(const SignedBigInteger& other) const
{
return !(*this == other);
}
bool SignedBigInteger::operator<(const SignedBigInteger& other) const
{
if (m_sign ^ other.m_sign)
return m_sign;
if (m_sign)
return other.m_unsigned_data < m_unsigned_data;
return m_unsigned_data < other.m_unsigned_data;
}
}

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* 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/Span.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
namespace Crypto {
struct SignedDivisionResult;
class SignedBigInteger {
public:
SignedBigInteger(i32 x)
: m_sign(x < 0)
, m_unsigned_data(abs(x))
{
}
SignedBigInteger(UnsignedBigInteger&& unsigned_data, bool sign)
: m_sign(sign)
, m_unsigned_data(move(unsigned_data))
{
}
explicit SignedBigInteger(UnsignedBigInteger unsigned_data)
: m_sign(false)
, m_unsigned_data(move(unsigned_data))
{
}
SignedBigInteger()
: m_sign(false)
, m_unsigned_data()
{
}
static SignedBigInteger create_invalid()
{
return { UnsignedBigInteger::create_invalid(), false };
}
static SignedBigInteger import_data(const AK::StringView& data) { return import_data((const u8*)data.characters_without_null_termination(), data.length()); }
static SignedBigInteger import_data(const u8* ptr, size_t length);
size_t export_data(Bytes, bool remove_leading_zeros = false) const;
static SignedBigInteger from_base10(StringView str);
String to_base10() const;
const UnsignedBigInteger& unsigned_value() const { return m_unsigned_data; }
const Vector<u32, STARTING_WORD_SIZE> words() const { return m_unsigned_data.words(); }
bool is_negative() const { return m_sign; }
void negate() { m_sign = !m_sign; }
void set_to_0() { m_unsigned_data.set_to_0(); }
void set_to(i32 other)
{
m_unsigned_data.set_to((u32)other);
m_sign = other < 0;
}
void set_to(const SignedBigInteger& other)
{
m_unsigned_data.set_to(other.m_unsigned_data);
m_sign = other.m_sign;
}
void invalidate()
{
m_unsigned_data.invalidate();
}
bool is_invalid() const { return m_unsigned_data.is_invalid(); }
// These get + 1 byte for the sign.
size_t length() const { return m_unsigned_data.length() + 1; }
size_t trimmed_length() const { return m_unsigned_data.trimmed_length() + 1; };
SignedBigInteger plus(const SignedBigInteger& other) const;
SignedBigInteger minus(const SignedBigInteger& other) const;
SignedBigInteger bitwise_or(const SignedBigInteger& other) const;
SignedBigInteger bitwise_and(const SignedBigInteger& other) const;
SignedBigInteger bitwise_xor(const SignedBigInteger& other) const;
SignedBigInteger bitwise_not() const;
SignedBigInteger shift_left(size_t num_bits) const;
SignedBigInteger multiplied_by(const SignedBigInteger& other) const;
SignedDivisionResult divided_by(const SignedBigInteger& divisor) const;
SignedBigInteger plus(const UnsignedBigInteger& other) const;
SignedBigInteger minus(const UnsignedBigInteger& other) const;
SignedBigInteger bitwise_or(const UnsignedBigInteger& other) const;
SignedBigInteger bitwise_and(const UnsignedBigInteger& other) const;
SignedBigInteger bitwise_xor(const UnsignedBigInteger& other) const;
SignedBigInteger multiplied_by(const UnsignedBigInteger& other) const;
SignedDivisionResult divided_by(const UnsignedBigInteger& divisor) const;
void set_bit_inplace(size_t bit_index);
bool operator==(const SignedBigInteger& other) const;
bool operator!=(const SignedBigInteger& other) const;
bool operator<(const SignedBigInteger& other) const;
bool operator==(const UnsignedBigInteger& other) const;
bool operator!=(const UnsignedBigInteger& other) const;
bool operator<(const UnsignedBigInteger& other) const;
private:
bool m_sign { false };
UnsignedBigInteger m_unsigned_data;
};
struct SignedDivisionResult {
Crypto::SignedBigInteger quotient;
Crypto::SignedBigInteger remainder;
};
}
inline const LogStream&
operator<<(const LogStream& stream, const Crypto::SignedBigInteger value)
{
if (value.is_invalid()) {
stream << "Invalid BigInt";
return stream;
}
if (value.is_negative())
stream << "-";
stream << value.unsigned_value();
return stream;
}
inline Crypto::SignedBigInteger
operator""_sbigint(const char* string, size_t length)
{
return Crypto::SignedBigInteger::from_base10({ string, length });
}

View file

@ -0,0 +1,745 @@
/*
* Copyright (c) 2020, Itamar S. <itamar8910@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.
*/
#include "UnsignedBigInteger.h"
#include <AK/StringBuilder.h>
namespace Crypto {
UnsignedBigInteger::UnsignedBigInteger(const u8* ptr, size_t length)
{
m_words.resize_and_keep_capacity((length + sizeof(u32) - 1) / sizeof(u32));
size_t in = length, out = 0;
while (in >= sizeof(u32)) {
in -= sizeof(u32);
u32 word = ((u32)ptr[in] << 24) | ((u32)ptr[in + 1] << 16) | ((u32)ptr[in + 2] << 8) | (u32)ptr[in + 3];
m_words[out++] = word;
}
if (in > 0) {
u32 word = 0;
for (size_t i = 0; i < in; i++) {
word <<= 8;
word |= (u32)ptr[i];
}
m_words[out++] = word;
}
}
UnsignedBigInteger UnsignedBigInteger::create_invalid()
{
UnsignedBigInteger invalid(0);
invalid.invalidate();
return invalid;
}
size_t UnsignedBigInteger::export_data(Bytes data, bool remove_leading_zeros) const
{
size_t word_count = trimmed_length();
size_t out = 0;
if (word_count > 0) {
ssize_t leading_zeros = -1;
if (remove_leading_zeros) {
u32 word = m_words[word_count - 1];
for (size_t i = 0; i < sizeof(u32); i++) {
u8 byte = (u8)(word >> ((sizeof(u32) - i - 1) * 8));
data[out++] = byte;
if (leading_zeros < 0 && byte != 0)
leading_zeros = (int)i;
}
}
for (size_t i = word_count - (remove_leading_zeros ? 1 : 0); i > 0; i--) {
auto word = m_words[i - 1];
data[out++] = (u8)(word >> 24);
data[out++] = (u8)(word >> 16);
data[out++] = (u8)(word >> 8);
data[out++] = (u8)word;
}
if (leading_zeros > 0)
out -= leading_zeros;
}
return out;
}
UnsignedBigInteger UnsignedBigInteger::from_base10(const String& str)
{
UnsignedBigInteger result;
UnsignedBigInteger ten { 10 };
for (auto& c : str) {
result = result.multiplied_by(ten).plus(c - '0');
}
return result;
}
String UnsignedBigInteger::to_base10() const
{
if (*this == UnsignedBigInteger { 0 })
return "0";
StringBuilder builder;
UnsignedBigInteger temp(*this);
UnsignedBigInteger quotient;
UnsignedBigInteger remainder;
while (temp != UnsignedBigInteger { 0 }) {
divide_u16_without_allocation(temp, 10, quotient, remainder);
ASSERT(remainder.words()[0] < 10);
builder.append(static_cast<char>(remainder.words()[0] + '0'));
temp.set_to(quotient);
}
auto reversed_string = builder.to_string();
builder.clear();
for (int i = reversed_string.length() - 1; i >= 0; --i) {
builder.append(reversed_string[i]);
}
return builder.to_string();
}
void UnsignedBigInteger::set_to_0()
{
m_words.clear_with_capacity();
m_is_invalid = false;
m_cached_trimmed_length = {};
}
void UnsignedBigInteger::set_to(u32 other)
{
m_is_invalid = false;
m_words.resize_and_keep_capacity(1);
m_words[0] = other;
m_cached_trimmed_length = {};
}
void UnsignedBigInteger::set_to(const UnsignedBigInteger& other)
{
m_is_invalid = other.m_is_invalid;
m_words.resize_and_keep_capacity(other.m_words.size());
__builtin_memcpy(m_words.data(), other.m_words.data(), other.m_words.size() * sizeof(u32));
m_cached_trimmed_length = {};
}
size_t UnsignedBigInteger::trimmed_length() const
{
if (!m_cached_trimmed_length.has_value()) {
size_t num_leading_zeroes = 0;
for (int i = length() - 1; i >= 0; --i, ++num_leading_zeroes) {
if (m_words[i] != 0)
break;
}
m_cached_trimmed_length = length() - num_leading_zeroes;
}
return m_cached_trimmed_length.value();
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::plus(const UnsignedBigInteger& other) const
{
UnsignedBigInteger result;
add_without_allocation(*this, other, result);
return result;
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::minus(const UnsignedBigInteger& other) const
{
UnsignedBigInteger result;
subtract_without_allocation(*this, other, result);
return result;
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_or(const UnsignedBigInteger& other) const
{
UnsignedBigInteger result;
bitwise_or_without_allocation(*this, other, result);
return result;
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_and(const UnsignedBigInteger& other) const
{
UnsignedBigInteger result;
bitwise_and_without_allocation(*this, other, result);
return result;
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_xor(const UnsignedBigInteger& other) const
{
UnsignedBigInteger result;
bitwise_xor_without_allocation(*this, other, result);
return result;
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_not() const
{
UnsignedBigInteger result;
bitwise_not_without_allocation(*this, result);
return result;
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::shift_left(size_t num_bits) const
{
UnsignedBigInteger output;
UnsignedBigInteger temp_result;
UnsignedBigInteger temp_plus;
shift_left_without_allocation(*this, num_bits, temp_result, temp_plus, output);
return output;
}
FLATTEN UnsignedBigInteger UnsignedBigInteger::multiplied_by(const UnsignedBigInteger& other) const
{
UnsignedBigInteger result;
UnsignedBigInteger temp_shift_result;
UnsignedBigInteger temp_shift_plus;
UnsignedBigInteger temp_shift;
UnsignedBigInteger temp_plus;
multiply_without_allocation(*this, other, temp_shift_result, temp_shift_plus, temp_shift, temp_plus, result);
return result;
}
FLATTEN UnsignedDivisionResult UnsignedBigInteger::divided_by(const UnsignedBigInteger& divisor) const
{
UnsignedBigInteger quotient;
UnsignedBigInteger remainder;
// If we actually have a u16-compatible divisor, short-circuit to the
// less computationally-intensive "divide_u16_without_allocation" method.
if (divisor.trimmed_length() == 1 && divisor.m_words[0] < (1 << 16)) {
divide_u16_without_allocation(*this, divisor.m_words[0], quotient, remainder);
return UnsignedDivisionResult { quotient, remainder };
}
UnsignedBigInteger temp_shift_result;
UnsignedBigInteger temp_shift_plus;
UnsignedBigInteger temp_shift;
UnsignedBigInteger temp_minus;
divide_without_allocation(*this, divisor, temp_shift_result, temp_shift_plus, temp_shift, temp_minus, quotient, remainder);
return UnsignedDivisionResult { quotient, remainder };
}
void UnsignedBigInteger::set_bit_inplace(size_t bit_index)
{
const size_t word_index = bit_index / UnsignedBigInteger::BITS_IN_WORD;
const size_t inner_word_index = bit_index % UnsignedBigInteger::BITS_IN_WORD;
m_words.ensure_capacity(word_index);
for (size_t i = length(); i <= word_index; ++i) {
m_words.unchecked_append(0);
}
m_words[word_index] |= (1 << inner_word_index);
m_cached_trimmed_length = {};
}
bool UnsignedBigInteger::operator==(const UnsignedBigInteger& other) const
{
if (is_invalid() != other.is_invalid())
return false;
auto length = trimmed_length();
if (length != other.trimmed_length())
return false;
return !__builtin_memcmp(m_words.data(), other.words().data(), length);
}
bool UnsignedBigInteger::operator!=(const UnsignedBigInteger& other) const
{
return !(*this == other);
}
bool UnsignedBigInteger::operator<(const UnsignedBigInteger& other) const
{
auto length = trimmed_length();
auto other_length = other.trimmed_length();
if (length < other_length) {
return true;
}
if (length > other_length) {
return false;
}
if (length == 0) {
return false;
}
for (int i = length - 1; i >= 0; --i) {
if (m_words[i] == other.m_words[i])
continue;
return m_words[i] < other.m_words[i];
}
return false;
}
/**
* Complexity: O(N) where N is the number of words in the larger number
*/
void UnsignedBigInteger::add_without_allocation(
const UnsignedBigInteger& left,
const UnsignedBigInteger& right,
UnsignedBigInteger& output)
{
const UnsignedBigInteger* const longer = (left.length() > right.length()) ? &left : &right;
const UnsignedBigInteger* const shorter = (longer == &right) ? &left : &right;
u8 carry = 0;
output.set_to_0();
output.m_words.resize_and_keep_capacity(longer->length());
for (size_t i = 0; i < shorter->length(); ++i) {
u32 word_addition_result = shorter->m_words[i] + longer->m_words[i];
u8 carry_out = 0;
// if there was a carry, the result will be smaller than any of the operands
if (word_addition_result + carry < shorter->m_words[i]) {
carry_out = 1;
}
if (carry) {
word_addition_result++;
}
carry = carry_out;
output.m_words[i] = word_addition_result;
}
for (size_t i = shorter->length(); i < longer->length(); ++i) {
u32 word_addition_result = longer->m_words[i] + carry;
carry = 0;
if (word_addition_result < longer->m_words[i]) {
carry = 1;
}
output.m_words[i] = word_addition_result;
}
if (carry) {
output.m_words.append(carry);
}
}
/**
* Complexity: O(N) where N is the number of words in the larger number
*/
void UnsignedBigInteger::subtract_without_allocation(
const UnsignedBigInteger& left,
const UnsignedBigInteger& right,
UnsignedBigInteger& output)
{
if (left < right) {
output.invalidate();
return;
}
u8 borrow = 0;
auto own_length = left.length();
auto other_length = right.length();
output.set_to_0();
output.m_words.resize_and_keep_capacity(own_length);
for (size_t i = 0; i < own_length; ++i) {
u32 other_word = (i < other_length) ? right.m_words[i] : 0;
i64 temp = static_cast<i64>(left.m_words[i]) - static_cast<i64>(other_word) - static_cast<i64>(borrow);
// If temp < 0, we had an underflow
borrow = (temp >= 0) ? 0 : 1;
if (temp < 0) {
temp += (UINT32_MAX + 1);
}
output.m_words[i] = temp;
}
// This assertion should not fail, because we verified that *this>=other at the beginning of the function
ASSERT(borrow == 0);
}
/**
* Complexity: O(N) where N is the number of words in the shorter value
* Method:
* Apply <op> word-wise until words in the shorter value are used up
* then copy the rest of the words verbatim from the longer value.
*/
FLATTEN void UnsignedBigInteger::bitwise_or_without_allocation(
const UnsignedBigInteger& left,
const UnsignedBigInteger& right,
UnsignedBigInteger& output)
{
// If either of the BigInts are invalid, the output is just the other one.
if (left.is_invalid()) {
output.set_to(right);
return;
}
if (right.is_invalid()) {
output.set_to(left);
return;
}
const UnsignedBigInteger *shorter, *longer;
if (left.length() < right.length()) {
shorter = &left;
longer = &right;
} else {
shorter = &right;
longer = &left;
}
output.m_words.resize_and_keep_capacity(longer->length());
size_t longer_offset = longer->length() - shorter->length();
for (size_t i = 0; i < shorter->length(); ++i)
output.m_words[i] = longer->words()[i] | shorter->words()[i];
__builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset);
}
/**
* Complexity: O(N) where N is the number of words in the shorter value
* Method:
* Apply 'and' word-wise until words in the shorter value are used up
* and zero the rest.
*/
FLATTEN void UnsignedBigInteger::bitwise_and_without_allocation(
const UnsignedBigInteger& left,
const UnsignedBigInteger& right,
UnsignedBigInteger& output)
{
// If either of the BigInts are invalid, the output is just the other one.
if (left.is_invalid()) {
output.set_to(right);
return;
}
if (right.is_invalid()) {
output.set_to(left);
return;
}
const UnsignedBigInteger *shorter, *longer;
if (left.length() < right.length()) {
shorter = &left;
longer = &right;
} else {
shorter = &right;
longer = &left;
}
output.m_words.resize_and_keep_capacity(longer->length());
size_t longer_offset = longer->length() - shorter->length();
for (size_t i = 0; i < shorter->length(); ++i)
output.m_words[i] = longer->words()[i] & shorter->words()[i];
__builtin_memset(output.m_words.data() + shorter->length(), 0, sizeof(u32) * longer_offset);
}
/**
* Complexity: O(N) where N is the number of words in the shorter value
* Method:
* Apply 'xor' word-wise until words in the shorter value are used up
* and copy the rest.
*/
FLATTEN void UnsignedBigInteger::bitwise_xor_without_allocation(
const UnsignedBigInteger& left,
const UnsignedBigInteger& right,
UnsignedBigInteger& output)
{
// If either of the BigInts are invalid, the output is just the other one.
if (left.is_invalid()) {
output.set_to(right);
return;
}
if (right.is_invalid()) {
output.set_to(left);
return;
}
const UnsignedBigInteger *shorter, *longer;
if (left.length() < right.length()) {
shorter = &left;
longer = &right;
} else {
shorter = &right;
longer = &left;
}
output.m_words.resize_and_keep_capacity(longer->length());
size_t longer_offset = longer->length() - shorter->length();
for (size_t i = 0; i < shorter->length(); ++i)
output.m_words[i] = longer->words()[i] ^ shorter->words()[i];
__builtin_memcpy(output.m_words.data() + shorter->length(), longer->words().data() + shorter->length(), sizeof(u32) * longer_offset);
}
/**
* Complexity: O(N) where N is the number of words
*/
FLATTEN void UnsignedBigInteger::bitwise_not_without_allocation(
const UnsignedBigInteger& right,
UnsignedBigInteger& output)
{
// If the value is invalid, the output value is invalid as well.
if (right.is_invalid()) {
output.invalidate();
return;
}
if (right.length() == 0) {
output.set_to_0();
return;
}
output.m_words.resize_and_keep_capacity(right.length());
if (right.length() > 1) {
for (size_t i = 0; i < right.length() - 1; ++i)
output.m_words[i] = ~right.words()[i];
}
auto last_word_index = right.length() - 1;
auto last_word = right.words()[last_word_index];
output.m_words[last_word_index] = ((u32)0xffffffffffffffff >> __builtin_clz(last_word)) & ~last_word;
}
/**
* Complexity : O(N + num_bits % 8) where N is the number of words in the number
* Shift method :
* Start by shifting by whole words in num_bits (by putting missing words at the start),
* then shift the number's words two by two by the remaining amount of bits.
*/
FLATTEN void UnsignedBigInteger::shift_left_without_allocation(
const UnsignedBigInteger& number,
size_t num_bits,
UnsignedBigInteger& temp_result,
UnsignedBigInteger& temp_plus,
UnsignedBigInteger& output)
{
// We can only do shift operations on individual words
// where the shift amount is <= size of word (32).
// But we do know how to shift by a multiple of word size (e.g 64=32*2)
// So we first shift the result by how many whole words fit in 'num_bits'
shift_left_by_n_words(number, num_bits / UnsignedBigInteger::BITS_IN_WORD, temp_result);
output.set_to(temp_result);
// And now we shift by the leftover amount of bits
num_bits %= UnsignedBigInteger::BITS_IN_WORD;
if (num_bits == 0) {
return;
}
for (size_t i = 0; i < temp_result.length(); ++i) {
u32 current_word_of_temp_result = shift_left_get_one_word(temp_result, num_bits, i);
output.m_words[i] = current_word_of_temp_result;
}
// Shifting the last word can produce a carry
u32 carry_word = shift_left_get_one_word(temp_result, num_bits, temp_result.length());
if (carry_word != 0) {
// output += (carry_word << temp_result.length())
// FIXME : Using temp_plus this way to transform carry_word into a bigint is not
// efficient nor pretty. Maybe we should have an "add_with_shift" method ?
temp_plus.set_to_0();
temp_plus.m_words.append(carry_word);
shift_left_by_n_words(temp_plus, temp_result.length(), temp_result);
add_without_allocation(output, temp_result, temp_plus);
output.set_to(temp_plus);
}
}
/**
* Complexity: O(N^2) where N is the number of words in the larger number
* Multiplication method:
* An integer is equal to the sum of the powers of two
* according to the indexes of its 'on' bits.
* So to multiple x*y, we go over each '1' bit in x (say the i'th bit),
* and add y<<i to the result.
*/
FLATTEN void UnsignedBigInteger::multiply_without_allocation(
const UnsignedBigInteger& left,
const UnsignedBigInteger& right,
UnsignedBigInteger& temp_shift_result,
UnsignedBigInteger& temp_shift_plus,
UnsignedBigInteger& temp_shift,
UnsignedBigInteger& temp_plus,
UnsignedBigInteger& output)
{
output.set_to_0();
// iterate all bits
for (size_t word_index = 0; word_index < left.length(); ++word_index) {
for (size_t bit_index = 0; bit_index < UnsignedBigInteger::BITS_IN_WORD; ++bit_index) {
// If the bit is off - skip over it
if (!(left.m_words[word_index] & (1 << bit_index)))
continue;
const size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index;
// output += (right << shift_amount);
shift_left_without_allocation(right, shift_amount, temp_shift_result, temp_shift_plus, temp_shift);
add_without_allocation(output, temp_shift, temp_plus);
output.set_to(temp_plus);
}
}
}
/**
* Complexity: O(N^2) where N is the number of words in the larger number
* Division method:
* We loop over the bits of the divisor, attempting to subtract divisor<<i from the dividend.
* If the result is non-negative, it means that divisor*2^i "fits" in the dividend,
* so we set the ith bit in the quotient and reduce divisor<<i from the dividend.
* When we're done, what's left from the dividend is the remainder.
*/
FLATTEN void UnsignedBigInteger::divide_without_allocation(
const UnsignedBigInteger& numerator,
const UnsignedBigInteger& denominator,
UnsignedBigInteger& temp_shift_result,
UnsignedBigInteger& temp_shift_plus,
UnsignedBigInteger& temp_shift,
UnsignedBigInteger& temp_minus,
UnsignedBigInteger& quotient,
UnsignedBigInteger& remainder)
{
quotient.set_to_0();
remainder.set_to(numerator);
// iterate all bits
for (int word_index = numerator.trimmed_length() - 1; word_index >= 0; --word_index) {
for (int bit_index = UnsignedBigInteger::BITS_IN_WORD - 1; bit_index >= 0; --bit_index) {
const size_t shift_amount = word_index * UnsignedBigInteger::BITS_IN_WORD + bit_index;
shift_left_without_allocation(denominator, shift_amount, temp_shift_result, temp_shift_plus, temp_shift);
subtract_without_allocation(remainder, temp_shift, temp_minus);
if (!temp_minus.is_invalid()) {
remainder.set_to(temp_minus);
quotient.set_bit_inplace(shift_amount);
}
}
}
}
/**
* Complexity : O(N) where N is the number of digits in the numerator
* Division method :
* Starting from the most significant one, for each half-word of the numerator, combine it
* with the existing remainder if any, divide the combined number as a u32 operation and
* update the quotient / remainder as needed.
*/
FLATTEN void UnsignedBigInteger::divide_u16_without_allocation(
const UnsignedBigInteger& numerator,
u32 denominator,
UnsignedBigInteger& quotient,
UnsignedBigInteger& remainder)
{
ASSERT(denominator < (1 << 16));
u32 remainder_word = 0;
auto numerator_length = numerator.trimmed_length();
quotient.set_to_0();
quotient.m_words.resize(numerator_length);
for (int word_index = numerator_length - 1; word_index >= 0; --word_index) {
auto word_high = numerator.m_words[word_index] >> 16;
auto word_low = numerator.m_words[word_index] & ((1 << 16) - 1);
auto number_to_divide_high = (remainder_word << 16) | word_high;
auto quotient_high = number_to_divide_high / denominator;
remainder_word = number_to_divide_high % denominator;
auto number_to_divide_low = remainder_word << 16 | word_low;
auto quotient_low = number_to_divide_low / denominator;
remainder_word = number_to_divide_low % denominator;
quotient.m_words[word_index] = (quotient_high << 16) | quotient_low;
}
remainder.set_to(remainder_word);
}
ALWAYS_INLINE void UnsignedBigInteger::shift_left_by_n_words(
const UnsignedBigInteger& number,
const size_t number_of_words,
UnsignedBigInteger& output)
{
// shifting left by N words means just inserting N zeroes to the beginning of the words vector
output.set_to_0();
output.m_words.resize_and_keep_capacity(number_of_words + number.length());
__builtin_memset(output.m_words.data(), 0, number_of_words * sizeof(unsigned));
__builtin_memcpy(&output.m_words.data()[number_of_words], number.m_words.data(), number.m_words.size() * sizeof(unsigned));
}
/**
* Returns the word at a requested index in the result of a shift operation
*/
ALWAYS_INLINE u32 UnsignedBigInteger::shift_left_get_one_word(
const UnsignedBigInteger& number,
const size_t num_bits,
const size_t result_word_index)
{
// "<= length()" (rather than length() - 1) is intentional,
// The result inedx of length() is used when calculating the carry word
ASSERT(result_word_index <= number.length());
ASSERT(num_bits <= UnsignedBigInteger::BITS_IN_WORD);
u32 result = 0;
// we need to check for "num_bits != 0" since shifting right by 32 is apparently undefined behaviour!
if (result_word_index > 0 && num_bits != 0) {
result += number.m_words[result_word_index - 1] >> (UnsignedBigInteger::BITS_IN_WORD - num_bits);
}
if (result_word_index < number.length() && num_bits < 32) {
result += number.m_words[result_word_index] << num_bits;
}
return result;
}
}
void AK::Formatter<Crypto::UnsignedBigInteger>::format(FormatBuilder& fmtbuilder, const Crypto::UnsignedBigInteger& value)
{
if (value.is_invalid())
return Formatter<StringView>::format(fmtbuilder, "invalid");
StringBuilder builder;
for (int i = value.length() - 1; i >= 0; --i)
builder.appendff("{}|", value.words()[i]);
return Formatter<StringView>::format(fmtbuilder, builder.string_view());
}

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2020, Itamar S. <itamar8910@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 <AK/LogStream.h>
#include <AK/Span.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <AK/Vector.h>
namespace Crypto {
struct UnsignedDivisionResult;
constexpr size_t STARTING_WORD_SIZE = 512;
class UnsignedBigInteger {
public:
UnsignedBigInteger(u32 x) { m_words.append(x); }
explicit UnsignedBigInteger(AK::Vector<u32, STARTING_WORD_SIZE>&& words)
: m_words(move(words))
{
}
explicit UnsignedBigInteger(const u8* ptr, size_t length);
UnsignedBigInteger() { }
static UnsignedBigInteger create_invalid();
static UnsignedBigInteger import_data(const AK::StringView& data) { return import_data((const u8*)data.characters_without_null_termination(), data.length()); }
static UnsignedBigInteger import_data(const u8* ptr, size_t length)
{
return UnsignedBigInteger(ptr, length);
}
size_t export_data(Bytes, bool remove_leading_zeros = false) const;
static UnsignedBigInteger from_base10(const String& str);
String to_base10() const;
const AK::Vector<u32, STARTING_WORD_SIZE>& words() const { return m_words; }
void set_to_0();
void set_to(u32 other);
void set_to(const UnsignedBigInteger& other);
void invalidate()
{
m_is_invalid = true;
m_cached_trimmed_length = {};
}
bool is_invalid() const { return m_is_invalid; }
size_t length() const { return m_words.size(); }
// The "trimmed length" is the number of words after trimming leading zeroed words
size_t trimmed_length() const;
UnsignedBigInteger plus(const UnsignedBigInteger& other) const;
UnsignedBigInteger minus(const UnsignedBigInteger& other) const;
UnsignedBigInteger bitwise_or(const UnsignedBigInteger& other) const;
UnsignedBigInteger bitwise_and(const UnsignedBigInteger& other) const;
UnsignedBigInteger bitwise_xor(const UnsignedBigInteger& other) const;
UnsignedBigInteger bitwise_not() const;
UnsignedBigInteger shift_left(size_t num_bits) const;
UnsignedBigInteger multiplied_by(const UnsignedBigInteger& other) const;
UnsignedDivisionResult divided_by(const UnsignedBigInteger& divisor) const;
void set_bit_inplace(size_t bit_index);
static void add_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
static void subtract_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
static void bitwise_or_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
static void bitwise_and_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
static void bitwise_xor_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& output);
static void bitwise_not_without_allocation(const UnsignedBigInteger& left, UnsignedBigInteger& output);
static void shift_left_without_allocation(const UnsignedBigInteger& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
static void multiply_without_allocation(const UnsignedBigInteger& left, const UnsignedBigInteger& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
static void divide_without_allocation(const UnsignedBigInteger& numerator, const UnsignedBigInteger& denominator, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_minus, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
static void divide_u16_without_allocation(const UnsignedBigInteger& numerator, u32 denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
bool operator==(const UnsignedBigInteger& other) const;
bool operator!=(const UnsignedBigInteger& other) const;
bool operator<(const UnsignedBigInteger& other) const;
private:
ALWAYS_INLINE static void shift_left_by_n_words(const UnsignedBigInteger& number, size_t number_of_words, UnsignedBigInteger& output);
ALWAYS_INLINE static u32 shift_left_get_one_word(const UnsignedBigInteger& number, size_t num_bits, size_t result_word_index);
static constexpr size_t BITS_IN_WORD = 32;
// Little endian
// m_word[0] + m_word[1] * 256 + m_word[2] * 65536 + ...
AK::Vector<u32, STARTING_WORD_SIZE> m_words;
// Used to indicate a negative result, or a result of an invalid operation
bool m_is_invalid { false };
mutable Optional<size_t> m_cached_trimmed_length;
};
struct UnsignedDivisionResult {
Crypto::UnsignedBigInteger quotient;
Crypto::UnsignedBigInteger remainder;
};
}
inline const LogStream&
operator<<(const LogStream& stream, const Crypto::UnsignedBigInteger& value)
{
if (value.is_invalid()) {
stream << "Invalid BigInt";
return stream;
}
for (int i = value.length() - 1; i >= 0; --i) {
stream << value.words()[i] << "|";
}
return stream;
}
template<>
struct AK::Formatter<Crypto::UnsignedBigInteger> : Formatter<StringView> {
void format(FormatBuilder&, const Crypto::UnsignedBigInteger&);
};
inline Crypto::UnsignedBigInteger
operator""_bigint(const char* string, size_t length)
{
return Crypto::UnsignedBigInteger::from_base10({ string, length });
}

View file

@ -0,0 +1,16 @@
set(SOURCES
Authentication/GHash.cpp
BigInt/SignedBigInteger.cpp
BigInt/UnsignedBigInteger.cpp
Checksum/Adler32.cpp
Checksum/CRC32.cpp
Cipher/AES.cpp
Hash/MD5.cpp
Hash/SHA1.cpp
Hash/SHA2.cpp
NumberTheory/ModularFunctions.cpp
PK/RSA.cpp
)
serenity_lib(LibCrypto crypto)
target_link_libraries(LibCrypto LibC)

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020, the SerenityOS developers
* 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.
*/
#include <AK/Span.h>
#include <AK/Types.h>
#include <LibCrypto/Checksum/Adler32.h>
namespace Crypto::Checksum {
void Adler32::update(ReadonlyBytes data)
{
for (size_t i = 0; i < data.size(); i++) {
m_state_a = (m_state_a + data.at(i)) % 65521;
m_state_b = (m_state_b + m_state_a) % 65521;
}
};
u32 Adler32::digest()
{
return (m_state_b << 16) | m_state_a;
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020, the SerenityOS developers
* 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/Span.h>
#include <AK/Types.h>
#include <LibCrypto/Checksum/ChecksumFunction.h>
namespace Crypto::Checksum {
class Adler32 : public ChecksumFunction<u32> {
public:
Adler32() { }
Adler32(ReadonlyBytes data)
{
update(data);
}
Adler32(u32 initial_a, u32 initial_b, ReadonlyBytes data)
: m_state_a(initial_a)
, m_state_b(initial_b)
{
update(data);
}
void update(ReadonlyBytes data);
u32 digest();
private:
u32 m_state_a { 1 };
u32 m_state_b { 0 };
};
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, the SerenityOS developers
* 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.
*/
#include <AK/Span.h>
#include <AK/Types.h>
#include <LibCrypto/Checksum/CRC32.h>
namespace Crypto::Checksum {
void CRC32::update(ReadonlyBytes data)
{
for (size_t i = 0; i < data.size(); i++) {
m_state = table[(m_state ^ data.at(i)) & 0xFF] ^ (m_state >> 8);
}
};
u32 CRC32::digest()
{
return ~m_state;
}
}

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2020, the SerenityOS developers
* 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/LogStream.h>
#include <AK/Span.h>
#include <AK/Types.h>
#include <LibCrypto/Checksum/ChecksumFunction.h>
namespace Crypto::Checksum {
struct Table {
u32 data[256];
constexpr Table()
: data()
{
for (auto i = 0; i < 256; i++) {
u32 value = i;
for (auto j = 0; j < 8; j++) {
if (value & 1) {
value = 0xEDB88320 ^ (value >> 1);
} else {
value = value >> 1;
}
}
data[i] = value;
}
}
constexpr u32 operator[](int index) const
{
return data[index];
}
};
constexpr static auto table = Table();
class CRC32 : public ChecksumFunction<u32> {
public:
CRC32() { }
CRC32(ReadonlyBytes data)
{
update(data);
}
CRC32(u32 initial_state, ReadonlyBytes data)
: m_state(initial_state)
{
update(data);
}
void update(ReadonlyBytes data);
u32 digest();
private:
u32 m_state { ~0u };
};
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020, the SerenityOS developers
* 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/Span.h>
namespace Crypto::Checksum {
template<typename ChecksumType>
class ChecksumFunction {
public:
virtual void update(ReadonlyBytes data) = 0;
virtual ChecksumType digest() = 0;
};
}

View file

@ -0,0 +1,429 @@
/*
* 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.
*/
#include <AK/StringBuilder.h>
#include <LibCrypto/Cipher/AES.h>
namespace Crypto {
namespace Cipher {
template<typename T>
constexpr u32 get_key(T pt)
{
return ((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]);
}
constexpr void swap_keys(u32* keys, size_t i, size_t j)
{
u32 temp = keys[i];
keys[i] = keys[j];
keys[j] = temp;
}
String AESCipherBlock::to_string() const
{
StringBuilder builder;
for (size_t i = 0; i < BlockSizeInBits / 8; ++i)
builder.appendf("%02x", m_data[i]);
return builder.build();
}
String AESCipherKey::to_string() const
{
StringBuilder builder;
for (size_t i = 0; i < (rounds() + 1) * 4; ++i)
builder.appendf("%02x", m_rd_keys[i]);
return builder.build();
}
void AESCipherKey::expand_encrypt_key(ReadonlyBytes user_key, size_t bits)
{
u32* round_key;
u32 temp;
size_t i { 0 };
ASSERT(!user_key.is_null());
ASSERT(is_valid_key_size(bits));
ASSERT(user_key.size() == bits / 8);
round_key = round_keys();
if (bits == 128) {
m_rounds = 10;
} else if (bits == 192) {
m_rounds = 12;
} else {
m_rounds = 14;
}
round_key[0] = get_key(user_key.data());
round_key[1] = get_key(user_key.data() + 4);
round_key[2] = get_key(user_key.data() + 8);
round_key[3] = get_key(user_key.data() + 12);
if (bits == 128) {
for (;;) {
temp = round_key[3];
// clang-format off
round_key[4] = round_key[0] ^
(AESTables::Encode2[(temp >> 16) & 0xff] & 0xff000000) ^
(AESTables::Encode3[(temp >> 8) & 0xff] & 0x00ff0000) ^
(AESTables::Encode0[(temp ) & 0xff] & 0x0000ff00) ^
(AESTables::Encode1[(temp >> 24) ] & 0x000000ff) ^ AESTables::RCON[i];
// clang-format on
round_key[5] = round_key[1] ^ round_key[4];
round_key[6] = round_key[2] ^ round_key[5];
round_key[7] = round_key[3] ^ round_key[6];
++i;
if (i == 10)
break;
round_key += 4;
}
return;
}
round_key[4] = get_key(user_key.data() + 16);
round_key[5] = get_key(user_key.data() + 20);
if (bits == 192) {
for (;;) {
temp = round_key[5];
// clang-format off
round_key[6] = round_key[0] ^
(AESTables::Encode2[(temp >> 16) & 0xff] & 0xff000000) ^
(AESTables::Encode3[(temp >> 8) & 0xff] & 0x00ff0000) ^
(AESTables::Encode0[(temp ) & 0xff] & 0x0000ff00) ^
(AESTables::Encode1[(temp >> 24) ] & 0x000000ff) ^ AESTables::RCON[i];
// clang-format on
round_key[7] = round_key[1] ^ round_key[6];
round_key[8] = round_key[2] ^ round_key[7];
round_key[9] = round_key[3] ^ round_key[8];
++i;
if (i == 8)
break;
round_key[10] = round_key[4] ^ round_key[9];
round_key[11] = round_key[5] ^ round_key[10];
round_key += 6;
}
return;
}
round_key[6] = get_key(user_key.data() + 24);
round_key[7] = get_key(user_key.data() + 28);
if (true) { // bits == 256
for (;;) {
temp = round_key[7];
// clang-format off
round_key[8] = round_key[0] ^
(AESTables::Encode2[(temp >> 16) & 0xff] & 0xff000000) ^
(AESTables::Encode3[(temp >> 8) & 0xff] & 0x00ff0000) ^
(AESTables::Encode0[(temp ) & 0xff] & 0x0000ff00) ^
(AESTables::Encode1[(temp >> 24) ] & 0x000000ff) ^ AESTables::RCON[i];
// clang-format on
round_key[9] = round_key[1] ^ round_key[8];
round_key[10] = round_key[2] ^ round_key[9];
round_key[11] = round_key[3] ^ round_key[10];
++i;
if (i == 7)
break;
temp = round_key[11];
// clang-format off
round_key[12] = round_key[4] ^
(AESTables::Encode2[(temp >> 24) ] & 0xff000000) ^
(AESTables::Encode3[(temp >> 16) & 0xff] & 0x00ff0000) ^
(AESTables::Encode0[(temp >> 8) & 0xff] & 0x0000ff00) ^
(AESTables::Encode1[(temp ) & 0xff] & 0x000000ff) ;
// clang-format on
round_key[13] = round_key[5] ^ round_key[12];
round_key[14] = round_key[6] ^ round_key[13];
round_key[15] = round_key[7] ^ round_key[14];
round_key += 8;
}
return;
}
}
void AESCipherKey::expand_decrypt_key(ReadonlyBytes user_key, size_t bits)
{
u32* round_key;
expand_encrypt_key(user_key, bits);
round_key = round_keys();
// reorder round keys
for (size_t i = 0, j = 4 * rounds(); i < j; i += 4, j -= 4) {
swap_keys(round_key, i, j);
swap_keys(round_key, i + 1, j + 1);
swap_keys(round_key, i + 2, j + 2);
swap_keys(round_key, i + 3, j + 3);
}
// apply inverse mix-column to middle rounds
for (size_t i = 1; i < rounds(); ++i) {
round_key += 4;
// clang-format off
round_key[0] =
AESTables::Decode0[AESTables::Encode1[(round_key[0] >> 24) ] & 0xff] ^
AESTables::Decode1[AESTables::Encode1[(round_key[0] >> 16) & 0xff] & 0xff] ^
AESTables::Decode2[AESTables::Encode1[(round_key[0] >> 8) & 0xff] & 0xff] ^
AESTables::Decode3[AESTables::Encode1[(round_key[0] ) & 0xff] & 0xff] ;
round_key[1] =
AESTables::Decode0[AESTables::Encode1[(round_key[1] >> 24) ] & 0xff] ^
AESTables::Decode1[AESTables::Encode1[(round_key[1] >> 16) & 0xff] & 0xff] ^
AESTables::Decode2[AESTables::Encode1[(round_key[1] >> 8) & 0xff] & 0xff] ^
AESTables::Decode3[AESTables::Encode1[(round_key[1] ) & 0xff] & 0xff] ;
round_key[2] =
AESTables::Decode0[AESTables::Encode1[(round_key[2] >> 24) ] & 0xff] ^
AESTables::Decode1[AESTables::Encode1[(round_key[2] >> 16) & 0xff] & 0xff] ^
AESTables::Decode2[AESTables::Encode1[(round_key[2] >> 8) & 0xff] & 0xff] ^
AESTables::Decode3[AESTables::Encode1[(round_key[2] ) & 0xff] & 0xff] ;
round_key[3] =
AESTables::Decode0[AESTables::Encode1[(round_key[3] >> 24) ] & 0xff] ^
AESTables::Decode1[AESTables::Encode1[(round_key[3] >> 16) & 0xff] & 0xff] ^
AESTables::Decode2[AESTables::Encode1[(round_key[3] >> 8) & 0xff] & 0xff] ^
AESTables::Decode3[AESTables::Encode1[(round_key[3] ) & 0xff] & 0xff] ;
// clang-format on
}
}
void AESCipher::encrypt_block(const AESCipherBlock& in, AESCipherBlock& out)
{
u32 s0, s1, s2, s3, t0, t1, t2, t3;
size_t r { 0 };
const auto& dec_key = key();
const auto* round_keys = dec_key.round_keys();
s0 = get_key(in.bytes().offset_pointer(0)) ^ round_keys[0];
s1 = get_key(in.bytes().offset_pointer(4)) ^ round_keys[1];
s2 = get_key(in.bytes().offset_pointer(8)) ^ round_keys[2];
s3 = get_key(in.bytes().offset_pointer(12)) ^ round_keys[3];
r = dec_key.rounds() >> 1;
// apply the first |r - 1| rounds
auto i { 0 };
for (;;) {
++i;
// clang-format off
t0 = AESTables::Encode0[(s0 >> 24) ] ^
AESTables::Encode1[(s1 >> 16) & 0xff] ^
AESTables::Encode2[(s2 >> 8) & 0xff] ^
AESTables::Encode3[(s3 ) & 0xff] ^ round_keys[4];
t1 = AESTables::Encode0[(s1 >> 24) ] ^
AESTables::Encode1[(s2 >> 16) & 0xff] ^
AESTables::Encode2[(s3 >> 8) & 0xff] ^
AESTables::Encode3[(s0 ) & 0xff] ^ round_keys[5];
t2 = AESTables::Encode0[(s2 >> 24) ] ^
AESTables::Encode1[(s3 >> 16) & 0xff] ^
AESTables::Encode2[(s0 >> 8) & 0xff] ^
AESTables::Encode3[(s1 ) & 0xff] ^ round_keys[6];
t3 = AESTables::Encode0[(s3 >> 24) ] ^
AESTables::Encode1[(s0 >> 16) & 0xff] ^
AESTables::Encode2[(s1 >> 8) & 0xff] ^
AESTables::Encode3[(s2 ) & 0xff] ^ round_keys[7];
// clang-format on
round_keys += 8;
--r;
++i;
if (r == 0)
break;
// clang-format off
s0 = AESTables::Encode0[(t0 >> 24) ] ^
AESTables::Encode1[(t1 >> 16) & 0xff] ^
AESTables::Encode2[(t2 >> 8) & 0xff] ^
AESTables::Encode3[(t3 ) & 0xff] ^ round_keys[0];
s1 = AESTables::Encode0[(t1 >> 24) ] ^
AESTables::Encode1[(t2 >> 16) & 0xff] ^
AESTables::Encode2[(t3 >> 8) & 0xff] ^
AESTables::Encode3[(t0 ) & 0xff] ^ round_keys[1];
s2 = AESTables::Encode0[(t2 >> 24) ] ^
AESTables::Encode1[(t3 >> 16) & 0xff] ^
AESTables::Encode2[(t0 >> 8) & 0xff] ^
AESTables::Encode3[(t1 ) & 0xff] ^ round_keys[2];
s3 = AESTables::Encode0[(t3 >> 24) ] ^
AESTables::Encode1[(t0 >> 16) & 0xff] ^
AESTables::Encode2[(t1 >> 8) & 0xff] ^
AESTables::Encode3[(t2 ) & 0xff] ^ round_keys[3];
// clang-format on
}
// apply the last round and put the encrypted data into out
// clang-format off
s0 = (AESTables::Encode2[(t0 >> 24) ] & 0xff000000) ^
(AESTables::Encode3[(t1 >> 16) & 0xff] & 0x00ff0000) ^
(AESTables::Encode0[(t2 >> 8) & 0xff] & 0x0000ff00) ^
(AESTables::Encode1[(t3 ) & 0xff] & 0x000000ff) ^ round_keys[0];
out.put(0, s0);
s1 = (AESTables::Encode2[(t1 >> 24) ] & 0xff000000) ^
(AESTables::Encode3[(t2 >> 16) & 0xff] & 0x00ff0000) ^
(AESTables::Encode0[(t3 >> 8) & 0xff] & 0x0000ff00) ^
(AESTables::Encode1[(t0 ) & 0xff] & 0x000000ff) ^ round_keys[1];
out.put(4, s1);
s2 = (AESTables::Encode2[(t2 >> 24) ] & 0xff000000) ^
(AESTables::Encode3[(t3 >> 16) & 0xff] & 0x00ff0000) ^
(AESTables::Encode0[(t0 >> 8) & 0xff] & 0x0000ff00) ^
(AESTables::Encode1[(t1 ) & 0xff] & 0x000000ff) ^ round_keys[2];
out.put(8, s2);
s3 = (AESTables::Encode2[(t3 >> 24) ] & 0xff000000) ^
(AESTables::Encode3[(t0 >> 16) & 0xff] & 0x00ff0000) ^
(AESTables::Encode0[(t1 >> 8) & 0xff] & 0x0000ff00) ^
(AESTables::Encode1[(t2 ) & 0xff] & 0x000000ff) ^ round_keys[3];
out.put(12, s3);
// clang-format on
}
void AESCipher::decrypt_block(const AESCipherBlock& in, AESCipherBlock& out)
{
u32 s0, s1, s2, s3, t0, t1, t2, t3;
size_t r { 0 };
const auto& dec_key = key();
const auto* round_keys = dec_key.round_keys();
s0 = get_key(in.bytes().offset_pointer(0)) ^ round_keys[0];
s1 = get_key(in.bytes().offset_pointer(4)) ^ round_keys[1];
s2 = get_key(in.bytes().offset_pointer(8)) ^ round_keys[2];
s3 = get_key(in.bytes().offset_pointer(12)) ^ round_keys[3];
r = dec_key.rounds() >> 1;
// apply the first |r - 1| rounds
for (;;) {
// clang-format off
t0 = AESTables::Decode0[(s0 >> 24) ] ^
AESTables::Decode1[(s3 >> 16) & 0xff] ^
AESTables::Decode2[(s2 >> 8) & 0xff] ^
AESTables::Decode3[(s1 ) & 0xff] ^ round_keys[4];
t1 = AESTables::Decode0[(s1 >> 24) ] ^
AESTables::Decode1[(s0 >> 16) & 0xff] ^
AESTables::Decode2[(s3 >> 8) & 0xff] ^
AESTables::Decode3[(s2 ) & 0xff] ^ round_keys[5];
t2 = AESTables::Decode0[(s2 >> 24) ] ^
AESTables::Decode1[(s1 >> 16) & 0xff] ^
AESTables::Decode2[(s0 >> 8) & 0xff] ^
AESTables::Decode3[(s3 ) & 0xff] ^ round_keys[6];
t3 = AESTables::Decode0[(s3 >> 24) ] ^
AESTables::Decode1[(s2 >> 16) & 0xff] ^
AESTables::Decode2[(s1 >> 8) & 0xff] ^
AESTables::Decode3[(s0 ) & 0xff] ^ round_keys[7];
// clang-format on
round_keys += 8;
--r;
if (r == 0)
break;
// clang-format off
s0 = AESTables::Decode0[(t0 >> 24) ] ^
AESTables::Decode1[(t3 >> 16) & 0xff] ^
AESTables::Decode2[(t2 >> 8) & 0xff] ^
AESTables::Decode3[(t1 ) & 0xff] ^ round_keys[0];
s1 = AESTables::Decode0[(t1 >> 24) ] ^
AESTables::Decode1[(t0 >> 16) & 0xff] ^
AESTables::Decode2[(t3 >> 8) & 0xff] ^
AESTables::Decode3[(t2 ) & 0xff] ^ round_keys[1];
s2 = AESTables::Decode0[(t2 >> 24) ] ^
AESTables::Decode1[(t1 >> 16) & 0xff] ^
AESTables::Decode2[(t0 >> 8) & 0xff] ^
AESTables::Decode3[(t3 ) & 0xff] ^ round_keys[2];
s3 = AESTables::Decode0[(t3 >> 24) ] ^
AESTables::Decode1[(t2 >> 16) & 0xff] ^
AESTables::Decode2[(t1 >> 8) & 0xff] ^
AESTables::Decode3[(t0 ) & 0xff] ^ round_keys[3];
// clang-format on
}
// apply the last round and put the decrypted data into out
// clang-format off
s0 = ((u32)AESTables::Decode4[(t0 >> 24) ] << 24) ^
((u32)AESTables::Decode4[(t3 >> 16) & 0xff] << 16) ^
((u32)AESTables::Decode4[(t2 >> 8) & 0xff] << 8) ^
((u32)AESTables::Decode4[(t1 ) & 0xff] ) ^ round_keys[0];
out.put(0, s0);
s1 = ((u32)AESTables::Decode4[(t1 >> 24) ] << 24) ^
((u32)AESTables::Decode4[(t0 >> 16) & 0xff] << 16) ^
((u32)AESTables::Decode4[(t3 >> 8) & 0xff] << 8) ^
((u32)AESTables::Decode4[(t2 ) & 0xff] ) ^ round_keys[1];
out.put(4, s1);
s2 = ((u32)AESTables::Decode4[(t2 >> 24) ] << 24) ^
((u32)AESTables::Decode4[(t1 >> 16) & 0xff] << 16) ^
((u32)AESTables::Decode4[(t0 >> 8) & 0xff] << 8) ^
((u32)AESTables::Decode4[(t3 ) & 0xff] ) ^ round_keys[2];
out.put(8, s2);
s3 = ((u32)AESTables::Decode4[(t3 >> 24) ] << 24) ^
((u32)AESTables::Decode4[(t2 >> 16) & 0xff] << 16) ^
((u32)AESTables::Decode4[(t1 >> 8) & 0xff] << 8) ^
((u32)AESTables::Decode4[(t0 ) & 0xff] ) ^ round_keys[3];
out.put(12, s3);
// clang-format on
}
void AESCipherBlock::overwrite(ReadonlyBytes bytes)
{
auto data = bytes.data();
auto length = bytes.size();
ASSERT(length <= this->data_size());
this->bytes().overwrite(0, data, length);
if (length < this->data_size()) {
switch (padding_mode()) {
case PaddingMode::Null:
// fill with zeros
__builtin_memset(m_data + length, 0, this->data_size() - length);
break;
case PaddingMode::CMS:
// fill with the length of the padding bytes
__builtin_memset(m_data + length, this->data_size() - length, this->data_size() - length);
break;
case PaddingMode::RFC5246:
// fill with the length of the padding bytes minus one
__builtin_memset(m_data + length, this->data_size() - length - 1, this->data_size() - length);
break;
default:
// FIXME: We should handle the rest of the common padding modes
ASSERT_NOT_REACHED();
break;
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,139 @@
/*
* 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/Optional.h>
#include <AK/Span.h>
#include <AK/Types.h>
namespace Crypto {
namespace Cipher {
enum class Intent {
Encryption,
Decryption,
};
enum class PaddingMode {
CMS, // RFC 1423
RFC5246, // very similar to CMS, but filled with |length - 1|, instead of |length|
Null,
// FIXME: We do not implement these yet
Bit,
Random,
Space,
ZeroLength,
};
template<typename B, typename T>
class Cipher;
struct CipherBlock {
public:
explicit CipherBlock(PaddingMode mode)
: m_padding_mode(mode)
{
}
static size_t block_size() { ASSERT_NOT_REACHED(); }
virtual ReadonlyBytes bytes() const = 0;
virtual void overwrite(ReadonlyBytes) = 0;
virtual void overwrite(const u8* data, size_t size) { overwrite({ data, size }); }
virtual void apply_initialization_vector(const u8* ivec) = 0;
PaddingMode padding_mode() const { return m_padding_mode; }
void set_padding_mode(PaddingMode mode) { m_padding_mode = mode; }
template<typename T>
void put(size_t offset, T value)
{
ASSERT(offset + sizeof(T) <= bytes().size());
auto* ptr = bytes().offset_pointer(offset);
auto index { 0 };
ASSERT(sizeof(T) <= 4);
if constexpr (sizeof(T) > 3)
ptr[index++] = (u8)(value >> 24);
if constexpr (sizeof(T) > 2)
ptr[index++] = (u8)(value >> 16);
if constexpr (sizeof(T) > 1)
ptr[index++] = (u8)(value >> 8);
ptr[index] = (u8)value;
}
private:
virtual Bytes bytes() = 0;
PaddingMode m_padding_mode;
};
struct CipherKey {
virtual ReadonlyBytes bytes() const = 0;
static bool is_valid_key_size(size_t) { return false; };
virtual ~CipherKey() { }
protected:
virtual void expand_encrypt_key(ReadonlyBytes user_key, size_t bits) = 0;
virtual void expand_decrypt_key(ReadonlyBytes user_key, size_t bits) = 0;
size_t bits { 0 };
};
template<typename KeyT = CipherKey, typename BlockT = CipherBlock>
class Cipher {
public:
using KeyType = KeyT;
using BlockType = BlockT;
explicit Cipher<KeyT, BlockT>(PaddingMode mode)
: m_padding_mode(mode)
{
}
virtual const KeyType& key() const = 0;
virtual KeyType& key() = 0;
static size_t block_size() { return BlockType::block_size(); }
PaddingMode padding_mode() const { return m_padding_mode; }
virtual void encrypt_block(const BlockType& in, BlockType& out) = 0;
virtual void decrypt_block(const BlockType& in, BlockType& out) = 0;
virtual String class_name() const = 0;
private:
PaddingMode m_padding_mode;
};
}
}

View file

@ -0,0 +1,142 @@
/*
* 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/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <LibCrypto/Cipher/Mode/Mode.h>
namespace Crypto {
namespace Cipher {
template<typename T>
class CBC : public Mode<T> {
public:
constexpr static size_t IVSizeInBits = 128;
virtual ~CBC() { }
template<typename... Args>
explicit constexpr CBC<T>(Args... args)
: Mode<T>(args...)
{
}
virtual String class_name() const override
{
StringBuilder builder;
builder.append(this->cipher().class_name());
builder.append("_CBC");
return builder.build();
}
virtual size_t IV_length() const override { return IVSizeInBits / 8; }
virtual void encrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}, Bytes* ivec_out = nullptr) 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.is_empty());
const auto* iv = ivec.data();
m_cipher_block.set_padding_mode(cipher.padding_mode());
size_t offset { 0 };
auto block_size = cipher.block_size();
while (length >= block_size) {
m_cipher_block.overwrite(in.slice(offset, block_size));
m_cipher_block.apply_initialization_vector(iv);
cipher.encrypt_block(m_cipher_block, m_cipher_block);
ASSERT(offset + block_size <= out.size());
__builtin_memcpy(out.offset(offset), m_cipher_block.bytes().data(), block_size);
iv = out.offset(offset);
length -= block_size;
offset += block_size;
}
if (length > 0) {
m_cipher_block.overwrite(in.slice(offset, length));
m_cipher_block.apply_initialization_vector(iv);
cipher.encrypt_block(m_cipher_block, m_cipher_block);
ASSERT(offset + block_size <= out.size());
__builtin_memcpy(out.offset(offset), m_cipher_block.bytes().data(), block_size);
iv = out.offset(offset);
}
if (ivec_out)
__builtin_memcpy(ivec_out->data(), iv, min(IV_length(), ivec_out->size()));
}
virtual void decrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}) override
{
auto length = in.size();
if (length == 0)
return;
auto& cipher = this->cipher();
ASSERT(!ivec.is_empty());
const auto* iv = ivec.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);
m_cipher_block.set_padding_mode(cipher.padding_mode());
size_t offset { 0 };
while (length > 0) {
auto* slice = in.offset(offset);
m_cipher_block.overwrite(slice, block_size);
cipher.decrypt_block(m_cipher_block, m_cipher_block);
m_cipher_block.apply_initialization_vector(iv);
auto decrypted = m_cipher_block.bytes();
ASSERT(offset + decrypted.size() <= out.size());
__builtin_memcpy(out.offset(offset), decrypted.data(), decrypted.size());
iv = slice;
length -= block_size;
offset += block_size;
}
out = out.slice(0, offset);
this->prune_padding(out);
}
private:
typename T::BlockType m_cipher_block {};
};
}
}

View file

@ -0,0 +1,209 @@
/*
* 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 {
/*
* Heads up: CTR is a *family* of modes, because the "counter" function is
* implementation-defined. This makes interoperability a pain in the neurons.
* Here are several contradicting(!) interpretations:
*
* "The counter can be *any function* which produces a sequence which is
* guaranteed not to repeat for a long time, although an actual increment-by-one
* counter is the simplest and most popular."
* The illustrations show that first increment should happen *after* the first
* round. I call this variant BIGINT_INCR_0.
* The AESAVS goes a step further and requires only that "counters" do not
* repeat, leaving the method of counting completely open.
* See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)
* See: https://csrc.nist.gov/csrc/media/projects/cryptographic-algorithm-validation-program/documents/aes/aesavs.pdf
*
* BIGINT_INCR_0 is the behavior of the OpenSSL command "openssl enc -aes-128-ctr",
* and the behavior of CRYPTO_ctr128_encrypt(). OpenSSL is not alone in the
* assumption that BIGINT_INCR_0 is all there is; even some NIST
* specification/survey(?) doesn't consider counting any other way.
* See: https://github.com/openssl/openssl/blob/33388b44b67145af2181b1e9528c381c8ea0d1b6/crypto/modes/ctr128.c#L71
* See: http://www.cryptogrium.com/aes-ctr.html
* See: https://web.archive.org/web/20150226072817/http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ctr/ctr-spec.pdf
*
* "[T]he successive counter blocks are derived by applying an incrementing
* function."
* It defines a *family* of functions called "Standard Incrementing Function"
* which only increment the lower-m bits, for some number 0<m<=blocksize.
* The included test vectors suggest that the first increment should happen
* *after* the first round. I call this INT32_INCR_0, or in general INTm_INCR_0.
* This in particular is the behavior of CRYPTO_ctr128_encrypt_ctr32() in OpenSSL.
* See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
* See: https://github.com/openssl/openssl/blob/33388b44b67145af2181b1e9528c381c8ea0d1b6/crypto/modes/ctr128.c#L147
*
* The python package "cryptography" and RFC 3686 (which appears among the
* first online search results when searching for "AES CTR 128 test vector")
* share a peculiar interpretation of CTR mode: the counter is incremented *before*
* the first round. RFC 3686 does not consider any other interpretation. I call
* this variant BIGINT_INCR_1.
* See: https://tools.ietf.org/html/rfc3686.html#section-6
* See: https://cryptography.io/en/latest/development/test-vectors/#symmetric-ciphers
*
* And finally, because the method is left open, a different increment could be
* used, for example little endian, or host endian, or mixed endian. Or any crazy
* LSFR with sufficiently large period. That is the reason for the constant part
* "INCR" in the previous counters.
*
* Due to this plethora of mutually-incompatible counters,
* the method of counting should be a template parameter.
* This currently implements BIGINT_INCR_0, which means perfect
* interoperability with openssl. The test vectors from RFC 3686 just need to be
* incremented by 1.
* TODO: Implement other counters?
*/
struct IncrementInplace {
void operator()(Bytes& in) const
{
for (size_t i = in.size(); i > 0;) {
--i;
if (in[i] == (u8)-1) {
in[i] = 0;
} else {
in[i]++;
break;
}
}
}
};
template<typename T, typename IncrementFunctionType = IncrementInplace>
class CTR : public Mode<T> {
public:
constexpr static size_t IVSizeInBits = 128;
virtual ~CTR() { }
// Must intercept `Intent`, because AES must always be set to
// Encryption, even when decrypting AES-CTR.
// TODO: How to deal with ciphers that take different arguments?
// FIXME: Add back the default intent parameter once clang-11 is the default in GitHub Actions.
// Once added back, remove the parameter where it's constructed in get_random_bytes in Kernel/Random.h.
template<typename KeyType, typename... Args>
explicit constexpr CTR(const KeyType& user_key, size_t key_bits, Intent, Args... args)
: Mode<T>(user_key, key_bits, Intent::Encryption, 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 void encrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}, Bytes* ivec_out = nullptr) override
{
// Our interpretation of "ivec" is what AES-CTR
// would define as nonce + IV + 4 zero bytes.
this->encrypt_or_stream(&in, out, ivec, ivec_out);
}
void key_stream(Bytes& out, const Bytes& ivec = {}, Bytes* ivec_out = nullptr)
{
this->encrypt_or_stream(nullptr, out, ivec, ivec_out);
}
virtual void decrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}) override
{
// XOR (and thus CTR) is the most symmetric mode.
this->encrypt(in, out, ivec);
}
private:
u8 m_ivec_storage[IVSizeInBits / 8];
typename T::BlockType m_cipher_block {};
protected:
constexpr static IncrementFunctionType increment {};
void encrypt_or_stream(const ReadonlyBytes* in, Bytes& out, ReadonlyBytes ivec, Bytes* ivec_out = nullptr)
{
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.is_empty());
ASSERT(ivec.size() >= IV_length());
m_cipher_block.set_padding_mode(cipher.padding_mode());
__builtin_memcpy(m_ivec_storage, ivec.data(), IV_length());
Bytes iv { m_ivec_storage, IV_length() };
size_t offset { 0 };
auto block_size = cipher.block_size();
while (length > 0) {
m_cipher_block.overwrite(iv.slice(0, block_size));
cipher.encrypt_block(m_cipher_block, m_cipher_block);
if (in) {
m_cipher_block.apply_initialization_vector(in->data() + offset);
}
auto write_size = min(block_size, length);
ASSERT(offset + write_size <= out.size());
__builtin_memcpy(out.offset(offset), m_cipher_block.bytes().data(), write_size);
increment(iv);
length -= write_size;
offset += write_size;
}
if (ivec_out)
__builtin_memcpy(ivec_out->data(), iv.data(), min(ivec_out->size(), IV_length()));
}
};
}
}

View file

@ -0,0 +1,154 @@
/*
* 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/OwnPtr.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <LibCrypto/Authentication/GHash.h>
#include <LibCrypto/Cipher/Mode/CTR.h>
#include <LibCrypto/Verification.h>
namespace Crypto {
namespace Cipher {
using IncrementFunction = IncrementInplace;
template<typename T>
class GCM : public CTR<T, IncrementFunction> {
public:
constexpr static size_t IVSizeInBits = 128;
virtual ~GCM() { }
template<typename... Args>
explicit constexpr GCM<T>(Args... args)
: CTR<T>(args...)
{
static_assert(T::BlockSizeInBits == 128u, "GCM Mode is only available for 128-bit Ciphers");
__builtin_memset(m_auth_key_storage, 0, block_size);
typename T::BlockType key_block(m_auth_key_storage, block_size);
this->cipher().encrypt_block(key_block, key_block);
key_block.bytes().copy_to(m_auth_key);
m_ghash = make<Authentication::GHash>(m_auth_key);
}
virtual String class_name() const override
{
StringBuilder builder;
builder.append(this->cipher().class_name());
builder.append("_GCM");
return builder.build();
}
virtual size_t IV_length() const override { return IVSizeInBits / 8; }
// FIXME: This overload throws away the auth stuff, think up a better way to return more than a single bytebuffer.
virtual void encrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}, Bytes* = nullptr) override
{
ASSERT(!ivec.is_empty());
static ByteBuffer dummy;
encrypt(in, out, ivec, dummy, dummy);
}
virtual void decrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}) override
{
encrypt(in, out, ivec);
}
void encrypt(const ReadonlyBytes& in, Bytes out, const ReadonlyBytes& iv_in, const ReadonlyBytes& aad, Bytes tag)
{
auto iv_buf = ByteBuffer::copy(iv_in.data(), iv_in.size());
auto iv = iv_buf.bytes();
// Increment the IV for block 0
CTR<T>::increment(iv);
typename T::BlockType block0;
block0.overwrite(iv);
this->cipher().encrypt_block(block0, block0);
// Skip past block 0
CTR<T>::increment(iv);
if (in.is_empty())
CTR<T>::key_stream(out, iv);
else
CTR<T>::encrypt(in, out, iv);
auto auth_tag = m_ghash->process(aad, out);
block0.apply_initialization_vector(auth_tag.data);
block0.bytes().copy_to(tag);
}
VerificationConsistency decrypt(ReadonlyBytes in, Bytes out, ReadonlyBytes iv_in, ReadonlyBytes aad, ReadonlyBytes tag)
{
auto iv_buf = ByteBuffer::copy(iv_in.data(), iv_in.size());
auto iv = iv_buf.bytes();
// Increment the IV for block 0
CTR<T>::increment(iv);
typename T::BlockType block0;
block0.overwrite(iv);
this->cipher().encrypt_block(block0, block0);
// Skip past block 0
CTR<T>::increment(iv);
auto auth_tag = m_ghash->process(aad, in);
block0.apply_initialization_vector(auth_tag.data);
auto test_consistency = [&] {
if (block0.block_size() != tag.size() || __builtin_memcmp(block0.bytes().data(), tag.data(), tag.size()) != 0)
return VerificationConsistency::Inconsistent;
return VerificationConsistency::Consistent;
};
// FIXME: This block needs constant-time comparisons.
if (in.is_empty()) {
out = {};
return test_consistency();
}
CTR<T>::encrypt(in, out, iv);
return test_consistency();
}
private:
static constexpr auto block_size = T::BlockType::BlockSizeInBits / 8;
u8 m_auth_key_storage[block_size];
Bytes m_auth_key { m_auth_key_storage, block_size };
OwnPtr<Authentication::GHash> m_ghash;
};
}
}

View file

@ -0,0 +1,118 @@
/*
* 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 <AK/Span.h>
#include <AK/StdLibExtras.h>
#include <LibCrypto/Cipher/Cipher.h>
namespace Crypto {
namespace Cipher {
template<typename T>
class Mode {
public:
virtual ~Mode() { }
virtual void encrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}, Bytes* ivec_out = nullptr) = 0;
virtual void decrypt(ReadonlyBytes in, Bytes& out, ReadonlyBytes ivec = {}) = 0;
virtual size_t IV_length() const = 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);
}
virtual String class_name() const = 0;
T& cipher() { return m_cipher; }
protected:
virtual void prune_padding(Bytes& 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 = size - maybe_padding_length; i < size; ++i) {
if (data[i] != maybe_padding_length) {
// not padding, part of data
return;
}
}
data = data.slice(0, size - maybe_padding_length);
break;
}
case PaddingMode::RFC5246: {
auto maybe_padding_length = data[size - 1];
// FIXME: If we want constant-time operations, this loop should not stop
for (auto i = size - maybe_padding_length - 1; i < size; ++i) {
if (data[i] != maybe_padding_length) {
// note that this is likely invalid padding
return;
}
}
data = data.slice(0, size - maybe_padding_length - 1);
break;
}
case PaddingMode::Null: {
while (data[size - 1] == 0)
--size;
data = data.slice(0, 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;
};
}
}

View file

@ -0,0 +1,62 @@
/*
* 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 <AK/StringView.h>
#include <AK/Types.h>
namespace Crypto {
namespace Hash {
template<size_t BlockS, typename DigestT>
class HashFunction {
public:
static constexpr auto BlockSize = BlockS / 8;
static constexpr auto DigestSize = DigestT::Size;
using DigestType = DigestT;
static size_t block_size() { return BlockSize; };
static size_t digest_size() { return DigestSize; };
virtual void update(const u8*, size_t) = 0;
void update(const Bytes& buffer) { update(buffer.data(), buffer.size()); };
void update(const ReadonlyBytes& buffer) { update(buffer.data(), buffer.size()); };
void update(const ByteBuffer& buffer) { update(buffer.data(), buffer.size()); };
void update(const StringView& string) { update((const u8*)string.characters_without_null_termination(), string.length()); };
virtual DigestType peek() = 0;
virtual DigestType digest() = 0;
virtual void reset() = 0;
virtual String class_name() const = 0;
};
}
}

View file

@ -0,0 +1,317 @@
/*
* 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/Optional.h>
#include <AK/OwnPtr.h>
#include <LibCrypto/Hash/HashFunction.h>
#include <LibCrypto/Hash/MD5.h>
#include <LibCrypto/Hash/SHA1.h>
#include <LibCrypto/Hash/SHA2.h>
namespace Crypto {
namespace Hash {
enum class HashKind {
None,
SHA1,
SHA256,
SHA512,
MD5,
};
struct MultiHashDigestVariant {
constexpr static size_t Size = 0;
MultiHashDigestVariant(SHA1::DigestType digest)
: sha1(digest)
, kind(HashKind::SHA1)
{
}
MultiHashDigestVariant(SHA256::DigestType digest)
: sha256(digest)
, kind(HashKind::SHA256)
{
}
MultiHashDigestVariant(SHA512::DigestType digest)
: sha512(digest)
, kind(HashKind::SHA512)
{
}
MultiHashDigestVariant(MD5::DigestType digest)
: md5(digest)
, kind(HashKind::MD5)
{
}
const u8* immutable_data() const
{
switch (kind) {
case HashKind::MD5:
return md5.value().immutable_data();
case HashKind::SHA1:
return sha1.value().immutable_data();
case HashKind::SHA256:
return sha256.value().immutable_data();
case HashKind::SHA512:
return sha512.value().immutable_data();
default:
case HashKind::None:
ASSERT_NOT_REACHED();
break;
}
}
size_t data_length()
{
switch (kind) {
case HashKind::MD5:
return md5.value().data_length();
case HashKind::SHA1:
return sha1.value().data_length();
case HashKind::SHA256:
return sha256.value().data_length();
case HashKind::SHA512:
return sha512.value().data_length();
default:
case HashKind::None:
ASSERT_NOT_REACHED();
break;
}
}
Optional<SHA1::DigestType> sha1;
Optional<SHA256::DigestType> sha256;
Optional<SHA512::DigestType> sha512;
Optional<MD5::DigestType> md5;
HashKind kind { HashKind::None };
};
class Manager final : public HashFunction<0, MultiHashDigestVariant> {
public:
using HashFunction::update;
Manager()
{
m_pre_init_buffer = ByteBuffer::create_zeroed(0);
}
Manager(const Manager& other) // NOT a copy constructor!
{
m_pre_init_buffer = ByteBuffer::create_zeroed(0); // will not be used
initialize(other.m_kind);
}
Manager(HashKind kind)
{
m_pre_init_buffer = ByteBuffer::create_zeroed(0);
initialize(kind);
}
~Manager()
{
m_sha1 = nullptr;
m_sha256 = nullptr;
m_sha512 = nullptr;
m_md5 = nullptr;
}
inline size_t digest_size() const
{
switch (m_kind) {
case HashKind::MD5:
return m_md5->digest_size();
case HashKind::SHA1:
return m_sha1->digest_size();
case HashKind::SHA256:
return m_sha256->digest_size();
case HashKind::SHA512:
return m_sha512->digest_size();
default:
case HashKind::None:
return 0;
}
}
inline size_t block_size() const
{
switch (m_kind) {
case HashKind::MD5:
return m_md5->block_size();
case HashKind::SHA1:
return m_sha1->block_size();
case HashKind::SHA256:
return m_sha256->block_size();
case HashKind::SHA512:
return m_sha512->block_size();
default:
case HashKind::None:
return 0;
}
}
inline void initialize(HashKind kind)
{
if (m_kind != HashKind::None) {
ASSERT_NOT_REACHED();
}
m_kind = kind;
switch (kind) {
case HashKind::MD5:
m_md5 = make<MD5>();
break;
case HashKind::SHA1:
m_sha1 = make<SHA1>();
break;
case HashKind::SHA256:
m_sha256 = make<SHA256>();
break;
case HashKind::SHA512:
m_sha512 = make<SHA512>();
break;
default:
case HashKind::None:
break;
}
}
virtual void update(const u8* data, size_t length) override
{
auto size = m_pre_init_buffer.size();
switch (m_kind) {
case HashKind::MD5:
if (size)
m_md5->update(m_pre_init_buffer);
m_md5->update(data, length);
break;
case HashKind::SHA1:
if (size)
m_sha1->update(m_pre_init_buffer);
m_sha1->update(data, length);
break;
case HashKind::SHA256:
if (size)
m_sha256->update(m_pre_init_buffer);
m_sha256->update(data, length);
break;
case HashKind::SHA512:
if (size)
m_sha512->update(m_pre_init_buffer);
m_sha512->update(data, length);
break;
default:
case HashKind::None:
m_pre_init_buffer.append(data, length);
return;
}
if (size)
m_pre_init_buffer.clear();
}
virtual DigestType peek() override
{
switch (m_kind) {
case HashKind::MD5:
return { m_md5->peek() };
case HashKind::SHA1:
return { m_sha1->peek() };
case HashKind::SHA256:
return { m_sha256->peek() };
case HashKind::SHA512:
return { m_sha512->peek() };
default:
case HashKind::None:
ASSERT_NOT_REACHED();
break;
}
}
virtual DigestType digest() override
{
auto digest = peek();
reset();
return digest;
}
virtual void reset() override
{
m_pre_init_buffer.clear();
switch (m_kind) {
case HashKind::MD5:
m_md5->reset();
break;
case HashKind::SHA1:
m_sha1->reset();
break;
case HashKind::SHA256:
m_sha256->reset();
break;
case HashKind::SHA512:
m_sha512->reset();
break;
default:
case HashKind::None:
break;
}
}
virtual String class_name() const override
{
switch (m_kind) {
case HashKind::MD5:
return m_md5->class_name();
case HashKind::SHA1:
return m_sha1->class_name();
case HashKind::SHA256:
return m_sha256->class_name();
case HashKind::SHA512:
return m_sha512->class_name();
default:
case HashKind::None:
return "UninitializedHashManager";
}
}
inline bool is(HashKind kind) const
{
return m_kind == kind;
}
private:
OwnPtr<SHA1> m_sha1;
OwnPtr<SHA256> m_sha256;
OwnPtr<SHA512> m_sha512;
OwnPtr<MD5> m_md5;
HashKind m_kind { HashKind::None };
ByteBuffer m_pre_init_buffer;
};
}
}

View file

@ -0,0 +1,225 @@
/*
* 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.
*/
#include <AK/Types.h>
#include <LibCrypto/Hash/MD5.h>
static constexpr u32 F(u32 x, u32 y, u32 z) { return (x & y) | ((~x) & z); };
static constexpr u32 G(u32 x, u32 y, u32 z) { return (x & z) | ((~z) & y); };
static constexpr u32 H(u32 x, u32 y, u32 z) { return x ^ y ^ z; };
static constexpr u32 I(u32 x, u32 y, u32 z) { return y ^ (x | ~z); };
static constexpr u32 ROTATE_LEFT(u32 x, size_t n)
{
return (x << n) | (x >> (32 - n));
}
static constexpr void round_1(u32& a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 ac)
{
a += F(b, c, d) + x + ac;
a = ROTATE_LEFT(a, s);
a += b;
}
static constexpr void round_2(u32& a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 ac)
{
a += G(b, c, d) + x + ac;
a = ROTATE_LEFT(a, s);
a += b;
}
static constexpr void round_3(u32& a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 ac)
{
a += H(b, c, d) + x + ac;
a = ROTATE_LEFT(a, s);
a += b;
}
static constexpr void round_4(u32& a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 ac)
{
a += I(b, c, d) + x + ac;
a = ROTATE_LEFT(a, s);
a += b;
}
namespace Crypto {
namespace Hash {
void MD5::update(const u8* input, size_t length)
{
auto index = (u32)(m_count[0] >> 3) & 0x3f;
size_t offset { 0 };
m_count[0] += (u32)length << 3;
if (m_count[0] < ((u32)length << 3)) {
++m_count[1];
}
m_count[1] += (u32)length >> 29;
auto part_length = 64 - index;
if (length >= part_length) {
m_buffer.overwrite(index, input, part_length);
transform(m_buffer.data());
for (offset = part_length; offset + 63 < length; offset += 64)
transform(&input[offset]);
index = 0;
}
ASSERT(length < part_length || length - offset <= 64);
m_buffer.overwrite(index, &input[offset], length - offset);
}
MD5::DigestType MD5::digest()
{
auto digest = peek();
reset();
return digest;
}
MD5::DigestType MD5::peek()
{
DigestType digest;
u8 bits[8];
encode(m_count, bits, 8);
// pad the data to 56%64
u32 index = (u32)((m_count[0] >> 3) & 0x3f);
u32 pad_length = index < 56 ? 56 - index : 120 - index;
update(MD5Constants::PADDING, pad_length);
// append length
update(bits, 8);
// store state (4 registers ABCD)
encode(&m_A, digest.data, 4 * sizeof(m_A));
return digest;
}
void MD5::encode(const u32* from, u8* to, size_t length)
{
for (size_t i = 0, j = 0; j < length; ++i, j += 4) {
to[j] = (u8)(from[i] & 0xff);
to[j + 1] = (u8)((from[i] >> 8) & 0xff);
to[j + 2] = (u8)((from[i] >> 16) & 0xff);
to[j + 3] = (u8)((from[i] >> 24) & 0xff);
}
}
void MD5::decode(const u8* from, u32* to, size_t length)
{
for (size_t i = 0, j = 0; j < length; ++i, j += 4)
to[i] = (((u32)from[j]) | (((u32)from[j + 1]) << 8) | (((u32)from[j + 2]) << 16) | (((u32)from[j + 3]) << 24));
}
void MD5::transform(const u8* block)
{
auto a = m_A;
auto b = m_B;
auto c = m_C;
auto d = m_D;
u32 x[16];
decode(block, x, 64);
round_1(a, b, c, d, x[0], MD5Constants::S11, 0xd76aa478); // 1
round_1(d, a, b, c, x[1], MD5Constants::S12, 0xe8c7b756); // 2
round_1(c, d, a, b, x[2], MD5Constants::S13, 0x242070db); // 3
round_1(b, c, d, a, x[3], MD5Constants::S14, 0xc1bdceee); // 4
round_1(a, b, c, d, x[4], MD5Constants::S11, 0xf57c0faf); // 5
round_1(d, a, b, c, x[5], MD5Constants::S12, 0x4787c62a); // 6
round_1(c, d, a, b, x[6], MD5Constants::S13, 0xa8304613); // 7
round_1(b, c, d, a, x[7], MD5Constants::S14, 0xfd469501); // 8
round_1(a, b, c, d, x[8], MD5Constants::S11, 0x698098d8); // 9
round_1(d, a, b, c, x[9], MD5Constants::S12, 0x8b44f7af); // 10
round_1(c, d, a, b, x[10], MD5Constants::S13, 0xffff5bb1); // 11
round_1(b, c, d, a, x[11], MD5Constants::S14, 0x895cd7be); // 12
round_1(a, b, c, d, x[12], MD5Constants::S11, 0x6b901122); // 13
round_1(d, a, b, c, x[13], MD5Constants::S12, 0xfd987193); // 14
round_1(c, d, a, b, x[14], MD5Constants::S13, 0xa679438e); // 15
round_1(b, c, d, a, x[15], MD5Constants::S14, 0x49b40821); // 16
round_2(a, b, c, d, x[1], MD5Constants::S21, 0xf61e2562); // 17
round_2(d, a, b, c, x[6], MD5Constants::S22, 0xc040b340); // 18
round_2(c, d, a, b, x[11], MD5Constants::S23, 0x265e5a51); // 19
round_2(b, c, d, a, x[0], MD5Constants::S24, 0xe9b6c7aa); // 20
round_2(a, b, c, d, x[5], MD5Constants::S21, 0xd62f105d); // 21
round_2(d, a, b, c, x[10], MD5Constants::S22, 0x2441453); // 22
round_2(c, d, a, b, x[15], MD5Constants::S23, 0xd8a1e681); // 23
round_2(b, c, d, a, x[4], MD5Constants::S24, 0xe7d3fbc8); // 24
round_2(a, b, c, d, x[9], MD5Constants::S21, 0x21e1cde6); // 25
round_2(d, a, b, c, x[14], MD5Constants::S22, 0xc33707d6); // 26
round_2(c, d, a, b, x[3], MD5Constants::S23, 0xf4d50d87); // 27
round_2(b, c, d, a, x[8], MD5Constants::S24, 0x455a14ed); // 28
round_2(a, b, c, d, x[13], MD5Constants::S21, 0xa9e3e905); // 29
round_2(d, a, b, c, x[2], MD5Constants::S22, 0xfcefa3f8); // 30
round_2(c, d, a, b, x[7], MD5Constants::S23, 0x676f02d9); // 31
round_2(b, c, d, a, x[12], MD5Constants::S24, 0x8d2a4c8a); // 32
round_3(a, b, c, d, x[5], MD5Constants::S31, 0xfffa3942); // 33
round_3(d, a, b, c, x[8], MD5Constants::S32, 0x8771f681); // 34
round_3(c, d, a, b, x[11], MD5Constants::S33, 0x6d9d6122); // 35
round_3(b, c, d, a, x[14], MD5Constants::S34, 0xfde5380c); // 36
round_3(a, b, c, d, x[1], MD5Constants::S31, 0xa4beea44); // 37
round_3(d, a, b, c, x[4], MD5Constants::S32, 0x4bdecfa9); // 38
round_3(c, d, a, b, x[7], MD5Constants::S33, 0xf6bb4b60); // 39
round_3(b, c, d, a, x[10], MD5Constants::S34, 0xbebfbc70); // 40
round_3(a, b, c, d, x[13], MD5Constants::S31, 0x289b7ec6); // 41
round_3(d, a, b, c, x[0], MD5Constants::S32, 0xeaa127fa); // 42
round_3(c, d, a, b, x[3], MD5Constants::S33, 0xd4ef3085); // 43
round_3(b, c, d, a, x[6], MD5Constants::S34, 0x4881d05); // 44
round_3(a, b, c, d, x[9], MD5Constants::S31, 0xd9d4d039); // 45
round_3(d, a, b, c, x[12], MD5Constants::S32, 0xe6db99e5); // 46
round_3(c, d, a, b, x[15], MD5Constants::S33, 0x1fa27cf8); // 47
round_3(b, c, d, a, x[2], MD5Constants::S34, 0xc4ac5665); // 48
round_4(a, b, c, d, x[0], MD5Constants::S41, 0xf4292244); // 49
round_4(d, a, b, c, x[7], MD5Constants::S42, 0x432aff97); // 50
round_4(c, d, a, b, x[14], MD5Constants::S43, 0xab9423a7); // 51
round_4(b, c, d, a, x[5], MD5Constants::S44, 0xfc93a039); // 52
round_4(a, b, c, d, x[12], MD5Constants::S41, 0x655b59c3); // 53
round_4(d, a, b, c, x[3], MD5Constants::S42, 0x8f0ccc92); // 54
round_4(c, d, a, b, x[10], MD5Constants::S43, 0xffeff47d); // 55
round_4(b, c, d, a, x[1], MD5Constants::S44, 0x85845dd1); // 56
round_4(a, b, c, d, x[8], MD5Constants::S41, 0x6fa87e4f); // 57
round_4(d, a, b, c, x[15], MD5Constants::S42, 0xfe2ce6e0); // 58
round_4(c, d, a, b, x[6], MD5Constants::S43, 0xa3014314); // 59
round_4(b, c, d, a, x[13], MD5Constants::S44, 0x4e0811a1); // 60
round_4(a, b, c, d, x[4], MD5Constants::S41, 0xf7537e82); // 61
round_4(d, a, b, c, x[11], MD5Constants::S42, 0xbd3af235); // 62
round_4(c, d, a, b, x[2], MD5Constants::S43, 0x2ad7d2bb); // 63
round_4(b, c, d, a, x[9], MD5Constants::S44, 0xeb86d391); // 64
m_A += a;
m_B += b;
m_C += c;
m_D += d;
__builtin_memset(x, 0, sizeof(x));
}
}
}

View file

@ -0,0 +1,128 @@
/*
* 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/String.h>
#include <AK/Types.h>
#include <LibCrypto/Hash/HashFunction.h>
namespace Crypto {
namespace Hash {
struct MD5Digest {
constexpr static size_t Size = 16;
u8 data[Size];
const u8* immutable_data() const { return data; }
size_t data_length() { return Size; }
};
namespace MD5Constants {
constexpr u32 init_A = 0x67452301;
constexpr u32 init_B = 0xefcdab89;
constexpr u32 init_C = 0x98badcfe;
constexpr u32 init_D = 0x10325476;
constexpr u32 S11 = 7;
constexpr u32 S12 = 12;
constexpr u32 S13 = 17;
constexpr u32 S14 = 22;
constexpr u32 S21 = 5;
constexpr u32 S22 = 9;
constexpr u32 S23 = 14;
constexpr u32 S24 = 20;
constexpr u32 S31 = 4;
constexpr u32 S32 = 11;
constexpr u32 S33 = 16;
constexpr u32 S34 = 23;
constexpr u32 S41 = 6;
constexpr u32 S42 = 10;
constexpr u32 S43 = 15;
constexpr u32 S44 = 21;
constexpr u8 PADDING[] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
};
}
class MD5 final : public HashFunction<512, MD5Digest> {
public:
using HashFunction::update;
MD5()
{
m_buffer = Bytes { m_data_buffer, sizeof(m_data_buffer) };
}
virtual void update(const u8*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
virtual String class_name() const override { return "MD5"; }
inline static DigestType hash(const u8* data, size_t length)
{
MD5 md5;
md5.update(data, length);
return md5.digest();
}
inline static DigestType hash(const ByteBuffer& buffer) { return hash(buffer.data(), buffer.size()); }
inline static DigestType hash(const StringView& buffer) { return hash((const u8*)buffer.characters_without_null_termination(), buffer.length()); }
inline virtual void reset() override
{
m_A = MD5Constants::init_A;
m_B = MD5Constants::init_B;
m_C = MD5Constants::init_C;
m_D = MD5Constants::init_D;
m_count[0] = 0;
m_count[1] = 0;
__builtin_memset(m_data_buffer, 0, sizeof(m_data_buffer));
}
private:
inline void transform(const u8*);
static void encode(const u32* from, u8* to, size_t length);
static void decode(const u8* from, u32* to, size_t length);
u32 m_A { MD5Constants::init_A }, m_B { MD5Constants::init_B }, m_C { MD5Constants::init_C }, m_D { MD5Constants::init_D };
u32 m_count[2] { 0, 0 };
Bytes m_buffer;
u8 m_data_buffer[64];
};
}
}

View file

@ -0,0 +1,169 @@
/*
* 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.
*/
#include <AK/Endian.h>
#include <AK/Types.h>
#include <LibCrypto/Hash/SHA1.h>
namespace Crypto {
namespace Hash {
inline static constexpr auto ROTATE_LEFT(u32 value, size_t bits)
{
return (value << bits) | (value >> (32 - bits));
}
inline void SHA1::transform(const u8* data)
{
u32 blocks[80];
for (size_t i = 0; i < 16; ++i)
blocks[i] = AK::convert_between_host_and_network_endian(((const u32*)data)[i]);
// w[i] = (w[i-3] xor w[i-8] xor w[i-14] xor w[i-16]) leftrotate 1
for (size_t i = 16; i < Rounds; ++i)
blocks[i] = ROTATE_LEFT(blocks[i - 3] ^ blocks[i - 8] ^ blocks[i - 14] ^ blocks[i - 16], 1);
auto a = m_state[0], b = m_state[1], c = m_state[2], d = m_state[3], e = m_state[4];
u32 f, k;
for (size_t i = 0; i < Rounds; ++i) {
if (i <= 19) {
f = (b & c) | ((~b) & d);
k = SHA1Constants::RoundConstants[0];
} else if (i <= 39) {
f = b ^ c ^ d;
k = SHA1Constants::RoundConstants[1];
} else if (i <= 59) {
f = (b & c) | (b & d) | (c & d);
k = SHA1Constants::RoundConstants[2];
} else {
f = b ^ c ^ d;
k = SHA1Constants::RoundConstants[3];
}
auto temp = ROTATE_LEFT(a, 5) + f + e + k + blocks[i];
e = d;
d = c;
c = ROTATE_LEFT(b, 30);
b = a;
a = temp;
}
m_state[0] += a;
m_state[1] += b;
m_state[2] += c;
m_state[3] += d;
m_state[4] += e;
// "security" measures, as if SHA1 is secure
a = 0;
b = 0;
c = 0;
d = 0;
e = 0;
__builtin_memset(blocks, 0, 16 * sizeof(u32));
}
void SHA1::update(const u8* message, size_t length)
{
for (size_t i = 0; i < length; ++i) {
if (m_data_length == BlockSize) {
transform(m_data_buffer);
m_bit_length += 512;
m_data_length = 0;
}
m_data_buffer[m_data_length++] = message[i];
}
}
SHA1::DigestType SHA1::digest()
{
auto digest = peek();
reset();
return digest;
}
SHA1::DigestType SHA1::peek()
{
DigestType digest;
size_t i = m_data_length;
// make a local copy of the data as we modify it
u8 data[BlockSize];
u32 state[5];
__builtin_memcpy(data, m_data_buffer, m_data_length);
__builtin_memcpy(state, m_state, 20);
if (BlockSize == m_data_length) {
transform(m_data_buffer);
m_bit_length += BlockSize * 8;
m_data_length = 0;
i = 0;
}
if (m_data_length < FinalBlockDataSize) {
m_data_buffer[i++] = 0x80;
while (i < FinalBlockDataSize)
m_data_buffer[i++] = 0x00;
} else {
// First, complete a block with some padding.
m_data_buffer[i++] = 0x80;
while (i < BlockSize)
m_data_buffer[i++] = 0x00;
transform(m_data_buffer);
// Then start another block with BlockSize - 8 bytes of zeros
__builtin_memset(m_data_buffer, 0, FinalBlockDataSize);
}
// append total message length
m_bit_length += m_data_length * 8;
m_data_buffer[BlockSize - 1] = m_bit_length;
m_data_buffer[BlockSize - 2] = m_bit_length >> 8;
m_data_buffer[BlockSize - 3] = m_bit_length >> 16;
m_data_buffer[BlockSize - 4] = m_bit_length >> 24;
m_data_buffer[BlockSize - 5] = m_bit_length >> 32;
m_data_buffer[BlockSize - 6] = m_bit_length >> 40;
m_data_buffer[BlockSize - 7] = m_bit_length >> 48;
m_data_buffer[BlockSize - 8] = m_bit_length >> 56;
transform(m_data_buffer);
for (size_t i = 0; i < 4; ++i) {
digest.data[i + 0] = (m_state[0] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 4] = (m_state[1] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 8] = (m_state[2] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 12] = (m_state[3] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 16] = (m_state[4] >> (24 - i * 8)) & 0x000000ff;
}
// restore the data
__builtin_memcpy(m_data_buffer, data, m_data_length);
__builtin_memcpy(m_state, state, 20);
return digest;
}
}
}

View file

@ -0,0 +1,107 @@
/*
* 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/String.h>
#include <LibCrypto/Hash/HashFunction.h>
namespace Crypto {
namespace Hash {
namespace SHA1Constants {
constexpr static u32 InitializationHashes[5] { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
constexpr static u32 RoundConstants[4] {
0X5a827999,
0X6ed9eba1,
0X8f1bbcdc,
0Xca62c1d6,
};
}
template<size_t Bytes>
struct SHA1Digest {
u8 data[Bytes];
constexpr static size_t Size = Bytes;
const u8* immutable_data() const { return data; }
size_t data_length() { return Bytes; }
};
class SHA1 final : public HashFunction<512, SHA1Digest<160 / 8>> {
public:
using HashFunction::update;
SHA1()
{
reset();
}
virtual void update(const u8*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
inline static DigestType hash(const u8* data, size_t length)
{
SHA1 sha;
sha.update(data, length);
return sha.digest();
}
inline static DigestType hash(const ByteBuffer& buffer) { return hash(buffer.data(), buffer.size()); }
inline static DigestType hash(const StringView& buffer) { return hash((const u8*)buffer.characters_without_null_termination(), buffer.length()); }
virtual String class_name() const override
{
return "SHA1";
};
inline virtual void reset() override
{
m_data_length = 0;
m_bit_length = 0;
for (auto i = 0; i < 5; ++i)
m_state[i] = SHA1Constants::InitializationHashes[i];
}
private:
inline void transform(const u8*);
u8 m_data_buffer[BlockSize];
size_t m_data_length { 0 };
u64 m_bit_length { 0 };
u32 m_state[5];
constexpr static auto FinalBlockDataSize = BlockSize - 8;
constexpr static auto Rounds = 80;
};
}
}

View file

@ -0,0 +1,282 @@
/*
* 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.
*/
#include <AK/Types.h>
#include <LibCrypto/Hash/SHA2.h>
namespace Crypto {
namespace Hash {
constexpr static auto ROTRIGHT(u32 a, size_t b) { return (a >> b) | (a << (32 - b)); }
constexpr static auto CH(u32 x, u32 y, u32 z) { return (x & y) ^ (z & ~x); }
constexpr static auto MAJ(u32 x, u32 y, u32 z) { return (x & y) ^ (x & z) ^ (y & z); }
constexpr static auto EP0(u32 x) { return ROTRIGHT(x, 2) ^ ROTRIGHT(x, 13) ^ ROTRIGHT(x, 22); }
constexpr static auto EP1(u32 x) { return ROTRIGHT(x, 6) ^ ROTRIGHT(x, 11) ^ ROTRIGHT(x, 25); }
constexpr static auto SIGN0(u32 x) { return ROTRIGHT(x, 7) ^ ROTRIGHT(x, 18) ^ (x >> 3); }
constexpr static auto SIGN1(u32 x) { return ROTRIGHT(x, 17) ^ ROTRIGHT(x, 19) ^ (x >> 10); }
constexpr static auto ROTRIGHT(u64 a, size_t b) { return (a >> b) | (a << (64 - b)); }
constexpr static auto CH(u64 x, u64 y, u64 z) { return (x & y) ^ (z & ~x); }
constexpr static auto MAJ(u64 x, u64 y, u64 z) { return (x & y) ^ (x & z) ^ (y & z); }
constexpr static auto EP0(u64 x) { return ROTRIGHT(x, 28) ^ ROTRIGHT(x, 34) ^ ROTRIGHT(x, 39); }
constexpr static auto EP1(u64 x) { return ROTRIGHT(x, 14) ^ ROTRIGHT(x, 18) ^ ROTRIGHT(x, 41); }
constexpr static auto SIGN0(u64 x) { return ROTRIGHT(x, 1) ^ ROTRIGHT(x, 8) ^ (x >> 7); }
constexpr static auto SIGN1(u64 x) { return ROTRIGHT(x, 19) ^ ROTRIGHT(x, 61) ^ (x >> 6); }
inline void SHA256::transform(const u8* data)
{
u32 m[64];
size_t i = 0;
for (size_t j = 0; i < 16; ++i, j += 4) {
m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | data[j + 3];
}
for (; i < BlockSize; ++i) {
m[i] = SIGN1(m[i - 2]) + m[i - 7] + SIGN0(m[i - 15]) + m[i - 16];
}
auto a = m_state[0], b = m_state[1],
c = m_state[2], d = m_state[3],
e = m_state[4], f = m_state[5],
g = m_state[6], h = m_state[7];
for (size_t i = 0; i < Rounds; ++i) {
auto temp0 = h + EP1(e) + CH(e, f, g) + SHA256Constants::RoundConstants[i] + m[i];
auto temp1 = EP0(a) + MAJ(a, b, c);
h = g;
g = f;
f = e;
e = d + temp0;
d = c;
c = b;
b = a;
a = temp0 + temp1;
}
m_state[0] += a;
m_state[1] += b;
m_state[2] += c;
m_state[3] += d;
m_state[4] += e;
m_state[5] += f;
m_state[6] += g;
m_state[7] += h;
}
void SHA256::update(const u8* message, size_t length)
{
for (size_t i = 0; i < length; ++i) {
if (m_data_length == BlockSize) {
transform(m_data_buffer);
m_bit_length += 512;
m_data_length = 0;
}
m_data_buffer[m_data_length++] = message[i];
}
}
SHA256::DigestType SHA256::digest()
{
auto digest = peek();
reset();
return digest;
}
SHA256::DigestType SHA256::peek()
{
DigestType digest;
size_t i = m_data_length;
if (BlockSize == m_data_length) {
transform(m_data_buffer);
m_bit_length += BlockSize * 8;
m_data_length = 0;
i = 0;
}
if (m_data_length < FinalBlockDataSize) {
m_data_buffer[i++] = 0x80;
while (i < FinalBlockDataSize)
m_data_buffer[i++] = 0x00;
} else {
// First, complete a block with some padding.
m_data_buffer[i++] = 0x80;
while (i < BlockSize)
m_data_buffer[i++] = 0x00;
transform(m_data_buffer);
// Then start another block with BlockSize - 8 bytes of zeros
__builtin_memset(m_data_buffer, 0, FinalBlockDataSize);
}
// append total message length
m_bit_length += m_data_length * 8;
m_data_buffer[BlockSize - 1] = m_bit_length;
m_data_buffer[BlockSize - 2] = m_bit_length >> 8;
m_data_buffer[BlockSize - 3] = m_bit_length >> 16;
m_data_buffer[BlockSize - 4] = m_bit_length >> 24;
m_data_buffer[BlockSize - 5] = m_bit_length >> 32;
m_data_buffer[BlockSize - 6] = m_bit_length >> 40;
m_data_buffer[BlockSize - 7] = m_bit_length >> 48;
m_data_buffer[BlockSize - 8] = m_bit_length >> 56;
transform(m_data_buffer);
// SHA uses big-endian and we assume little-endian
// FIXME: looks like a thing for AK::NetworkOrdered,
// but he doesn't support shifting operations
for (size_t i = 0; i < 4; ++i) {
digest.data[i + 0] = (m_state[0] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 4] = (m_state[1] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 8] = (m_state[2] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 12] = (m_state[3] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 16] = (m_state[4] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 20] = (m_state[5] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 24] = (m_state[6] >> (24 - i * 8)) & 0x000000ff;
digest.data[i + 28] = (m_state[7] >> (24 - i * 8)) & 0x000000ff;
}
return digest;
}
inline void SHA512::transform(const u8* data)
{
u64 m[80];
size_t i = 0;
for (size_t j = 0; i < 16; ++i, j += 8) {
m[i] = ((u64)data[j] << 56) | ((u64)data[j + 1] << 48) | ((u64)data[j + 2] << 40) | ((u64)data[j + 3] << 32) | ((u64)data[j + 4] << 24) | ((u64)data[j + 5] << 16) | ((u64)data[j + 6] << 8) | (u64)data[j + 7];
}
for (; i < Rounds; ++i) {
m[i] = SIGN1(m[i - 2]) + m[i - 7] + SIGN0(m[i - 15]) + m[i - 16];
}
auto a = m_state[0], b = m_state[1],
c = m_state[2], d = m_state[3],
e = m_state[4], f = m_state[5],
g = m_state[6], h = m_state[7];
for (size_t i = 0; i < Rounds; ++i) {
auto temp0 = h + EP1(e) + CH(e, f, g) + SHA512Constants::RoundConstants[i] + m[i];
auto temp1 = EP0(a) + MAJ(a, b, c);
h = g;
g = f;
f = e;
e = d + temp0;
d = c;
c = b;
b = a;
a = temp0 + temp1;
}
m_state[0] += a;
m_state[1] += b;
m_state[2] += c;
m_state[3] += d;
m_state[4] += e;
m_state[5] += f;
m_state[6] += g;
m_state[7] += h;
}
void SHA512::update(const u8* message, size_t length)
{
for (size_t i = 0; i < length; ++i) {
if (m_data_length == BlockSize) {
transform(m_data_buffer);
m_bit_length += 1024;
m_data_length = 0;
}
m_data_buffer[m_data_length++] = message[i];
}
}
SHA512::DigestType SHA512::digest()
{
auto digest = peek();
reset();
return digest;
}
SHA512::DigestType SHA512::peek()
{
DigestType digest;
size_t i = m_data_length;
if (BlockSize == m_data_length) {
transform(m_data_buffer);
m_bit_length += BlockSize * 8;
m_data_length = 0;
i = 0;
}
if (m_data_length < FinalBlockDataSize) {
m_data_buffer[i++] = 0x80;
while (i < FinalBlockDataSize)
m_data_buffer[i++] = 0x00;
} else {
// First, complete a block with some padding.
m_data_buffer[i++] = 0x80;
while (i < BlockSize)
m_data_buffer[i++] = 0x00;
transform(m_data_buffer);
// Then start another block with BlockSize - 8 bytes of zeros
__builtin_memset(m_data_buffer, 0, FinalBlockDataSize);
}
// append total message length
m_bit_length += m_data_length * 8;
m_data_buffer[BlockSize - 1] = m_bit_length;
m_data_buffer[BlockSize - 2] = m_bit_length >> 8;
m_data_buffer[BlockSize - 3] = m_bit_length >> 16;
m_data_buffer[BlockSize - 4] = m_bit_length >> 24;
m_data_buffer[BlockSize - 5] = m_bit_length >> 32;
m_data_buffer[BlockSize - 6] = m_bit_length >> 40;
m_data_buffer[BlockSize - 7] = m_bit_length >> 48;
m_data_buffer[BlockSize - 8] = m_bit_length >> 56;
transform(m_data_buffer);
// SHA uses big-endian and we assume little-endian
// FIXME: looks like a thing for AK::NetworkOrdered,
// but he doesn't support shifting operations
for (size_t i = 0; i < 8; ++i) {
digest.data[i + 0] = (m_state[0] >> (56 - i * 8)) & 0x000000ff;
digest.data[i + 8] = (m_state[1] >> (56 - i * 8)) & 0x000000ff;
digest.data[i + 16] = (m_state[2] >> (56 - i * 8)) & 0x000000ff;
digest.data[i + 24] = (m_state[3] >> (56 - i * 8)) & 0x000000ff;
digest.data[i + 32] = (m_state[4] >> (56 - i * 8)) & 0x000000ff;
digest.data[i + 40] = (m_state[5] >> (56 - i * 8)) & 0x000000ff;
digest.data[i + 48] = (m_state[6] >> (56 - i * 8)) & 0x000000ff;
digest.data[i + 56] = (m_state[7] >> (56 - i * 8)) & 0x000000ff;
}
return digest;
}
}
}

View file

@ -0,0 +1,202 @@
/*
* 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/String.h>
#include <AK/StringBuilder.h>
#include <LibCrypto/Hash/HashFunction.h>
namespace Crypto {
namespace Hash {
namespace SHA256Constants {
constexpr static u32 RoundConstants[64] {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
constexpr static u32 InitializationHashes[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
}
namespace SHA512Constants {
constexpr static u64 RoundConstants[80] {
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
};
constexpr static u64 InitializationHashes[8] = {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
};
}
template<size_t Bytes>
struct SHA2Digest {
u8 data[Bytes];
constexpr static size_t Size = Bytes;
const u8* immutable_data() const { return data; }
size_t data_length() { return Bytes; }
};
// FIXME: I want template<size_t BlockSize> but the compiler gets confused
class SHA256 final : public HashFunction<512, SHA2Digest<256 / 8>> {
public:
using HashFunction::update;
SHA256()
{
reset();
}
virtual void update(const u8*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
inline static DigestType hash(const u8* data, size_t length)
{
SHA256 sha;
sha.update(data, length);
return sha.digest();
}
inline static DigestType hash(const ByteBuffer& buffer) { return hash(buffer.data(), buffer.size()); }
inline static DigestType hash(const StringView& buffer) { return hash((const u8*)buffer.characters_without_null_termination(), buffer.length()); }
virtual String class_name() const override
{
StringBuilder builder;
builder.append("SHA");
builder.appendf("%zu", this->DigestSize * 8);
return builder.build();
};
inline virtual void reset() override
{
m_data_length = 0;
m_bit_length = 0;
for (size_t i = 0; i < 8; ++i)
m_state[i] = SHA256Constants::InitializationHashes[i];
}
private:
inline void transform(const u8*);
u8 m_data_buffer[BlockSize];
size_t m_data_length { 0 };
u64 m_bit_length { 0 };
u32 m_state[8];
constexpr static auto FinalBlockDataSize = BlockSize - 8;
constexpr static auto Rounds = 64;
};
class SHA512 final : public HashFunction<1024, SHA2Digest<512 / 8>> {
public:
using HashFunction::update;
SHA512()
{
reset();
}
virtual void update(const u8*, size_t) override;
virtual DigestType digest() override;
virtual DigestType peek() override;
inline static DigestType hash(const u8* data, size_t length)
{
SHA512 sha;
sha.update(data, length);
return sha.digest();
}
inline static DigestType hash(const ByteBuffer& buffer) { return hash(buffer.data(), buffer.size()); }
inline static DigestType hash(const StringView& buffer) { return hash((const u8*)buffer.characters_without_null_termination(), buffer.length()); }
virtual String class_name() const override
{
StringBuilder builder;
builder.append("SHA");
builder.appendf("%zu", this->DigestSize * 8);
return builder.build();
};
inline virtual void reset() override
{
m_data_length = 0;
m_bit_length = 0;
for (size_t i = 0; i < 8; ++i)
m_state[i] = SHA512Constants::InitializationHashes[i];
}
private:
inline void transform(const u8*);
u8 m_data_buffer[BlockSize];
size_t m_data_length { 0 };
u64 m_bit_length { 0 };
u64 m_state[8];
constexpr static auto FinalBlockDataSize = BlockSize - 8;
constexpr static auto Rounds = 80;
};
}
}

View file

@ -0,0 +1,359 @@
/*
* 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.
*/
#include <LibCrypto/NumberTheory/ModularFunctions.h>
namespace Crypto {
namespace NumberTheory {
UnsignedBigInteger ModularInverse(const UnsignedBigInteger& a_, const UnsignedBigInteger& b)
{
if (b == 1)
return { 1 };
UnsignedBigInteger one { 1 };
UnsignedBigInteger temp_1;
UnsignedBigInteger temp_2;
UnsignedBigInteger temp_3;
UnsignedBigInteger temp_4;
UnsignedBigInteger temp_plus;
UnsignedBigInteger temp_minus;
UnsignedBigInteger temp_quotient;
UnsignedBigInteger temp_remainder;
UnsignedBigInteger d;
auto a = a_;
auto u = a;
if (a.words()[0] % 2 == 0) {
// u += b
UnsignedBigInteger::add_without_allocation(u, b, temp_plus);
u.set_to(temp_plus);
}
auto v = b;
UnsignedBigInteger x { 0 };
// d = b - 1
UnsignedBigInteger::subtract_without_allocation(b, one, d);
while (!(v == 1)) {
while (v < u) {
// u -= v
UnsignedBigInteger::subtract_without_allocation(u, v, temp_minus);
u.set_to(temp_minus);
// d += x
UnsignedBigInteger::add_without_allocation(d, x, temp_plus);
d.set_to(temp_plus);
while (u.words()[0] % 2 == 0) {
if (d.words()[0] % 2 == 1) {
// d += b
UnsignedBigInteger::add_without_allocation(d, b, temp_plus);
d.set_to(temp_plus);
}
// u /= 2
UnsignedBigInteger::divide_u16_without_allocation(u, 2, temp_quotient, temp_remainder);
u.set_to(temp_quotient);
// d /= 2
UnsignedBigInteger::divide_u16_without_allocation(d, 2, temp_quotient, temp_remainder);
d.set_to(temp_quotient);
}
}
// v -= u
UnsignedBigInteger::subtract_without_allocation(v, u, temp_minus);
v.set_to(temp_minus);
// x += d
UnsignedBigInteger::add_without_allocation(x, d, temp_plus);
x.set_to(temp_plus);
while (v.words()[0] % 2 == 0) {
if (x.words()[0] % 2 == 1) {
// x += b
UnsignedBigInteger::add_without_allocation(x, b, temp_plus);
x.set_to(temp_plus);
}
// v /= 2
UnsignedBigInteger::divide_u16_without_allocation(v, 2, temp_quotient, temp_remainder);
v.set_to(temp_quotient);
// x /= 2
UnsignedBigInteger::divide_u16_without_allocation(x, 2, temp_quotient, temp_remainder);
x.set_to(temp_quotient);
}
}
// x % b
UnsignedBigInteger::divide_without_allocation(x, b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
return temp_remainder;
}
UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& e, const UnsignedBigInteger& m)
{
if (m == 1)
return 0;
UnsignedBigInteger ep { e };
UnsignedBigInteger base { b };
UnsignedBigInteger exp { 1 };
UnsignedBigInteger temp_1;
UnsignedBigInteger temp_2;
UnsignedBigInteger temp_3;
UnsignedBigInteger temp_4;
UnsignedBigInteger temp_multiply;
UnsignedBigInteger temp_quotient;
UnsignedBigInteger temp_remainder;
while (!(ep < 1)) {
if (ep.words()[0] % 2 == 1) {
// exp = (exp * base) % m;
UnsignedBigInteger::multiply_without_allocation(exp, base, temp_1, temp_2, temp_3, temp_4, temp_multiply);
UnsignedBigInteger::divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
exp.set_to(temp_remainder);
}
// ep = ep / 2;
UnsignedBigInteger::divide_u16_without_allocation(ep, 2, temp_quotient, temp_remainder);
ep.set_to(temp_quotient);
// base = (base * base) % m;
UnsignedBigInteger::multiply_without_allocation(base, base, temp_1, temp_2, temp_3, temp_4, temp_multiply);
UnsignedBigInteger::divide_without_allocation(temp_multiply, m, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
base.set_to(temp_remainder);
}
return exp;
}
static void GCD_without_allocation(
const UnsignedBigInteger& a,
const UnsignedBigInteger& b,
UnsignedBigInteger& temp_a,
UnsignedBigInteger& temp_b,
UnsignedBigInteger& temp_1,
UnsignedBigInteger& temp_2,
UnsignedBigInteger& temp_3,
UnsignedBigInteger& temp_4,
UnsignedBigInteger& temp_quotient,
UnsignedBigInteger& temp_remainder,
UnsignedBigInteger& output)
{
temp_a.set_to(a);
temp_b.set_to(b);
for (;;) {
if (temp_a == 0) {
output.set_to(temp_b);
return;
}
// temp_b %= temp_a
UnsignedBigInteger::divide_without_allocation(temp_b, temp_a, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
temp_b.set_to(temp_remainder);
if (temp_b == 0) {
output.set_to(temp_a);
return;
}
// temp_a %= temp_b
UnsignedBigInteger::divide_without_allocation(temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
temp_a.set_to(temp_remainder);
}
}
UnsignedBigInteger GCD(const UnsignedBigInteger& a, const UnsignedBigInteger& b)
{
UnsignedBigInteger temp_a;
UnsignedBigInteger temp_b;
UnsignedBigInteger temp_1;
UnsignedBigInteger temp_2;
UnsignedBigInteger temp_3;
UnsignedBigInteger temp_4;
UnsignedBigInteger temp_quotient;
UnsignedBigInteger temp_remainder;
UnsignedBigInteger output;
GCD_without_allocation(a, b, temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, output);
return output;
}
UnsignedBigInteger LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b)
{
UnsignedBigInteger temp_a;
UnsignedBigInteger temp_b;
UnsignedBigInteger temp_1;
UnsignedBigInteger temp_2;
UnsignedBigInteger temp_3;
UnsignedBigInteger temp_4;
UnsignedBigInteger temp_quotient;
UnsignedBigInteger temp_remainder;
UnsignedBigInteger gcd_output;
UnsignedBigInteger output { 0 };
GCD_without_allocation(a, b, temp_a, temp_b, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder, gcd_output);
if (gcd_output == 0) {
#ifdef NT_DEBUG
dbgln("GCD is zero");
#endif
return output;
}
// output = (a / gcd_output) * b
UnsignedBigInteger::divide_without_allocation(a, gcd_output, temp_1, temp_2, temp_3, temp_4, temp_quotient, temp_remainder);
UnsignedBigInteger::multiply_without_allocation(temp_quotient, b, temp_1, temp_2, temp_3, temp_4, output);
#ifdef NT_DEBUG
dbg() << "quot: " << temp_quotient << " rem: " << temp_remainder << " out: " << output;
#endif
return output;
}
static bool MR_primality_test(UnsignedBigInteger n, const Vector<UnsignedBigInteger, 256>& tests)
{
// Written using Wikipedia:
// https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Miller%E2%80%93Rabin_test
ASSERT(!(n < 4));
auto predecessor = n.minus({ 1 });
auto d = predecessor;
size_t r = 0;
{
auto div_result = d.divided_by(2);
while (div_result.remainder == 0) {
d = div_result.quotient;
div_result = d.divided_by(2);
++r;
}
}
if (r == 0) {
// n - 1 is odd, so n was even. But there is only one even prime:
return n == 2;
}
for (auto a : tests) {
// Technically: ASSERT(2 <= a && a <= n - 2)
ASSERT(a < n);
auto x = ModularPower(a, d, n);
if (x == 1 || x == predecessor)
continue;
bool skip_this_witness = false;
// r 1 iterations.
for (size_t i = 0; i < r - 1; ++i) {
x = ModularPower(x, 2, n);
if (x == predecessor) {
skip_this_witness = true;
break;
}
}
if (skip_this_witness)
continue;
return false; // "composite"
}
return true; // "probably prime"
}
UnsignedBigInteger random_number(const UnsignedBigInteger& min, const UnsignedBigInteger& max_excluded)
{
ASSERT(min < max_excluded);
auto range = max_excluded.minus(min);
UnsignedBigInteger base;
auto size = range.trimmed_length() * sizeof(u32) + 2;
// "+2" is intentional (see below).
// Also, if we're about to crash anyway, at least produce a nice error:
ASSERT(size < 8 * MiB);
u8 buf[size];
AK::fill_with_random(buf, size);
UnsignedBigInteger random { buf, size };
// At this point, `random` is a large number, in the range [0, 256^size).
// To get down to the actual range, we could just compute random % range.
// This introduces "modulo bias". However, since we added 2 to `size`,
// we know that the generated range is at least 65536 times as large as the
// required range! This means that the modulo bias is only 0.0015%, if all
// inputs are chosen adversarially. Let's hope this is good enough.
auto divmod = random.divided_by(range);
// The proper way to fix this is to restart if `divmod.quotient` is maximal.
return divmod.remainder.plus(min);
}
bool is_probably_prime(const UnsignedBigInteger& p)
{
// Is it a small number?
if (p < 49) {
u32 p_value = p.words()[0];
// Is it a very small prime?
if (p_value == 2 || p_value == 3 || p_value == 5 || p_value == 7)
return true;
// Is it the multiple of a very small prime?
if (p_value % 2 == 0 || p_value % 3 == 0 || p_value % 5 == 0 || p_value % 7 == 0)
return false;
// Then it must be a prime, but not a very small prime, like 37.
return true;
}
Vector<UnsignedBigInteger, 256> tests;
// Make some good initial guesses that are guaranteed to find all primes < 2^64.
tests.append(UnsignedBigInteger(2));
tests.append(UnsignedBigInteger(3));
tests.append(UnsignedBigInteger(5));
tests.append(UnsignedBigInteger(7));
tests.append(UnsignedBigInteger(11));
tests.append(UnsignedBigInteger(13));
UnsignedBigInteger seventeen { 17 };
for (size_t i = tests.size(); i < 256; ++i) {
tests.append(random_number(seventeen, p.minus(2)));
}
// Miller-Rabin's "error" is 8^-k. In adversarial cases, it's 4^-k.
// With 200 random numbers, this would mean an error of about 2^-400.
// So we don't need to worry too much about the quality of the random numbers.
return MR_primality_test(p, tests);
}
UnsignedBigInteger random_big_prime(size_t bits)
{
ASSERT(bits >= 33);
UnsignedBigInteger min = UnsignedBigInteger::from_base10("6074001000").shift_left(bits - 33);
UnsignedBigInteger max = UnsignedBigInteger { 1 }.shift_left(bits).minus(1);
for (;;) {
auto p = random_number(min, max);
if ((p.words()[0] & 1) == 0) {
// An even number is definitely not a large prime.
continue;
}
if (is_probably_prime(p))
return p;
}
}
}
}

View file

@ -0,0 +1,72 @@
/*
* 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/Random.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
//#define NT_DEBUG
namespace Crypto {
namespace NumberTheory {
UnsignedBigInteger ModularInverse(const UnsignedBigInteger& a_, const UnsignedBigInteger& b);
UnsignedBigInteger ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& e, const UnsignedBigInteger& m);
// Note: This function _will_ generate extremely huge numbers, and in doing so,
// it will allocate and free a lot of memory!
// Please use |ModularPower| if your use-case is modexp.
template<typename IntegerType>
static IntegerType Power(const IntegerType& b, const IntegerType& e)
{
IntegerType ep { e };
IntegerType base { b };
IntegerType exp { 1 };
while (!(ep < IntegerType { 1 })) {
if (ep.words()[0] % 2 == 1)
exp.set_to(exp.multiplied_by(base));
// ep = ep / 2;
ep.set_to(ep.divided_by(IntegerType { 2 }).quotient);
// base = base * base
base.set_to(base.multiplied_by(base));
}
return exp;
}
UnsignedBigInteger GCD(const UnsignedBigInteger& a, const UnsignedBigInteger& b);
UnsignedBigInteger LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b);
UnsignedBigInteger random_number(const UnsignedBigInteger& min, const UnsignedBigInteger& max_excluded);
bool is_probably_prime(const UnsignedBigInteger& p);
UnsignedBigInteger random_big_prime(size_t bits);
}
}

View file

@ -0,0 +1,55 @@
/*
* 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/Hash/HashFunction.h>
#include <LibCrypto/Verification.h>
namespace Crypto {
namespace PK {
template<typename HashFunction>
class Code {
public:
template<typename... Args>
Code(Args... args)
: m_hasher(args...)
{
}
virtual void encode(ReadonlyBytes in, ByteBuffer& out, size_t em_bits) = 0;
virtual VerificationConsistency verify(ReadonlyBytes msg, ReadonlyBytes emsg, size_t em_bits) = 0;
const HashFunction& hasher() const { return m_hasher; }
HashFunction& hasher() { return m_hasher; }
protected:
HashFunction m_hasher;
};
}
}

View file

@ -0,0 +1,180 @@
/*
* 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/Random.h>
#include <LibCrypto/PK/Code/Code.h>
static constexpr u8 zeros[] { 0, 0, 0, 0, 0, 0, 0, 0 };
namespace Crypto {
namespace PK {
template<typename HashFunction, size_t SaltSize>
class EMSA_PSS : public Code<HashFunction> {
public:
template<typename... Args>
EMSA_PSS(Args... args)
: Code<HashFunction>(args...)
{
m_buffer = Bytes { m_data_buffer, sizeof(m_data_buffer) };
}
static constexpr auto SaltLength = SaltSize;
virtual void encode(ReadonlyBytes in, ByteBuffer& out, size_t em_bits) override
{
// FIXME: we're supposed to check if in.size() > HashFunction::input_limitation
// however, all of our current hash functions can hash unlimited blocks
auto& hash_fn = this->hasher();
hash_fn.update(in);
auto message_hash = hash_fn.digest();
auto hash_length = hash_fn.DigestSize;
auto em_length = (em_bits + 7) / 8;
u8 salt[SaltLength];
AK::fill_with_random(salt, SaltLength);
if (em_length < hash_length + SaltLength + 2) {
dbgln("Ooops...encoding error");
return;
}
m_buffer.overwrite(0, zeros, 8);
m_buffer.overwrite(8, message_hash.data, HashFunction::DigestSize);
m_buffer.overwrite(8 + HashFunction::DigestSize, salt, SaltLength);
hash_fn.update(m_buffer);
auto hash = hash_fn.digest();
u8 DB_data[em_length - HashFunction::DigestSize - 1];
auto DB = Bytes { DB_data, em_length - HashFunction::DigestSize - 1 };
auto DB_offset = 0;
for (size_t i = 0; i < em_length - SaltLength - HashFunction::DigestSize - 2; ++i)
DB[DB_offset++] = 0;
DB[DB_offset++] = 0x01;
DB.overwrite(DB_offset, salt, SaltLength);
auto mask_length = em_length - HashFunction::DigestSize - 1;
u8 DB_mask[mask_length];
auto DB_mask_buffer = Bytes { DB_mask, mask_length };
// FIXME: we should probably allow reading from u8*
MGF1(ReadonlyBytes { hash.data, HashFunction::DigestSize }, mask_length, DB_mask_buffer);
for (size_t i = 0; i < DB.size(); ++i)
DB_data[i] ^= DB_mask[i];
auto count = (8 - (em_length * 8 - em_bits));
DB_data[0] &= (0xff >> count) << count;
out.overwrite(0, DB.data(), DB.size());
out.overwrite(DB.size(), hash.data, hash_fn.DigestSize);
out[DB.size() + hash_fn.DigestSize] = 0xbc;
}
virtual VerificationConsistency verify(ReadonlyBytes msg, ReadonlyBytes emsg, size_t em_bits) override
{
auto& hash_fn = this->hasher();
hash_fn.update(msg);
auto message_hash = hash_fn.digest();
if (emsg.size() < HashFunction::DigestSize + SaltLength + 2)
return VerificationConsistency::Inconsistent;
if (emsg[emsg.size() - 1] != 0xbc)
return VerificationConsistency::Inconsistent;
auto mask_length = emsg.size() - HashFunction::DigestSize - 1;
auto masked_DB = emsg.slice(0, mask_length);
auto H = emsg.slice(mask_length, HashFunction::DigestSize);
auto length_to_check = 8 * emsg.size() - em_bits;
auto octet = masked_DB[0];
for (size_t i = 0; i < length_to_check; ++i)
if ((octet >> (8 - i)) & 0x01)
return VerificationConsistency::Inconsistent;
u8 DB_mask[mask_length];
auto DB_mask_buffer = Bytes { DB_mask, mask_length };
MGF1(H, mask_length, DB_mask_buffer);
u8 DB[mask_length];
for (size_t i = 0; i < mask_length; ++i)
DB[i] = masked_DB[i] ^ DB_mask[i];
DB[0] &= 0xff >> (8 - length_to_check);
auto check_octets = emsg.size() - HashFunction::DigestSize - SaltLength - 2;
for (size_t i = 0; i < check_octets; ++i) {
if (DB[i])
return VerificationConsistency::Inconsistent;
}
if (DB[check_octets + 1] != 0x01)
return VerificationConsistency::Inconsistent;
auto* salt = DB + mask_length - SaltLength;
u8 m_prime[8 + HashFunction::DigestSize + SaltLength] { 0, 0, 0, 0, 0, 0, 0, 0 };
auto m_prime_buffer = Bytes { m_prime, sizeof(m_prime) };
m_prime_buffer.overwrite(8, message_hash.data, HashFunction::DigestSize);
m_prime_buffer.overwrite(8 + HashFunction::DigestSize, salt, SaltLength);
hash_fn.update(m_prime_buffer);
auto H_prime = hash_fn.digest();
if (__builtin_memcmp(message_hash.data, H_prime.data, HashFunction::DigestSize))
return VerificationConsistency::Inconsistent;
return VerificationConsistency::Consistent;
}
void MGF1(ReadonlyBytes seed, size_t length, Bytes out)
{
auto& hash_fn = this->hasher();
ByteBuffer T = ByteBuffer::create_zeroed(0);
for (size_t counter = 0; counter < length / HashFunction::DigestSize - 1; ++counter) {
hash_fn.update(seed);
hash_fn.update((u8*)&counter, 4);
T.append(hash_fn.digest().data, HashFunction::DigestSize);
}
out.overwrite(0, T.data(), length);
}
private:
u8 m_data_buffer[8 + HashFunction::DigestSize + SaltLength];
Bytes m_buffer;
};
}
}

View file

@ -0,0 +1,68 @@
/*
* 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 <AK/String.h>
namespace Crypto {
namespace PK {
// FIXME: Fixing name up for grabs
template<typename PrivKeyT, typename PubKeyT>
class PKSystem {
public:
using PublicKeyType = PubKeyT;
using PrivateKeyType = PrivKeyT;
PKSystem(PublicKeyType& pubkey, PrivateKeyType& privkey)
: m_public_key(pubkey)
, m_private_key(privkey)
{
}
PKSystem()
{
}
virtual void encrypt(ReadonlyBytes in, Bytes& out) = 0;
virtual void decrypt(ReadonlyBytes in, Bytes& out) = 0;
virtual void sign(ReadonlyBytes in, Bytes& out) = 0;
virtual void verify(ReadonlyBytes in, Bytes& out) = 0;
virtual String class_name() const = 0;
virtual size_t output_size() const = 0;
protected:
PublicKeyType m_public_key;
PrivateKeyType m_private_key;
};
}
}

View file

@ -0,0 +1,329 @@
/*
* 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.
*/
#include <AK/Random.h>
#include <LibCrypto/ASN1/ASN1.h>
#include <LibCrypto/ASN1/DER.h>
#include <LibCrypto/ASN1/PEM.h>
#include <LibCrypto/PK/RSA.h>
namespace Crypto {
namespace PK {
RSA::KeyPairType RSA::parse_rsa_key(ReadonlyBytes in)
{
// we are going to assign to at least one of these
KeyPairType keypair;
// TODO: move ASN parsing logic out
u64 t, x, y, z, tmp_oid[16];
u8 tmp_buf[4096] { 0 };
UnsignedBigInteger n, e, d;
ASN1::List pubkey_hash_oid[2], pubkey[2];
ASN1::set(pubkey_hash_oid[0], ASN1::Kind::ObjectIdentifier, tmp_oid, sizeof(tmp_oid) / sizeof(tmp_oid[0]));
ASN1::set(pubkey_hash_oid[1], ASN1::Kind::Null, nullptr, 0);
// DER is weird in that it stores pubkeys as bitstrings
// we must first extract that crap
ASN1::set(pubkey[0], ASN1::Kind::Sequence, &pubkey_hash_oid, 2);
ASN1::set(pubkey[1], ASN1::Kind::Null, nullptr, 0);
dbgln("we were offered {} bytes of input", in.size());
if (der_decode_sequence(in.data(), in.size(), pubkey, 2)) {
// yay, now we have to reassemble the bitstring to a bytestring
t = 0;
y = 0;
z = 0;
x = 0;
for (; x < pubkey[1].size; ++x) {
y = (y << 1) | tmp_buf[x];
if (++z == 8) {
tmp_buf[t++] = (u8)y;
y = 0;
z = 0;
}
}
// now the buffer is correct (Sequence { Integer, Integer })
if (!der_decode_sequence_many<2>(tmp_buf, t,
ASN1::Kind::Integer, 1, &n,
ASN1::Kind::Integer, 1, &e)) {
// something was fucked up
dbgln("bad pubkey: e={} n={}", e, n);
return keypair;
}
// correct public key
keypair.public_key.set(n, e);
return keypair;
}
// could be a private key
if (!der_decode_sequence_many<1>(in.data(), in.size(),
ASN1::Kind::Integer, 1, &n)) {
// that's no key
// that's a death star
dbgln("that's a death star");
return keypair;
}
if (n == 0) {
// it is a private key
UnsignedBigInteger zero;
if (!der_decode_sequence_many<4>(in.data(), in.size(),
ASN1::Kind::Integer, 1, &zero,
ASN1::Kind::Integer, 1, &n,
ASN1::Kind::Integer, 1, &e,
ASN1::Kind::Integer, 1, &d)) {
dbgln("bad privkey n={} e={} d={}", n, e, d);
return keypair;
}
keypair.private_key.set(n, d, e);
return keypair;
}
if (n == 1) {
// multiprime key, we don't know how to deal with this
dbgln("Unsupported key type");
return keypair;
}
// it's a broken public key
keypair.public_key.set(n, 65537);
return keypair;
}
void RSA::encrypt(ReadonlyBytes in, Bytes& out)
{
#ifdef CRYPTO_DEBUG
dbg() << "in size: " << in.size();
#endif
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
if (!(in_integer < m_public_key.modulus())) {
dbgln("value too large for key");
out = {};
return;
}
auto exp = NumberTheory::ModularPower(in_integer, m_public_key.public_exponent(), m_public_key.modulus());
auto size = exp.export_data(out);
auto outsize = out.size();
if (size != outsize) {
dbgln("POSSIBLE RSA BUG!!! Size mismatch: {} requested but {} bytes generated", outsize, size);
out = out.slice(outsize - size, size);
}
}
void RSA::decrypt(ReadonlyBytes in, Bytes& out)
{
// FIXME: Actually use the private key properly
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
auto exp = NumberTheory::ModularPower(in_integer, m_private_key.private_exponent(), m_private_key.modulus());
auto size = exp.export_data(out);
auto align = m_private_key.length();
auto aligned_size = (size + align - 1) / align * align;
for (auto i = size; i < aligned_size; ++i)
out[out.size() - i - 1] = 0; // zero the non-aligned values
out = out.slice(out.size() - aligned_size, aligned_size);
}
void RSA::sign(ReadonlyBytes in, Bytes& out)
{
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
auto exp = NumberTheory::ModularPower(in_integer, m_private_key.private_exponent(), m_private_key.modulus());
auto size = exp.export_data(out);
out = out.slice(out.size() - size, size);
}
void RSA::verify(ReadonlyBytes in, Bytes& out)
{
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
auto exp = NumberTheory::ModularPower(in_integer, m_public_key.public_exponent(), m_public_key.modulus());
auto size = exp.export_data(out);
out = out.slice(out.size() - size, size);
}
void RSA::import_private_key(ReadonlyBytes bytes, bool pem)
{
ByteBuffer buffer;
if (pem) {
buffer = decode_pem(bytes);
bytes = buffer;
}
auto key = parse_rsa_key(bytes);
if (!key.private_key.length()) {
dbgln("We expected to see a private key, but we found none");
ASSERT_NOT_REACHED();
}
m_private_key = key.private_key;
}
void RSA::import_public_key(ReadonlyBytes bytes, bool pem)
{
ByteBuffer buffer;
if (pem) {
buffer = decode_pem(bytes);
bytes = buffer;
}
auto key = parse_rsa_key(bytes);
if (!key.public_key.length()) {
dbgln("We expected to see a public key, but we found none");
ASSERT_NOT_REACHED();
}
m_public_key = key.public_key;
}
template<typename HashFunction>
void RSA_EMSA_PSS<HashFunction>::sign(ReadonlyBytes in, Bytes& out)
{
// -- encode via EMSA_PSS
auto mod_bits = m_rsa.private_key().modulus().trimmed_length() * sizeof(u32) * 8;
u8 EM[mod_bits];
auto EM_buf = Bytes { EM, mod_bits };
m_emsa_pss.encode(in, EM_buf, mod_bits - 1);
// -- sign via RSA
m_rsa.sign(EM_buf, out);
}
template<typename HashFunction>
VerificationConsistency RSA_EMSA_PSS<HashFunction>::verify(ReadonlyBytes in)
{
auto mod_bytes = m_rsa.public_key().modulus().trimmed_length() * sizeof(u32);
if (in.size() != mod_bytes)
return VerificationConsistency::Inconsistent;
u8 EM[mod_bytes];
auto EM_buf = Bytes { EM, mod_bytes };
// -- verify via RSA
m_rsa.verify(in, EM_buf);
// -- verify via EMSA_PSS
return m_emsa_pss.verify(in, EM, mod_bytes * 8 - 1);
}
void RSA_PKCS1_EME::encrypt(ReadonlyBytes in, Bytes& out)
{
auto mod_len = (m_public_key.modulus().trimmed_length() * sizeof(u32) * 8 + 7) / 8;
#ifdef CRYPTO_DEBUG
dbg() << "key size: " << mod_len;
#endif
if (in.size() > mod_len - 11) {
dbgln("message too long :(");
out = out.trim(0);
return;
}
if (out.size() < mod_len) {
dbgln("output buffer too small");
return;
}
auto ps_length = mod_len - in.size() - 3;
u8 ps[ps_length];
// FIXME: Without this assertion, GCC refuses to compile due to a memcpy overflow(!?)
ASSERT(ps_length < 16384);
AK::fill_with_random(ps, ps_length);
// since arc4random can create zeros (shocking!)
// we have to go through and un-zero the zeros
for (size_t i = 0; i < ps_length; ++i)
while (!ps[i])
AK::fill_with_random(ps + i, 1);
u8 paddings[] { 0x00, 0x02 };
out.overwrite(0, paddings, 2);
out.overwrite(2, ps, ps_length);
out.overwrite(2 + ps_length, paddings, 1);
out.overwrite(3 + ps_length, in.data(), in.size());
out = out.trim(3 + ps_length + in.size()); // should be a single block
#ifdef CRYPTO_DEBUG
dbg() << "padded output size: " << 3 + ps_length + in.size() << " buffer size: " << out.size();
#endif
RSA::encrypt(out, out);
}
void RSA_PKCS1_EME::decrypt(ReadonlyBytes in, Bytes& out)
{
auto mod_len = (m_public_key.modulus().trimmed_length() * sizeof(u32) * 8 + 7) / 8;
if (in.size() != mod_len) {
dbgln("decryption error: wrong amount of data: {}", in.size());
out = out.trim(0);
return;
}
RSA::decrypt(in, out);
if (out.size() < RSA::output_size()) {
dbgln("decryption error: not enough data after decryption: {}", out.size());
out = out.trim(0);
return;
}
if (out[0] != 0x00) {
dbgln("invalid padding byte 0 : {}", out[0]);
return;
}
if (out[1] != 0x02) {
dbgln("invalid padding byte 1 : {}", out[1]);
return;
}
size_t offset = 2;
while (offset < out.size() && out[offset])
++offset;
if (offset == out.size()) {
dbgln("garbage data, no zero to split padding");
return;
}
++offset;
if (offset - 3 < 8) {
dbgln("PS too small");
return;
}
out = out.slice(offset, out.size() - offset);
}
void RSA_PKCS1_EME::sign(ReadonlyBytes, Bytes&)
{
dbgln("FIXME: RSA_PKCS_EME::sign");
}
void RSA_PKCS1_EME::verify(ReadonlyBytes, Bytes&)
{
dbgln("FIXME: RSA_PKCS_EME::verify");
}
}
}

View file

@ -0,0 +1,235 @@
/*
* 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/Span.h>
#include <AK/Vector.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibCrypto/NumberTheory/ModularFunctions.h>
#include <LibCrypto/PK/Code/EMSA_PSS.h>
#include <LibCrypto/PK/PK.h>
namespace Crypto {
namespace PK {
template<typename Integer = u64>
class RSAPublicKey {
public:
RSAPublicKey(const Integer& n, const Integer& e)
: m_modulus(n)
, m_public_exponent(e)
{
}
RSAPublicKey()
: m_modulus(0)
, m_public_exponent(0)
{
}
//--stuff it should do
const Integer& modulus() const { return m_modulus; }
const Integer& public_exponent() const { return m_public_exponent; }
size_t length() const { return m_length; }
void set_length(size_t length) { m_length = length; }
void set(const Integer& n, const Integer& e)
{
m_modulus = n;
m_public_exponent = e;
m_length = (n.trimmed_length() * sizeof(u32));
}
private:
Integer m_modulus;
Integer m_public_exponent;
size_t m_length { 0 };
};
template<typename Integer = UnsignedBigInteger>
class RSAPrivateKey {
public:
RSAPrivateKey(const Integer& n, const Integer& d, const Integer& e)
: m_modulus(n)
, m_private_exponent(d)
, m_public_exponent(e)
{
}
RSAPrivateKey()
{
}
//--stuff it should do
const Integer& modulus() const { return m_modulus; }
const Integer& private_exponent() const { return m_private_exponent; }
const Integer& public_exponent() const { return m_public_exponent; }
size_t length() const { return m_length; }
void set_length(size_t length) { m_length = length; }
void set(const Integer& n, const Integer& d, const Integer& e)
{
m_modulus = n;
m_private_exponent = d;
m_public_exponent = e;
m_length = (n.length() * sizeof(u32));
}
private:
Integer m_modulus;
Integer m_private_exponent;
Integer m_public_exponent;
size_t m_length { 0 };
};
template<typename PubKey, typename PrivKey>
struct RSAKeyPair {
PubKey public_key;
PrivKey private_key;
};
using IntegerType = UnsignedBigInteger;
class RSA : public PKSystem<RSAPrivateKey<IntegerType>, RSAPublicKey<IntegerType>> {
template<typename T>
friend class RSA_EMSA_PSS;
public:
using KeyPairType = RSAKeyPair<PublicKeyType, PrivateKeyType>;
static KeyPairType parse_rsa_key(ReadonlyBytes);
static KeyPairType generate_key_pair(size_t bits = 256)
{
IntegerType e { 65537 }; // :P
IntegerType p, q;
IntegerType lambda;
do {
p = NumberTheory::random_big_prime(bits / 2);
q = NumberTheory::random_big_prime(bits / 2);
lambda = NumberTheory::LCM(p.minus(1), q.minus(1));
dbgln("checking combination p={}, q={}, lambda={}", p, q, lambda.length());
} while (!(NumberTheory::GCD(e, lambda) == 1));
auto n = p.multiplied_by(q);
auto d = NumberTheory::ModularInverse(e, lambda);
dbgln("Your keys are Pub(n={}, e={}) and Priv(n={}, d={})", n, e, n, d);
RSAKeyPair<PublicKeyType, PrivateKeyType> keys {
{ n, e },
{ n, d, e }
};
keys.public_key.set_length(bits / 2 / 8);
keys.private_key.set_length(bits / 2 / 8);
return keys;
}
RSA(IntegerType n, IntegerType d, IntegerType e)
{
m_public_key.set(n, e);
m_private_key.set(n, d, e);
}
RSA(PublicKeyType& pubkey, PrivateKeyType& privkey)
: PKSystem<RSAPrivateKey<IntegerType>, RSAPublicKey<IntegerType>>(pubkey, privkey)
{
}
RSA(const ByteBuffer& publicKeyPEM, const ByteBuffer& privateKeyPEM)
{
import_public_key(publicKeyPEM);
import_private_key(privateKeyPEM);
}
RSA(const StringView& privKeyPEM)
{
import_private_key(privKeyPEM.bytes());
m_public_key.set(m_private_key.modulus(), m_private_key.public_exponent());
}
// create our own keys
RSA()
{
auto pair = generate_key_pair();
m_public_key = pair.public_key;
m_private_key = pair.private_key;
}
virtual void encrypt(ReadonlyBytes in, Bytes& out) override;
virtual void decrypt(ReadonlyBytes in, Bytes& out) override;
virtual void sign(ReadonlyBytes in, Bytes& out) override;
virtual void verify(ReadonlyBytes in, Bytes& out) override;
virtual String class_name() const override { return "RSA"; }
virtual size_t output_size() const override { return m_public_key.length(); }
void import_public_key(ReadonlyBytes, bool pem = true);
void import_private_key(ReadonlyBytes, bool pem = true);
const PrivateKeyType& private_key() const { return m_private_key; }
const PublicKeyType& public_key() const { return m_public_key; }
};
template<typename HashFunction>
class RSA_EMSA_PSS {
public:
RSA_EMSA_PSS(RSA& rsa)
: m_rsa(rsa)
{
}
void sign(ReadonlyBytes in, Bytes& out);
VerificationConsistency verify(ReadonlyBytes in);
private:
EMSA_PSS<HashFunction, HashFunction::DigestSize> m_emsa_pss;
RSA m_rsa;
};
class RSA_PKCS1_EME : public RSA {
public:
// forward all constructions to RSA
template<typename... Args>
RSA_PKCS1_EME(Args... args)
: RSA(args...)
{
}
~RSA_PKCS1_EME() { }
virtual void encrypt(ReadonlyBytes in, Bytes& out) override;
virtual void decrypt(ReadonlyBytes in, Bytes& out) override;
virtual void sign(ReadonlyBytes, Bytes&) override;
virtual void verify(ReadonlyBytes, Bytes&) override;
virtual String class_name() const override { return "RSA_PKCS1-EME"; }
virtual size_t output_size() const override { return m_public_key.length(); }
};
}
}

View file

@ -0,0 +1,36 @@
/*
* 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
namespace Crypto {
enum class VerificationConsistency {
Consistent,
Inconsistent
};
}