Update libsecp to latest master, c18b869e58aa4d3bff6958f370f6b643d1223c44

This commit is contained in:
Andrew Poelstra 2016-01-14 18:35:54 +00:00
parent 16b36f18e1
commit 458a3d9417
29 changed files with 2174 additions and 380 deletions

View File

@ -43,7 +43,7 @@ fn main() {
.define("ENABLE_MODULE_RECOVERY", Some("1"));
// secp256k1
base_config.file("depend/secp256k1/src/laxder_shim.c")
base_config.file("depend/secp256k1/contrib/lax_der_parsing.c")
.file("depend/secp256k1/src/secp256k1.c")
.compile("libsecp256k1.a");
}

View File

@ -8,20 +8,20 @@ compiler:
- gcc
env:
global:
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no
- FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no schnorr=no RECOVERY=no EXPERIMENTAL=no
matrix:
- SCALAR=32bit RECOVERY=yes
- SCALAR=32bit FIELD=32bit ECDH=yes
- SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes
- SCALAR=64bit
- FIELD=64bit RECOVERY=yes
- FIELD=64bit ENDOMORPHISM=yes
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes
- FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes
- FIELD=64bit ASM=x86_64
- FIELD=64bit ENDOMORPHISM=yes ASM=x86_64
- FIELD=32bit SCHNORR=yes
- FIELD=32bit SCHNORR=yes EXPERIMENTAL=yes
- FIELD=32bit ENDOMORPHISM=yes
- BIGNUM=no
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes EXPERIMENTAL=yes
- BIGNUM=no STATICPRECOMPUTATION=no
- BUILD=distcheck
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
@ -59,5 +59,5 @@ before_script: ./autogen.sh
script:
- if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi
- if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi
- ./configure --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
- ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-schnorr=$SCHNORR --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
os: linux

View File

@ -41,7 +41,9 @@ noinst_HEADERS += src/field.h
noinst_HEADERS += src/field_impl.h
noinst_HEADERS += src/bench.h
noinst_HEADERS += contrib/lax_der_parsing.h
noinst_HEADERS += contrib/lax_der_parsing.c
noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
noinst_HEADERS += contrib/lax_der_privatekey_parsing.c
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libsecp256k1.pc
@ -55,9 +57,9 @@ noinst_PROGRAMS =
if USE_BENCHMARK
noinst_PROGRAMS += bench_verify bench_sign bench_internal
bench_verify_SOURCES = src/bench_verify.c
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS)
bench_sign_SOURCES = src/bench_sign.c
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS)
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS)
bench_internal_SOURCES = src/bench_internal.c
bench_internal_LDADD = $(SECP_LIBS)
bench_internal_CPPFLAGS = $(SECP_INCLUDES)
@ -73,7 +75,7 @@ TESTS = tests
endif
if USE_ECMULT_STATIC_PRECOMPUTATION
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)/
CPPFLAGS_FOR_BUILD +=-I$(top_srcdir)
CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function
gen_context_OBJECTS = gen_context.o

View File

@ -93,23 +93,28 @@ AC_ARG_ENABLE(tests,
[use_tests=$enableval],
[use_tests=yes])
AC_ARG_ENABLE(experimental,
AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]),
[use_experimental=$enableval],
[use_experimental=no])
AC_ARG_ENABLE(endomorphism,
AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]),
[use_endomorphism=$enableval],
[use_endomorphism=no])
AC_ARG_ENABLE(ecmult_static_precomputation,
AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]),
[use_ecmult_static_precomputation=$enableval],
[use_ecmult_static_precomputation=yes])
AC_ARG_ENABLE(module_ecdh,
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (default is no)]),
AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]),
[enable_module_ecdh=$enableval],
[enable_module_ecdh=no])
AC_ARG_ENABLE(module_schnorr,
AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (default is no)]),
AS_HELP_STRING([--enable-module-schnorr],[enable Schnorr signature module (experimental)]),
[enable_module_schnorr=$enableval],
[enable_module_schnorr=no])
@ -350,11 +355,24 @@ AC_MSG_NOTICE([Using field implementation: $set_field])
AC_MSG_NOTICE([Using bignum implementation: $set_bignum])
AC_MSG_NOTICE([Using scalar implementation: $set_scalar])
AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery])
if test x"$enable_experimental" = x"yes"; then
AC_MSG_NOTICE([******])
AC_MSG_NOTICE([WARNING: experimental build])
AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.])
AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh])
AC_MSG_NOTICE([Building Schnorr signatures module: $enable_module_schnorr])
AC_MSG_NOTICE([******])
else
if test x"$enable_module_schnorr" = x"yes"; then
AC_MSG_ERROR([Schnorr signature module is experimental. Use --enable-experimental to allow.])
fi
if test x"$enable_module_ecdh" = x"yes"; then
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
fi
fi
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
AC_SUBST(SECP_INCLUDES)

View File

@ -0,0 +1,150 @@
/**********************************************************************
* Copyright (c) 2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#include <string.h>
#include <secp256k1.h>
#include "lax_der_parsing.h"
int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
size_t rpos, rlen, spos, slen;
size_t pos = 0;
size_t lenbyte;
unsigned char tmpsig[64] = {0};
int overflow = 0;
/* Hack to initialize sig with a correctly-parsed but invalid signature. */
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
/* Sequence tag byte */
if (pos == inputlen || input[pos] != 0x30) {
return 0;
}
pos++;
/* Sequence length bytes */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (pos + lenbyte > inputlen) {
return 0;
}
pos += lenbyte;
}
/* Integer tag byte for R */
if (pos == inputlen || input[pos] != 0x02) {
return 0;
}
pos++;
/* Integer length for R */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (pos + lenbyte > inputlen) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
pos++;
lenbyte--;
}
if (lenbyte >= sizeof(size_t)) {
return 0;
}
rlen = 0;
while (lenbyte > 0) {
rlen = (rlen << 8) + input[pos];
pos++;
lenbyte--;
}
} else {
rlen = lenbyte;
}
if (rlen > inputlen - pos) {
return 0;
}
rpos = pos;
pos += rlen;
/* Integer tag byte for S */
if (pos == inputlen || input[pos] != 0x02) {
return 0;
}
pos++;
/* Integer length for S */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (pos + lenbyte > inputlen) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
pos++;
lenbyte--;
}
if (lenbyte >= sizeof(size_t)) {
return 0;
}
slen = 0;
while (lenbyte > 0) {
slen = (slen << 8) + input[pos];
pos++;
lenbyte--;
}
} else {
slen = lenbyte;
}
if (slen > inputlen - pos) {
return 0;
}
spos = pos;
pos += slen;
/* Ignore leading zeroes in R */
while (rlen > 0 && input[rpos] == 0) {
rlen--;
rpos++;
}
/* Copy R value */
if (rlen > 32) {
overflow = 1;
} else {
memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
}
/* Ignore leading zeroes in S */
while (slen > 0 && input[spos] == 0) {
slen--;
spos++;
}
/* Copy S value */
if (slen > 32) {
overflow = 1;
} else {
memcpy(tmpsig + 64 - slen, input + spos, slen);
}
if (!overflow) {
overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
}
if (overflow) {
memset(tmpsig, 0, 64);
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
}
return 1;
}

