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:
parent
dc28c07fa5
commit
13d7c09125
1857 changed files with 266 additions and 274 deletions
112
Userland/Libraries/LibCrypto/ASN1/ASN1.h
Normal file
112
Userland/Libraries/LibCrypto/ASN1/ASN1.h
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
474
Userland/Libraries/LibCrypto/ASN1/DER.h
Normal file
474
Userland/Libraries/LibCrypto/ASN1/DER.h
Normal 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;
|
||||
}
|
||||
}
|
75
Userland/Libraries/LibCrypto/ASN1/PEM.h
Normal file
75
Userland/Libraries/LibCrypto/ASN1/PEM.h
Normal 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);
|
||||
}
|
||||
|
||||
}
|
148
Userland/Libraries/LibCrypto/Authentication/GHash.cpp
Normal file
148
Userland/Libraries/LibCrypto/Authentication/GHash.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
76
Userland/Libraries/LibCrypto/Authentication/GHash.h
Normal file
76
Userland/Libraries/LibCrypto/Authentication/GHash.h
Normal 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];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
138
Userland/Libraries/LibCrypto/Authentication/HMAC.h
Normal file
138
Userland/Libraries/LibCrypto/Authentication/HMAC.h
Normal 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];
|
||||
};
|
||||
|
||||
}
|
||||
}
|
272
Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp
Normal file
272
Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
162
Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h
Normal file
162
Userland/Libraries/LibCrypto/BigInt/SignedBigInteger.h
Normal 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 });
|
||||
}
|
745
Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp
Normal file
745
Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.cpp
Normal 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());
|
||||
}
|
156
Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h
Normal file
156
Userland/Libraries/LibCrypto/BigInt/UnsignedBigInteger.h
Normal 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 });
|
||||
}
|
16
Userland/Libraries/LibCrypto/CMakeLists.txt
Normal file
16
Userland/Libraries/LibCrypto/CMakeLists.txt
Normal 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)
|
46
Userland/Libraries/LibCrypto/Checksum/Adler32.cpp
Normal file
46
Userland/Libraries/LibCrypto/Checksum/Adler32.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
58
Userland/Libraries/LibCrypto/Checksum/Adler32.h
Normal file
58
Userland/Libraries/LibCrypto/Checksum/Adler32.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
45
Userland/Libraries/LibCrypto/Checksum/CRC32.cpp
Normal file
45
Userland/Libraries/LibCrypto/Checksum/CRC32.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
86
Userland/Libraries/LibCrypto/Checksum/CRC32.h
Normal file
86
Userland/Libraries/LibCrypto/Checksum/CRC32.h
Normal 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 };
|
||||
};
|
||||
|
||||
}
|
40
Userland/Libraries/LibCrypto/Checksum/ChecksumFunction.h
Normal file
40
Userland/Libraries/LibCrypto/Checksum/ChecksumFunction.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
429
Userland/Libraries/LibCrypto/Cipher/AES.cpp
Normal file
429
Userland/Libraries/LibCrypto/Cipher/AES.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
2481
Userland/Libraries/LibCrypto/Cipher/AES.h
Normal file
2481
Userland/Libraries/LibCrypto/Cipher/AES.h
Normal file
File diff suppressed because it is too large
Load diff
139
Userland/Libraries/LibCrypto/Cipher/Cipher.h
Normal file
139
Userland/Libraries/LibCrypto/Cipher/Cipher.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
142
Userland/Libraries/LibCrypto/Cipher/Mode/CBC.h
Normal file
142
Userland/Libraries/LibCrypto/Cipher/Mode/CBC.h
Normal 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 {};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
209
Userland/Libraries/LibCrypto/Cipher/Mode/CTR.h
Normal file
209
Userland/Libraries/LibCrypto/Cipher/Mode/CTR.h
Normal 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()));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
154
Userland/Libraries/LibCrypto/Cipher/Mode/GCM.h
Normal file
154
Userland/Libraries/LibCrypto/Cipher/Mode/GCM.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
118
Userland/Libraries/LibCrypto/Cipher/Mode/Mode.h
Normal file
118
Userland/Libraries/LibCrypto/Cipher/Mode/Mode.h
Normal 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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
62
Userland/Libraries/LibCrypto/Hash/HashFunction.h
Normal file
62
Userland/Libraries/LibCrypto/Hash/HashFunction.h
Normal 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;
|
||||
};
|
||||
}
|
||||
}
|
317
Userland/Libraries/LibCrypto/Hash/HashManager.h
Normal file
317
Userland/Libraries/LibCrypto/Hash/HashManager.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
225
Userland/Libraries/LibCrypto/Hash/MD5.cpp
Normal file
225
Userland/Libraries/LibCrypto/Hash/MD5.cpp
Normal 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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
128
Userland/Libraries/LibCrypto/Hash/MD5.h
Normal file
128
Userland/Libraries/LibCrypto/Hash/MD5.h
Normal 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];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
169
Userland/Libraries/LibCrypto/Hash/SHA1.cpp
Normal file
169
Userland/Libraries/LibCrypto/Hash/SHA1.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
107
Userland/Libraries/LibCrypto/Hash/SHA1.h
Normal file
107
Userland/Libraries/LibCrypto/Hash/SHA1.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
282
Userland/Libraries/LibCrypto/Hash/SHA2.cpp
Normal file
282
Userland/Libraries/LibCrypto/Hash/SHA2.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
202
Userland/Libraries/LibCrypto/Hash/SHA2.h
Normal file
202
Userland/Libraries/LibCrypto/Hash/SHA2.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
359
Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp
Normal file
359
Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
72
Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.h
Normal file
72
Userland/Libraries/LibCrypto/NumberTheory/ModularFunctions.h
Normal 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);
|
||||
|
||||
}
|
||||
}
|
55
Userland/Libraries/LibCrypto/PK/Code/Code.h
Normal file
55
Userland/Libraries/LibCrypto/PK/Code/Code.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
180
Userland/Libraries/LibCrypto/PK/Code/EMSA_PSS.h
Normal file
180
Userland/Libraries/LibCrypto/PK/Code/EMSA_PSS.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
68
Userland/Libraries/LibCrypto/PK/PK.h
Normal file
68
Userland/Libraries/LibCrypto/PK/PK.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
329
Userland/Libraries/LibCrypto/PK/RSA.cpp
Normal file
329
Userland/Libraries/LibCrypto/PK/RSA.cpp
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
235
Userland/Libraries/LibCrypto/PK/RSA.h
Normal file
235
Userland/Libraries/LibCrypto/PK/RSA.h
Normal 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(); }
|
||||
};
|
||||
}
|
||||
}
|
36
Userland/Libraries/LibCrypto/Verification.h
Normal file
36
Userland/Libraries/LibCrypto/Verification.h
Normal 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
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue