commit
a974c69058
|
@ -3,61 +3,43 @@
|
|||
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include <cstdio>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Crypto/bn.h"
|
||||
|
||||
static void bn_zero(u8* d, u32 n)
|
||||
static void bn_zero(u8* d, int n)
|
||||
{
|
||||
memset(d, 0, n);
|
||||
std::memset(d, 0, n);
|
||||
}
|
||||
|
||||
static void bn_copy(u8* d, const u8* a, u32 n)
|
||||
static void bn_copy(u8* d, const u8* a, int n)
|
||||
{
|
||||
memcpy(d, a, n);
|
||||
std::memcpy(d, a, n);
|
||||
}
|
||||
|
||||
int bn_compare(const u8* a, const u8* b, u32 n)
|
||||
int bn_compare(const u8* a, const u8* b, int n)
|
||||
{
|
||||
u32 i;
|
||||
return std::memcmp(a, b, n);
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
void bn_sub_modulus(u8* a, const u8* N, int n)
|
||||
{
|
||||
u8 c = 0;
|
||||
for (int i = n - 1; i >= 0; --i)
|
||||
{
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
if (a[i] > b[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bn_sub_modulus(u8* a, const u8* N, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--)
|
||||
{
|
||||
dig = N[i] + c;
|
||||
u32 dig = N[i] + c;
|
||||
c = (a[i] < dig);
|
||||
a[i] -= dig;
|
||||
}
|
||||
}
|
||||
|
||||
void bn_add(u8* d, const u8* a, const u8* b, const u8* N, u32 n)
|
||||
void bn_add(u8* d, const u8* a, const u8* b, const u8* N, int n)
|
||||
{
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--)
|
||||
u8 c = 0;
|
||||
for (int i = n - 1; i >= 0; --i)
|
||||
{
|
||||
dig = a[i] + b[i] + c;
|
||||
u32 dig = a[i] + b[i] + c;
|
||||
c = (dig >= 0x100);
|
||||
d[i] = dig;
|
||||
}
|
||||
|
@ -69,32 +51,30 @@ void bn_add(u8* d, const u8* a, const u8* b, const u8* N, u32 n)
|
|||
bn_sub_modulus(d, N, n);
|
||||
}
|
||||
|
||||
void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, u32 n)
|
||||
void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, int n)
|
||||
{
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
bn_zero(d, n);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
for (u8 mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
bn_add(d, d, d, N, n);
|
||||
if ((a[i] & mask) != 0)
|
||||
bn_add(d, d, b, N, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bn_exp(u8* d, const u8* a, const u8* N, u32 n, const u8* e, u32 en)
|
||||
void bn_exp(u8* d, const u8* a, const u8* N, int n, const u8* e, int en)
|
||||
{
|
||||
u8 t[512];
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
bn_zero(d, n);
|
||||
d[n - 1] = 1;
|
||||
for (i = 0; i < en; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
for (int i = 0; i < en; i++)
|
||||
{
|
||||
for (u8 mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
bn_mul(t, d, d, N, n);
|
||||
if ((e[i] & mask) != 0)
|
||||
|
@ -102,10 +82,11 @@ void bn_exp(u8* d, const u8* a, const u8* N, u32 n, const u8* e, u32 en)
|
|||
else
|
||||
bn_copy(d, t, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only for prime N -- stupid but lazy, see if I care
|
||||
void bn_inv(u8* d, const u8* a, const u8* N, u32 n)
|
||||
void bn_inv(u8* d, const u8* a, const u8* N, int n)
|
||||
{
|
||||
u8 t[512], s[512];
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
// bignum arithmetic
|
||||
|
||||
int bn_compare(const u8* a, const u8* b, u32 n);
|
||||
void bn_sub_modulus(u8* a, const u8* N, u32 n);
|
||||
void bn_add(u8* d, const u8* a, const u8* b, const u8* N, u32 n);
|
||||
void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, u32 n);
|
||||
void bn_inv(u8* d, const u8* a, const u8* N, u32 n); // only for prime N
|
||||
void bn_exp(u8* d, const u8* a, const u8* N, u32 n, const u8* e, u32 en);
|
||||
int bn_compare(const u8* a, const u8* b, int n);
|
||||
void bn_sub_modulus(u8* a, const u8* N, int n);
|
||||
void bn_add(u8* d, const u8* a, const u8* b, const u8* N, int n);
|
||||
void bn_mul(u8* d, const u8* a, const u8* b, const u8* N, int n);
|
||||
void bn_inv(u8* d, const u8* a, const u8* N, int n); // only for prime N
|
||||
void bn_exp(u8* d, const u8* a, const u8* N, int n, const u8* e, int en);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
// Licensed under the terms of the GNU GPL, version 2
|
||||
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
|
@ -20,6 +21,158 @@
|
|||
#pragma warning(disable : 4505)
|
||||
#endif
|
||||
|
||||
namespace Common::ec
|
||||
{
|
||||
static const u8 square[16] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
|
||||
0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55};
|
||||
|
||||
struct Elt;
|
||||
static Elt operator*(const Elt& a, const Elt& b);
|
||||
|
||||
struct Elt
|
||||
{
|
||||
bool IsZero() const
|
||||
{
|
||||
return std::all_of(data.begin(), data.end(), [](u8 b) { return b == 0; });
|
||||
}
|
||||
|
||||
void MulX()
|
||||
{
|
||||
u8 carry = data[0] & 1;
|
||||
u8 x = 0;
|
||||
for (std::size_t i = 0; i < data.size() - 1; i++)
|
||||
{
|
||||
u8 y = data[i + 1];
|
||||
data[i] = x ^ (y >> 7);
|
||||
x = y << 1;
|
||||
}
|
||||
data[29] = x ^ carry;
|
||||
data[20] ^= carry << 2;
|
||||
}
|
||||
|
||||
Elt Square() const
|
||||
{
|
||||
std::array<u8, 60> wide;
|
||||
for (std::size_t i = 0; i < data.size(); i++)
|
||||
{
|
||||
wide[2 * i] = square[data[i] >> 4];
|
||||
wide[2 * i + 1] = square[data[i] & 15];
|
||||
}
|
||||
for (std::size_t i = 0; i < data.size(); i++)
|
||||
{
|
||||
u8 x = wide[i];
|
||||
|
||||
wide[i + 19] ^= x >> 7;
|
||||
wide[i + 20] ^= x << 1;
|
||||
|
||||
wide[i + 29] ^= x >> 1;
|
||||
wide[i + 30] ^= x << 7;
|
||||
}
|
||||
|
||||
u8 x = wide[30] & ~1;
|
||||
wide[49] ^= x >> 7;
|
||||
wide[50] ^= x << 1;
|
||||
wide[59] ^= x >> 1;
|
||||
wide[30] &= 1;
|
||||
|
||||
Elt result;
|
||||
std::copy(wide.cbegin() + 30, wide.cend(), result.data.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
Elt ItohTsujii(const Elt& b, std::size_t j) const
|
||||
{
|
||||
Elt t = *this;
|
||||
while (j--)
|
||||
t = t.Square();
|
||||
return t * b;
|
||||
}
|
||||
|
||||
Elt Inv() const
|
||||
{
|
||||
Elt t = ItohTsujii(*this, 1);
|
||||
Elt s = t.ItohTsujii(*this, 1);
|
||||
t = s.ItohTsujii(s, 3);
|
||||
s = t.ItohTsujii(*this, 1);
|
||||
t = s.ItohTsujii(s, 7);
|
||||
s = t.ItohTsujii(t, 14);
|
||||
t = s.ItohTsujii(*this, 1);
|
||||
s = t.ItohTsujii(t, 29);
|
||||
t = s.ItohTsujii(s, 58);
|
||||
s = t.ItohTsujii(t, 116);
|
||||
return s.Square();
|
||||
}
|
||||
|
||||
std::array<u8, 30> data{};
|
||||
};
|
||||
|
||||
static Elt operator+(const Elt& a, const Elt& b)
|
||||
{
|
||||
Elt d;
|
||||
for (std::size_t i = 0; i < std::tuple_size<decltype(Elt::data)>{}; i++)
|
||||
d.data[i] = a.data[i] ^ b.data[i];
|
||||
return d;
|
||||
}
|
||||
|
||||
static Elt operator*(const Elt& a, const Elt& b)
|
||||
{
|
||||
Elt d;
|
||||
std::size_t i = 0;
|
||||
u8 mask = 1;
|
||||
for (std::size_t n = 0; n < 233; n++)
|
||||
{
|
||||
d.MulX();
|
||||
|
||||
if ((a.data[i] & mask) != 0)
|
||||
d = d + b;
|
||||
|
||||
mask >>= 1;
|
||||
if (mask == 0)
|
||||
{
|
||||
mask = 0x80;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static Elt operator/(const Elt& dividend, const Elt& divisor)
|
||||
{
|
||||
return dividend * divisor.Inv();
|
||||
}
|
||||
|
||||
struct Point
|
||||
{
|
||||
Point() = default;
|
||||
constexpr explicit Point(Elt x, Elt y) : m_data{{std::move(x), std::move(y)}} {}
|
||||
explicit Point(const u8* data) { std::copy_n(data, sizeof(m_data), Data()); }
|
||||
|
||||
bool IsZero() const { return X().IsZero() && Y().IsZero(); }
|
||||
Elt& X() { return m_data[0]; }
|
||||
Elt& Y() { return m_data[1]; }
|
||||
u8* Data() { return m_data[0].data.data(); }
|
||||
const Elt& X() const { return m_data[0]; }
|
||||
const Elt& Y() const { return m_data[1]; }
|
||||
const u8* Data() const { return m_data[0].data.data(); }
|
||||
|
||||
Point Double() const
|
||||
{
|
||||
Point r;
|
||||
if (X().IsZero())
|
||||
return r;
|
||||
|
||||
const auto s = Y() / X() + X();
|
||||
r.X() = s.Square() + s;
|
||||
r.X().data[29] ^= 1;
|
||||
r.Y() = s * r.X() + r.X() + X().Square();
|
||||
return r;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<Elt, 2> m_data{};
|
||||
static_assert(sizeof(decltype(m_data)) == 60, "Wrong size for m_data");
|
||||
};
|
||||
|
||||
// y**2 + x*y = x**3 + x + b
|
||||
UNUSED static const u8 ec_b[30] = {0x00, 0x66, 0x64, 0x7e, 0xde, 0x6c, 0x33, 0x2c, 0x7f, 0x8c,
|
||||
0x09, 0x23, 0xbb, 0x58, 0x21, 0x3b, 0x33, 0x3b, 0x20, 0xe9,
|
||||
|
@ -31,303 +184,50 @@ static const u8 ec_N[30] = {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|||
0x8a, 0x69, 0x22, 0x03, 0x1d, 0x26, 0x03, 0xcf, 0xe0, 0xd7};
|
||||
|
||||
// base point
|
||||
static const u8 ec_G[60] = {0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1,
|
||||
0xbb, 0x75, 0x5f, 0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8,
|
||||
0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b, 0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19,
|
||||
0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, 0x28, 0xbe, 0xbf, 0x8a, 0x0b, 0xef,
|
||||
0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52};
|
||||
constexpr Point ec_G{
|
||||
{{{0x00, 0xfa, 0xc9, 0xdf, 0xcb, 0xac, 0x83, 0x13, 0xbb, 0x21, 0x39, 0xf1, 0xbb, 0x75, 0x5f,
|
||||
0xef, 0x65, 0xbc, 0x39, 0x1f, 0x8b, 0x36, 0xf8, 0xf8, 0xeb, 0x73, 0x71, 0xfd, 0x55, 0x8b}}},
|
||||
{{{0x01, 0x00, 0x6a, 0x08, 0xa4, 0x19, 0x03, 0x35, 0x06, 0x78, 0xe5, 0x85, 0x28, 0xbe, 0xbf,
|
||||
0x8a, 0x0b, 0xef, 0xf8, 0x67, 0xa7, 0xca, 0x36, 0x71, 0x6f, 0x7e, 0x01, 0xf8, 0x10, 0x52}}}};
|
||||
|
||||
static void elt_copy(u8* d, const u8* a)
|
||||
static Point operator+(const Point& a, const Point& b)
|
||||
{
|
||||
memcpy(d, a, 30);
|
||||
}
|
||||
if (a.IsZero())
|
||||
return b;
|
||||
if (b.IsZero())
|
||||
return a;
|
||||
|
||||
static void elt_zero(u8* d)
|
||||
{
|
||||
memset(d, 0, 30);
|
||||
}
|
||||
|
||||
static int elt_is_zero(const u8* d)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
if (d[i] != 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void elt_add(u8* d, const u8* a, const u8* b)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
d[i] = a[i] ^ b[i];
|
||||
}
|
||||
|
||||
static void elt_mul_x(u8* d, const u8* a)
|
||||
{
|
||||
u8 carry, x, y;
|
||||
u32 i;
|
||||
|
||||
carry = a[0] & 1;
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < 29; i++)
|
||||
Elt u = a.X() + b.X();
|
||||
if (u.IsZero())
|
||||
{
|
||||
y = a[i + 1];
|
||||
d[i] = x ^ (y >> 7);
|
||||
x = y << 1;
|
||||
u = a.Y() + b.Y();
|
||||
if (u.IsZero())
|
||||
return a.Double();
|
||||
return Point{};
|
||||
}
|
||||
d[29] = x ^ carry;
|
||||
|
||||
d[20] ^= carry << 2;
|
||||
const Elt s = (a.Y() + b.Y()) / u;
|
||||
Elt t = s.Square() + s + b.X();
|
||||
t.data[29] ^= 1;
|
||||
|
||||
const Elt rx = t + a.X();
|
||||
const Elt ry = s * t + a.Y() + rx;
|
||||
return Point{rx, ry};
|
||||
}
|
||||
|
||||
static void elt_mul(u8* d, const u8* a, const u8* b)
|
||||
static Point operator*(const u8* a, const Point& b)
|
||||
{
|
||||
u32 i, n;
|
||||
u8 mask;
|
||||
|
||||
elt_zero(d);
|
||||
|
||||
i = 0;
|
||||
mask = 1;
|
||||
for (n = 0; n < 233; n++)
|
||||
Point d;
|
||||
for (std::size_t i = 0; i < 30; i++)
|
||||
{
|
||||
elt_mul_x(d, d);
|
||||
|
||||
for (u8 mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
d = d.Double();
|
||||
if ((a[i] & mask) != 0)
|
||||
elt_add(d, d, b);
|
||||
|
||||
mask >>= 1;
|
||||
if (mask == 0)
|
||||
{
|
||||
mask = 0x80;
|
||||
i++;
|
||||
d = d + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const u8 square[16] = {0x00, 0x01, 0x04, 0x05, 0x10, 0x11, 0x14, 0x15,
|
||||
0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55};
|
||||
|
||||
static void elt_square_to_wide(u8* d, const u8* a)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
{
|
||||
d[2 * i] = square[a[i] >> 4];
|
||||
d[2 * i + 1] = square[a[i] & 15];
|
||||
}
|
||||
}
|
||||
|
||||
static void wide_reduce(u8* d)
|
||||
{
|
||||
u32 i;
|
||||
u8 x;
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
{
|
||||
x = d[i];
|
||||
|
||||
d[i + 19] ^= x >> 7;
|
||||
d[i + 20] ^= x << 1;
|
||||
|
||||
d[i + 29] ^= x >> 1;
|
||||
d[i + 30] ^= x << 7;
|
||||
}
|
||||
|
||||
x = d[30] & ~1;
|
||||
|
||||
d[49] ^= x >> 7;
|
||||
d[50] ^= x << 1;
|
||||
|
||||
d[59] ^= x >> 1;
|
||||
|
||||
d[30] &= 1;
|
||||
}
|
||||
|
||||
static void elt_square(u8* d, const u8* a)
|
||||
{
|
||||
u8 wide[60];
|
||||
|
||||
elt_square_to_wide(wide, a);
|
||||
wide_reduce(wide);
|
||||
|
||||
elt_copy(d, wide + 30);
|
||||
}
|
||||
|
||||
static void itoh_tsujii(u8* d, const u8* a, const u8* b, u32 j)
|
||||
{
|
||||
u8 t[30];
|
||||
|
||||
elt_copy(t, a);
|
||||
while (j--)
|
||||
{
|
||||
elt_square(d, t);
|
||||
elt_copy(t, d);
|
||||
}
|
||||
|
||||
elt_mul(d, t, b);
|
||||
}
|
||||
|
||||
static void elt_inv(u8* d, const u8* a)
|
||||
{
|
||||
u8 t[30];
|
||||
u8 s[30];
|
||||
|
||||
itoh_tsujii(t, a, a, 1);
|
||||
itoh_tsujii(s, t, a, 1);
|
||||
itoh_tsujii(t, s, s, 3);
|
||||
itoh_tsujii(s, t, a, 1);
|
||||
itoh_tsujii(t, s, s, 7);
|
||||
itoh_tsujii(s, t, t, 14);
|
||||
itoh_tsujii(t, s, a, 1);
|
||||
itoh_tsujii(s, t, t, 29);
|
||||
itoh_tsujii(t, s, s, 58);
|
||||
itoh_tsujii(s, t, t, 116);
|
||||
elt_square(d, s);
|
||||
}
|
||||
|
||||
UNUSED static int point_is_on_curve(u8* p)
|
||||
{
|
||||
u8 s[30], t[30];
|
||||
u8 *x, *y;
|
||||
|
||||
x = p;
|
||||
y = p + 30;
|
||||
|
||||
elt_square(t, x);
|
||||
elt_mul(s, t, x);
|
||||
|
||||
elt_add(s, s, t);
|
||||
|
||||
elt_square(t, y);
|
||||
elt_add(s, s, t);
|
||||
|
||||
elt_mul(t, x, y);
|
||||
elt_add(s, s, t);
|
||||
|
||||
elt_add(s, s, ec_b);
|
||||
|
||||
return elt_is_zero(s);
|
||||
}
|
||||
|
||||
static int point_is_zero(const u8* p)
|
||||
{
|
||||
return elt_is_zero(p) && elt_is_zero(p + 30);
|
||||
}
|
||||
|
||||
static void point_double(u8* r, const u8* p)
|
||||
{
|
||||
u8 s[30], t[30];
|
||||
const u8 *px, *py;
|
||||
u8 *rx, *ry;
|
||||
|
||||
px = p;
|
||||
py = p + 30;
|
||||
rx = r;
|
||||
ry = r + 30;
|
||||
|
||||
if (elt_is_zero(px))
|
||||
{
|
||||
elt_zero(rx);
|
||||
elt_zero(ry);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
elt_inv(t, px);
|
||||
elt_mul(s, py, t);
|
||||
elt_add(s, s, px);
|
||||
|
||||
elt_square(t, px);
|
||||
|
||||
elt_square(rx, s);
|
||||
elt_add(rx, rx, s);
|
||||
rx[29] ^= 1;
|
||||
|
||||
elt_mul(ry, s, rx);
|
||||
elt_add(ry, ry, rx);
|
||||
elt_add(ry, ry, t);
|
||||
}
|
||||
|
||||
static void point_add(u8* r, const u8* p, const u8* q)
|
||||
{
|
||||
u8 s[30], t[30], u[30];
|
||||
const u8 *px, *py, *qx, *qy;
|
||||
u8 *rx, *ry;
|
||||
|
||||
px = p;
|
||||
py = p + 30;
|
||||
qx = q;
|
||||
qy = q + 30;
|
||||
rx = r;
|
||||
ry = r + 30;
|
||||
|
||||
if (point_is_zero(p))
|
||||
{
|
||||
elt_copy(rx, qx);
|
||||
elt_copy(ry, qy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (point_is_zero(q))
|
||||
{
|
||||
elt_copy(rx, px);
|
||||
elt_copy(ry, py);
|
||||
return;
|
||||
}
|
||||
|
||||
elt_add(u, px, qx);
|
||||
|
||||
if (elt_is_zero(u))
|
||||
{
|
||||
elt_add(u, py, qy);
|
||||
if (elt_is_zero(u))
|
||||
point_double(r, p);
|
||||
else
|
||||
{
|
||||
elt_zero(rx);
|
||||
elt_zero(ry);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
elt_inv(t, u);
|
||||
elt_add(u, py, qy);
|
||||
elt_mul(s, t, u);
|
||||
|
||||
elt_square(t, s);
|
||||
elt_add(t, t, s);
|
||||
elt_add(t, t, qx);
|
||||
t[29] ^= 1;
|
||||
|
||||
elt_mul(u, s, t);
|
||||
elt_add(s, u, py);
|
||||
elt_add(rx, t, px);
|
||||
elt_add(ry, s, rx);
|
||||
}
|
||||
|
||||
void point_mul(u8* d, const u8* a, const u8* b) // a is bignum
|
||||
{
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
elt_zero(d);
|
||||
elt_zero(d + 30);
|
||||
|
||||
for (i = 0; i < 30; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
point_double(d, d);
|
||||
if ((a[i] & mask) != 0)
|
||||
point_add(d, d, b);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
static void silly_random(u8* rndArea, u8 count)
|
||||
|
@ -341,19 +241,13 @@ static void silly_random(u8* rndArea, u8 count)
|
|||
}
|
||||
}
|
||||
|
||||
void generate_ecdsa(u8* R, u8* S, const u8* k, const u8* hash)
|
||||
std::array<u8, 60> Sign(const u8* key, const u8* hash)
|
||||
{
|
||||
u8 e[30];
|
||||
u8 kk[30];
|
||||
u8 m[30];
|
||||
u8 minv[30];
|
||||
u8 mG[60];
|
||||
// FILE *fp;
|
||||
|
||||
elt_zero(e);
|
||||
u8 e[30]{};
|
||||
memcpy(e + 10, hash, 20);
|
||||
|
||||
// Changing random number generator to a lame one...
|
||||
u8 m[30];
|
||||
silly_random(m, sizeof(m));
|
||||
// fp = fopen("/dev/random", "rb");
|
||||
// if (fread(m, sizeof m, 1, fp) != 1)
|
||||
|
@ -361,55 +255,65 @@ void generate_ecdsa(u8* R, u8* S, const u8* k, const u8* hash)
|
|||
// fclose(fp);
|
||||
m[0] = 0;
|
||||
|
||||
// R = (mG).x
|
||||
|
||||
point_mul(mG, m, ec_G);
|
||||
elt_copy(R, mG);
|
||||
if (bn_compare(R, ec_N, 30) >= 0)
|
||||
bn_sub_modulus(R, ec_N, 30);
|
||||
Elt r = (m * ec_G).X();
|
||||
if (bn_compare(r.data.data(), ec_N, 30) >= 0)
|
||||
bn_sub_modulus(r.data.data(), ec_N, 30);
|
||||
|
||||
// S = m**-1*(e + Rk) (mod N)
|
||||
|
||||
elt_copy(kk, k);
|
||||
if (bn_compare(kk, ec_N, 30) >= 0)
|
||||
bn_sub_modulus(kk, ec_N, 30);
|
||||
bn_mul(S, R, kk, ec_N, 30);
|
||||
bn_add(kk, S, e, ec_N, 30);
|
||||
bn_inv(minv, m, ec_N, 30);
|
||||
bn_mul(S, minv, kk, ec_N, 30);
|
||||
u8 kk[30];
|
||||
std::copy_n(key, sizeof(kk), kk);
|
||||
if (bn_compare(kk, ec_N, sizeof(kk)) >= 0)
|
||||
bn_sub_modulus(kk, ec_N, sizeof(kk));
|
||||
Elt s;
|
||||
bn_mul(s.data.data(), r.data.data(), kk, ec_N, 30);
|
||||
bn_add(kk, s.data.data(), e, ec_N, sizeof(kk));
|
||||
u8 minv[30];
|
||||
bn_inv(minv, m, ec_N, sizeof(minv));
|
||||
bn_mul(s.data.data(), minv, kk, ec_N, 30);
|
||||
|
||||
std::array<u8, 60> signature;
|
||||
std::copy(r.data.cbegin(), r.data.cend(), signature.begin());
|
||||
std::copy(s.data.cbegin(), s.data.cend(), signature.begin() + 30);
|
||||
return signature;
|
||||
}
|
||||
|
||||
UNUSED static int check_ecdsa(u8* Q, u8* R, u8* S, const u8* hash)
|
||||
{
|
||||
u8 Sinv[30];
|
||||
u8 e[30];
|
||||
u8 w1[30], w2[30];
|
||||
u8 r1[60], r2[60];
|
||||
|
||||
bn_inv(Sinv, S, ec_N, 30);
|
||||
|
||||
elt_zero(e);
|
||||
u8 e[30]{};
|
||||
memcpy(e + 10, hash, 20);
|
||||
|
||||
u8 w1[30], w2[30];
|
||||
bn_mul(w1, e, Sinv, ec_N, 30);
|
||||
bn_mul(w2, R, Sinv, ec_N, 30);
|
||||
|
||||
point_mul(r1, w1, ec_G);
|
||||
point_mul(r2, w2, Q);
|
||||
Point r1 = w1 * ec_G + w2 * Point{Q};
|
||||
auto& rx = r1.X().data;
|
||||
if (bn_compare(rx.data(), ec_N, 30) >= 0)
|
||||
bn_sub_modulus(rx.data(), ec_N, 30);
|
||||
|
||||
point_add(r1, r1, r2);
|
||||
|
||||
if (bn_compare(r1, ec_N, 30) >= 0)
|
||||
bn_sub_modulus(r1, ec_N, 30);
|
||||
|
||||
return (bn_compare(r1, R, 30) == 0);
|
||||
return (bn_compare(rx.data(), R, 30) == 0);
|
||||
}
|
||||
|
||||
void ec_priv_to_pub(const u8* k, u8* Q)
|
||||
std::array<u8, 60> PrivToPub(const u8* key)
|
||||
{
|
||||
point_mul(Q, k, ec_G);
|
||||
const Point data = key * ec_G;
|
||||
std::array<u8, 60> result;
|
||||
std::copy_n(data.Data(), result.size(), result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::array<u8, 60> ComputeSharedSecret(const u8* private_key, const u8* public_key)
|
||||
{
|
||||
std::array<u8, 60> shared_secret;
|
||||
const Point data = private_key * Point{public_key};
|
||||
std::copy_n(data.Data(), shared_secret.size(), shared_secret.begin());
|
||||
return shared_secret;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
} // namespace Common::ec
|
||||
|
|
|
@ -4,10 +4,18 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
void generate_ecdsa(u8* R, u8* S, const u8* k, const u8* hash);
|
||||
namespace Common::ec
|
||||
{
|
||||
/// Generate a signature using ECDSA.
|
||||
std::array<u8, 60> Sign(const u8* key, const u8* hash);
|
||||
|
||||
void ec_priv_to_pub(const u8* k, u8* Q);
|
||||
/// Compute a shared secret from a private key (30 bytes) and public key (60 bytes).
|
||||
std::array<u8, 60> ComputeSharedSecret(const u8* private_key, const u8* public_key);
|
||||
|
||||
void point_mul(u8* d, const u8* a, const u8* b);
|
||||
/// Convert a ECC private key (30 bytes) to a public key (60 bytes).
|
||||
std::array<u8, 60> PrivToPub(const u8* key);
|
||||
} // namespace Common::ec
|
||||
|
|
|
@ -250,8 +250,8 @@ ReturnCode IOSC::ComputeSharedKey(Handle dest_handle, Handle private_handle, Han
|
|||
}
|
||||
|
||||
// Calculate the ECC shared secret.
|
||||
std::array<u8, 0x3c> shared_secret;
|
||||
point_mul(shared_secret.data(), private_entry->data.data(), public_entry->data.data());
|
||||
const std::array<u8, 0x3c> shared_secret =
|
||||
Common::ec::ComputeSharedSecret(private_entry->data.data(), public_entry->data.data());
|
||||
|
||||
std::array<u8, 20> sha1;
|
||||
mbedtls_sha1(shared_secret.data(), shared_secret.size() / 2, sha1.data());
|
||||
|
@ -425,7 +425,8 @@ static Certificate MakeBlankSigECCert(const char* signer, const char* name, cons
|
|||
std::strncpy(reinterpret_cast<char*>(cert_out.data()) + 0xc4, name, 0x40);
|
||||
const u32 swapped_key_id = Common::swap32(key_id);
|
||||
std::memcpy(cert_out.data() + 0x104, &swapped_key_id, sizeof(swapped_key_id));
|
||||
ec_priv_to_pub(private_key, cert_out.data() + 0x108);
|
||||
const std::array<u8, 60> public_key = Common::ec::PrivToPub(private_key);
|
||||
std::copy(public_key.cbegin(), public_key.cend(), cert_out.begin() + 0x108);
|
||||
return cert_out;
|
||||
}
|
||||
|
||||
|
@ -454,11 +455,12 @@ void IOSC::Sign(u8* sig_out, u8* ap_cert_out, u64 title_id, const u8* data, u32
|
|||
std::copy(cert.begin(), cert.end(), ap_cert_out);
|
||||
|
||||
mbedtls_sha1(ap_cert_out + 0x80, 0x100, hash.data());
|
||||
generate_ecdsa(ap_cert_out + 4, ap_cert_out + 34, m_key_entries[HANDLE_CONSOLE_KEY].data.data(),
|
||||
hash.data());
|
||||
auto signature = Common::ec::Sign(m_key_entries[HANDLE_CONSOLE_KEY].data.data(), hash.data());
|
||||
std::copy(signature.cbegin(), signature.cend(), ap_cert_out + 4);
|
||||
|
||||
mbedtls_sha1(data, data_size, hash.data());
|
||||
generate_ecdsa(sig_out, sig_out + 30, ap_priv.data(), hash.data());
|
||||
signature = Common::ec::Sign(ap_priv.data(), hash.data());
|
||||
std::copy(signature.cbegin(), signature.cend(), sig_out);
|
||||
}
|
||||
|
||||
constexpr std::array<u8, 512> ROOT_PUBLIC_KEY = {
|
||||
|
|
|
@ -4,6 +4,7 @@ add_dolphin_test(BitUtilsTest BitUtilsTest.cpp)
|
|||
add_dolphin_test(BlockingLoopTest BlockingLoopTest.cpp)
|
||||
add_dolphin_test(BusyLoopTest BusyLoopTest.cpp)
|
||||
add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp)
|
||||
add_dolphin_test(CryptoEcTest Crypto/EcTest.cpp)
|
||||
add_dolphin_test(EventTest EventTest.cpp)
|
||||
add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp)
|
||||
add_dolphin_test(FlagTest FlagTest.cpp)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2018 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Common/Crypto/ec.h"
|
||||
|
||||
constexpr std::array<u8, 30> PRIVATE_KEY{{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9,
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9,
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9}};
|
||||
|
||||
constexpr std::array<u8, 60> PUBLIC_KEY{
|
||||
{0x00, 0x21, 0x5b, 0xf7, 0x48, 0x2a, 0x64, 0x4b, 0xda, 0x9e, 0x02, 0x87, 0xaa, 0x37, 0x7d,
|
||||
0x0c, 0x5d, 0x27, 0x48, 0x72, 0xf1, 0x19, 0x45, 0x44, 0xdf, 0x74, 0x57, 0x67, 0x60, 0xcd,
|
||||
0x00, 0xa8, 0x6c, 0xe8, 0x55, 0xdd, 0x52, 0x98, 0x95, 0xc5, 0xc3, 0x3f, 0x7b, 0x0f, 0xc6,
|
||||
0x9f, 0x95, 0x8b, 0x3e, 0xe3, 0x33, 0x84, 0x2f, 0x32, 0xe9, 0x03, 0xe6, 0xfb, 0xc8, 0x51}};
|
||||
|
||||
TEST(ec, Sign)
|
||||
{
|
||||
static constexpr std::array<u8, 20> HASH{{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9,
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9}};
|
||||
const std::array<u8, 60> sig = Common::ec::Sign(PRIVATE_KEY.data(), HASH.data());
|
||||
// r and s must be non-null.
|
||||
EXPECT_FALSE(std::all_of(sig.cbegin(), sig.cbegin() + 30, [](u8 b) { return b == 0; }));
|
||||
EXPECT_FALSE(std::all_of(sig.cbegin() + 30, sig.cend(), [](u8 b) { return b == 0; }));
|
||||
}
|
||||
|
||||
TEST(ec, PrivToPub)
|
||||
{
|
||||
EXPECT_EQ(Common::ec::PrivToPub(PRIVATE_KEY.data()), PUBLIC_KEY);
|
||||
}
|
||||
|
||||
TEST(ec, GenerateSharedSecret)
|
||||
{
|
||||
static constexpr std::array<u8, 60> SECRET = {
|
||||
{0x01, 0x20, 0x2b, 0x3b, 0x63, 0x18, 0x5b, 0x2f, 0x05, 0x4f, 0xb5, 0x2c, 0xe5, 0x46, 0xc2,
|
||||
0x2d, 0x4e, 0x73, 0xf4, 0x15, 0xcb, 0xd2, 0x56, 0x7f, 0xff, 0x3f, 0x02, 0x23, 0xbe, 0xda,
|
||||
0x01, 0xf3, 0x0c, 0x34, 0xb6, 0x37, 0xbf, 0x55, 0x5b, 0x04, 0x49, 0x5a, 0x07, 0xee, 0x78,
|
||||
0xd2, 0x9a, 0x31, 0xce, 0x10, 0x42, 0xbf, 0x79, 0xc3, 0xcb, 0x22, 0x40, 0xe5, 0x94, 0x7f}};
|
||||
|
||||
EXPECT_EQ(Common::ec::ComputeSharedSecret(PRIVATE_KEY.data(), PUBLIC_KEY.data()), SECRET);
|
||||
}
|
Loading…
Reference in New Issue