Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Schnorr signature #93

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crypto/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ ifdef SMALL
CFLAGS += -DUSE_PRECOMPUTED_CP=0
endif

SRCS = bignum.c ecdsa.c curves.c secp256k1.c nist256p1.c rand.c hmac.c bip32.c bip39.c pbkdf2.c base58.c base32.c
SRCS = bignum.c ecdsa.c curves.c secp256k1.c nist256p1.c rand.c hmac.c bip32.c bip39.c pbkdf2.c base58.c base32.c schnorr.c
SRCS += address.c
SRCS += script.c
SRCS += ripemd160.c
Expand Down
101 changes: 101 additions & 0 deletions crypto/schnorr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/**
* Copyright (c) 2019 Anatolii Kurotych
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

#include "schnorr.h"
prusnak marked this conversation as resolved.
Show resolved Hide resolved

// r = H(Q, kpub, m)
static void calc_r(const curve_point *Q, const uint8_t pub_key[33],
const uint8_t *msg, const uint32_t msg_len, bignum256 *r) {
uint8_t Q_compress[33];
compress_coords(Q, Q_compress);

SHA256_CTX ctx;
uint8_t digest[SHA256_DIGEST_LENGTH];
sha256_Init(&ctx);
sha256_Update(&ctx, Q_compress, 33);
sha256_Update(&ctx, pub_key, 33);
sha256_Update(&ctx, msg, msg_len);
sha256_Final(&ctx, digest);
bn_read_be(digest, r);
}

// returns 0 if signing succeeded
int schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key,
const bignum256 *k, const uint8_t *msg, const uint32_t msg_len,
schnorr_sign_pair *result) {
uint8_t pub_key[33];
curve_point Q;
bignum256 private_key_scalar;

bn_read_be(priv_key, &private_key_scalar);
ecdsa_get_public_key33(curve, priv_key, pub_key);

/* Q = kG */
point_multiply(curve, k, &curve->G, &Q);

/* r = H(Q, kpub, m) */
calc_r(&Q, pub_key, msg, msg_len, &result->r);

/* s = k - r*kpriv mod(order) */
bignum256 s_temp;
bn_copy(&result->r, &s_temp);
bn_multiply(&private_key_scalar, &s_temp, &curve->order);
bn_subtractmod(k, &s_temp, &result->s, &curve->order);

while (bn_is_less(&curve->order, &result->s))
bn_mod(&result->s, &curve->order);

if (bn_is_zero(&result->s) || bn_is_zero(&result->r)) return 1;

return 0;
}

// returns 0 if verification succeeded
int schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key,
prusnak marked this conversation as resolved.
Show resolved Hide resolved
const uint8_t *msg, const uint32_t msg_len,
const schnorr_sign_pair *sign) {
if (msg_len == 0) return 1;
if (bn_is_zero(&sign->r)) return 2;
if (bn_is_zero(&sign->s)) return 3;
if (bn_is_less(&curve->order, &sign->r)) return 4;
if (bn_is_less(&curve->order, &sign->s)) return 5;

curve_point pub_key_point;
if (!ecdsa_read_pubkey(curve, pub_key, &pub_key_point)) {
return 6;
}

// Compute Q = sG + r*kpub
curve_point sG, Q;
point_multiply(curve, &sign->s, &curve->G, &sG);
point_multiply(curve, &sign->r, &pub_key_point, &Q);
point_add(curve, &sG, &Q);

/* r = H(Q, kpub, m) */
bignum256 r;
calc_r(&Q, pub_key, msg, msg_len, &r);

if (bn_is_equal(&r, &sign->r)) return 0; // success

return 10;
}

43 changes: 43 additions & 0 deletions crypto/schnorr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2019 Anatolii Kurotych
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef __SCHNORR_H__
#define __SCHNORR_H__

#include "ecdsa.h"

// result of sign operation
typedef struct {
bignum256 r, s;
} schnorr_sign_pair;

// sign/verify returns 0 if operation succeeded

// k is a random from [1, ..., order-1]
int schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key,
const bignum256 *k, const uint8_t *msg, const uint32_t msg_len,
schnorr_sign_pair *result);
int schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key,
const uint8_t *msg, const uint32_t msg_len,
const schnorr_sign_pair *sign);
#endif

191 changes: 191 additions & 0 deletions crypto/tests/test_check.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#include "rand.h"
#include "rc4.h"
#include "rfc6979.h"
#include "schnorr.h"
#include "script.h"
#include "secp256k1.h"
#include "sha2.h"
Expand Down Expand Up @@ -8347,6 +8348,191 @@ START_TEST(test_compress_coords) {
}
END_TEST

