367 lines
8.4 KiB
C++
367 lines
8.4 KiB
C++
/*
|
|
ecc.cpp - inplementations of ECC operations using keys
|
|
defined with sect233r1 / NIST B-233
|
|
This is NOT intended to be used in an actual cryptographic
|
|
scheme; as written, it is vulnerable to several attacks.
|
|
This might or might not change in the future. It is intended
|
|
to be used for doing operations on keys which are already known.
|
|
|
|
Copyright © 2018 Jbop (https://github.com/jbop1626);
|
|
Modification of a part of iQueCrypt
|
|
(https://github.com/jbop1626/iquecrypt)
|
|
|
|
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 <iostream>
|
|
#include <iomanip>
|
|
#include "ecc.hpp"
|
|
|
|
/*
|
|
Printing
|
|
*/
|
|
void print_element(const element a) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
std::cout << std::setw(8) << std::setfill('0') << std::hex << a[i] << " ";
|
|
}
|
|
std::cout << std::endl << std::endl;;
|
|
}
|
|
|
|
void print_point(const ec_point & a) {
|
|
std::cout << "x: ";
|
|
print_element(a.x);
|
|
std::cout << "y: ";
|
|
print_element(a.y);
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
/*
|
|
Helper functions for working with elements in GF(2^m)
|
|
*/
|
|
bool gf2m_is_equal(const element a, const element b) {
|
|
for (int i = 0; i < 7; ++i) {
|
|
if (a[i] != b[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void gf2m_set_zero(element a) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
a[i] = 0;
|
|
}
|
|
}
|
|
|
|
void gf2m_copy(const element src, element dst) {
|
|
std::memcpy(dst, src, 32);
|
|
}
|
|
|
|
int gf2m_get_bit(const element a, int index) {
|
|
int word_index = ((index / 32) - 7) * -1;
|
|
int shift = index - (32 * (7 - word_index));
|
|
return (a[word_index] >> shift) & 1;
|
|
}
|
|
|
|
void gf2m_left_shift(element a, int shift) {
|
|
if (!shift) {
|
|
a[0] &= 0x1FF;
|
|
return;
|
|
}
|
|
for (int i = 0; i < 7; ++i) {
|
|
a[i] <<= 1;
|
|
if (a[i + 1] >= 0x80000000) a[i] |= 1;
|
|
}
|
|
a[7] <<= 1;
|
|
gf2m_left_shift(a, shift - 1);
|
|
}
|
|
|
|
bool gf2m_is_one(const element a) {
|
|
if (a[7] != 1) return false;
|
|
else {
|
|
for (int i = 0; i < 7; ++i) {
|
|
if (a[i] != 0) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int gf2m_degree(const element a) {
|
|
int degree = 0;
|
|
int i = 0;
|
|
while (a[i] == 0) {
|
|
i++;
|
|
}
|
|
degree = (7 - i) * 32;
|
|
uint32_t MSW = a[i];
|
|
while (MSW != 0) {
|
|
MSW >>= 1;
|
|
degree += 1;
|
|
}
|
|
return degree - 1;
|
|
}
|
|
|
|
void gf2m_swap(element a, element b) {
|
|
element temp;
|
|
gf2m_copy(a, temp);
|
|
gf2m_copy(b, a);
|
|
gf2m_copy(temp, b);
|
|
}
|
|
|
|
/*
|
|
Arithmetic operations on elements in GF(2^m)
|
|
*/
|
|
void gf2m_add(const element a, const element b, element c) {
|
|
for (int i = 0; i < 8; ++i) {
|
|
c[i] = a[i] ^ b[i];
|
|
}
|
|
}
|
|
|
|
void gf2m_inv(const element a, element c) {
|
|
element u, v, g_1, g_2, temp;
|
|
gf2m_copy(a, u);
|
|
gf2m_copy(poly_f, v);
|
|
gf2m_set_zero(g_1);
|
|
g_1[7] |= 1;
|
|
gf2m_set_zero(g_2);
|
|
int j = gf2m_degree(u) - 233;
|
|
while (!gf2m_is_one(u)) {
|
|
if (j < 0) {
|
|
gf2m_swap(u, v);
|
|
gf2m_swap(g_1, g_2);
|
|
j = -j;
|
|
}
|
|
gf2m_copy(v, temp);
|
|
gf2m_left_shift(temp, j);
|
|
gf2m_add(u, temp, u);
|
|
gf2m_copy(g_2, temp);
|
|
gf2m_left_shift(temp, j);
|
|
gf2m_add(g_1, temp, g_1);
|
|
|
|
u[0] &= 0x1FF;
|
|
g_1[0] &= 0x1FF;
|
|
|
|
j = gf2m_degree(u) - gf2m_degree(v);
|
|
}
|
|
gf2m_copy(g_1, c);
|
|
}
|
|
|
|
// basic implementation
|
|
void gf2m_mul(const element a, const element b, element c) {
|
|
element t1, t2, t3;
|
|
gf2m_copy(a, t1);
|
|
gf2m_copy(b, t2);
|
|
gf2m_set_zero(t3);
|
|
for (int i = 0; i < 233; ++i) {
|
|
if (gf2m_get_bit(t2, i)) {
|
|
gf2m_add(t3, t1, t3);
|
|
}
|
|
int carry = gf2m_get_bit(t1, 232);
|
|
gf2m_left_shift(t1, 1);
|
|
if (carry == 1) {
|
|
gf2m_add(poly_r, t1, t1);
|
|
}
|
|
}
|
|
gf2m_copy(t3, c);
|
|
}
|
|
|
|
void gf2m_div(const element a, const element b, element c) {
|
|
element temp;
|
|
gf2m_inv(b, temp);
|
|
gf2m_mul(a, temp, c);
|
|
}
|
|
// void gf2m_reduce(element c)
|
|
// void gf2m_square(const element a, element c)
|
|
|
|
/*
|
|
Operations on points on the elliptic curve
|
|
y^2 + xy = x^3 + ax^2 + b over GF(2^m)
|
|
*/
|
|
void ec_point_copy(const ec_point & src, ec_point & dst) {
|
|
gf2m_copy(src.x, dst.x);
|
|
gf2m_copy(src.y, dst.y);
|
|
}
|
|
|
|
bool ec_point_is_equal(const ec_point & a, const ec_point & c) {
|
|
return gf2m_is_equal(a.x, c.x) && gf2m_is_equal(a.y, c.y);
|
|
}
|
|
|
|
void ec_point_neg(const ec_point & a, ec_point & c) {
|
|
element temp;
|
|
gf2m_copy(a.x, c.x);
|
|
gf2m_add(a.x, a.y, temp);
|
|
gf2m_copy(temp, c.y);
|
|
}
|
|
|
|
void ec_point_double(const ec_point & a, ec_point & c) {
|
|
ec_point temp;
|
|
ec_point zero;
|
|
gf2m_set_zero(zero.x);
|
|
gf2m_set_zero(zero.y);
|
|
|
|
ec_point_neg(a, temp);
|
|
if (ec_point_is_equal(a, temp) || ec_point_is_equal(a, zero)) {
|
|
ec_point_copy(zero, c);
|
|
return;
|
|
}
|
|
|
|
element lambda, x, y, t, t2;
|
|
// Compute lambda (a.x + (a.y / a.x))
|
|
gf2m_div(a.y, a.x, t);
|
|
gf2m_add(a.x, t, lambda);
|
|
// Compute X (lambda^2 + lambda + a_coeff)
|
|
gf2m_mul(lambda, lambda, t);
|
|
gf2m_add(t, lambda, t);
|
|
gf2m_add(t, a_coeff, x);
|
|
// Compute Y (a.x^2 + (lambda * X) + X)
|
|
gf2m_mul(a.x, a.x, t);
|
|
gf2m_mul(lambda, x, t2);
|
|
gf2m_add(t, t2, t);
|
|
gf2m_add(t, x, y);
|
|
// Copy X,Y to output point c
|
|
gf2m_copy(x, c.x);
|
|
gf2m_copy(y, c.y);
|
|
}
|
|
|
|
void ec_point_add(const ec_point & a, const ec_point & b, ec_point & c) {
|
|
if (!ec_point_is_equal(a, b)) {
|
|
ec_point temp;
|
|
ec_point zero;
|
|
gf2m_set_zero(zero.x);
|
|
gf2m_set_zero(zero.y);
|
|
ec_point_neg(b, temp);
|
|
if (ec_point_is_equal(a, temp)) {
|
|
ec_point_copy(zero, c);
|
|
return;
|
|
}
|
|
else if (ec_point_is_equal(a, zero)) {
|
|
ec_point_copy(b, c);
|
|
return;
|
|
}
|
|
else if (ec_point_is_equal(b, zero)) {
|
|
ec_point_copy(a, c);
|
|
return;
|
|
}
|
|
else {
|
|
element lambda, x, y, t, t2;
|
|
// Compute lambda ((b.y + a.y) / (b.x + a.x))
|
|
gf2m_add(b.y, a.y, t);
|
|
gf2m_add(b.x, a.x, t2);
|
|
gf2m_div(t, t2, lambda);
|
|
// Compute X (lambda^2 + lambda + a.x + b.x + a_coeff)
|
|
gf2m_mul(lambda, lambda, t);
|
|
gf2m_add(t, lambda, t2);
|
|
gf2m_add(t2, a.x, t);
|
|
gf2m_add(t, b.x, t2);
|
|
gf2m_add(t2, a_coeff, x);
|
|
// Compute Y ((lambda * (a.x + X)) + X + a.y)
|
|
gf2m_add(a.x, x, t);
|
|
gf2m_mul(lambda, t, t2);
|
|
gf2m_add(t2, x, t);
|
|
gf2m_add(t, a.y, y);
|
|
// Copy X,Y to output point c
|
|
gf2m_copy(x, c.x);
|
|
gf2m_copy(y, c.y);
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
ec_point_double(a, c);
|
|
}
|
|
}
|
|
|
|
void ec_point_mul(const element a, const ec_point & b, ec_point & c) {
|
|
element k;
|
|
ec_point P;
|
|
ec_point Q;
|
|
|
|
gf2m_copy(a, k);
|
|
ec_point_copy(b, P);
|
|
gf2m_set_zero(Q.x);
|
|
gf2m_set_zero(Q.y);
|
|
for (int i = 0; i < 233; ++i) {
|
|
if (gf2m_get_bit(k, i)) {
|
|
ec_point_add(Q, P, Q);
|
|
}
|
|
ec_point_double(P, P);
|
|
}
|
|
ec_point_copy(Q, c);
|
|
}
|
|
|
|
bool ec_point_on_curve(const ec_point & P) {
|
|
// y^2 + xy = x^3 + ax^2 + b
|
|
element xx, yy, xy, lhs, rhs;
|
|
|
|
gf2m_mul(P.y, P.y, yy);
|
|
gf2m_mul(P.x, P.y, xy);
|
|
gf2m_add(yy, xy, lhs);
|
|
|
|
gf2m_mul(P.x, P.x, xx);
|
|
gf2m_add(P.x, a_coeff, rhs);
|
|
gf2m_mul(xx, rhs, rhs);
|
|
gf2m_add(rhs, b_coeff, rhs);
|
|
|
|
return gf2m_is_equal(lhs, rhs);
|
|
}
|
|
|
|
/*
|
|
I/O Helpers
|
|
Private keys are expected to be 32 bytes; Public keys
|
|
are expected to be 64 bytes and in uncompressed form.
|
|
|
|
Wii keys will need to be padded - two 0 bytes at the
|
|
start of the private key, and two 0 bytes before each
|
|
coordinate in the public key.
|
|
|
|
These functions are mainly intended for reading/writing
|
|
*keys* as byte arrays or octet streams, but they will
|
|
work fine for any input with the correct length.
|
|
*/
|
|
// (32-byte) octet stream to GF(2^m) element
|
|
void os_to_elem(const uint8_t * os, element elem) {
|
|
int j = 0;
|
|
for (int i = 0; i < 8; ++i) {
|
|
uint32_t temp = 0;
|
|
temp |= (os[j] << 24);
|
|
temp |= (os[j + 1] << 16);
|
|
temp |= (os[j + 2] << 8);
|
|
temp |= os[j + 3];
|
|
elem[i] = temp;
|
|
j += 4;
|
|
}
|
|
}
|
|
|
|
// (64-byte) octet stream to elliptic curve point
|
|
void os_to_point(const uint8_t * os, ec_point & point) {
|
|
os_to_elem(os, point.x);
|
|
os_to_elem(os + 32, point.y);
|
|
}
|
|
|
|
// GF(2^m) element to (32-byte) octet stream
|
|
void elem_to_os(const element src, uint8_t * output_os) {
|
|
int j = 0;
|
|
for (int i = 0; i < 7; ++i) {
|
|
output_os[j] = ((src[i] & 0xFF000000) >> 24);
|
|
output_os[j + 1] = ((src[i] & 0x00FF0000) >> 16);
|
|
output_os[j + 2] = ((src[i] & 0x0000FF00) >> 8);
|
|
output_os[j + 3] = src[i] & 0x000000FF;
|
|
j += 4;
|
|
}
|
|
}
|
|
|
|
// Elliptic curve point to (64-byte) octet stream
|
|
void point_to_os(const ec_point & src, uint8_t * output_os) {
|
|
elem_to_os(src.x, output_os);
|
|
elem_to_os(src.y, output_os + 32);
|
|
}
|