From b9dd94b9b2a409459d6231cb6f196dfeefc85c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 16 May 2018 00:27:43 +0200 Subject: [PATCH 1/4] bn: Use int instead of u32 for sizes The loops relied on unsigned integer overflow, which is not immediately obvious. Replace them with less clever variants that are clearer. Also implement bn_compare using std::memcmp. --- Source/Core/Common/Crypto/bn.cpp | 75 ++++++++++++-------------------- Source/Core/Common/Crypto/bn.h | 12 ++--- 2 files changed, 34 insertions(+), 53 deletions(-) diff --git a/Source/Core/Common/Crypto/bn.cpp b/Source/Core/Common/Crypto/bn.cpp index 78ea444384..339b269d39 100644 --- a/Source/Core/Common/Crypto/bn.cpp +++ b/Source/Core/Common/Crypto/bn.cpp @@ -3,61 +3,43 @@ // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt #include -#include +#include #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]; diff --git a/Source/Core/Common/Crypto/bn.h b/Source/Core/Common/Crypto/bn.h index 5c0c85b157..7ab2ab3fb1 100644 --- a/Source/Core/Common/Crypto/bn.h +++ b/Source/Core/Common/Crypto/bn.h @@ -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); From e83591f188f329e9f29f2c5c59ee68a551ac454c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 15 May 2018 21:24:45 +0200 Subject: [PATCH 2/4] ec: Avoid exposing internal function --- Source/Core/Common/Crypto/ec.cpp | 8 +++++++- Source/Core/Common/Crypto/ec.h | 4 +++- Source/Core/Core/IOS/IOSC.cpp | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Core/Common/Crypto/ec.cpp b/Source/Core/Common/Crypto/ec.cpp index 5fe7e9bb2b..27b9c32325 100644 --- a/Source/Core/Common/Crypto/ec.cpp +++ b/Source/Core/Common/Crypto/ec.cpp @@ -313,7 +313,7 @@ static void point_add(u8* r, const u8* p, const u8* q) elt_add(ry, s, rx); } -void point_mul(u8* d, const u8* a, const u8* b) // a is bignum +static void point_mul(u8* d, const u8* a, const u8* b) // a is bignum { u32 i; u8 mask; @@ -410,6 +410,12 @@ void ec_priv_to_pub(const u8* k, u8* Q) point_mul(Q, k, ec_G); } +std::array ComputeSharedSecret(const u8* private_key, const u8* public_key) +{ + std::array shared_secret; + point_mul(shared_secret.data(), private_key, public_key); + return shared_secret; +} #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/Source/Core/Common/Crypto/ec.h b/Source/Core/Common/Crypto/ec.h index 3bf8b9904f..da2281efda 100644 --- a/Source/Core/Common/Crypto/ec.h +++ b/Source/Core/Common/Crypto/ec.h @@ -4,10 +4,12 @@ #pragma once +#include + #include "Common/CommonTypes.h" void generate_ecdsa(u8* R, u8* S, const u8* k, const u8* hash); void ec_priv_to_pub(const u8* k, u8* Q); -void point_mul(u8* d, const u8* a, const u8* b); +std::array ComputeSharedSecret(const u8* private_key, const u8* public_key); diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp index 7f47421ca8..d344e40b74 100644 --- a/Source/Core/Core/IOS/IOSC.cpp +++ b/Source/Core/Core/IOS/IOSC.cpp @@ -250,8 +250,8 @@ ReturnCode IOSC::ComputeSharedKey(Handle dest_handle, Handle private_handle, Han } // Calculate the ECC shared secret. - std::array shared_secret; - point_mul(shared_secret.data(), private_entry->data.data(), public_entry->data.data()); + const std::array shared_secret = + ComputeSharedSecret(private_entry->data.data(), public_entry->data.data()); std::array sha1; mbedtls_sha1(shared_secret.data(), shared_secret.size() / 2, sha1.data()); From 355b1b5d5b99fbd7be7a1db490b381acb1e46f28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 16 May 2018 00:04:10 +0200 Subject: [PATCH 3/4] ec: Improve readability and clarity - Move all of the ec functions into the Common::ec namespace. - Give the public functions better names and some usage information. - Move all of the "elt" related functions into an "elt" class including all of the arithmetic operations, so that the logic becomes clearer and feels less like assembly. This also makes it much more obvious what the parameters are, instead of only using unsigned char* (which doesn't tell anything about what the pointer is used for or the size). - Similarly, add a new "Point" class and move point functions there. Overload the arithmetic operators to make calculations easier to read --- Source/Core/Common/Crypto/ec.cpp | 542 +++++++++++++------------------ Source/Core/Common/Crypto/ec.h | 12 +- Source/Core/Core/IOS/IOSC.cpp | 12 +- 3 files changed, 236 insertions(+), 330 deletions(-) diff --git a/Source/Core/Common/Crypto/ec.cpp b/Source/Core/Common/Crypto/ec.cpp index 27b9c32325..9906ccd5e9 100644 --- a/Source/Core/Common/Crypto/ec.cpp +++ b/Source/Core/Common/Crypto/ec.cpp @@ -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 #include #include #include @@ -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 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 data{}; +}; + +static Elt operator+(const Elt& a, const Elt& b) +{ + Elt d; + for (std::size_t i = 0; i < std::tuple_size{}; 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 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); - - if ((a[i] & mask) != 0) - elt_add(d, d, b); - - mask >>= 1; - if (mask == 0) + for (u8 mask = 0x80; mask != 0; mask >>= 1) { - mask = 0x80; - i++; - } - } -} - -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); -} - -static 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); + d = d.Double(); if ((a[i] & mask) != 0) - point_add(d, d, b); + 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 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,61 +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 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 PrivToPub(const u8* key) { - point_mul(Q, k, ec_G); + const Point data = key * ec_G; + std::array result; + std::copy_n(data.Data(), result.size(), result.begin()); + return result; } std::array ComputeSharedSecret(const u8* private_key, const u8* public_key) { std::array shared_secret; - point_mul(shared_secret.data(), private_key, public_key); + 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 diff --git a/Source/Core/Common/Crypto/ec.h b/Source/Core/Common/Crypto/ec.h index da2281efda..18b0d48d98 100644 --- a/Source/Core/Common/Crypto/ec.h +++ b/Source/Core/Common/Crypto/ec.h @@ -8,8 +8,14 @@ #include "Common/CommonTypes.h" -void generate_ecdsa(u8* R, u8* S, const u8* k, const u8* hash); - -void ec_priv_to_pub(const u8* k, u8* Q); +namespace Common::ec +{ +/// Generate a signature using ECDSA. +std::array Sign(const u8* key, const u8* hash); +/// Compute a shared secret from a private key (30 bytes) and public key (60 bytes). std::array ComputeSharedSecret(const u8* private_key, const u8* public_key); + +/// Convert a ECC private key (30 bytes) to a public key (60 bytes). +std::array PrivToPub(const u8* key); +} // namespace Common::ec diff --git a/Source/Core/Core/IOS/IOSC.cpp b/Source/Core/Core/IOS/IOSC.cpp index d344e40b74..569568395d 100644 --- a/Source/Core/Core/IOS/IOSC.cpp +++ b/Source/Core/Core/IOS/IOSC.cpp @@ -251,7 +251,7 @@ ReturnCode IOSC::ComputeSharedKey(Handle dest_handle, Handle private_handle, Han // Calculate the ECC shared secret. const std::array shared_secret = - ComputeSharedSecret(private_entry->data.data(), public_entry->data.data()); + Common::ec::ComputeSharedSecret(private_entry->data.data(), public_entry->data.data()); std::array 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(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 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 ROOT_PUBLIC_KEY = { From 67ce7e34e979e139c0854a73351a44a344eafa32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Wed, 16 May 2018 10:59:45 +0200 Subject: [PATCH 4/4] UnitTests: Add basic tests for ec crypto code Verifies that PrivToPub and ComputeSharedSecret give expected results. --- Source/UnitTests/Common/CMakeLists.txt | 1 + Source/UnitTests/Common/Crypto/EcTest.cpp | 45 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Source/UnitTests/Common/Crypto/EcTest.cpp diff --git a/Source/UnitTests/Common/CMakeLists.txt b/Source/UnitTests/Common/CMakeLists.txt index 8b94438c30..ecd8ca0f29 100644 --- a/Source/UnitTests/Common/CMakeLists.txt +++ b/Source/UnitTests/Common/CMakeLists.txt @@ -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) diff --git a/Source/UnitTests/Common/Crypto/EcTest.cpp b/Source/UnitTests/Common/Crypto/EcTest.cpp new file mode 100644 index 0000000000..3fc955da89 --- /dev/null +++ b/Source/UnitTests/Common/Crypto/EcTest.cpp @@ -0,0 +1,45 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/Crypto/ec.h" + +constexpr std::array 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 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 HASH{{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9}}; + const std::array 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 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); +}