LibCrypto: Implement RSA in terms of UnsignedBigInteger
This commit also adds enough ASN.1/DER to parse RSA keys
This commit is contained in:
parent
6b742c69bd
commit
7adb93ede9
Notes:
sideshowbarker
2024-07-19 07:05:21 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/7adb93ede9c Pull-request: https://github.com/SerenityOS/serenity/pull/1661 Reviewed-by: https://github.com/Dexesttp Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/itamar8910
11 changed files with 1887 additions and 1 deletions
111
Libraries/LibCrypto/ASN1/ASN1.h
Normal file
111
Libraries/LibCrypto/ASN1/ASN1.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 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
Libraries/LibCrypto/ASN1/DER.h
Normal file
474
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) {
|
||||
dbg() << "invalid header size";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t x { 0 };
|
||||
// must start with 0x02
|
||||
if ((in[x++] & 0x1f) != 0x02) {
|
||||
dbg() << "not an integer " << in[x - 1] << " (" << in[x] << " follows)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// decode length
|
||||
size_t z = in[x++];
|
||||
if ((x & 0x80) == 0) {
|
||||
// overflow
|
||||
if (x + z > length) {
|
||||
dbg() << "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) {
|
||||
dbg() << "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) {
|
||||
dbg() << "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) {
|
||||
dbg() << "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:
|
||||
dbg() << "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 {
|
||||
dbg() << "invalid length " << y;
|
||||
return false;
|
||||
}
|
||||
*out_length = y;
|
||||
return true;
|
||||
}
|
||||
|
||||
static 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) {
|
||||
dbg() << "header too small";
|
||||
return false; // invalid header
|
||||
}
|
||||
size_t x { 0 };
|
||||
if (in[x++] != 0x30) {
|
||||
dbg() << "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)) {
|
||||
dbg() << "invalid length element " << in[x];
|
||||
return false;
|
||||
}
|
||||
|
||||
y = in[x++] & 0x7f;
|
||||
|
||||
if (x + y > in_length) {
|
||||
dbg() << "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) {
|
||||
dbg() << "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)) {
|
||||
dbg() << "could not decode an integer";
|
||||
return false;
|
||||
}
|
||||
if (!der_length_integer((UnsignedBigInteger*)data, &z)) {
|
||||
dbg() << "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) {
|
||||
dbg() << "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:
|
||||
dbg() << "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) {
|
||||
dbg() << "index " << i << " was not read";
|
||||
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;
|
||||
}
|
||||
}
|
74
Libraries/LibCrypto/ASN1/PEM.h
Normal file
74
Libraries/LibCrypto/ASN1/PEM.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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/ASN1/ASN1.h>
|
||||
#include <LibCrypto/ASN1/DER.h>
|
||||
|
||||
namespace Crypto {
|
||||
|
||||
static ByteBuffer decode_pem(const ByteBuffer& 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_pointer(start_at), end_idx - start_at, output);
|
||||
break;
|
||||
}
|
||||
} else
|
||||
start_at = i + 1;
|
||||
}
|
||||
}
|
||||
return output.slice(0, idx);
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@ LIBCRYPTO_OBJS = \
|
|||
Cipher/AES.o \
|
||||
Hash/MD5.o \
|
||||
Hash/SHA2.o \
|
||||
PK/RSA.o \
|
||||
BigInt/UnsignedBigInteger.o
|
||||
|
||||
OBJS = $(LIBCRYPTO_OBJS)
|
||||
|
@ -9,7 +10,7 @@ OBJS = $(LIBCRYPTO_OBJS)
|
|||
LIBRARY = libcrypto.a
|
||||
|
||||
install:
|
||||
for dir in . Cipher Cipher/Mode Hash; do \
|
||||
for dir in . Cipher Cipher/Mode Hash PK PK/Code; do \
|
||||
mkdir -p $(SERENITY_BASE_DIR)/Root/usr/include/LibCrypto/$$dir; \
|
||||
cp $$dir/*.h $(SERENITY_BASE_DIR)/Root/usr/include/LibCrypto/$$dir/; \
|
||||
done
|
||||
|
|
202
Libraries/LibCrypto/NumberTheory/ModularFunctions.h
Normal file
202
Libraries/LibCrypto/NumberTheory/ModularFunctions.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 <LibCrypto/BigInt/UnsignedBigInteger.h>
|
||||
|
||||
//#define NT_DEBUG
|
||||
|
||||
namespace Crypto {
|
||||
namespace NumberTheory {
|
||||
static auto ModularInverse(const UnsignedBigInteger& a_, const UnsignedBigInteger& b) -> UnsignedBigInteger
|
||||
{
|
||||
if (b == 1)
|
||||
return { 1 };
|
||||
|
||||
auto a = a_;
|
||||
auto u = a;
|
||||
if (a.words()[0] % 2 == 0)
|
||||
u = u.add(b);
|
||||
|
||||
auto v = b;
|
||||
auto x = UnsignedBigInteger { 0 };
|
||||
auto d = b.sub(1);
|
||||
|
||||
while (!(v == 1)) {
|
||||
while (v < u) {
|
||||
u = u.sub(v);
|
||||
d = d.add(x);
|
||||
while (u.words()[0] % 2 == 0) {
|
||||
if (d.words()[0] % 2 == 1) {
|
||||
d = d.add(b);
|
||||
}
|
||||
u = u.divide(2).quotient;
|
||||
d = d.divide(2).quotient;
|
||||
}
|
||||
}
|
||||
v = v.sub(u);
|
||||
x = x.add(d);
|
||||
while (v.words()[0] % 2 == 0) {
|
||||
if (x.words()[0] % 2 == 1) {
|
||||
x = x.add(b);
|
||||
}
|
||||
v = v.divide(2).quotient;
|
||||
x = x.divide(2).quotient;
|
||||
}
|
||||
}
|
||||
return x.divide(b).remainder;
|
||||
}
|
||||
|
||||
static auto ModularPower(const UnsignedBigInteger& b, const UnsignedBigInteger& e, const UnsignedBigInteger& m) -> UnsignedBigInteger
|
||||
{
|
||||
if (m == 1)
|
||||
return 0;
|
||||
|
||||
UnsignedBigInteger ep { e };
|
||||
UnsignedBigInteger base { b };
|
||||
UnsignedBigInteger exp { 1 };
|
||||
|
||||
while (!(ep < 1)) {
|
||||
#ifdef NT_DEBUG
|
||||
dbg() << ep.to_base10();
|
||||
#endif
|
||||
if (ep.words()[0] % 2 == 1) {
|
||||
exp = exp.multiply(base).divide(m).remainder;
|
||||
}
|
||||
ep = ep.divide(2).quotient;
|
||||
base = base.multiply(base).divide(m).remainder;
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
static auto GCD(const UnsignedBigInteger& a, const UnsignedBigInteger& b) -> UnsignedBigInteger
|
||||
{
|
||||
UnsignedBigInteger a_ { a }, b_ { b };
|
||||
for (;;) {
|
||||
if (a_ == 0)
|
||||
return b_;
|
||||
b_ = b_.divide(a_).remainder;
|
||||
if (b_ == 0)
|
||||
return a_;
|
||||
a_ = a_.divide(b_).remainder;
|
||||
}
|
||||
}
|
||||
|
||||
static auto LCM(const UnsignedBigInteger& a, const UnsignedBigInteger& b) -> UnsignedBigInteger
|
||||
{
|
||||
auto temp = GCD(a, b);
|
||||
|
||||
auto div = a.divide(temp);
|
||||
|
||||
#ifdef NT_DEBUG
|
||||
dbg() << "quot: " << div.quotient << " rem: " << div.remainder;
|
||||
#endif
|
||||
return temp == 0 ? 0 : (a.divide(temp).quotient.multiply(b));
|
||||
}
|
||||
|
||||
template <size_t test_count>
|
||||
static bool MR_primality_test(UnsignedBigInteger n, const Vector<UnsignedBigInteger, test_count>& tests)
|
||||
{
|
||||
auto prev = n.sub({ 1 });
|
||||
auto b = prev;
|
||||
auto r = 0;
|
||||
|
||||
auto div_result = b.divide(2);
|
||||
while (div_result.quotient == 0) {
|
||||
div_result = b.divide(2);
|
||||
b = div_result.quotient;
|
||||
++r;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < tests.size(); ++i) {
|
||||
auto return_ = true;
|
||||
if (n < tests[i])
|
||||
continue;
|
||||
auto x = ModularPower(tests[i], b, n);
|
||||
if (x == 1 || x == prev)
|
||||
continue;
|
||||
for (auto d = r - 1; d != 0; --d) {
|
||||
x = ModularPower(x, 2, n);
|
||||
if (x == 1)
|
||||
return false;
|
||||
if (x == prev) {
|
||||
return_ = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (return_)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static UnsignedBigInteger random_number(const UnsignedBigInteger& min, const UnsignedBigInteger& max)
|
||||
{
|
||||
ASSERT(min < max);
|
||||
auto range = max.minus(min);
|
||||
UnsignedBigInteger base;
|
||||
// FIXME: Need a cryptographically secure rng
|
||||
auto size = range.trimmed_length() * sizeof(u32);
|
||||
u8 buf[size];
|
||||
arc4random_buf(buf, size);
|
||||
Vector<u32> vec;
|
||||
for (size_t i = 0; i < size / sizeof(u32); ++i) {
|
||||
vec.append(*(u32*)buf + i);
|
||||
}
|
||||
UnsignedBigInteger offset { move(vec) };
|
||||
return offset.add(min);
|
||||
}
|
||||
|
||||
static bool is_probably_prime(const UnsignedBigInteger& p)
|
||||
{
|
||||
if (p == 2 || p == 3 || p == 5)
|
||||
return true;
|
||||
if (p < 49)
|
||||
return true;
|
||||
|
||||
Vector<UnsignedBigInteger, 256> tests;
|
||||
UnsignedBigInteger seven { 7 };
|
||||
for (size_t i = 0; i < tests.size(); ++i)
|
||||
tests.append(random_number(seven, p.sub(2)));
|
||||
|
||||
return MR_primality_test(p, tests);
|
||||
}
|
||||
|
||||
static 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).sub(1);
|
||||
for (;;) {
|
||||
auto p = random_number(min, max);
|
||||
if (is_probably_prime(p))
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
Libraries/LibCrypto/PK/Code/Code.h
Normal file
59
Libraries/LibCrypto/PK/Code/Code.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
namespace Crypto {
|
||||
namespace PK {
|
||||
|
||||
enum class VerificationConsistency {
|
||||
Consistent,
|
||||
Inconsistent
|
||||
};
|
||||
|
||||
template <typename HashFunction>
|
||||
class Code {
|
||||
public:
|
||||
template <typename... Args>
|
||||
Code(Args... args)
|
||||
: m_hasher(args...)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void encode(const ByteBuffer& in, ByteBuffer& out, size_t em_bits) = 0;
|
||||
virtual VerificationConsistency verify(const ByteBuffer& msg, const ByteBuffer& emsg, size_t em_bits) = 0;
|
||||
|
||||
const HashFunction& hasher() const { return m_hasher; }
|
||||
HashFunction& hasher() { return m_hasher; }
|
||||
|
||||
protected:
|
||||
HashFunction m_hasher;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
179
Libraries/LibCrypto/PK/Code/EMSA_PSS.h
Normal file
179
Libraries/LibCrypto/PK/Code/EMSA_PSS.h
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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/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 = ByteBuffer::wrap(m_data_buffer, sizeof(m_data_buffer));
|
||||
}
|
||||
|
||||
static constexpr auto SaltLength = SaltSize;
|
||||
|
||||
virtual void encode(const ByteBuffer& 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];
|
||||
|
||||
arc4random_buf(salt, SaltLength);
|
||||
|
||||
if (em_length < hash_length + SaltLength + 2) {
|
||||
dbg() << "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 = ByteBuffer::wrap(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 = ByteBuffer::wrap(DB_mask, mask_length);
|
||||
// FIXME: we should probably allow reading from u8*
|
||||
auto hash_buffer = ByteBuffer::wrap(hash.data, HashFunction::DigestSize);
|
||||
MGF1(hash_buffer, 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(const ByteBuffer& msg, const ByteBuffer& 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_view(0, mask_length);
|
||||
auto H = emsg.slice_view(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 = ByteBuffer::wrap(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 = ByteBuffer::wrap(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(const ByteBuffer& seed, size_t length, ByteBuffer& 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];
|
||||
ByteBuffer m_buffer;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
68
Libraries/LibCrypto/PK/PK.h
Normal file
68
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(const ByteBuffer& in, ByteBuffer& out) = 0;
|
||||
virtual void decrypt(const ByteBuffer& in, ByteBuffer& out) = 0;
|
||||
|
||||
virtual void sign(const ByteBuffer& in, ByteBuffer& out) = 0;
|
||||
virtual void verify(const ByteBuffer& in, ByteBuffer& out) = 0;
|
||||
|
||||
virtual String class_name() const = 0;
|
||||
|
||||
virtual size_t output_size() const = 0;
|
||||
|
||||
protected:
|
||||
PublicKeyType m_public_key;
|
||||
PrivateKeyType m_private_key;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
303
Libraries/LibCrypto/PK/RSA.cpp
Normal file
303
Libraries/LibCrypto/PK/RSA.cpp
Normal file
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* 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/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(const ByteBuffer& 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);
|
||||
|
||||
dbg() << "we were offered " << in.size() << " bytes of input";
|
||||
|
||||
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
|
||||
dbg() << "bad pubkey: " << e << " in " << 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
|
||||
dbg() << "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)) {
|
||||
dbg() << "bad privkey " << 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
|
||||
dbg() << "Unsupported key type";
|
||||
return keypair;
|
||||
}
|
||||
// it's a broken public key
|
||||
keypair.public_key.set(n, 65537);
|
||||
return keypair;
|
||||
}
|
||||
|
||||
void RSA::encrypt(const ByteBuffer& in, ByteBuffer& out)
|
||||
{
|
||||
dbg() << "in size: " << in.size();
|
||||
auto in_integer = UnsignedBigInteger::import_data(in.data(), in.size());
|
||||
if (!(in_integer < m_public_key.modulus())) {
|
||||
dbg() << "value too large for key";
|
||||
out.clear();
|
||||
return;
|
||||
}
|
||||
auto exp = NumberTheory::ModularPower(in_integer, m_public_key.public_exponent(), m_public_key.modulus());
|
||||
auto size = exp.export_data(out);
|
||||
// FIXME: We should probably not do this...
|
||||
if (size != out.size())
|
||||
out = out.slice(out.size() - size, size);
|
||||
}
|
||||
|
||||
void RSA::decrypt(const ByteBuffer& in, ByteBuffer& 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(const ByteBuffer& in, ByteBuffer& 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(const ByteBuffer& in, ByteBuffer& 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(const ByteBuffer& buffer, bool pem)
|
||||
{
|
||||
// so gods help me, I hate DER
|
||||
auto decoded_buffer = pem ? decode_pem(buffer) : buffer;
|
||||
auto key = parse_rsa_key(decoded_buffer);
|
||||
if (!key.private_key.length()) {
|
||||
dbg() << "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(const ByteBuffer& buffer, bool pem)
|
||||
{
|
||||
// so gods help me, I hate DER
|
||||
auto decoded_buffer = pem ? decode_pem(buffer) : buffer;
|
||||
auto key = parse_rsa_key(decoded_buffer);
|
||||
if (!key.public_key.length()) {
|
||||
dbg() << "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(const ByteBuffer& in, ByteBuffer& 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 = ByteBuffer::wrap(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(const ByteBuffer& 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 = ByteBuffer::wrap(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(const ByteBuffer& in, ByteBuffer& out)
|
||||
{
|
||||
auto mod_len = (m_public_key.modulus().trimmed_length() * sizeof(u32) * 8 + 7) / 8;
|
||||
dbg() << "key size: " << mod_len;
|
||||
if (in.size() > mod_len - 11) {
|
||||
dbg() << "message too long :(";
|
||||
out.trim(0);
|
||||
return;
|
||||
}
|
||||
if (out.size() < mod_len) {
|
||||
dbg() << "output buffer too small";
|
||||
return;
|
||||
}
|
||||
|
||||
auto ps_length = mod_len - in.size() - 3;
|
||||
u8 ps[ps_length];
|
||||
|
||||
arc4random_buf(ps, ps_length);
|
||||
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.trim(3 + ps_length + in.size()); // should be a single block
|
||||
|
||||
dbg() << "padded output size: " << 3 + ps_length + in.size() << " buffer size: " << out.size();
|
||||
|
||||
RSA::encrypt(out, out);
|
||||
}
|
||||
void RSA_PKCS1_EME::decrypt(const ByteBuffer& in, ByteBuffer& out)
|
||||
{
|
||||
auto mod_len = (m_public_key.modulus().trimmed_length() * sizeof(u32) * 8 + 7) / 8;
|
||||
if (in.size() != mod_len) {
|
||||
dbg() << "decryption error: wrong amount of data: " << in.size();
|
||||
out.trim(0);
|
||||
return;
|
||||
}
|
||||
|
||||
RSA::decrypt(in, out);
|
||||
|
||||
if (out.size() < RSA::output_size()) {
|
||||
dbg() << "decryption error: not enough data after decryption: " << out.size();
|
||||
out.trim(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (out[0] != 0x00) {
|
||||
dbg() << "invalid padding byte 0 : " << out[0];
|
||||
return;
|
||||
}
|
||||
|
||||
if (out[1] != 0x02) {
|
||||
dbg() << "invalid padding byte 1" << out[1];
|
||||
return;
|
||||
}
|
||||
|
||||
size_t offset = 2;
|
||||
while (offset < out.size() && out[offset])
|
||||
++offset;
|
||||
|
||||
if (offset == out.size()) {
|
||||
dbg() << "garbage data, no zero to split padding";
|
||||
return;
|
||||
}
|
||||
|
||||
++offset;
|
||||
|
||||
if (offset - 3 < 8) {
|
||||
dbg() << "PS too small";
|
||||
return;
|
||||
}
|
||||
|
||||
out = out.slice(offset, out.size() - offset);
|
||||
}
|
||||
|
||||
void RSA_PKCS1_EME::sign(const ByteBuffer&, ByteBuffer&)
|
||||
{
|
||||
dbg() << "FIXME: RSA_PKCS_EME::sign";
|
||||
}
|
||||
void RSA_PKCS1_EME::verify(const ByteBuffer&, ByteBuffer&)
|
||||
{
|
||||
dbg() << "FIXME: RSA_PKCS_EME::verify";
|
||||
}
|
||||
}
|
||||
}
|
234
Libraries/LibCrypto/PK/RSA.h
Normal file
234
Libraries/LibCrypto/PK/RSA.h
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* 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/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(const ByteBuffer&);
|
||||
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.sub(1), q.sub(1));
|
||||
dbg() << "checking combination p=" << p << ", q=" << q << ", lambda=" << lambda.length();
|
||||
} while (!(NumberTheory::GCD(e, lambda) == 1));
|
||||
|
||||
auto n = p.multiply(q);
|
||||
|
||||
auto d = NumberTheory::ModularInverse(e, lambda);
|
||||
dbg() << "Your keys are Pub{n=" << n << ", e=" << e << "} and Priv{n=" << n << ", d=" << 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(ByteBuffer::wrap(privKeyPEM.characters_without_null_termination(), privKeyPEM.length()));
|
||||
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(const ByteBuffer& in, ByteBuffer& out) override;
|
||||
virtual void decrypt(const ByteBuffer& in, ByteBuffer& out) override;
|
||||
|
||||
virtual void sign(const ByteBuffer& in, ByteBuffer& out) override;
|
||||
virtual void verify(const ByteBuffer& in, ByteBuffer& 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(const ByteBuffer& buffer, bool pem = true);
|
||||
void import_private_key(const ByteBuffer& buffer, 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(const ByteBuffer& in, ByteBuffer& out);
|
||||
VerificationConsistency verify(const ByteBuffer& 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(const ByteBuffer& in, ByteBuffer& out) override;
|
||||
virtual void decrypt(const ByteBuffer& in, ByteBuffer& out) override;
|
||||
|
||||
virtual void sign(const ByteBuffer&, ByteBuffer&) override;
|
||||
virtual void verify(const ByteBuffer&, ByteBuffer&) override;
|
||||
|
||||
virtual String class_name() const override { return "RSA_PKCS1-EME"; }
|
||||
virtual size_t output_size() const override { return m_public_key.length(); }
|
||||
};
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
#include <LibCrypto/Cipher/AES.h>
|
||||
#include <LibCrypto/Hash/MD5.h>
|
||||
#include <LibCrypto/Hash/SHA2.h>
|
||||
#include <LibCrypto/PK/RSA.h>
|
||||
#include <LibLine/Editor.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
@ -37,6 +38,9 @@ int hmac_md5_tests();
|
|||
int hmac_sha256_tests();
|
||||
int hmac_sha512_tests();
|
||||
|
||||
// Public-Key
|
||||
int rsa_tests();
|
||||
|
||||
// Big Integer
|
||||
int bigint_tests();
|
||||
|
||||
|
@ -235,6 +239,9 @@ auto main(int argc, char** argv) -> int
|
|||
printf("unknown hash function '%s'\n", suite);
|
||||
return 1;
|
||||
}
|
||||
if (mode_sv == "pk") {
|
||||
return rsa_tests();
|
||||
}
|
||||
if (mode_sv == "bigint") {
|
||||
return bigint_tests();
|
||||
}
|
||||
|
@ -305,6 +312,12 @@ void hmac_sha256_test_process();
|
|||
void hmac_sha512_test_name();
|
||||
void hmac_sha512_test_process();
|
||||
|
||||
void rsa_test_encrypt();
|
||||
void rsa_test_der_parse();
|
||||
void rsa_test_encrypt_decrypt();
|
||||
void rsa_emsa_pss_test_create();
|
||||
void bigint_test_number_theory(); // FIXME: we should really move these num theory stuff out
|
||||
|
||||
void bigint_test_fibo500();
|
||||
void bigint_addition_edgecases();
|
||||
void bigint_subtraction();
|
||||
|
@ -801,6 +814,142 @@ void hmac_sha512_test_process()
|
|||
}
|
||||
}
|
||||
|
||||
int rsa_tests()
|
||||
{
|
||||
rsa_test_encrypt();
|
||||
rsa_test_der_parse();
|
||||
bigint_test_number_theory();
|
||||
rsa_test_encrypt_decrypt();
|
||||
rsa_emsa_pss_test_create();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsa_test_encrypt()
|
||||
{
|
||||
{
|
||||
I_TEST((RSA RAW | Encryption));
|
||||
ByteBuffer data { "hellohellohellohellohellohellohellohellohellohellohellohello123-"_b };
|
||||
u8 result[] { 0x6f, 0x7b, 0xe2, 0xd3, 0x95, 0xf8, 0x8d, 0x87, 0x6d, 0x10, 0x5e, 0xc3, 0xcd, 0xf7, 0xbb, 0xa6, 0x62, 0x8e, 0x45, 0xa0, 0xf1, 0xe5, 0x0f, 0xdf, 0x69, 0xcb, 0xb6, 0xd5, 0x42, 0x06, 0x7d, 0x72, 0xa9, 0x5e, 0xae, 0xbf, 0xbf, 0x0f, 0xe0, 0xeb, 0x31, 0x31, 0xca, 0x8a, 0x81, 0x1e, 0xb9, 0xec, 0x6d, 0xcc, 0xb8, 0xa4, 0xac, 0xa3, 0x31, 0x05, 0xa9, 0xac, 0xc9, 0xd3, 0xe6, 0x2a, 0x18, 0xfe };
|
||||
Crypto::PK::RSA rsa(
|
||||
"8126832723025844890518845777858816391166654950553329127845898924164623511718747856014227624997335860970996746552094406240834082304784428582653994490504519"_bigint,
|
||||
"4234603516465654167360850580101327813936403862038934287300450163438938741499875303761385527882335478349599685406941909381269804396099893549838642251053393"_bigint,
|
||||
"65537"_bigint);
|
||||
u8 buffer[rsa.output_size()];
|
||||
auto buf = ByteBuffer::wrap(buffer, sizeof(buffer));
|
||||
rsa.encrypt(data, buf);
|
||||
if (memcmp(result, buf.data(), buf.size())) {
|
||||
FAIL(Invalid encryption result);
|
||||
print_buffer(buf, 16);
|
||||
} else {
|
||||
PASS;
|
||||
}
|
||||
}
|
||||
{
|
||||
I_TEST((RSA PKCS #1 1.5 | Encryption));
|
||||
ByteBuffer data { "hellohellohellohellohellohellohellohellohello123-"_b };
|
||||
Crypto::PK::RSA_PKCS1_EME rsa(
|
||||
"8126832723025844890518845777858816391166654950553329127845898924164623511718747856014227624997335860970996746552094406240834082304784428582653994490504519"_bigint,
|
||||
"4234603516465654167360850580101327813936403862038934287300450163438938741499875303761385527882335478349599685406941909381269804396099893549838642251053393"_bigint,
|
||||
"65537"_bigint);
|
||||
u8 buffer[rsa.output_size()];
|
||||
auto buf = ByteBuffer::wrap(buffer, sizeof(buffer));
|
||||
rsa.encrypt(data, buf);
|
||||
rsa.decrypt(buf, buf);
|
||||
|
||||
if (memcmp(buf.data(), "hellohellohellohellohellohellohellohellohello123-", 49))
|
||||
FAIL(Invalid encryption);
|
||||
else {
|
||||
dbg() << "out size " << buf.size() << " values: " << StringView { (char*)buf.data(), buf.size() };
|
||||
|
||||
PASS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bigint_test_number_theory()
|
||||
{
|
||||
{
|
||||
I_TEST((Number Theory | Modular Inverse));
|
||||
if (Crypto::NumberTheory::ModularInverse(7, 87) == 25)
|
||||
PASS;
|
||||
else
|
||||
FAIL(Invalid result);
|
||||
}
|
||||
{
|
||||
I_TEST((Number Theory | Modular Power));
|
||||
auto exp = Crypto::NumberTheory::ModularPower(
|
||||
Crypto::UnsignedBigInteger::from_base10("2988348162058574136915891421498819466320163312926952423791023078876139"),
|
||||
Crypto::UnsignedBigInteger::from_base10("2351399303373464486466122544523690094744975233415544072992656881240319"),
|
||||
10000);
|
||||
|
||||
if (exp == 3059) {
|
||||
PASS;
|
||||
} else {
|
||||
FAIL(Invalid result);
|
||||
puts(exp.to_base10().characters());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rsa_emsa_pss_test_create()
|
||||
{
|
||||
{
|
||||
// This is a template validity test
|
||||
I_TEST((RSA EMSA_PSS | Construction));
|
||||
Crypto::PK::RSA rsa;
|
||||
Crypto::PK::RSA_EMSA_PSS<Crypto::Hash::SHA256> rsa_esma_pss(rsa);
|
||||
PASS;
|
||||
}
|
||||
}
|
||||
|
||||
void rsa_test_der_parse()
|
||||
{
|
||||
I_TEST((RSA | ASN1 DER / PEM encoded Key import));
|
||||
auto privkey = R"(-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOgIBAAJBAJsrIYHxs1YL9tpfodaWs1lJoMdF4kgFisUFSj6nvBhJUlmBh607AlgTaX0E
|
||||
DGPYycXYGZ2n6rqmms5lpDXBpUcCAwEAAQJAUNpPkmtEHDENxsoQBUXvXDYeXdePSiIBJhpU
|
||||
joNOYoR5R9z5oX2cpcyykQ58FC2vKKg+x8N6xczG7qO95tw5UQIhAN354CP/FA+uTeJ6KJ+i
|
||||
zCBCl58CjNCzO0s5HTc56el5AiEAsvPKXo5/9gS/S4UzDRP6abq7GreixTfjR8LXidk3FL8C
|
||||
IQCTjYI861Y+hjMnlORkGSdvWlTHUj6gjEOh4TlWeJzQoQIgAxMZOQKtxCZUuxFwzRq4xLRG
|
||||
nrDlBQpuxz7bwSyQO7UCIHrYMnDohgNbwtA5ZpW3H1cKKQQvueWm6sxW9P5sUrZ3
|
||||
-----END RSA PRIVATE KEY-----)";
|
||||
|
||||
Crypto::PK::RSA rsa(privkey);
|
||||
if (rsa.public_key().public_exponent() == 65537) {
|
||||
if (rsa.private_key().private_exponent() == "4234603516465654167360850580101327813936403862038934287300450163438938741499875303761385527882335478349599685406941909381269804396099893549838642251053393"_bigint) {
|
||||
PASS;
|
||||
} else
|
||||
FAIL(Invalid private exponent);
|
||||
} else {
|
||||
FAIL(Invalid public exponent);
|
||||
}
|
||||
}
|
||||
|
||||
void rsa_test_encrypt_decrypt()
|
||||
{
|
||||
I_TEST((RSA | Encrypt));
|
||||
dbg() << " creating rsa object";
|
||||
Crypto::PK::RSA rsa(
|
||||
"9527497237087650398000977129550904920919162360737979403539302312977329868395261515707123424679295515888026193056908173564681660256268221509339074678416049"_bigint,
|
||||
"39542231845947188736992321577701849924317746648774438832456325878966594812143638244746284968851807975097653255909707366086606867657273809465195392910913"_bigint,
|
||||
"65537"_bigint);
|
||||
dbg() << "Output size: " << rsa.output_size();
|
||||
auto dec = ByteBuffer::create_zeroed(rsa.output_size());
|
||||
auto enc = ByteBuffer::create_zeroed(rsa.output_size());
|
||||
enc.overwrite(0, "WellHelloFriendsWellHelloFriendsWellHelloFriendsWellHelloFriends", 64);
|
||||
|
||||
rsa.encrypt(enc, dec);
|
||||
rsa.decrypt(dec, enc);
|
||||
|
||||
dbg() << "enc size " << enc.size() << " dec size " << dec.size();
|
||||
|
||||
if (memcmp(enc.data(), "WellHelloFriendsWellHelloFriendsWellHelloFriendsWellHelloFriends", 64) != 0) {
|
||||
FAIL(Could not encrypt then decrypt);
|
||||
} else {
|
||||
PASS;
|
||||
}
|
||||
}
|
||||
|
||||
int bigint_tests()
|
||||
{
|
||||
bigint_test_fibo500();
|
||||
|
@ -1047,4 +1196,36 @@ void bigint_import_export()
|
|||
else
|
||||
PASS;
|
||||
}
|
||||
{
|
||||
I_TEST((BigInteger | BigEndian Encode / Decode roundtrip));
|
||||
u8 target_buffer[128];
|
||||
auto encoded = "12345678901234567890"_bigint;
|
||||
auto size = encoded.export_data(target_buffer, 128);
|
||||
auto decoded = Crypto::UnsignedBigInteger::import_data(target_buffer, size);
|
||||
if (encoded != decoded)
|
||||
FAIL(Could not roundtrip);
|
||||
else
|
||||
PASS;
|
||||
}
|
||||
{
|
||||
I_TEST((BigInteger | BigEndian Import));
|
||||
auto number = Crypto::UnsignedBigInteger::import_data("hello");
|
||||
if (number == "448378203247"_bigint) {
|
||||
PASS;
|
||||
} else {
|
||||
FAIL(Invalid value);
|
||||
}
|
||||
}
|
||||
{
|
||||
I_TEST((BigInteger | BigEndian Export));
|
||||
auto number = "448378203247"_bigint;
|
||||
char exported[8] { 0 };
|
||||
auto exported_length = number.export_data((u8*)exported, 8);
|
||||
if (exported_length == 5 && memcmp(exported + 3, "hello", 5) == 0) {
|
||||
PASS;
|
||||
} else {
|
||||
FAIL(Invalid value);
|
||||
print_buffer(ByteBuffer::wrap(exported - exported_length + 8, exported_length), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue