mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 13:07:46 +00:00
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
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 \
|
Cipher/AES.o \
|
||||||
Hash/MD5.o \
|
Hash/MD5.o \
|
||||||
Hash/SHA2.o \
|
Hash/SHA2.o \
|
||||||
|
PK/RSA.o \
|
||||||
BigInt/UnsignedBigInteger.o
|
BigInt/UnsignedBigInteger.o
|
||||||
|
|
||||||
OBJS = $(LIBCRYPTO_OBJS)
|
OBJS = $(LIBCRYPTO_OBJS)
|
||||||
|
@ -9,7 +10,7 @@ OBJS = $(LIBCRYPTO_OBJS)
|
||||||
LIBRARY = libcrypto.a
|
LIBRARY = libcrypto.a
|
||||||
|
|
||||||
install:
|
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; \
|
mkdir -p $(SERENITY_BASE_DIR)/Root/usr/include/LibCrypto/$$dir; \
|
||||||
cp $$dir/*.h $(SERENITY_BASE_DIR)/Root/usr/include/LibCrypto/$$dir/; \
|
cp $$dir/*.h $(SERENITY_BASE_DIR)/Root/usr/include/LibCrypto/$$dir/; \
|
||||||
done
|
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/Cipher/AES.h>
|
||||||
#include <LibCrypto/Hash/MD5.h>
|
#include <LibCrypto/Hash/MD5.h>
|
||||||
#include <LibCrypto/Hash/SHA2.h>
|
#include <LibCrypto/Hash/SHA2.h>
|
||||||
|
#include <LibCrypto/PK/RSA.h>
|
||||||
#include <LibLine/Editor.h>
|
#include <LibLine/Editor.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
@ -37,6 +38,9 @@ int hmac_md5_tests();
|
||||||
int hmac_sha256_tests();
|
int hmac_sha256_tests();
|
||||||
int hmac_sha512_tests();
|
int hmac_sha512_tests();
|
||||||
|
|
||||||
|
// Public-Key
|
||||||
|
int rsa_tests();
|
||||||
|
|
||||||
// Big Integer
|
// Big Integer
|
||||||
int bigint_tests();
|
int bigint_tests();
|
||||||
|
|
||||||
|
@ -235,6 +239,9 @@ auto main(int argc, char** argv) -> int
|
||||||
printf("unknown hash function '%s'\n", suite);
|
printf("unknown hash function '%s'\n", suite);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (mode_sv == "pk") {
|
||||||
|
return rsa_tests();
|
||||||
|
}
|
||||||
if (mode_sv == "bigint") {
|
if (mode_sv == "bigint") {
|
||||||
return bigint_tests();
|
return bigint_tests();
|
||||||
}
|
}
|
||||||
|
@ -305,6 +312,12 @@ void hmac_sha256_test_process();
|
||||||
void hmac_sha512_test_name();
|
void hmac_sha512_test_name();
|
||||||
void hmac_sha512_test_process();
|
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_test_fibo500();
|
||||||
void bigint_addition_edgecases();
|
void bigint_addition_edgecases();
|
||||||
void bigint_subtraction();
|
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()
|
int bigint_tests()
|
||||||
{
|
{
|
||||||
bigint_test_fibo500();
|
bigint_test_fibo500();
|
||||||
|
@ -1047,4 +1196,36 @@ void bigint_import_export()
|
||||||
else
|
else
|
||||||
PASS;
|
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
Add a link
Reference in a new issue