View File

@ -4,7 +4,14 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
/* This file contains a code snippet that parses DER with various errors and
/****
* Please do not link this file directly. It is not part of the libsecp256k1
* project and does not promise any stability in its API, functionality or
* presence. Projects which use this code should instead copy this header
* and its accompanying .c file directly into their codebase.
****/
/* This file defines a function that parses DER with various errors and
* violations. This is not a part of the library itself, because the allowed
* violations are chosen arbitrarily and do not follow or establish any
* standard.
@ -44,148 +51,41 @@
#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
#include <string.h>
#include <secp256k1.h>
static int secp256k1_ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen);
# ifdef __cplusplus
extern "C" {
# endif
static int secp256k1_ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
size_t rpos, rlen, spos, slen;
size_t pos = 0;
size_t lenbyte;
unsigned char tmpsig[64] = {0};
int overflow = 0;
/** Parse a signature in "lax DER" format
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
* Args: ctx: a secp256k1 context object
* Out: sig: a pointer to a signature object
* In: input: a pointer to the signature to be parsed
* inputlen: the length of the array pointed to be input
*
* This function will accept any valid DER encoded signature, even if the
* encoded numbers are out of range. In addition, it will accept signatures
* which violate the DER spec in various ways. Its purpose is to allow
* validation of the Bitcoin blockchain, which includes non-DER signatures
* from before the network rules were updated to enforce DER. Note that
* the set of supported violations is a strict subset of what OpenSSL will
* accept.
*
* After the call, sig will always be initialized. If parsing failed or the
* encoded numbers are out of range, signature validation with it is
* guaranteed to fail for every message and public key.
*/
int ecdsa_signature_parse_der_lax(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature* sig,
const unsigned char *input,
size_t inputlen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
/* Hack to initialize sig with a correctly-parsed but invalid signature. */
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
/* Sequence tag byte */
if (pos == inputlen || input[pos] != 0x30) {
return 0;
}
pos++;
/* Sequence length bytes */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (pos + lenbyte > inputlen) {
return 0;
}
pos += lenbyte;
}
/* Integer tag byte for R */
if (pos == inputlen || input[pos] != 0x02) {
return 0;
}
pos++;
/* Integer length for R */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (pos + lenbyte > inputlen) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
pos++;
lenbyte--;
}
if (lenbyte >= sizeof(size_t)) {
return 0;
}
rlen = 0;
while (lenbyte > 0) {
rlen = (rlen << 8) + input[pos];
pos++;
lenbyte--;
}
} else {
rlen = lenbyte;
}
if (rlen > inputlen - pos) {
return 0;
}
rpos = pos;
pos += rlen;
/* Integer tag byte for S */
if (pos == inputlen || input[pos] != 0x02) {
return 0;
}
pos++;
/* Integer length for S */
if (pos == inputlen) {
return 0;
}
lenbyte = input[pos++];
if (lenbyte & 0x80) {
lenbyte -= 0x80;
if (pos + lenbyte > inputlen) {
return 0;
}
while (lenbyte > 0 && input[pos] == 0) {
pos++;
lenbyte--;
}
if (lenbyte >= sizeof(size_t)) {
return 0;
}
slen = 0;
while (lenbyte > 0) {
slen = (slen << 8) + input[pos];
pos++;
lenbyte--;
}
} else {
slen = lenbyte;
}
if (slen > inputlen - pos) {
return 0;
}
spos = pos;
pos += slen;
/* Ignore leading zeroes in R */
while (rlen > 0 && input[rpos] == 0) {
rlen--;
rpos++;
}
/* Copy R value */
if (rlen > 32) {
overflow = 1;
} else {
memcpy(tmpsig + 32 - rlen, input + rpos, rlen);
}
/* Ignore leading zeroes in S */
while (slen > 0 && input[spos] == 0) {
slen--;
spos++;
}
/* Copy S value */
if (slen > 32) {
overflow = 1;
} else {
memcpy(tmpsig + 64 - slen, input + spos, slen);
}
if (!overflow) {
overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
}
if (overflow) {
memset(tmpsig, 0, 64);
secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig);
}
return 1;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,113 @@
/**********************************************************************
* Copyright (c) 2014, 2015 Pieter Wuille *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
#include <string.h>
#include <secp256k1.h>
#include "lax_der_privatekey_parsing.h"
int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) {
const unsigned char *end = privkey + privkeylen;
int lenb = 0;
int len = 0;
memset(out32, 0, 32);
/* sequence header */
if (end < privkey+1 || *privkey != 0x30) {
return 0;
}
privkey++;
/* sequence length constructor */
if (end < privkey+1 || !(*privkey & 0x80)) {
return 0;
}
lenb = *privkey & ~0x80; privkey++;
if (lenb < 1 || lenb > 2) {
return 0;
}
if (end < privkey+lenb) {
return 0;
}
/* sequence length */
len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0);
privkey += lenb;
if (end < privkey+len) {
return 0;
}
/* sequence element 0: version number (=1) */
if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) {
return 0;
}
privkey += 3;
/* sequence element 1: octet string, up to 32 bytes */
if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) {
return 0;
}
memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]);
if (!secp256k1_ec_seckey_verify(ctx, out32)) {
memset(out32, 0, 32);
return 0;
}
return 1;
}
int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) {
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
*privkeylen = 0;
return 0;
}
if (compressed) {
static const unsigned char begin[] = {
0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20
};
static const unsigned char middle[] = {
0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00
};
unsigned char *ptr = privkey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
pubkeylen = 33;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
} else {
static const unsigned char begin[] = {
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
};
static const unsigned char middle[] = {
0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11,
0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10,
0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00
};
unsigned char *ptr = privkey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
pubkeylen = 65;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
}
return 1;
}

View File