START_TEST(test_schnorr_sign_verify) {
static struct {
const char *message;
const char *priv_key;
const char *k_hex;
const char *s_hex;
const char *r_hex;
} test_cases[] = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do these test vectors come from?

If these are your own, can you please also add some that are not your own? Simple google search for "schnorr test vectors" returns some results such as:

Copy link
Contributor Author

@kurotych kurotych Apr 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got test vectors from results this function.

{
"123",
"3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6",
"669301F724C555D7BB1185C04909E9CACA3EC7A292B3A1C92DDCCD5A5A7DDDD3",
"FFD72C290B98C93A4BCEDC0EDCDF040C35579BE962FE83E6821D4F3CB4B795D2",
"74AAE9C3E069E2806E1B0D890970BE387AEBED8040F37991AACAD70B27895E39",
},
{
"1234",
"51a2758eed776c40b367364909c8a9c98cc969104f69ff316f7a287495c37c9b",
"A0A1A9B3570AAE963535B8D4376C58A61646C18182C9FDDA5FB13703F88D4D1E",
"99A0CB942C81571B77C682F79CD3CB663CE9E1C55BB425BA24B9F11A0DE84FE2",
"C3C10363E38158BBA20556A36DE9358DFD81A31C180ABC9E7617C1CC1CAF03B3",
},
{
"12345",
"2685adffdbb4b2c515054cffc25cfcbfe2e462df65bbe82fb50f71e1e68dd285",
"38DE7B3315F201433D271E91FBE62966576CA05CBFEC1770B77D7EC9D6A01D6D",
"28982FA6C2B620CBC550F7EF9EAB605F409C584FBE5A765678877B79AB517086",
"9A0788E5B0947DEDEDE386DF57A006CF3FE43919A74D9CA630F8A1A9D97B4650",
},
{
"fun",
"7457dc574d927e5dae84b05264a5b637b5a68e34a85b3965084ed6fed5b7f12d",
"E005ABD242C7C602AB5EED080C5083C7C5F8DAEC6D046A54F384A8B8CDECF740",
"51070ABCA039DAC294F6BA3BFC8C36CFC66020EDF66D1ACF1A9B545B0BF09F52",
"330A924525EF722FA20E8E25CB6E8BD7DF4394886FA4414E4A0B6812AA25BBC0",
},
{
"funny",
"52c395a6d304de1a959e73e4604e32c5ad3f2bf01c8f730af426b38d7d5dd908",
"0CF28B5C40A8830F3195BB99A9F0E2808F576105F41D16ABCF596AC5A8CFE88A",
"3D60FB4664C994AD956378B9402BC68F7B4799D74F4783A6199C0D74865EA2B6",
"5ED5EDEE0314DFFBEE39EE4E9C76DE8BC3EB8CB891AEC32B83957514284B205B",
},
{
"What is great in man is that he is a bridge and not a goal",
"52c395a6d304de1a959e73e4604e32c5ad3f2bf01c8f730af426b38d7d5dd908",
"000000000000000000000000000000000000000000000000000000000000007B",
"546F70AA1FEE3718C95508240CDC073B9FEFED05959C5319DD8E2BF07A1DD028",
"B8667BE5E10B113608BFE5327C44E9F0462BE26F789177E10DCE53019AA33DAA",
},
{
"123456789147258369qwertyuiopasdfghjklzxcvbnm,",
"2685adffdbb4b2c515054cffc25cfcbfe2e462df65bbe82fb50f71e1e68dd285",
"1D0CB70310C4D793A4561FE592B7C156771E3E26283B28AB588E968243B52DD0",
"54D7A435E5E3F2811AA542F8895C20CCB760F2713DBDDB7291DAB6DA4E4F927E",
"20A3BDABFFF2C1BF8E2AF709F6CDCAFE70DA9A1DBC22305B6332E36844092984",
},
{
"11111111111111111111111111111111111111111111111111111111111111111"
"11111111111111111111111111111111111111111111111111111111111111111"
"111111111111111111",
"3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6",
"A669F372B3C2EEA351210082CAEC3B96767A7B222D19FF2EE3D814860F0D703A",
"4890F9AC3A8D102EE3A2A473930C01CAD29DCE3860ACB7A5DADAEF16FE808991",
"979F088E58F1814D5E462CB9F935D2924ABD8D32211D8F02DD7E0991726DF573",
},
{
"qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=",
"7457dc574d927e5dae84b05264a5b637b5a68e34a85b3965084ed6fed5b7f12d",
"000000000000000000000000000000000000000000000000000000000000007C",
"0AA595A649E517133D3448CA657424DD07BBED289030F0C0AA6738D26AB9A910",
"83812632F1443A70B198D112D075D886BE7BBC6EC6275AE52661E52B7358BB8B",
},
};

