205 lines
5.1 KiB
C++
205 lines
5.1 KiB
C++
/*
|
|
ninty-233.cpp
|
|
|
|
Copyright © 2018 Jbop (https://github.com/jbop1626);
|
|
|
|
This file is a part of ninty-233.
|
|
|
|
ninty-233 is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
ninty-233 is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "ninty-233.hpp"
|
|
#include <iomanip>
|
|
#include <random>
|
|
#include <limits>
|
|
|
|
/*
|
|
BigUnsigned <--> GF(2^m) element conversions
|
|
*/
|
|
BigUnsigned gf2m_to_bigunsigned(const element src) {
|
|
BigUnsigned dst = src[0];
|
|
for (int i = 1; i < 8; ++i) {
|
|
dst <<= 32;
|
|
dst += src[i];
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
void bigunsigned_to_gf2m(const BigUnsigned & src, element dst) {
|
|
gf2m_set_zero(dst);
|
|
BigUnsigned temp = src;
|
|
for (int i = 7; i >= 0; --i) {
|
|
BigUnsigned low32 = temp & 0xFFFFFFFF;
|
|
dst[i] = low32.toUnsignedInt();
|
|
temp >>= 32;
|
|
}
|
|
}
|
|
|
|
/*
|
|
SHA-1 result as big (unsigned) integer
|
|
*/
|
|
BigUnsigned sha1(uint8_t * input, int length) {
|
|
SHA1_HASH hash;
|
|
Sha1Context context;
|
|
|
|
Sha1Initialise(&context);
|
|
Sha1Update(&context, input, length);
|
|
#if defined(IQUE_ECC) && (IQUE_ECC == 1)
|
|
uint8_t ique_magic[4] = { 0x06, 0x09, 0x19, 0x68 }; // iQue-specific magic
|
|
Sha1Update(&context, &ique_magic, 4);
|
|
#endif
|
|
Sha1Finalise(&context, &hash);
|
|
|
|
BigUnsigned hash_bigint = hash.bytes[0];
|
|
for (int i = 1; i < 20; ++i) {
|
|
hash_bigint <<= 8;
|
|
hash_bigint += hash.bytes[i];
|
|
}
|
|
|
|
return hash_bigint;
|
|
}
|
|
|
|
/*
|
|
Generation of k, for signing
|
|
*/
|
|
BigUnsigned generate_k(const BigUnsigned & n, const BigUnsigned & hash) {
|
|
BigUnsigned bu_temp = hash;
|
|
element elem_temp;
|
|
uint8_t os_temp[32];
|
|
|
|
std::random_device rd;
|
|
std::uniform_int_distribution<unsigned int> dist(1, std::numeric_limits<unsigned int>::max());
|
|
unsigned int salt = dist(rd);
|
|
salt = salt * dist(rd);
|
|
bu_temp += salt;
|
|
|
|
bigunsigned_to_gf2m(bu_temp, elem_temp);
|
|
elem_to_os(elem_temp, os_temp);
|
|
BigUnsigned k = sha1(os_temp, 32);
|
|
k *= k;
|
|
while(k >= n) {
|
|
k /= 7;
|
|
}
|
|
return k;
|
|
}
|
|
|
|
/*
|
|
ECC algorithms
|
|
*/
|
|
void ecdh(const uint8_t * private_key, const uint8_t * public_key, uint8_t * output) {
|
|
element private_copy;
|
|
ec_point public_copy;
|
|
ec_point shared_secret;
|
|
|
|
os_to_elem(private_key, private_copy);
|
|
os_to_point(public_key, public_copy);
|
|
|
|
ec_point_mul(private_copy, public_copy, shared_secret);
|
|
|
|
point_to_os(shared_secret, output);
|
|
}
|
|
|
|
void ecdsa_sign(const BigUnsigned z, const uint8_t * private_key, element r_out, element s_out) {
|
|
element private_copy;
|
|
os_to_elem(private_key, private_copy);
|
|
|
|
BigUnsigned r = 0;
|
|
BigUnsigned s = 0;
|
|
BigUnsigned n = gf2m_to_bigunsigned(G_order);
|
|
BigUnsigned D = gf2m_to_bigunsigned(private_copy);
|
|
|
|
while(r == 0 || s == 0) {
|
|
// Generate k in [1, n - 1]
|
|
BigUnsigned k = generate_k(n, z);
|
|
element k_elem;
|
|
bigunsigned_to_gf2m(k, k_elem);
|
|
|
|
// Calculate P = kG
|
|
ec_point G;
|
|
gf2m_copy(G_x, G.x);
|
|
gf2m_copy(G_y, G.y);
|
|
ec_point P;
|
|
ec_point_mul(k_elem, G, P);
|
|
|
|
// Calculate r = x_p mod n
|
|
BigUnsigned x_p = gf2m_to_bigunsigned(P.x);
|
|
r = x_p % n;
|
|
|
|
// Calculate s = k^-1(z + rD) mod n
|
|
BigUnsigned k_inv = modinv(k, n);
|
|
BigUnsigned med = (z + (r * D)) % n;
|
|
s = (k_inv * med) % n;
|
|
}
|
|
bigunsigned_to_gf2m(r, r_out);
|
|
bigunsigned_to_gf2m(s, s_out);
|
|
}
|
|
|
|
bool ecdsa_verify(const BigUnsigned z, const uint8_t * public_key, const element r_input, const element s_input) {
|
|
ec_point Q, test;
|
|
os_to_point(public_key, Q);
|
|
element zero = { 0 };
|
|
|
|
// If Q is the identity, Q is invalid
|
|
if (gf2m_is_equal(Q.x, zero) && gf2m_is_equal(Q.y, zero)) {
|
|
return false;
|
|
}
|
|
// If Q is not a point on the curve, Q is invalid
|
|
if (!ec_point_on_curve(Q)) {
|
|
return false;
|
|
}
|
|
// If nQ is not the identity, Q is invalid (or n is messed up)
|
|
ec_point_mul(G_order, Q, test);
|
|
if (!(gf2m_is_equal(test.x, zero) && gf2m_is_equal(test.y, zero))) {
|
|
return false;
|
|
}
|
|
// Public key is valid, now verify signature...
|
|
BigUnsigned r = gf2m_to_bigunsigned(r_input);
|
|
BigUnsigned s = gf2m_to_bigunsigned(s_input);
|
|
BigUnsigned n = gf2m_to_bigunsigned(G_order);
|
|
|
|
// If r,s are not in [1, n - 1], sig is invalid
|
|
if (r < 1 || r >= n) {
|
|
return false;
|
|
}
|
|
if (s < 1 || s >= n) {
|
|
return false;
|
|
}
|
|
|
|
// Calculate u_1 and u_2
|
|
BigUnsigned s_inv = modinv(s, n);
|
|
BigUnsigned u_1 = (z * s_inv) % n;
|
|
BigUnsigned u_2 = (r * s_inv) % n;
|
|
|
|
// Calculate P3 = u_1G + u_2Q
|
|
element u_1_elem, u_2_elem;
|
|
ec_point P1, P2, P3;
|
|
ec_point G;
|
|
gf2m_copy(G_x, G.x);
|
|
gf2m_copy(G_y, G.y);
|
|
bigunsigned_to_gf2m(u_1, u_1_elem);
|
|
bigunsigned_to_gf2m(u_2, u_2_elem);
|
|
|
|
ec_point_mul(u_1_elem, G, P1);
|
|
ec_point_mul(u_2_elem, Q, P2);
|
|
ec_point_add(P1, P2, P3);
|
|
|
|
// If P3 is the identity, sig is invalid
|
|
if (gf2m_is_equal(P3.x, zero) && gf2m_is_equal(P3.y, zero)) {
|
|
return false;
|
|
}
|
|
|
|
// And finally, is r congruent to P3.x mod n?
|
|
BigUnsigned x_p = gf2m_to_bigunsigned(P3.x);
|
|
return r == (x_p % n);
|
|
}
|