@ -4,6 +4,13 @@
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
/****
* Please do not link this file directly. It is not part of the libsecp256k1
* project and does not promise any stability in its API, functionality or
* presence. Projects which use this code should instead copy this header
* and its accompanying .c file directly into their codebase.
****/
/* This file contains code snippets that parse DER private keys with
* various errors and violations. This is not a part of the library
* itself, because the allowed violations are chosen arbitrarily and
@ -21,9 +28,12 @@
#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
#include <string.h>
#include <secp256k1.h>
# ifdef __cplusplus
extern "C" {
# endif
/** Export a private key in DER format.
*
* Returns: 1 if the private key was valid.
@ -44,7 +54,7 @@
* Note that this function does not guarantee correct DER output. It is
* guaranteed to be parsable by secp256k1_ec_privkey_import_der
*/
static SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_export_der(
SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der(
const secp256k1_context* ctx,
unsigned char *privkey,
size_t *privkeylen,
@ -66,144 +76,15 @@ static SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_export_der(
* only if you know in advance it is supposed to contain a secp256k1 private
* key.
*/
static SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_import_der(
SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der(
const secp256k1_context* ctx,
unsigned char *seckey,
const unsigned char *privkey,
size_t privkeylen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
static int secp256k1_eckey_privkey_parse(secp256k1_scalar *key, const unsigned char *privkey, size_t privkeylen) {
unsigned char c[32] = {0};
const unsigned char *end = privkey + privkeylen;
int lenb = 0;
int len = 0;
int overflow = 0;
/* sequence header */
if (end < privkey+1 || *privkey != 0x30) {
return 0;
}
privkey++;
/* sequence length constructor */
if (end < privkey+1 || !(*privkey & 0x80)) {
return 0;
}
lenb = *privkey & ~0x80; privkey++;
if (lenb < 1 || lenb > 2) {
return 0;
}
if (end < privkey+lenb) {
return 0;
}
/* sequence length */
len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0);
privkey += lenb;
if (end < privkey+len) {
return 0;
}
/* sequence element 0: version number (=1) */
if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) {
return 0;
}
privkey += 3;
/* sequence element 1: octet string, up to 32 bytes */
if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) {
return 0;
}
memcpy(c + 32 - privkey[1], privkey + 2, privkey[1]);
secp256k1_scalar_set_b32(key, c, &overflow);
memset(c, 0, 32);
return !overflow;
}
static int secp256k1_eckey_privkey_serialize(const secp256k1_ecmult_gen_context *ctx, unsigned char *privkey, size_t *privkeylen, const secp256k1_scalar *key, int compressed) {
secp256k1_gej rp;
secp256k1_ge r;
size_t pubkeylen = 0;
secp256k1_ecmult_gen(ctx, &rp, key);
secp256k1_ge_set_gej(&r, &rp);
if (compressed) {
static const unsigned char begin[] = {
0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20
};
static const unsigned char middle[] = {
0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00
};
unsigned char *ptr = privkey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
secp256k1_scalar_get_b32(ptr, key); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
if (!secp256k1_eckey_pubkey_serialize(&r, ptr, &pubkeylen, 1)) {
return 0;
}
ptr += pubkeylen;
*privkeylen = ptr - privkey;
} else {
static const unsigned char begin[] = {
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
};
static const unsigned char middle[] = {
0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48,
0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04,
0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87,
0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8,
0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11,
0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10,
0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E,
0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00
};
unsigned char *ptr = privkey;
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
secp256k1_scalar_get_b32(ptr, key); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
if (!secp256k1_eckey_pubkey_serialize(&r, ptr, &pubkeylen, 0)) {
return 0;
}
ptr += pubkeylen;
*privkeylen = ptr - privkey;
}
return 1;
}
static int secp256k1_ec_privkey_export_der(const secp256k1_context* ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *seckey, int compressed) {
secp256k1_scalar key;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(seckey != NULL);
ARG_CHECK(privkey != NULL);
ARG_CHECK(privkeylen != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
secp256k1_scalar_set_b32(&key, seckey, NULL);
ret = secp256k1_eckey_privkey_serialize(&ctx->ecmult_gen_ctx, privkey, privkeylen, &key, compressed);
secp256k1_scalar_clear(&key);
return ret;
}
static int secp256k1_ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *privkey, size_t privkeylen) {
secp256k1_scalar key;
int ret = 0;
ARG_CHECK(seckey != NULL);
ARG_CHECK(privkey != NULL);
(void)ctx;
ret = secp256k1_eckey_privkey_parse(&key, privkey, privkeylen);
if (ret) {
secp256k1_scalar_get_b32(seckey, &key);
}
secp256k1_scalar_clear(&key);
return ret;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -229,7 +229,7 @@ SECP256K1_API void secp256k1_context_set_illegal_callback(
* crashing.
*
* Args: ctx: an existing context object (cannot be NULL)
* In: fun: a pointer to a function to call when an interal error occurs,
* In: fun: a pointer to a function to call when an internal error occurs,
* taking a message and an opaque pointer (NULL restores a default
* handler that calls abort).
* data: the opaque pointer to pass to fun above.
@ -264,15 +264,17 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse(
/** Serialize a pubkey object into a serialized byte sequence.
*
* Returns: 1 always.
* Args: ctx: a secp256k1 context object.
* Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if
* compressed==1) byte array to place the serialized key in.
* outputlen: a pointer to an integer which will contain the serialized
* size.
* In: pubkey: a pointer to a secp256k1_pubkey containing an initialized
* public key.
* flags: SECP256K1_EC_COMPRESSED if serialization should be in
* compressed format, otherwise SECP256K1_EC_UNCOMPRESSED.
* Args: ctx: a secp256k1 context object.
* Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if
* compressed==1) byte array to place the serialized key
* in.
* In/Out: outputlen: a pointer to an integer which is initially set to the
* size of output, and is overwritten with the written
* size.
* In: pubkey: a pointer to a secp256k1_pubkey containing an
* initialized public key.
* flags: SECP256K1_EC_COMPRESSED if serialization should be in
* compressed format, otherwise SECP256K1_EC_UNCOMPRESSED.
*/
SECP256K1_API int secp256k1_ec_pubkey_serialize(
const secp256k1_context* ctx,
@ -562,18 +564,16 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
* Returns: 1: the sum of the public keys is valid.
* 0: the sum of the public keys is not valid.
* Args: ctx: pointer to a context object
* Out: out: pointer to pubkey for placing the resulting public key
* Out: out: pointer to a public key object for placing the resulting public key
* (cannot be NULL)
* In: ins: pointer to array of pointers to public keys (cannot be NULL)
* n: the number of public keys to add together (must be at least 1)
* Use secp256k1_ec_pubkey_compress and secp256k1_ec_pubkey_decompress if the
* uncompressed format is needed.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine(
const secp256k1_context* ctx,
secp256k1_pubkey *out,
const secp256k1_pubkey * const * ins,
int n
size_t n
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
# ifdef __cplusplus

View File

@ -92,7 +92,7 @@ SECP256K1_API int secp256k1_ecdsa_sign_recoverable(
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
* 0: otherwise.
* Args: ctx: pointer to a context object, initialized for verification (cannot be NULL)
* Out: pubkey: pointer to the recoved public key (cannot be NULL)
* Out: pubkey: pointer to the recovered public key (cannot be NULL)
* In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL)
* msg32: the 32-byte message hash assumed to be signed (cannot be NULL)
*/

View File

@ -99,7 +99,7 @@ SECP256K1_API int secp256k1_schnorr_generate_nonce_pair(
/** Produce a partial Schnorr signature, which can be combined using
* secp256k1_schnorr_partial_combine, to end up with a full signature that is
* verifiable using secp256k1_schnorr_verify.
* Returns: 1: signature created succesfully.
* Returns: 1: signature created successfully.
* 0: no valid signature exists with this combination of keys, nonces
* and message (chance around 1 in 2^128)
* -1: invalid private key, nonce, or public nonces.
@ -148,7 +148,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign(
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
/** Combine multiple Schnorr partial signatures.
* Returns: 1: the passed signatures were succesfully combined.
* Returns: 1: the passed signatures were successfully combined.
* 0: the resulting signature is not valid (chance of 1 in 2^256)
* -1: some inputs were invalid, or the signatures were not created
* using the same set of nonces
@ -163,7 +163,7 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine
const secp256k1_context* ctx,
unsigned char *sig64,
const unsigned char * const * sig64sin,
int n
size_t n
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
# ifdef __cplusplus

View File

@ -0,0 +1,322 @@
# This code supports verifying group implementations which have branches
# or conditional statements (like cmovs), by allowing each execution path
# to independently set assumptions on input or intermediary variables.
#
# The general approach is:
# * A constraint is a tuple of two sets of of symbolic expressions:
# the first of which are required to evaluate to zero, the second of which
# are required to evaluate to nonzero.
# - A constraint is said to be conflicting if any of its nonzero expressions
# is in the ideal with basis the zero expressions (in other words: when the
# zero expressions imply that one of the nonzero expressions are zero).
# * There is a list of laws that describe the intended behaviour, including
# laws for addition and doubling. Each law is called with the symbolic point
# coordinates as arguments, and returns:
# - A constraint describing the assumptions under which it is applicable,
# called "assumeLaw"
# - A constraint describing the requirements of the law, called "require"
# * Implementations are transliterated into functions that operate as well on
# algebraic input points, and are called once per combination of branches
# exectured. Each execution returns:
# - A constraint describing the assumptions this implementation requires
# (such as Z1=1), called "assumeFormula"
# - A constraint describing the assumptions this specific branch requires,
# but which is by construction guaranteed to cover the entire space by
# merging the results from all branches, called "assumeBranch"
# - The result of the computation
# * All combinations of laws with implementation branches are tried, and:
# - If the combination of assumeLaw, assumeFormula, and assumeBranch results
# in a conflict, it means this law does not apply to this branch, and it is
# skipped.
# - For others, we try to prove the require constraints hold, assuming the
# information in assumeLaw + assumeFormula + assumeBranch, and if this does
# not succeed, we fail.
# + To prove an expression is zero, we check whether it belongs to the
# ideal with the assumed zero expressions as basis. This test is exact.
# + To prove an expression is nonzero, we check whether each of its
# factors is contained in the set of nonzero assumptions' factors.
# This test is not exact, so various combinations of original and
# reduced expressions' factors are tried.
# - If we succeed, we print out the assumptions from assumeFormula that
# weren't implied by assumeLaw already. Those from assumeBranch are skipped,
# as we assume that all constraints in it are complementary with each other.
#
# Based on the sage verification scripts used in the Explicit-Formulas Database
# by Tanja Lange and others, see http://hyperelliptic.org/EFD
class fastfrac:
"""Fractions over rings."""
def __init__(self,R,top,bot=1):
"""Construct a fractional, given a ring, a numerator, and denominator."""
self.R = R
if parent(top) == ZZ or parent(top) == R:
self.top = R(top)
self.bot = R(bot)
elif top.__class__ == fastfrac:
self.top = top.top
self.bot = top.bot * bot
else:
self.top = R(numerator(top))
self.bot = R(denominator(top)) * bot
def iszero(self,I):
"""Return whether this fraction is zero given an ideal."""
return self.top in I and self.bot not in I
def reduce(self,assumeZero):
zero = self.R.ideal(map(numerator, assumeZero))
return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot))
def __add__(self,other):
"""Add two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top + self.bot * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot)
return NotImplemented
def __sub__(self,other):
"""Subtract two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top - self.bot * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot)
return NotImplemented
def __neg__(self):
"""Return the negation of a fraction."""
return fastfrac(self.R,-self.top,self.bot)
def __mul__(self,other):
"""Multiply two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top * other,self.bot)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.top,self.bot * other.bot)
return NotImplemented
def __rmul__(self,other):
"""Multiply something else with a fraction."""
return self.__mul__(other)
def __div__(self,other):
"""Divide two fractions."""
if parent(other) == ZZ:
return fastfrac(self.R,self.top,self.bot * other)
if other.__class__ == fastfrac:
return fastfrac(self.R,self.top * other.bot,self.bot * other.top)
return NotImplemented
def __pow__(self,other):
"""Compute a power of a fraction."""
if parent(other) == ZZ:
if other < 0:
# Negative powers require flipping top and bottom
return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other))
else:
return fastfrac(self.R,self.top ^ other,self.bot ^ other)
return NotImplemented
def __str__(self):
return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))"
def __repr__(self):
return "%s" % self
def numerator(self):
return self.top
class constraints:
"""A set of constraints, consisting of zero and nonzero expressions.
Constraints can either be used to express knowledge or a requirement.
Both the fields zero and nonzero are maps from expressions to description
strings. The expressions that are the keys in zero are required to be zero,
and the expressions that are the keys in nonzero are required to be nonzero.
Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in
nonzero could be multiplied into a single key. This is often much less
efficient to work with though, so we keep them separate inside the
constraints. This allows higher-level code to do fast checks on the individual
nonzero elements, or combine them if needed for stronger checks.
We can't multiply the different zero elements, as it would suffice for one of
the factors to be zero, instead of all of them. Instead, the zero elements are
typically combined into an ideal first.
"""
def __init__(self, **kwargs):
if 'zero' in kwargs:
self.zero = dict(kwargs['zero'])
else:
self.zero = dict()
if 'nonzero' in kwargs:
self.nonzero = dict(kwargs['nonzero'])
else:
self.nonzero = dict()
def negate(self):
return constraints(zero=self.nonzero, nonzero=self.zero)
def __add__(self, other):
zero = self.zero.copy()
zero.update(other.zero)
nonzero = self.nonzero.copy()
nonzero.update(other.nonzero)
return constraints(zero=zero, nonzero=nonzero)
def __str__(self):
return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero)
def __repr__(self):
return "%s" % self
def conflicts(R, con):
"""Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
zero = R.ideal(map(numerator, con.zero))
if 1 in zero:
return True
# First a cheap check whether any of the individual nonzero terms conflict on
# their own.
for nonzero in con.nonzero:
if nonzero.iszero(zero):
return True
# It can be the case that entries in the nonzero set do not individually
# conflict with the zero set, but their combination does. For example, knowing
# that either x or y is zero is equivalent to having x*y in the zero set.
# Having x or y individually in the nonzero set is not a conflict, but both
# simultaneously is, so that is the right thing to check for.
if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero):
return True
return False
def get_nonzero_set(R, assume):
"""Calculate a simple set of nonzero expressions"""
zero = R.ideal(map(numerator, assume.zero))
nonzero = set()
for nz in map(numerator, assume.nonzero):
for (f,n) in nz.factor():
nonzero.add(f)
rnz = zero.reduce(nz)
for (f,n) in rnz.factor():
nonzero.add(f)
return nonzero
def prove_nonzero(R, exprs, assume):
"""Check whether an expression is provably nonzero, given assumptions"""
zero = R.ideal(map(numerator, assume.zero))
nonzero = get_nonzero_set(R, assume)
expl = set()
ok = True
for expr in exprs:
if numerator(expr) in zero:
return (False, [exprs[expr]])
allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
for (f, n) in allexprs.factor():
if f not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for (f, n) in zero.reduce(numerator(allexprs)).factor():
if f not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for expr in exprs:
for (f,n) in numerator(expr).factor():
if f not in nonzero:
ok = False
if ok:
return (True, None)
ok = True
for expr in exprs:
for (f,n) in zero.reduce(numerator(expr)).factor():
if f not in nonzero:
expl.add(exprs[expr])
if expl:
return (False, list(expl))
else:
return (True, None)
def prove_zero(R, exprs, assume):
"""Check whether all of the passed expressions are provably zero, given assumptions"""
r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
if not r:
return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
zero = R.ideal(map(numerator, assume.zero))
nonzero = prod(x for x in assume.nonzero)
expl = []
for expr in exprs:
if not expr.iszero(zero):
expl.append(exprs[expr])
if not expl:
return (True, None)
return (False, expl)
def describe_extra(R, assume, assumeExtra):
"""Describe what assumptions are added, given existing assumptions"""
zerox = assume.zero.copy()
zerox.update(assumeExtra.zero)
zero = R.ideal(map(numerator, assume.zero))
zeroextra = R.ideal(map(numerator, zerox))
nonzero = get_nonzero_set(R, assume)
ret = set()
# Iterate over the extra zero expressions
for base in assumeExtra.zero:
if base not in zero:
add = []
for (f, n) in numerator(base).factor():
if f not in nonzero:
add += ["%s" % f]
if add:
ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base])
# Iterate over the extra nonzero expressions
for nz in assumeExtra.nonzero:
nzr = zeroextra.reduce(numerator(nz))
if nzr not in zeroextra:
for (f,n) in nzr.factor():
if zeroextra.reduce(f) not in nonzero:
ret.add("%s != 0" % zeroextra.reduce(f))
return ", ".join(x for x in ret)
def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require):
"""Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions"""
assume = assumeLaw + assumeAssert + assumeBranch
if conflicts(R, assume):
# This formula does not apply
return None
describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert)
ok, msg = prove_zero(R, require.zero, assume)
if not ok:
return "FAIL, %s fails (assuming %s)" % (str(msg), describe)
res, expl = prove_nonzero(R, require.nonzero, assume)
if not res:
return "FAIL, %s fails (assuming %s)" % (str(expl), describe)
if describe != "":
return "OK (assuming %s)" % describe
else:
return "OK"
def concrete_verify(c):
for k in c.zero:
if k != 0:
return (False, c.zero[k])
for k in c.nonzero:
if k == 0:
return (False, c.nonzero[k])
return (True, None)

View File

@ -0,0 +1,306 @@
# Test libsecp256k1' group operation implementations using prover.sage
import sys
load("group_prover.sage")
load("weierstrass_prover.sage")
def formula_secp256k1_gej_double_var(a):
"""libsecp256k1's secp256k1_gej_double_var, used by various addition functions"""
rz = a.Z * a.Y
rz = rz * 2
t1 = a.X^2
t1 = t1 * 3
t2 = t1^2
t3 = a.Y^2
t3 = t3 * 2
t4 = t3^2
t4 = t4 * 2
t3 = t3 * a.X
rx = t3
rx = rx * 4
rx = -rx
rx = rx + t2
t2 = -t2
t3 = t3 * 6
t3 = t3 + t2
ry = t1 * t3
t2 = -t4
ry = ry + t2
return jacobianpoint(rx, ry, rz)
def formula_secp256k1_gej_add_var(branch, a, b):
"""libsecp256k1's secp256k1_gej_add_var"""
if branch == 0:
return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b)
if branch == 1:
return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a)
z22 = b.Z^2
z12 = a.Z^2
u1 = a.X * z22
u2 = b.X * z12
s1 = a.Y * z22
s1 = s1 * b.Z
s2 = b.Y * z12
s2 = s2 * a.Z
h = -u1
h = h + u2
i = -s1
i = i + s2
if branch == 2:
r = formula_secp256k1_gej_double_var(a)
return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r)
if branch == 3:
return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity())
i2 = i^2
h2 = h^2
h3 = h2 * h
h = h * b.Z
rz = a.Z * h
t = u1 * h2
rx = t
rx = rx * 2
rx = rx + h3
rx = -rx
rx = rx + i2
ry = -rx
ry = ry + t
ry = ry * i
h3 = h3 * s1
h3 = -h3
ry = ry + h3
return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
def formula_secp256k1_gej_add_ge_var(branch, a, b):
"""libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1"""
if branch == 0:
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b)
if branch == 1:
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a)
z12 = a.Z^2
u1 = a.X
u2 = b.X * z12
s1 = a.Y
s2 = b.Y * z12
s2 = s2 * a.Z
h = -u1
h = h + u2
i = -s1
i = i + s2
if (branch == 2):
r = formula_secp256k1_gej_double_var(a)
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r)
if (branch == 3):
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity())
i2 = i^2
h2 = h^2
h3 = h * h2
rz = a.Z * h
t = u1 * h2
rx = t
rx = rx * 2
rx = rx + h3
rx = -rx
rx = rx + i2
ry = -rx
ry = ry + t
ry = ry * i
h3 = h3 * s1
h3 = -h3
ry = ry + h3
return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
def formula_secp256k1_gej_add_zinv_var(branch, a, b):
"""libsecp256k1's secp256k1_gej_add_zinv_var"""
bzinv = b.Z^(-1)
if branch == 0:
return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a)
if branch == 1:
bzinv2 = bzinv^2
bzinv3 = bzinv2 * bzinv
rx = b.X * bzinv2
ry = b.Y * bzinv3
rz = 1
return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz))
azz = a.Z * bzinv
z12 = azz^2
u1 = a.X
u2 = b.X * z12
s1 = a.Y
s2 = b.Y * z12
s2 = s2 * azz
h = -u1
h = h + u2
i = -s1
i = i + s2
if branch == 2:
r = formula_secp256k1_gej_double_var(a)
return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r)
if branch == 3:
return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity())
i2 = i^2
h2 = h^2
h3 = h * h2
rz = a.Z
rz = rz * h
t = u1 * h2
rx = t
rx = rx * 2
rx = rx + h3
rx = -rx
rx = rx + i2
ry = -rx
ry = ry + t
ry = ry * i
h3 = h3 * s1
h3 = -h3
ry = ry + h3
return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz))
def formula_secp256k1_gej_add_ge(branch, a, b):
"""libsecp256k1's secp256k1_gej_add_ge"""
zeroes = {}
nonzeroes = {}
a_infinity = False
if (branch & 4) != 0:
nonzeroes.update({a.Infinity : 'a_infinite'})
a_infinity = True
else:
zeroes.update({a.Infinity : 'a_finite'})
zz = a.Z^2
u1 = a.X
u2 = b.X * zz
s1 = a.Y
s2 = b.Y * zz
s2 = s2 * a.Z
t = u1
t = t + u2
m = s1
m = m + s2
rr = t^2
m_alt = -u2
tt = u1 * m_alt
rr = rr + tt
degenerate = (branch & 3) == 3
if (branch & 1) != 0:
zeroes.update({m : 'm_zero'})
else:
nonzeroes.update({m : 'm_nonzero'})
if (branch & 2) != 0:
zeroes.update({rr : 'rr_zero'})
else:
nonzeroes.update({rr : 'rr_nonzero'})
rr_alt = s1
rr_alt = rr_alt * 2
m_alt = m_alt + u1
if not degenerate:
rr_alt = rr
m_alt = m
n = m_alt^2
q = n * t
n = n^2
if degenerate:
n = m
t = rr_alt^2
rz = a.Z * m_alt
infinity = False
if (branch & 8) != 0:
if not a_infinity:
infinity = True
zeroes.update({rz : 'r.z=0'})
else:
nonzeroes.update({rz : 'r.z!=0'})
rz = rz * 2
q = -q
t = t + q
rx = t
t = t * 2
t = t + q
t = t * rr_alt
t = t + n
ry = -t
rx = rx * 4
ry = ry * 4
if a_infinity:
rx = b.X
ry = b.Y
rz = 1
if infinity:
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity())
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz))
def formula_secp256k1_gej_add_ge_old(branch, a, b):
"""libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx"""
a_infinity = (branch & 1) != 0
zero = {}
nonzero = {}
if a_infinity:
nonzero.update({a.Infinity : 'a_infinite'})
else:
zero.update({a.Infinity : 'a_finite'})
zz = a.Z^2
u1 = a.X
u2 = b.X * zz
s1 = a.Y
s2 = b.Y * zz
s2 = s2 * a.Z
z = a.Z
t = u1
t = t + u2
m = s1
m = m + s2
n = m^2
q = n * t
n = n^2
rr = t^2
t = u1 * u2
t = -t
rr = rr + t
t = rr^2
rz = m * z
infinity = False
if (branch & 2) != 0:
if not a_infinity:
infinity = True
else:
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity())
zero.update({rz : 'r.z=0'})
else:
nonzero.update({rz : 'r.z!=0'})
rz = rz * (0 if a_infinity else 2)
rx = t
q = -q
rx = rx + q
q = q * 3
t = t * 2
t = t + q
t = t * rr
t = t + n
ry = -t
rx = rx * (0 if a_infinity else 4)
ry = ry * (0 if a_infinity else 4)
t = b.X
t = t * (1 if a_infinity else 0)
rx = rx + t
t = b.Y
t = t * (1 if a_infinity else 0)
ry = ry + t
t = (1 if a_infinity else 0)
rz = rz + t
if infinity:
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity())
return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz))
if __name__ == "__main__":
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var)
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var)
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var)
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge)
check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)
if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive":
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43)
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43)
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43)
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43)
check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)

View File

@ -0,0 +1,264 @@
# Prover implementation for Weierstrass curves of the form
# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws
# operating on affine and Jacobian coordinates, including the point at infinity
# represented by a 4th variable in coordinates.
load("group_prover.sage")
class affinepoint:
def __init__(self, x, y, infinity=0):
self.x = x
self.y = y
self.infinity = infinity
def __str__(self):
return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity)
class jacobianpoint:
def __init__(self, x, y, z, infinity=0):
self.X = x
self.Y = y
self.Z = z
self.Infinity = infinity
def __str__(self):
return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity)
def point_at_infinity():
return jacobianpoint(1, 1, 1, 1)
def negate(p):
if p.__class__ == affinepoint:
return affinepoint(p.x, -p.y)
if p.__class__ == jacobianpoint:
return jacobianpoint(p.X, -p.Y, p.Z)
assert(False)
def on_weierstrass_curve(A, B, p):
"""Return a set of zero-expressions for an affine point to be on the curve"""
return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'})
def tangential_to_weierstrass_curve(A, B, p12, p3):
"""Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)"""
return constraints(zero={
(p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve'
})
def colinear(p1, p2, p3):
"""Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear"""
return constraints(zero={
(p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1',
(p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2',
(p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3'
})
def good_affine_point(p):
return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'})
def good_jacobian_point(p):
return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'})
def good_point(p):
return constraints(nonzero={p.Z^6 : 'nonzero_X'})
def finite(p, *affine_fns):
con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'})
if p.Z != 0:
return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con)
else:
return con
def infinite(p):
return constraints(nonzero={p.Infinity : 'infinite_point'})
def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC):
"""Check whether the passed set of coordinates is a valid Jacobian add, given assumptions"""
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pa) +
on_weierstrass_curve(A, B, pb) +
finite(pA) +
finite(pB) +
constraints(nonzero={pa.x - pb.x : 'different_x'}))
require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
colinear(pa, pb, negate(pc))))
return (assumeLaw, require)
def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC):
"""Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions"""
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pa) +
on_weierstrass_curve(A, B, pb) +
finite(pA) +
finite(pB) +
constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'}))
require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) +
tangential_to_weierstrass_curve(A, B, pa, negate(pc))))
return (assumeLaw, require)
def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC):
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pa) +
on_weierstrass_curve(A, B, pb) +
finite(pA) +
finite(pB) +
constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'}))
require = infinite(pC)
return (assumeLaw, require)
def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC):
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pb) +
infinite(pA) +
finite(pB))
require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'}))
return (assumeLaw, require)
def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC):
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
on_weierstrass_curve(A, B, pa) +
infinite(pB) +
finite(pA))
require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'}))
return (assumeLaw, require)
def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC):
assumeLaw = (good_affine_point(pa) +
good_affine_point(pb) +
good_jacobian_point(pA) +
good_jacobian_point(pB) +
infinite(pA) +
infinite(pB))
require = infinite(pC)
return (assumeLaw, require)
laws_jacobian_weierstrass = {
'add': law_jacobian_weierstrass_add,
'double': law_jacobian_weierstrass_double,
'add_opposite': law_jacobian_weierstrass_add_opposites,
'add_infinite_a': law_jacobian_weierstrass_add_infinite_a,
'add_infinite_b': law_jacobian_weierstrass_add_infinite_b,
'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab
}
def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
"""Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field"""
F = Integers(p)
print "Formula %s on Z%i:" % (name, p)
points = []
for x in xrange(0, p):
for y in xrange(0, p):
point = affinepoint(F(x), F(y))
r, e = concrete_verify(on_weierstrass_curve(A, B, point))
if r:
points.append(point)
for za in xrange(1, p):
for zb in xrange(1, p):
for pa in points:
for pb in points:
for ia in xrange(2):
for ib in xrange(2):
pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia)
pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib)
for branch in xrange(0, branches):
assumeAssert, assumeBranch, pC = formula(branch, pA, pB)
pC.X = F(pC.X)
pC.Y = F(pC.Y)
pC.Z = F(pC.Z)
pC.Infinity = F(pC.Infinity)
r, e = concrete_verify(assumeAssert + assumeBranch)
if r:
match = False
for key in laws_jacobian_weierstrass:
assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC)
r, e = concrete_verify(assumeLaw)
if r:
if match:
print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity)
else:
match = True
r, e = concrete_verify(require)
if not r:
print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e)
print
def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
assumeLaw, require = f(A, B, pa, pb, pA, pB, pC)
return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require)
def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
"""Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically"""
R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')
lift = lambda x: fastfrac(R,x)
ax = lift(ax)
ay = lift(ay)
Az = lift(Az)
bx = lift(bx)
by = lift(by)
Bz = lift(Bz)
Ai = lift(Ai)
Bi = lift(Bi)
pa = affinepoint(ax, ay, Ai)
pb = affinepoint(bx, by, Bi)
pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai)
pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi)
res = {}
for key in laws_jacobian_weierstrass:
res[key] = []
print ("Formula " + name + ":")
count = 0
for branch in xrange(branches):
assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
pC.X = lift(pC.X)
pC.Y = lift(pC.Y)
pC.Z = lift(pC.Z)
pC.Infinity = lift(pC.Infinity)
for key in laws_jacobian_weierstrass:
res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
for key in res:
print " %s:" % key
val = res[key]
for x in val:
if x[0] is not None:
print " branch %i: %s" % (x[1], x[0])
print

View File

@ -11,6 +11,12 @@
#include "util.h"
#include "bench.h"
#ifdef ENABLE_OPENSSL_TESTS
#include <openssl/bn.h>
#include <openssl/ecdsa.h>
#include <openssl/obj_mac.h>
#endif
typedef struct {
secp256k1_context *ctx;
unsigned char msg[32];
@ -19,6 +25,9 @@ typedef struct {
size_t siglen;
unsigned char pubkey[33];
size_t pubkeylen;
#ifdef ENABLE_OPENSSL_TESTS
EC_GROUP* ec_group;
#endif
} benchmark_verify_t;
static void benchmark_verify(void* arg) {
@ -40,6 +49,36 @@ static void benchmark_verify(void* arg) {
}
}
#ifdef ENABLE_OPENSSL_TESTS
static void benchmark_verify_openssl(void* arg) {
int i;
benchmark_verify_t* data = (benchmark_verify_t*)arg;
for (i = 0; i < 20000; i++) {
data->sig[data->siglen - 1] ^= (i & 0xFF);
data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
{
EC_KEY *pkey = EC_KEY_new();
const unsigned char *pubkey = &data->pubkey[0];
int result;
CHECK(pkey != NULL);
result = EC_KEY_set_group(pkey, data->ec_group);
CHECK(result);
result = (o2i_ECPublicKey(&pkey, &pubkey, data->pubkeylen)) != NULL;
CHECK(result);
result = ECDSA_verify(0, &data->msg[0], sizeof(data->msg), &data->sig[0], data->siglen, pkey) == (i == 0);
CHECK(result);
EC_KEY_free(pkey);
}
data->sig[data->siglen - 1] ^= (i & 0xFF);
data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF);
data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF);
}
}
#endif
int main(void) {
int i;
secp256k1_pubkey pubkey;
@ -58,9 +97,15 @@ int main(void) {
CHECK(secp256k1_ecdsa_sign(data.ctx, &sig, data.msg, data.key, NULL, NULL));
CHECK(secp256k1_ecdsa_signature_serialize_der(data.ctx, data.sig, &data.siglen, &sig));
CHECK(secp256k1_ec_pubkey_create(data.ctx, &pubkey, data.key));
data.pubkeylen = 33;
CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1);
run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000);
#ifdef ENABLE_OPENSSL_TESTS
data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1);
run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000);
EC_GROUP_free(data.ec_group);
#endif
secp256k1_context_destroy(data.ctx);
return 0;

View File

@ -75,8 +75,9 @@ static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned cha
return -1;
}
if ((size_t)lenleft > sizeof(size_t)) {
/* The resulthing length would exceed the range of a size_t, so
certainly longer than the passed array size. */
/* The resulting length would exceed the range of a size_t, so
* certainly longer than the passed array size.
*/
return -1;
}
while (lenleft > 0) {
@ -267,13 +268,17 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
secp256k1_fe_get_b32(b, &r.x);
secp256k1_scalar_set_b32(sigr, b, &overflow);
if (secp256k1_scalar_is_zero(sigr)) {
/* P.x = order is on the curve, so technically sig->r could end up zero, which would be an invalid signature. */
/* This branch is cryptographically unreachable as hitting it requires finding the discrete log of P.x = N. */
/* P.x = order is on the curve, so technically sig->r could end up zero, which would be an invalid signature.
* This branch is cryptographically unreachable as hitting it requires finding the discrete log of P.x = N.
*/
secp256k1_gej_clear(&rp);
secp256k1_ge_clear(&r);
return 0;
}
if (recid) {
/* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log
* of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria.
*/
*recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0);
}
secp256k1_scalar_mul(&n, sigr, seckey);

View File

@ -10,7 +10,7 @@
/** Field element module.
*
* Field elements can be represented in several ways, but code accessing
* it (and implementations) need to take certain properaties into account:
* it (and implementations) need to take certain properties into account:
* - Each field element can be normalized or not.
* - Each field element has a magnitude, which represents how far away
* its representation is away from normalization. Normalized elements
@ -87,9 +87,11 @@ static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp2
* The output magnitude is 1 (but not guaranteed to be normalized). */
static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a);
/** Sets a field element to be the (modular) square root (if any exist) of another. Requires the
* input's magnitude to be at most 8. The output magnitude is 1 (but not guaranteed to be
* normalized). Return value indicates whether a square root was found. */
/** If a has a square root, it is computed in r and 1 is returned. If a does not
* have a square root, the root of its negation is computed and 0 is returned.
* The input's magnitude can be at most 8. The output magnitude is 1 (but not
* guaranteed to be normalized). The result in r will always be a square
* itself. */
static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a);
/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be

View File

@ -29,6 +29,15 @@ SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const
}
static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) {
/** Given that p is congruent to 3 mod 4, we can compute the square root of
* a mod p as the (p+1)/4'th power of a.
*
* As (p+1)/4 is an even number, it will have the same result for a and for
* (-a). Only one of these two numbers actually has a square root however,
* so we test at the end by squaring and comparing to the input.
* Also because (p+1)/4 is an even number, the computed square root is
* itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)).
*/
secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1;
int j;

View File

@ -43,6 +43,12 @@ typedef struct {
/** Set a group element equal to the point with given X and Y coordinates */
static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y);
/** Set a group element (affine) equal to the point with the given X coordinate
* and a Y coordinate that is a quadratic residue modulo p. The return value
* is true iff a coordinate with the given X coordinate exists.
*/
static int secp256k1_ge_set_xquad_var(secp256k1_ge *r, const secp256k1_fe *x);
/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness
* for Y. Return value indicates whether the result is valid. */
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd);

View File

@ -165,7 +165,7 @@ static void secp256k1_ge_clear(secp256k1_ge *r) {
secp256k1_fe_clear(&r->y);
}
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
static int secp256k1_ge_set_xquad_var(secp256k1_ge *r, const secp256k1_fe *x) {
secp256k1_fe x2, x3, c;
r->x = *x;
secp256k1_fe_sqr(&x2, x);
@ -173,7 +173,11 @@ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int o
r->infinity = 0;
secp256k1_fe_set_int(&c, 7);
secp256k1_fe_add(&c, &x3);
if (!secp256k1_fe_sqrt_var(&r->y, &c)) {
return secp256k1_fe_sqrt_var(&r->y, &c);
}
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
if (!secp256k1_ge_set_xquad_var(r, x)) {
return 0;
}
secp256k1_fe_normalize_var(&r->y);
@ -181,6 +185,7 @@ static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int o
secp256k1_fe_negate(&r->y, &r->y, 1);
}
return 1;
}
static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) {

View File

@ -1,21 +0,0 @@
/* Bitcoin secp256k1 bindings
* Written in 2015 by
* Andrew Poelstra
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to
* the public domain worldwide. This software is distributed without
* any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication
* along with this software.
* If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include "contrib/lax_der_parsing.h"
int secp256k1_ecdsa_signature_parse_der_lax_(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
return secp256k1_ecdsa_signature_parse_der_lax(ctx, sig, input, inputlen);
}

View File

@ -154,7 +154,7 @@ int secp256k1_schnorr_partial_sign(const secp256k1_context* ctx, unsigned char *
return secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, &pubnon, secp256k1_schnorr_msghash_sha256, msg32);
}
int secp256k1_schnorr_partial_combine(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char * const *sig64sin, int n) {
int secp256k1_schnorr_partial_combine(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char * const *sig64sin, size_t n) {
ARG_CHECK(sig64 != NULL);
ARG_CHECK(n >= 1);
ARG_CHECK(sig64sin != NULL);

View File

@ -15,6 +15,6 @@ typedef void (*secp256k1_schnorr_msghash)(unsigned char *h32, const unsigned cha
static int secp256k1_schnorr_sig_sign(const secp256k1_ecmult_gen_context* ctx, unsigned char *sig64, const secp256k1_scalar *key, const secp256k1_scalar *nonce, const secp256k1_ge *pubnonce, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
static int secp256k1_schnorr_sig_verify(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, const secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context* ctx, const unsigned char *sig64, secp256k1_ge *pubkey, secp256k1_schnorr_msghash hash, const unsigned char *msg32);
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins);
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, size_t n, const unsigned char * const *sig64ins);
#endif

View File

@ -178,9 +178,9 @@ static int secp256k1_schnorr_sig_recover(const secp256k1_ecmult_context* ctx, co
return 1;
}
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins) {
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, size_t n, const unsigned char * const *sig64ins) {
secp256k1_scalar s = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
int i;
size_t i;
for (i = 0; i < n; i++) {
secp256k1_scalar si;
int overflow;

View File

@ -167,15 +167,26 @@ int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pu
int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags) {
secp256k1_ge Q;
size_t len;
int ret = 0;
(void)ctx;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(output != NULL);
ARG_CHECK(outputlen != NULL);
ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65));
len = *outputlen;
*outputlen = 0;
ARG_CHECK(output != NULL);
memset(output, 0, len);
ARG_CHECK(pubkey != NULL);
ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION);
return (secp256k1_pubkey_load(ctx, &Q, pubkey) &&
secp256k1_eckey_pubkey_serialize(&Q, output, outputlen, flags & SECP256K1_FLAGS_BIT_COMPRESSION));
if (secp256k1_pubkey_load(ctx, &Q, pubkey)) {
ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION);
if (ret) {
*outputlen = len;
}
}
return ret;
}
static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) {
@ -402,13 +413,13 @@ int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *p
int overflow;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(pubkey != NULL);
memset(pubkey, 0, sizeof(*pubkey));
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(seckey != NULL);
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec));
memset(pubkey, 0, sizeof(*pubkey));
if (ret) {
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
secp256k1_ge_set_gej(&p, &pj);
@ -520,12 +531,13 @@ int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *see
return 1;
}
int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, int n) {
int i;
int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, size_t n) {
size_t i;
secp256k1_gej Qj;
secp256k1_ge Q;
ARG_CHECK(pubnonce != NULL);
memset(pubnonce, 0, sizeof(*pubnonce));
ARG_CHECK(n >= 1);
ARG_CHECK(pubnonces != NULL);
@ -536,7 +548,6 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *
secp256k1_gej_add_ge(&Qj, &Qj, &Q);
}
if (secp256k1_gej_is_infinity(&Qj)) {
memset(pubnonce, 0, sizeof(*pubnonce));
return 0;
}
secp256k1_ge_set_gej(&Q, &Qj);

File diff suppressed because it is too large Load Diff

View File

@ -150,9 +150,9 @@ extern "C" {
input: *const c_uchar, in_len: size_t)
-> c_int;
pub fn secp256k1_ecdsa_signature_parse_der_lax_(cx: *const Context, sig: *mut Signature,
input: *const c_uchar, in_len: size_t)
-> c_int;
pub fn ecdsa_signature_parse_der_lax(cx: *const Context, sig: *mut Signature,
input: *const c_uchar, in_len: size_t)
-> c_int;
pub fn secp256k1_ecdsa_signature_serialize_der(cx: *const Context, output: *const c_uchar,
out_len: *mut size_t, sig: *const Signature)

View File

@ -160,7 +160,7 @@ impl PublicKey {
let mut ret = ArrayVec::new();
unsafe {
let mut ret_len = ret.len() as ::libc::size_t;
let mut ret_len = constants::PUBLIC_KEY_SIZE as ::libc::size_t;
let compressed = if compressed { ffi::SECP256K1_SER_COMPRESSED } else { ffi::SECP256K1_SER_UNCOMPRESSED };
let err = ffi::secp256k1_ec_pubkey_serialize(secp.ctx, ret.as_ptr(),
&mut ret_len, self.as_ptr(),

View File

@ -111,8 +111,8 @@ impl Signature {
pub fn from_der_lax(secp: &Secp256k1, data: &[u8]) -> Result<Signature, Error> {
unsafe {
let mut ret = ffi::Signature::blank();
if ffi::secp256k1_ecdsa_signature_parse_der_lax_(secp.ctx, &mut ret,
data.as_ptr(), data.len() as libc::size_t) == 1 {
if ffi::ecdsa_signature_parse_der_lax(secp.ctx, &mut ret,
data.as_ptr(), data.len() as libc::size_t) == 1 {
Ok(Signature(ret))
} else {
Err(Error::InvalidSignature)