const ecdsa_curve *curve = &secp256k1;
bignum256 k;
uint8_t priv_key[32], buf_raw[32];
schnorr_sign_pair result;
for (size_t i = 0; i < sizeof(test_cases) / sizeof(*test_cases); i++) {
memcpy(priv_key, fromhex(test_cases[i].priv_key), 32);
memcpy(&buf_raw, fromhex(test_cases[i].k_hex), 32);
bn_read_be(buf_raw, &k);
schnorr_sign(curve, priv_key, &k, (const uint8_t *)test_cases[i].message,
strlen(test_cases[i].message), &result);

schnorr_sign_pair expected;

memcpy(&buf_raw, fromhex(test_cases[i].s_hex), 32);
bn_read_be(buf_raw, &expected.s);
memcpy(&buf_raw, fromhex(test_cases[i].r_hex), 32);
bn_read_be(buf_raw, &expected.r);

ck_assert_mem_eq(&expected.r, &result.r, 32);
ck_assert_mem_eq(&expected.s, &result.s, 32);

uint8_t pub_key[33];
ecdsa_get_public_key33(curve, priv_key, pub_key);
int res =
schnorr_verify(curve, pub_key, (const uint8_t *)test_cases[i].message,
strlen(test_cases[i].message), &result);
ck_assert_int_eq(res, 0);
}
}
END_TEST

START_TEST(test_schnorr_fail_verify) {
static struct {
const char *message;
const char *priv_key;
const char *k_hex;
const char *s_hex;
const char *r_hex;
} test_case = {
"123",
"3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6",
"669301F724C555D7BB1185C04909E9CACA3EC7A292B3A1C92DDCCD5A5A7DDDD3",
"FFD72C290B98C93A4BCEDC0EDCDF040C35579BE962FE83E6821D4F3CB4B795D2",
"74AAE9C3E069E2806E1B0D890970BE387AEBED8040F37991AACAD70B27895E39",
};

const ecdsa_curve *curve = &secp256k1;
bignum256 k;
uint8_t priv_key[32], buf_raw[32];
memcpy(priv_key, fromhex(test_case.priv_key), 32);
memcpy(&buf_raw, fromhex(test_case.k_hex), 32);
bn_read_be(buf_raw, &k);

schnorr_sign_pair result;
schnorr_sign(curve, priv_key, &k, (const uint8_t *)test_case.message,
strlen(test_case.message), &result);
uint8_t pub_key[33];
ecdsa_get_public_key33(curve, priv_key, pub_key);

// OK
int res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message,
strlen(test_case.message), &result);
ck_assert_int_eq(res, 0);

res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, 0,
&result);
ck_assert_int_eq(res, 1);

schnorr_sign_pair bad_result;

bn_copy(&result.s, &bad_result.s);
bn_zero(&bad_result.r);
// r == 0
res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message,
strlen(test_case.message), &bad_result);
ck_assert_int_eq(res, 2);

bn_copy(&result.r, &bad_result.r);
bn_zero(&bad_result.s);
// s == 0
res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message,
strlen(test_case.message), &bad_result);
ck_assert_int_eq(res, 3);

bn_copy(&result.s, &bad_result.s);
bn_copy(&curve->order, &bad_result.r);
bn_addi(&bad_result.r, 1);
// r == curve->order + 1
res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message,
strlen(test_case.message), &bad_result);
ck_assert_int_eq(res, 4);

bn_copy(&result.r, &bad_result.r);
bn_copy(&curve->order, &bad_result.s);
bn_addi(&bad_result.s, 1);
// s == curve->order + 1
res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message,
strlen(test_case.message), &bad_result);
ck_assert_int_eq(res, 5);

bn_copy(&result.r, &bad_result.r);
bn_copy(&result.s, &bad_result.s);
// change message
test_case.message = "12";
res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message,
strlen(test_case.message), &bad_result);
ck_assert_int_eq(res, 10);
}
END_TEST

static int my_strncasecmp(const char *s1, const char *s2, size_t n) {
size_t i = 0;
while (i < n) {
Expand Down Expand Up @@ -8624,6 +8810,11 @@ Suite *test_suite(void) {
tcase_add_test(tc, test_compress_coords);
suite_add_tcase(s, tc);

tc = tcase_create("schnorr");
tcase_add_test(tc, test_schnorr_sign_verify);
tcase_add_test(tc, test_schnorr_fail_verify);
suite_add_tcase(s, tc);

#if USE_CARDANO
tc = tcase_create("bip32-cardano");

Expand Down