crypto/secp256k1: update to github.com/bitcoin-core/secp256k1 @ 9d560f9 (#3544)
- Use defined constants instead of hard-coding their integer value. - Allocate secp256k1 structs on the C stack instead of converting []byte - Remove dead code
This commit is contained in:
parent
93077c98e4
commit
e0ceeab0d1
|
@ -72,14 +72,6 @@ func BenchmarkSha3(b *testing.B) {
|
|||
fmt.Println(amount, ":", time.Since(start))
|
||||
}
|
||||
|
||||
func Test0Key(t *testing.T) {
|
||||
key := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
_, err := secp256k1.GeneratePubKey(key)
|
||||
if err == nil {
|
||||
t.Errorf("expected error due to zero privkey")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
key, _ := HexToECDSA(testPrivHex)
|
||||
addr := common.HexToAddress(testAddrHex)
|
||||
|
|
|
@ -33,7 +33,6 @@ package secp256k1
|
|||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"io"
|
||||
"math/big"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
@ -224,6 +223,7 @@ func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int,
|
|||
if len(scalar) > 32 {
|
||||
panic("can't handle scalars > 256 bits")
|
||||
}
|
||||
// NOTE: potential timing issue
|
||||
padded := make([]byte, 32)
|
||||
copy(padded[32-len(scalar):], scalar)
|
||||
scalar = padded
|
||||
|
@ -257,31 +257,6 @@ func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
|
|||
return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k)
|
||||
}
|
||||
|
||||
var mask = []byte{0xff, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f}
|
||||
|
||||
//TODO: double check if it is okay
|
||||
// GenerateKey returns a public/private key pair. The private key is generated
|
||||
// using the given reader, which must return random data.
|
||||
func (BitCurve *BitCurve) GenerateKey(rand io.Reader) (priv []byte, x, y *big.Int, err error) {
|
||||
byteLen := (BitCurve.BitSize + 7) >> 3
|
||||
priv = make([]byte, byteLen)
|
||||
|
||||
for x == nil {
|
||||
_, err = io.ReadFull(rand, priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// We have to mask off any excess bits in the case that the size of the
|
||||
// underlying field is not a whole number of bytes.
|
||||
priv[0] &= mask[BitCurve.BitSize%8]
|
||||
// This is because, in tests, rand will return all zeros and we don't
|
||||
// want to get the point at infinity and loop forever.
|
||||
priv[1] ^= 0x42
|
||||
x, y = BitCurve.ScalarBaseMult(priv)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Marshal converts a point into the form specified in section 4.3.6 of ANSI
|
||||
// X9.62.
|
||||
func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte {
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// secp256k1_context_create_sign_verify creates a context for signing and signature verification.
|
||||
static secp256k1_context* secp256k1_context_create_sign_verify() {
|
||||
return secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
}
|
||||
|
||||
// secp256k1_ecdsa_recover_pubkey recovers the public key of an encoded compact signature.
|
||||
//
|
||||
// Returns: 1: recovery was successful
|
||||
// 0: recovery was not successful
|
||||
// Args: ctx: pointer to a context object (cannot be NULL)
|
||||
// Out: pubkey_out: the serialized 65-byte public key of the signer (cannot be NULL)
|
||||
// In: sigdata: pointer to a 65-byte signature with the recovery id at the end (cannot be NULL)
|
||||
// msgdata: pointer to a 32-byte message (cannot be NULL)
|
||||
static int secp256k1_ecdsa_recover_pubkey(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *pubkey_out,
|
||||
const unsigned char *sigdata,
|
||||
const unsigned char *msgdata
|
||||
) {
|
||||
secp256k1_ecdsa_recoverable_signature sig;
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &sig, sigdata, (int)sigdata[64])) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_ecdsa_recover(ctx, &pubkey, &sig, msgdata)) {
|
||||
return 0;
|
||||
}
|
||||
size_t outputlen = 65;
|
||||
return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
|
||||
}
|
||||
|
||||
// secp256k1_pubkey_scalar_mul multiplies a point by a scalar in constant time.
|
||||
//
|
||||
// Returns: 1: multiplication was successful
|
||||
// 0: scalar was invalid (zero or overflow)
|
||||
// Args: ctx: pointer to a context object (cannot be NULL)
|
||||
// Out: point: the multiplied point (usually secret)
|
||||
// In: point: pointer to a 64-byte public point,
|
||||
// encoded as two 256bit big-endian numbers.
|
||||
// scalar: a 32-byte scalar with which to multiply the point
|
||||
int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) {
|
||||
int ret = 0;
|
||||
int overflow = 0;
|
||||
secp256k1_fe feX, feY;
|
||||
secp256k1_gej res;
|
||||
secp256k1_ge ge;
|
||||
secp256k1_scalar s;
|
||||
ARG_CHECK(point != NULL);
|
||||
ARG_CHECK(scalar != NULL);
|
||||
(void)ctx;
|
||||
|
||||
secp256k1_fe_set_b32(&feX, point);
|
||||
secp256k1_fe_set_b32(&feY, point+32);
|
||||
secp256k1_ge_set_xy(&ge, &feX, &feY);
|
||||
secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&s)) {
|
||||
ret = 0;
|
||||
} else {
|
||||
secp256k1_ecmult_const(&res, &ge, &s);
|
||||
secp256k1_ge_set_gej(&ge, &res);
|
||||
/* Note: can't use secp256k1_pubkey_save here because it is not constant time. */
|
||||
secp256k1_fe_normalize(&ge.x);
|
||||
secp256k1_fe_normalize(&ge.y);
|
||||
secp256k1_fe_get_b32(point, &ge.x);
|
||||
secp256k1_fe_get_b32(point+32, &ge.y);
|
||||
ret = 1;
|
||||
}
|
||||
secp256k1_scalar_clear(&s);
|
||||
return ret;
|
||||
}
|
|
@ -6,6 +6,7 @@ bench_schnorr_verify
|
|||
bench_recover
|
||||
bench_internal
|
||||
tests
|
||||
exhaustive_tests
|
||||
gen_context
|
||||
*.exe
|
||||
*.so
|
||||
|
@ -25,17 +26,24 @@ config.status
|
|||
libtool
|
||||
.deps/
|
||||
.dirstamp
|
||||
build-aux/
|
||||
*.lo
|
||||
*.o
|
||||
*~
|
||||
src/libsecp256k1-config.h
|
||||
src/libsecp256k1-config.h.in
|
||||
src/ecmult_static_context.h
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
build-aux/config.guess
|
||||
build-aux/config.sub
|
||||
build-aux/depcomp
|
||||
build-aux/install-sh
|
||||
build-aux/ltmain.sh
|
||||
build-aux/m4/libtool.m4
|
||||
build-aux/m4/lt~obsolete.m4
|
||||
build-aux/m4/ltoptions.m4
|
||||
build-aux/m4/ltsugar.m4
|
||||
build-aux/m4/ltversion.m4
|
||||
build-aux/missing
|
||||
build-aux/compile
|
||||
build-aux/test-driver
|
||||
src/stamp-h1
|
||||
libsecp256k1.pc
|
||||
|
|
|
@ -6,25 +6,30 @@ addons:
|
|||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
cache:
|
||||
directories:
|
||||
- src/java/guava/
|
||||
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 RECOVERY=no EXPERIMENTAL=no
|
||||
- GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar
|
||||
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 ENDOMORPHISM=yes
|
||||
- BIGNUM=no
|
||||
- BIGNUM=no ENDOMORPHISM=yes SCHNORR=yes RECOVERY=yes
|
||||
- BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes
|
||||
- BIGNUM=no STATICPRECOMPUTATION=no
|
||||
- BUILD=distcheck
|
||||
- EXTRAFLAGS=CFLAGS=-DDETERMINISTIC
|
||||
- EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC
|
||||
- EXTRAFLAGS=CFLAGS=-O0
|
||||
- BUILD=check-java ECDH=yes EXPERIMENTAL=yes
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
|
@ -54,9 +59,11 @@ matrix:
|
|||
packages:
|
||||
- gcc-multilib
|
||||
- libgmp-dev:i386
|
||||
before_install: mkdir -p `dirname $GUAVA_JAR`
|
||||
install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi
|
||||
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 $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-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD
|
||||
os: linux
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
ACLOCAL_AMFLAGS = -I build-aux/m4
|
||||
|
||||
lib_LTLIBRARIES = libsecp256k1.la
|
||||
if USE_JNI
|
||||
JNI_LIB = libsecp256k1_jni.la
|
||||
noinst_LTLIBRARIES = $(JNI_LIB)
|
||||
else
|
||||
JNI_LIB =
|
||||
endif
|
||||
include_HEADERS = include/secp256k1.h
|
||||
noinst_HEADERS =
|
||||
noinst_HEADERS += src/scalar.h
|
||||
noinst_HEADERS += src/scalar_4x64.h
|
||||
noinst_HEADERS += src/scalar_8x32.h
|
||||
noinst_HEADERS += src/scalar_low.h
|
||||
noinst_HEADERS += src/scalar_impl.h
|
||||
noinst_HEADERS += src/scalar_4x64_impl.h
|
||||
noinst_HEADERS += src/scalar_8x32_impl.h
|
||||
noinst_HEADERS += src/scalar_low_impl.h
|
||||
noinst_HEADERS += src/group.h
|
||||
noinst_HEADERS += src/group_impl.h
|
||||
noinst_HEADERS += src/num_gmp.h
|
||||
|
@ -32,6 +40,7 @@ noinst_HEADERS += src/field_5x52_impl.h
|
|||
noinst_HEADERS += src/field_5x52_int128_impl.h
|
||||
noinst_HEADERS += src/field_5x52_asm_impl.h
|
||||
noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h
|
||||
noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
|
||||
noinst_HEADERS += src/util.h
|
||||
noinst_HEADERS += src/testrand.h
|
||||
noinst_HEADERS += src/testrand_impl.h
|
||||
|
@ -40,41 +49,103 @@ noinst_HEADERS += src/hash_impl.h
|
|||
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
|
||||
|
||||
if USE_EXTERNAL_ASM
|
||||
COMMON_LIB = libsecp256k1_common.la
|
||||
noinst_LTLIBRARIES = $(COMMON_LIB)
|
||||
else
|
||||
COMMON_LIB =
|
||||
endif
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libsecp256k1.pc
|
||||
|
||||
libsecp256k1_la_SOURCES = src/secp256k1.c
|
||||
libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
|
||||
libsecp256k1_la_LIBADD = $(SECP_LIBS)
|
||||
if USE_EXTERNAL_ASM
|
||||
if USE_ASM_ARM
|
||||
libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s
|
||||
endif
|
||||
endif
|
||||
|
||||
libsecp256k1_la_SOURCES = src/secp256k1.c
|
||||
libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
|
||||
libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB)
|
||||
|
||||
libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c
|
||||
libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES)
|
||||
|
||||
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_LDFLAGS = -static
|
||||
bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
|
||||
bench_sign_SOURCES = src/bench_sign.c
|
||||
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
bench_sign_LDFLAGS = -static
|
||||
bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
|
||||
bench_internal_SOURCES = src/bench_internal.c
|
||||
bench_internal_LDADD = $(SECP_LIBS)
|
||||
bench_internal_LDFLAGS = -static
|
||||
bench_internal_CPPFLAGS = $(SECP_INCLUDES)
|
||||
bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB)
|
||||
bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES)
|
||||
endif
|
||||
|
||||
TESTS =
|
||||
if USE_TESTS
|
||||
noinst_PROGRAMS += tests
|
||||
tests_SOURCES = src/tests.c
|
||||
tests_CPPFLAGS = -DVERIFY -I$(top_srcdir)/src $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
|
||||
tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS)
|
||||
tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES)
|
||||
if !ENABLE_COVERAGE
|
||||
tests_CPPFLAGS += -DVERIFY
|
||||
endif
|
||||
tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB)
|
||||
tests_LDFLAGS = -static
|
||||
TESTS = tests
|
||||
TESTS += tests
|
||||
endif
|
||||
|
||||
if USE_EXHAUSTIVE_TESTS
|
||||
noinst_PROGRAMS += exhaustive_tests
|
||||
exhaustive_tests_SOURCES = src/tests_exhaustive.c
|
||||
exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDES)
|
||||
if !ENABLE_COVERAGE
|
||||
exhaustive_tests_CPPFLAGS += -DVERIFY
|
||||
endif
|
||||
exhaustive_tests_LDADD = $(SECP_LIBS)
|
||||
exhaustive_tests_LDFLAGS = -static
|
||||
TESTS += exhaustive_tests
|
||||
endif
|
||||
|
||||
JAVAROOT=src/java
|
||||
JAVAORG=org/bitcoin
|
||||
JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar
|
||||
CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA)
|
||||
JAVA_FILES= \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \
|
||||
$(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java
|
||||
|
||||
if USE_JNI
|
||||
|
||||
$(JAVA_GUAVA):
|
||||
@echo Guava is missing. Fetch it via: \
|
||||
wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@)
|
||||
@false
|
||||
|
||||
.stamp-java: $(JAVA_FILES)
|
||||
@echo Compiling $^
|
||||
$(AM_V_at)$(CLASSPATH_ENV) javac $^
|
||||
@touch $@
|
||||
|
||||
if USE_TESTS
|
||||
|
||||
check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java
|
||||
$(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test
|
||||
|
||||
endif
|
||||
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
|
||||
|
@ -92,19 +163,15 @@ $(bench_internal_OBJECTS): src/ecmult_static_context.h
|
|||
src/ecmult_static_context.h: $(gen_context_BIN)
|
||||
./$(gen_context_BIN)
|
||||
|
||||
CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h
|
||||
CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java
|
||||
endif
|
||||
|
||||
EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h
|
||||
EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES)
|
||||
|
||||
if ENABLE_MODULE_ECDH
|
||||
include src/modules/ecdh/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_SCHNORR
|
||||
include src/modules/schnorr/Makefile.am.include
|
||||
endif
|
||||
|
||||
if ENABLE_MODULE_RECOVERY
|
||||
include src/modules/recovery/Makefile.am.include
|
||||
endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
libsecp256k1
|
||||
============
|
||||
|
||||
[![Build Status](https://travis-ci.org/bitcoin/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin/secp256k1)
|
||||
[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1)
|
||||
|
||||
Optimized C library for EC operations on curve secp256k1.
|
||||
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR finds include directories needed for compiling
|
||||
# programs using the JNI interface.
|
||||
#
|
||||
# JNI include directories are usually in the Java distribution. This is
|
||||
# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in
|
||||
# that order. When this macro completes, a list of directories is left in
|
||||
# the variable JNI_INCLUDE_DIRS.
|
||||
#
|
||||
# Example usage follows:
|
||||
#
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS
|
||||
# do
|
||||
# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR"
|
||||
# done
|
||||
#
|
||||
# If you want to force a specific compiler:
|
||||
#
|
||||
# - at the configure.in level, set JAVAC=yourcompiler before calling
|
||||
# AX_JNI_INCLUDE_DIR
|
||||
#
|
||||
# - at the configure level, setenv JAVAC
|
||||
#
|
||||
# Note: This macro can work with the autoconf M4 macros for Java programs.
|
||||
# This particular macro is not part of the original set of macros.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Don Anderson <dda@sleepycat.com>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 10
|
||||
|
||||
AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR])
|
||||
AC_DEFUN([AX_JNI_INCLUDE_DIR],[
|
||||
|
||||
JNI_INCLUDE_DIRS=""
|
||||
|
||||
if test "x$JAVA_HOME" != x; then
|
||||
_JTOPDIR="$JAVA_HOME"
|
||||
else
|
||||
if test "x$JAVAC" = x; then
|
||||
JAVAC=javac
|
||||
fi
|
||||
AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no])
|
||||
if test "x$_ACJNI_JAVAC" = xno; then
|
||||
AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME])
|
||||
fi
|
||||
_ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC")
|
||||
_JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'`
|
||||
fi
|
||||
|
||||
case "$host_os" in
|
||||
darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
|
||||
_JINC="$_JTOPDIR/Headers";;
|
||||
*) _JINC="$_JTOPDIR/include";;
|
||||
esac
|
||||
_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR])
|
||||
_AS_ECHO_LOG([_JINC=$_JINC])
|
||||
|
||||
# On Mac OS X 10.6.4, jni.h is a symlink:
|
||||
# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h
|
||||
# -> ../../CurrentJDK/Headers/jni.h.
|
||||
|
||||
AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path,
|
||||
[
|
||||
if test -f "$_JINC/jni.h"; then
|
||||
ac_cv_jni_header_path="$_JINC"
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
|
||||
else
|
||||
_JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'`
|
||||
if test -f "$_JTOPDIR/include/jni.h"; then
|
||||
ac_cv_jni_header_path="$_JTOPDIR/include"
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path"
|
||||
else
|
||||
ac_cv_jni_header_path=none
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
|
||||
# get the likely subdirectories for system specific java includes
|
||||
case "$host_os" in
|
||||
bsdi*) _JNI_INC_SUBDIRS="bsdos";;
|
||||
darwin*) _JNI_INC_SUBDIRS="darwin";;
|
||||
freebsd*) _JNI_INC_SUBDIRS="freebsd";;
|
||||
linux*) _JNI_INC_SUBDIRS="linux genunix";;
|
||||
osf*) _JNI_INC_SUBDIRS="alpha";;
|
||||
solaris*) _JNI_INC_SUBDIRS="solaris";;
|
||||
mingw*) _JNI_INC_SUBDIRS="win32";;
|
||||
cygwin*) _JNI_INC_SUBDIRS="win32";;
|
||||
*) _JNI_INC_SUBDIRS="genunix";;
|
||||
esac
|
||||
|
||||
if test "x$ac_cv_jni_header_path" != "xnone"; then
|
||||
# add any subdirectories that are present
|
||||
for JINCSUBDIR in $_JNI_INC_SUBDIRS
|
||||
do
|
||||
if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then
|
||||
JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
])
|
||||
|
||||
# _ACJNI_FOLLOW_SYMLINKS <path>
|
||||
# Follows symbolic links on <path>,
|
||||
# finally setting variable _ACJNI_FOLLOWED
|
||||
# ----------------------------------------
|
||||
AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[
|
||||
# find the include directory relative to the javac executable
|
||||
_cur="$1"
|
||||
while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do
|
||||
AC_MSG_CHECKING([symlink for $_cur])
|
||||
_slink=`ls -ld "$_cur" | sed 's/.* -> //'`
|
||||
case "$_slink" in
|
||||
/*) _cur="$_slink";;
|
||||
# 'X' avoids triggering unwanted echo options.
|
||||
*) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";;
|
||||
esac
|
||||
AC_MSG_RESULT([$_cur])
|
||||
done
|
||||
_ACJNI_FOLLOWED="$_cur"
|
||||
])# _ACJNI
|
|
@ -0,0 +1,125 @@
|
|||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PROG_CC_FOR_BUILD
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro searches for a C compiler that generates native executables,
|
||||
# that is a C compiler that surely is not a cross-compiler. This can be
|
||||
# useful if you have to generate source code at compile-time like for
|
||||
# example GCC does.
|
||||
#
|
||||
# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything
|
||||
# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD).
|
||||
# The value of these variables can be overridden by the user by specifying
|
||||
# a compiler with an environment variable (like you do for standard CC).
|
||||
#
|
||||
# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object
|
||||
# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if
|
||||
# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are
|
||||
# substituted in the Makefile.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Paolo Bonzini <bonzini@gnu.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 8
|
||||
|
||||
AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD])
|
||||
AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl
|
||||
AC_REQUIRE([AC_PROG_CC])dnl
|
||||
AC_REQUIRE([AC_PROG_CPP])dnl
|
||||
AC_REQUIRE([AC_EXEEXT])dnl
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])dnl
|
||||
|
||||
dnl Use the standard macros, but make them use other variable names
|
||||
dnl
|
||||
pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl
|
||||
pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl
|
||||
pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl
|
||||
pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl
|
||||
pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl
|
||||
pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl
|
||||
pushdef([ac_cv_objext], ac_cv_build_objext)dnl
|
||||
pushdef([ac_exeext], ac_build_exeext)dnl
|
||||
pushdef([ac_objext], ac_build_objext)dnl
|
||||
pushdef([CC], CC_FOR_BUILD)dnl
|
||||
pushdef([CPP], CPP_FOR_BUILD)dnl
|
||||
pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl
|
||||
pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl
|
||||
pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl
|
||||
pushdef([host], build)dnl
|
||||
pushdef([host_alias], build_alias)dnl
|
||||
pushdef([host_cpu], build_cpu)dnl
|
||||
pushdef([host_vendor], build_vendor)dnl
|
||||
pushdef([host_os], build_os)dnl
|
||||
pushdef([ac_cv_host], ac_cv_build)dnl
|
||||
pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl
|
||||
pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl
|
||||
pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl
|
||||
pushdef([ac_cv_host_os], ac_cv_build_os)dnl
|
||||
pushdef([ac_cpp], ac_build_cpp)dnl
|
||||
pushdef([ac_compile], ac_build_compile)dnl
|
||||
pushdef([ac_link], ac_build_link)dnl
|
||||
|
||||
save_cross_compiling=$cross_compiling
|
||||
save_ac_tool_prefix=$ac_tool_prefix
|
||||
cross_compiling=no
|
||||
ac_tool_prefix=
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CPP
|
||||
AC_EXEEXT
|
||||
|
||||
ac_tool_prefix=$save_ac_tool_prefix
|
||||
cross_compiling=$save_cross_compiling
|
||||
|
||||
dnl Restore the old definitions
|
||||
dnl
|
||||
popdef([ac_link])dnl
|
||||
popdef([ac_compile])dnl
|
||||
popdef([ac_cpp])dnl
|
||||
popdef([ac_cv_host_os])dnl
|
||||
popdef([ac_cv_host_vendor])dnl
|
||||
popdef([ac_cv_host_cpu])dnl
|
||||
popdef([ac_cv_host_alias])dnl
|
||||
popdef([ac_cv_host])dnl
|
||||
popdef([host_os])dnl
|
||||
popdef([host_vendor])dnl
|
||||
popdef([host_cpu])dnl
|
||||
popdef([host_alias])dnl
|
||||
popdef([host])dnl
|
||||
popdef([LDFLAGS])dnl
|
||||
popdef([CPPFLAGS])dnl
|
||||
popdef([CFLAGS])dnl
|
||||
popdef([CPP])dnl
|
||||
popdef([CC])dnl
|
||||
popdef([ac_objext])dnl
|
||||
popdef([ac_exeext])dnl
|
||||
popdef([ac_cv_objext])dnl
|
||||
popdef([ac_cv_exeext])dnl
|
||||
popdef([ac_cv_prog_cc_g])dnl
|
||||
popdef([ac_cv_prog_cc_cross])dnl
|
||||
popdef([ac_cv_prog_cc_works])dnl
|
||||
popdef([ac_cv_prog_gcc])dnl
|
||||
popdef([ac_cv_prog_CPP])dnl
|
||||
|
||||
dnl Finally, set Makefile variables
|
||||
dnl
|
||||
BUILD_EXEEXT=$ac_build_exeext
|
||||
BUILD_OBJEXT=$ac_build_objext
|
||||
AC_SUBST(BUILD_EXEEXT)dnl
|
||||
AC_SUBST(BUILD_OBJEXT)dnl
|
||||
AC_SUBST([CFLAGS_FOR_BUILD])dnl
|
||||
AC_SUBST([CPPFLAGS_FOR_BUILD])dnl
|
||||
AC_SUBST([LDFLAGS_FOR_BUILD])dnl
|
||||
])
|
|
@ -0,0 +1,69 @@
|
|||
dnl libsecp25k1 helper checks
|
||||
AC_DEFUN([SECP_INT128_CHECK],[
|
||||
has_int128=$ac_cv_type___int128
|
||||
])
|
||||
|
||||
dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell.
|
||||
AC_DEFUN([SECP_64BIT_ASM_CHECK],[
|
||||
AC_MSG_CHECKING(for x86_64 assembly availability)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <stdint.h>]],[[
|
||||
uint64_t a = 11, tmp;
|
||||
__asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx");
|
||||
]])],[has_64bit_asm=yes],[has_64bit_asm=no])
|
||||
AC_MSG_RESULT([$has_64bit_asm])
|
||||
])
|
||||
|
||||
dnl
|
||||
AC_DEFUN([SECP_OPENSSL_CHECK],[
|
||||
has_libcrypto=no
|
||||
m4_ifdef([PKG_CHECK_MODULES],[
|
||||
PKG_CHECK_MODULES([CRYPTO], [libcrypto], [has_libcrypto=yes],[has_libcrypto=no])
|
||||
if test x"$has_libcrypto" = x"yes"; then
|
||||
TEMP_LIBS="$LIBS"
|
||||
LIBS="$LIBS $CRYPTO_LIBS"
|
||||
AC_CHECK_LIB(crypto, main,[AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])],[has_libcrypto=no])
|
||||
LIBS="$TEMP_LIBS"
|
||||
fi
|
||||
])
|
||||
if test x$has_libcrypto = xno; then
|
||||
AC_CHECK_HEADER(openssl/crypto.h,[
|
||||
AC_CHECK_LIB(crypto, main,[
|
||||
has_libcrypto=yes
|
||||
CRYPTO_LIBS=-lcrypto
|
||||
AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])
|
||||
])
|
||||
])
|
||||
LIBS=
|
||||
fi
|
||||
if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then
|
||||
AC_MSG_CHECKING(for EC functions in libcrypto)
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
|
||||
#include <openssl/ec.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/obj_mac.h>]],[[
|
||||
EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
ECDSA_sign(0, NULL, 0, NULL, NULL, eckey);
|
||||
ECDSA_verify(0, NULL, 0, NULL, 0, eckey);
|
||||
EC_KEY_free(eckey);
|
||||
ECDSA_SIG *sig_openssl;
|
||||
sig_openssl = ECDSA_SIG_new();
|
||||
(void)sig_openssl->r;
|
||||
ECDSA_SIG_free(sig_openssl);
|
||||
]])],[has_openssl_ec=yes],[has_openssl_ec=no])
|
||||
AC_MSG_RESULT([$has_openssl_ec])
|
||||
fi
|
||||
])
|
||||
|
||||
dnl
|
||||
AC_DEFUN([SECP_GMP_CHECK],[
|
||||
if test x"$has_gmp" != x"yes"; then
|
||||
CPPFLAGS_TEMP="$CPPFLAGS"
|
||||
CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS"
|
||||
LIBS_TEMP="$LIBS"
|
||||
LIBS="$GMP_LIBS $LIBS"
|
||||
AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])])
|
||||
CPPFLAGS="$CPPFLAGS_TEMP"
|
||||
LIBS="$LIBS_TEMP"
|
||||
fi
|
||||
])
|
|
@ -20,7 +20,7 @@ AC_PATH_TOOL(STRIP, strip)
|
|||
AX_PROG_CC_FOR_BUILD
|
||||
|
||||
if test "x$CFLAGS" = "x"; then
|
||||
CFLAGS="-O3 -g"
|
||||
CFLAGS="-g"
|
||||
fi
|
||||
|
||||
AM_PROG_CC_C_O
|
||||
|
@ -29,6 +29,7 @@ AC_PROG_CC_C89
|
|||
if test x"$ac_cv_prog_cc_c89" = x"no"; then
|
||||
AC_MSG_ERROR([c89 compiler support required])
|
||||
fi
|
||||
AM_PROG_AS
|
||||
|
||||
case $host_os in
|
||||
*darwin*)
|
||||
|
@ -88,36 +89,56 @@ AC_ARG_ENABLE(benchmark,
|
|||
[use_benchmark=$enableval],
|
||||
[use_benchmark=no])
|
||||
|
||||
AC_ARG_ENABLE(coverage,
|
||||
AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]),
|
||||
[enable_coverage=$enableval],
|
||||
[enable_coverage=no])
|
||||
|
||||
AC_ARG_ENABLE(tests,
|
||||
AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]),
|
||||
[use_tests=$enableval],
|
||||
[use_tests=yes])
|
||||
|
||||
AC_ARG_ENABLE(openssl_tests,
|
||||
AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]),
|
||||
[enable_openssl_tests=$enableval],
|
||||
[enable_openssl_tests=auto])
|
||||
|
||||
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(exhaustive_tests,
|
||||
AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]),
|
||||
[use_exhaustive_tests=$enableval],
|
||||
[use_exhaustive_tests=yes])
|
||||
|
||||
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])
|
||||
[use_ecmult_static_precomputation=auto])
|
||||
|
||||
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)]),
|
||||
[enable_module_schnorr=$enableval],
|
||||
[enable_module_schnorr=no])
|
||||
|
||||
AC_ARG_ENABLE(module_recovery,
|
||||
AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]),
|
||||
[enable_module_recovery=$enableval],
|
||||
[enable_module_recovery=no])
|
||||
|
||||
AC_ARG_ENABLE(jni,
|
||||
AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]),
|
||||
[use_jni=$enableval],
|
||||
[use_jni=auto])
|
||||
|
||||
AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto],
|
||||
[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto])
|
||||
|
||||
|
@ -127,8 +148,8 @@ AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto],
|
|||
AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto],
|
||||
[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto])
|
||||
|
||||
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|no|auto]
|
||||
[Specify assembly optimizations to use. Default is auto])],[req_asm=$withval], [req_asm=auto])
|
||||
AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto]
|
||||
[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto])
|
||||
|
||||
AC_CHECK_TYPES([__int128])
|
||||
|
||||
|
@ -138,6 +159,42 @@ AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])],
|
|||
[ AC_MSG_RESULT([no])
|
||||
])
|
||||
|
||||
if test x"$enable_coverage" = x"yes"; then
|
||||
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
|
||||
CFLAGS="$CFLAGS -O0 --coverage"
|
||||
LDFLAGS="--coverage"
|
||||
else
|
||||
CFLAGS="$CFLAGS -O3"
|
||||
fi
|
||||
|
||||
if test x"$use_ecmult_static_precomputation" != x"no"; then
|
||||
save_cross_compiling=$cross_compiling
|
||||
cross_compiling=no
|
||||
TEMP_CC="$CC"
|
||||
CC="$CC_FOR_BUILD"
|
||||
AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}])
|
||||
AC_RUN_IFELSE(
|
||||
[AC_LANG_PROGRAM([], [return 0])],
|
||||
[working_native_cc=yes],
|
||||
[working_native_cc=no],[dnl])
|
||||
CC="$TEMP_CC"
|
||||
cross_compiling=$save_cross_compiling
|
||||
|
||||
if test x"$working_native_cc" = x"no"; then
|
||||
set_precomp=no
|
||||
if test x"$use_ecmult_static_precomputation" = x"yes"; then
|
||||
AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
|
||||
else
|
||||
AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD])
|
||||
fi
|
||||
else
|
||||
AC_MSG_RESULT([ok])
|
||||
set_precomp=yes
|
||||
fi
|
||||
else
|
||||
set_precomp=no
|
||||
fi
|
||||
|
||||
if test x"$req_asm" = x"auto"; then
|
||||
SECP_64BIT_ASM_CHECK
|
||||
if test x"$has_64bit_asm" = x"yes"; then
|
||||
|
@ -155,6 +212,8 @@ else
|
|||
AC_MSG_ERROR([x86_64 assembly optimization requested but not available])
|
||||
fi
|
||||
;;
|
||||
arm)
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
|
@ -247,10 +306,15 @@ else
|
|||
fi
|
||||
|
||||
# select assembly optimization
|
||||
use_external_asm=no
|
||||
|
||||
case $set_asm in
|
||||
x86_64)
|
||||
AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations])
|
||||
;;
|
||||
arm)
|
||||
use_external_asm=yes
|
||||
;;
|
||||
no)
|
||||
;;
|
||||
*)
|
||||
|
@ -305,16 +369,48 @@ esac
|
|||
if test x"$use_tests" = x"yes"; then
|
||||
SECP_OPENSSL_CHECK
|
||||
if test x"$has_openssl_ec" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
|
||||
SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
|
||||
SECP_TEST_LIBS="$CRYPTO_LIBS"
|
||||
if test x"$enable_openssl_tests" != x"no"; then
|
||||
AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available])
|
||||
SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS"
|
||||
SECP_TEST_LIBS="$CRYPTO_LIBS"
|
||||
|
||||
case $host in
|
||||
*mingw*)
|
||||
SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
|
||||
;;
|
||||
esac
|
||||
case $host in
|
||||
*mingw*)
|
||||
SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
if test x"$enable_openssl_tests" = x"yes"; then
|
||||
AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available])
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if test x"$enable_openssl_tests" = x"yes"; then
|
||||
AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$use_jni" != x"no"; then
|
||||
AX_JNI_INCLUDE_DIR
|
||||
have_jni_dependencies=yes
|
||||
if test x"$enable_module_ecdh" = x"no"; then
|
||||
have_jni_dependencies=no
|
||||
fi
|
||||
if test "x$JNI_INCLUDE_DIRS" = "x"; then
|
||||
have_jni_dependencies=no
|
||||
fi
|
||||
if test "x$have_jni_dependencies" = "xno"; then
|
||||
if test x"$use_jni" = x"yes"; then
|
||||
AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.])
|
||||
fi
|
||||
AC_MSG_WARN([jni headers/dependencies not found. jni support disabled])
|
||||
use_jni=no
|
||||
else
|
||||
use_jni=yes
|
||||
for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do
|
||||
JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -327,7 +423,7 @@ if test x"$use_endomorphism" = x"yes"; then
|
|||
AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization])
|
||||
fi
|
||||
|
||||
if test x"$use_ecmult_static_precomputation" = x"yes"; then
|
||||
if test x"$set_precomp" = x"yes"; then
|
||||
AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table])
|
||||
fi
|
||||
|
||||
|
@ -335,38 +431,59 @@ if test x"$enable_module_ecdh" = x"yes"; then
|
|||
AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module])
|
||||
fi
|
||||
|
||||
if test x"$enable_module_schnorr" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_SCHNORR, 1, [Define this symbol to enable the Schnorr signature module])
|
||||
fi
|
||||
|
||||
if test x"$enable_module_recovery" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module])
|
||||
fi
|
||||
|
||||
AC_C_BIGENDIAN()
|
||||
|
||||
if test x"$use_external_asm" = x"yes"; then
|
||||
AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used])
|
||||
fi
|
||||
|
||||
AC_MSG_NOTICE([Using static precomputation: $set_precomp])
|
||||
AC_MSG_NOTICE([Using assembly optimizations: $set_asm])
|
||||
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 for coverage analysis: $enable_coverage])
|
||||
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])
|
||||
AC_MSG_NOTICE([Using jni: $use_jni])
|
||||
|
||||
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([******])
|
||||
else
|
||||
if test x"$enable_module_ecdh" = x"yes"; then
|
||||
AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.])
|
||||
fi
|
||||
if test x"$set_asm" = x"arm"; then
|
||||
AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.])
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_CONFIG_HEADERS([src/libsecp256k1-config.h])
|
||||
AC_CONFIG_FILES([Makefile libsecp256k1.pc])
|
||||
AC_SUBST(JNI_INCLUDES)
|
||||
AC_SUBST(SECP_INCLUDES)
|
||||
AC_SUBST(SECP_LIBS)
|
||||
AC_SUBST(SECP_TEST_LIBS)
|
||||
AC_SUBST(SECP_TEST_INCLUDES)
|
||||
AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"])
|
||||
AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
|
||||
AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
|
||||
AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$use_ecmult_static_precomputation" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_SCHNORR], [test x"$enable_module_schnorr" = x"yes"])
|
||||
AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
|
||||
AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"])
|
||||
AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
|
||||
AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
|
||||
|
||||
dnl make sure nothing new is exported so that we don't break the cache
|
||||
PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/**********************************************************************
|
||||
* 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.*
|
||||
**********************************************************************/
|
||||
|
||||
/****
|
||||
* 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.
|
||||
*
|
||||
* In many places it matters that different implementations do not only accept
|
||||
* the same set of valid signatures, but also reject the same set of signatures.
|
||||
* The only means to accomplish that is by strictly obeying a standard, and not
|
||||
* accepting anything else.
|
||||
*
|
||||
* Nonetheless, sometimes there is a need for compatibility with systems that
|
||||
* use signatures which do not strictly obey DER. The snippet below shows how
|
||||
* certain violations are easily supported. You may need to adapt it.
|
||||
*
|
||||
* Do not use this for new systems. Use well-defined DER or compact signatures
|
||||
* instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and
|
||||
* secp256k1_ecdsa_signature_parse_compact).
|
||||
*
|
||||
* The supported violations are:
|
||||
* - All numbers are parsed as nonnegative integers, even though X.609-0207
|
||||
* section 8.3.3 specifies that integers are always encoded as two's
|
||||
* complement.
|
||||
* - Integers can have length 0, even though section 8.3.1 says they can't.
|
||||
* - Integers with overly long padding are accepted, violation section
|
||||
* 8.3.2.
|
||||
* - 127-byte long length descriptors are accepted, even though section
|
||||
* 8.1.3.5.c says that they are not.
|
||||
* - Trailing garbage data inside or after the signature is ignored.
|
||||
* - The length descriptor of the sequence is ignored.
|
||||
*
|
||||
* Compared to for example OpenSSL, many violations are NOT supported:
|
||||
* - Using overly long tag descriptors for the sequence or integers inside,
|
||||
* violating section 8.1.2.2.
|
||||
* - Encoding primitive integers as constructed values, violating section
|
||||
* 8.3.1.
|
||||
*/
|
||||
|
||||
#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
|
||||
#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_
|
||||
|
||||
#include <secp256k1.h>
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
/** 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/**********************************************************************
|
||||
* 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.*
|
||||
**********************************************************************/
|
||||
|
||||
/****
|
||||
* 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
|
||||
* do not follow or establish any standard.
|
||||
*
|
||||
* It also contains code to serialize private keys in a compatible
|
||||
* manner.
|
||||
*
|
||||
* These functions are meant for compatibility with applications
|
||||
* that require BER encoded keys. When working with secp256k1-specific
|
||||
* code, the simple 32-byte private keys normally used by the
|
||||
* library are sufficient.
|
||||
*/
|
||||
|
||||
#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
|
||||
#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_
|
||||
|
||||
#include <secp256k1.h>
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
/** Export a private key in DER format.
|
||||
*
|
||||
* Returns: 1 if the private key was valid.
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot
|
||||
* be NULL)
|
||||
* Out: privkey: pointer to an array for storing the private key in BER.
|
||||
* Should have space for 279 bytes, and cannot be NULL.
|
||||
* privkeylen: Pointer to an int where the length of the private key in
|
||||
* privkey will be stored.
|
||||
* In: seckey: pointer to a 32-byte secret key to export.
|
||||
* compressed: 1 if the key should be exported in
|
||||
* compressed format, 0 otherwise
|
||||
*
|
||||
* This function is purely meant for compatibility with applications that
|
||||
* require BER encoded keys. When working with secp256k1-specific code, the
|
||||
* simple 32-byte private keys are sufficient.
|
||||
*
|
||||
* Note that this function does not guarantee correct DER output. It is
|
||||
* guaranteed to be parsable by secp256k1_ec_privkey_import_der
|
||||
*/
|
||||
SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *privkey,
|
||||
size_t *privkeylen,
|
||||
const unsigned char *seckey,
|
||||
int compressed
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Import a private key in DER format.
|
||||
* Returns: 1 if a private key was extracted.
|
||||
* Args: ctx: pointer to a context object (cannot be NULL).
|
||||
* Out: seckey: pointer to a 32-byte array for storing the private key.
|
||||
* (cannot be NULL).
|
||||
* In: privkey: pointer to a private key in DER format (cannot be NULL).
|
||||
* privkeylen: length of the DER private key pointed to be privkey.
|
||||
*
|
||||
* This function will accept more than just strict DER, and even allow some BER
|
||||
* violations. The public key stored inside the DER-encoded private key is not
|
||||
* verified for correctness, nor are the curve parameters. Use this function
|
||||
* only if you know in advance it is supposed to contain a secp256k1 private
|
||||
* key.
|
||||
*/
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -47,11 +47,8 @@ typedef struct secp256k1_context_struct secp256k1_context;
|
|||
* The exact representation of data inside is implementation defined and not
|
||||
* guaranteed to be portable between different platforms or versions. It is
|
||||
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
|
||||
* If you need to convert to a format suitable for storage or transmission, use
|
||||
* secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
|
||||
*
|
||||
* Furthermore, it is guaranteed that identical public keys (ignoring
|
||||
* compression) will have identical representation, so they can be memcmp'ed.
|
||||
* If you need to convert to a format suitable for storage, transmission, or
|
||||
* comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[64];
|
||||
|
@ -62,12 +59,9 @@ typedef struct {
|
|||
* The exact representation of data inside is implementation defined and not
|
||||
* guaranteed to be portable between different platforms or versions. It is
|
||||
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
|
||||
* If you need to convert to a format suitable for storage or transmission, use
|
||||
* the secp256k1_ecdsa_signature_serialize_* and
|
||||
* If you need to convert to a format suitable for storage, transmission, or
|
||||
* comparison, use the secp256k1_ecdsa_signature_serialize_* and
|
||||
* secp256k1_ecdsa_signature_serialize_* functions.
|
||||
*
|
||||
* Furthermore, it is guaranteed to identical signatures will have identical
|
||||
* representation, so they can be memcmp'ed.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char data[64];
|
||||
|
@ -147,12 +141,23 @@ typedef int (*secp256k1_nonce_function)(
|
|||
# define SECP256K1_ARG_NONNULL(_x)
|
||||
# endif
|
||||
|
||||
/** All flags' lower 8 bits indicate what they're for. Do not use directly. */
|
||||
#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1)
|
||||
#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0)
|
||||
#define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1)
|
||||
/** The higher bits contain the actual data. Do not use directly. */
|
||||
#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8)
|
||||
#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9)
|
||||
#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8)
|
||||
|
||||
/** Flags to pass to secp256k1_context_create. */
|
||||
# define SECP256K1_CONTEXT_VERIFY (1 << 0)
|
||||
# define SECP256K1_CONTEXT_SIGN (1 << 1)
|
||||
#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
|
||||
#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
|
||||
#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
|
||||
|
||||
/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */
|
||||
# define SECP256K1_EC_COMPRESSED (1 << 0)
|
||||
#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION)
|
||||
#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION)
|
||||
|
||||
/** Create a secp256k1 context object.
|
||||
*
|
||||
|
@ -218,7 +223,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.
|
||||
|
@ -253,15 +258,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.
|
||||
* 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,
|
||||
|
@ -271,6 +278,27 @@ SECP256K1_API int secp256k1_ec_pubkey_serialize(
|
|||
unsigned int flags
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Parse an ECDSA signature in compact (64 bytes) 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: input64: a pointer to the 64-byte array to parse
|
||||
*
|
||||
* The signature must consist of a 32-byte big endian R value, followed by a
|
||||
* 32-byte big endian S value. If R or S fall outside of [0..order-1], the
|
||||
* encoding is invalid. R and S with value 0 are allowed in the encoding.
|
||||
*
|
||||
* After the call, sig will always be initialized. If parsing failed or R or
|
||||
* S are zero, the resulting sig value is guaranteed to fail validation for any
|
||||
* message and public key.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_ecdsa_signature_parse_compact(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_ecdsa_signature* sig,
|
||||
const unsigned char *input64
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Parse a DER ECDSA signature.
|
||||
*
|
||||
* Returns: 1 when the signature could be parsed, 0 otherwise.
|
||||
|
@ -279,7 +307,12 @@ SECP256K1_API int secp256k1_ec_pubkey_serialize(
|
|||
* In: input: a pointer to the signature to be parsed
|
||||
* inputlen: the length of the array pointed to be input
|
||||
*
|
||||
* Note that this function also supports some violations of DER and even BER.
|
||||
* This function will accept any valid DER encoded signature, even if the
|
||||
* encoded numbers are out of range.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_ecdsa_signature_parse_der(
|
||||
const secp256k1_context* ctx,
|
||||
|
@ -306,6 +339,21 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_der(
|
|||
const secp256k1_ecdsa_signature* sig
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Serialize an ECDSA signature in compact (64 byte) format.
|
||||
*
|
||||
* Returns: 1
|
||||
* Args: ctx: a secp256k1 context object
|
||||
* Out: output64: a pointer to a 64-byte array to store the compact serialization
|
||||
* In: sig: a pointer to an initialized signature object
|
||||
*
|
||||
* See secp256k1_ecdsa_signature_parse_compact for details about the encoding.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *output64,
|
||||
const secp256k1_ecdsa_signature* sig
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Verify an ECDSA signature.
|
||||
*
|
||||
* Returns: 1: correct signature
|
||||
|
@ -314,6 +362,15 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_der(
|
|||
* In: sig: the signature being verified (cannot be NULL)
|
||||
* msg32: the 32-byte message hash being verified (cannot be NULL)
|
||||
* pubkey: pointer to an initialized public key to verify with (cannot be NULL)
|
||||
*
|
||||
* To avoid accepting malleable signatures, only ECDSA signatures in lower-S
|
||||
* form are accepted.
|
||||
*
|
||||
* If you need to accept ECDSA signatures from sources that do not obey this
|
||||
* rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to
|
||||
* validation, but be aware that doing so results in malleable signatures.
|
||||
*
|
||||
* For details, see the comments for that function.
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
|
||||
const secp256k1_context* ctx,
|
||||
|
@ -322,14 +379,62 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
|
|||
const secp256k1_pubkey *pubkey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Convert a signature to a normalized lower-S form.
|
||||
*
|
||||
* Returns: 1 if sigin was not normalized, 0 if it already was.
|
||||
* Args: ctx: a secp256k1 context object
|
||||
* Out: sigout: a pointer to a signature to fill with the normalized form,
|
||||
* or copy if the input was already normalized. (can be NULL if
|
||||
* you're only interested in whether the input was already
|
||||
* normalized).
|
||||
* In: sigin: a pointer to a signature to check/normalize (cannot be NULL,
|
||||
* can be identical to sigout)
|
||||
*
|
||||
* With ECDSA a third-party can forge a second distinct signature of the same
|
||||
* message, given a single initial signature, but without knowing the key. This
|
||||
* is done by negating the S value modulo the order of the curve, 'flipping'
|
||||
* the sign of the random point R which is not included in the signature.
|
||||
*
|
||||
* Forgery of the same message isn't universally problematic, but in systems
|
||||
* where message malleability or uniqueness of signatures is important this can
|
||||
* cause issues. This forgery can be blocked by all verifiers forcing signers
|
||||
* to use a normalized form.
|
||||
*
|
||||
* The lower-S form reduces the size of signatures slightly on average when
|
||||
* variable length encodings (such as DER) are used and is cheap to verify,
|
||||
* making it a good choice. Security of always using lower-S is assured because
|
||||
* anyone can trivially modify a signature after the fact to enforce this
|
||||
* property anyway.
|
||||
*
|
||||
* The lower S value is always between 0x1 and
|
||||
* 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
|
||||
* inclusive.
|
||||
*
|
||||
* No other forms of ECDSA malleability are known and none seem likely, but
|
||||
* there is no formal proof that ECDSA, even with this additional restriction,
|
||||
* is free of other malleability. Commonly used serialization schemes will also
|
||||
* accept various non-unique encodings, so care should be taken when this
|
||||
* property is required for an application.
|
||||
*
|
||||
* The secp256k1_ecdsa_sign function will by default create signatures in the
|
||||
* lower-S form, and secp256k1_ecdsa_verify will not accept others. In case
|
||||
* signatures come from a system that cannot enforce this property,
|
||||
* secp256k1_ecdsa_signature_normalize must be called before verification.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_ecdsa_signature_normalize(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_ecdsa_signature *sigout,
|
||||
const secp256k1_ecdsa_signature *sigin
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function.
|
||||
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
|
||||
* extra entropy.
|
||||
*/
|
||||
extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979;
|
||||
SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979;
|
||||
|
||||
/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */
|
||||
extern const secp256k1_nonce_function secp256k1_nonce_function_default;
|
||||
SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default;
|
||||
|
||||
/** Create an ECDSA signature.
|
||||
*
|
||||
|
@ -342,32 +447,8 @@ extern const secp256k1_nonce_function secp256k1_nonce_function_default;
|
|||
* noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
|
||||
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
|
||||
*
|
||||
* The sig always has an s value in the lower half of the range (From 0x1
|
||||
* to 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
|
||||
* inclusive), unlike many other implementations.
|
||||
*
|
||||
* With ECDSA a third-party can can forge a second distinct signature
|
||||
* of the same message given a single initial signature without knowing
|
||||
* the key by setting s to its additive inverse mod-order, 'flipping' the
|
||||
* sign of the random point R which is not included in the signature.
|
||||
* Since the forgery is of the same message this isn't universally
|
||||
* problematic, but in systems where message malleability or uniqueness
|
||||
* of signatures is important this can cause issues. This forgery can be
|
||||
* blocked by all verifiers forcing signers to use a canonical form. The
|
||||
* lower-S form reduces the size of signatures slightly on average when
|
||||
* variable length encodings (such as DER) are used and is cheap to
|
||||
* verify, making it a good choice. Security of always using lower-S is
|
||||
* assured because anyone can trivially modify a signature after the
|
||||
* fact to enforce this property. Adjusting it inside the signing
|
||||
* function avoids the need to re-serialize or have curve specific
|
||||
* constants outside of the library. By always using a canonical form
|
||||
* even in applications where it isn't needed it becomes possible to
|
||||
* impose a requirement later if a need is discovered.
|
||||
* No other forms of ECDSA malleability are known and none seem likely,
|
||||
* but there is no formal proof that ECDSA, even with this additional
|
||||
* restriction, is free of other malleability. Commonly used serialization
|
||||
* schemes will also accept various non-unique encodings, so care should
|
||||
* be taken when this property is required for an application.
|
||||
* The created signature is always in lower-S form. See
|
||||
* secp256k1_ecdsa_signature_normalize for more details.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_ecdsa_sign(
|
||||
const secp256k1_context* ctx,
|
||||
|
@ -404,55 +485,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create(
|
|||
const unsigned char *seckey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** Export a private key in BER format.
|
||||
*
|
||||
* Returns: 1 if the private key was valid.
|
||||
* Args: ctx: pointer to a context object, initialized for signing (cannot
|
||||
* be NULL)
|
||||
* Out: privkey: pointer to an array for storing the private key in BER.
|
||||
* Should have space for 279 bytes, and cannot be NULL.
|
||||
* privkeylen: Pointer to an int where the length of the private key in
|
||||
* privkey will be stored.
|
||||
* In: seckey: pointer to a 32-byte secret key to export.
|
||||
* flags: SECP256K1_EC_COMPRESSED if the key should be exported in
|
||||
* compressed format.
|
||||
*
|
||||
* This function is purely meant for compatibility with applications that
|
||||
* require BER encoded keys. When working with secp256k1-specific code, the
|
||||
* simple 32-byte private keys are sufficient.
|
||||
*
|
||||
* Note that this function does not guarantee correct DER output. It is
|
||||
* guaranteed to be parsable by secp256k1_ec_privkey_import.
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_export(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *privkey,
|
||||
size_t *privkeylen,
|
||||
const unsigned char *seckey,
|
||||
unsigned int flags
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Import a private key in DER format.
|
||||
* Returns: 1 if a private key was extracted.
|
||||
* Args: ctx: pointer to a context object (cannot be NULL).
|
||||
* Out: seckey: pointer to a 32-byte array for storing the private key.
|
||||
* (cannot be NULL).
|
||||
* In: privkey: pointer to a private key in DER format (cannot be NULL).
|
||||
* privkeylen: length of the DER private key pointed to be privkey.
|
||||
*
|
||||
* This function will accept more than just strict DER, and even allow some BER
|
||||
* violations. The public key stored inside the DER-encoded private key is not
|
||||
* verified for correctness, nor are the curve parameters. Use this function
|
||||
* only if you know in advance it is supposed to contain a secp256k1 private
|
||||
* key.
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_import(
|
||||
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);
|
||||
|
||||
/** Tweak a private key by adding tweak to it.
|
||||
* Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for
|
||||
* uniformly random 32-byte arrays, or if the resulting private key
|
||||
|
@ -526,18 +558,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
|
||||
|
|
|
@ -10,17 +10,18 @@ extern "C" {
|
|||
/** Compute an EC Diffie-Hellman secret in constant time
|
||||
* Returns: 1: exponentiation was successful
|
||||
* 0: scalar was invalid (zero or overflow)
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* Out: result: a 32-byte array which will be populated by an ECDH
|
||||
* secret computed from the point and scalar
|
||||
* In: point: pointer to a public point
|
||||
* scalar: a 32-byte scalar with which to multiply the point
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* Out: result: a 32-byte array which will be populated by an ECDH
|
||||
* secret computed from the point and scalar
|
||||
* In: pubkey: a pointer to a secp256k1_pubkey containing an
|
||||
* initialized public key
|
||||
* privkey: a 32-byte scalar with which to multiply the point
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *result,
|
||||
const secp256k1_pubkey *point,
|
||||
const unsigned char *scalar
|
||||
const secp256k1_pubkey *pubkey,
|
||||
const unsigned char *privkey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
# ifdef __cplusplus
|
||||
|
|
|
@ -65,7 +65,7 @@ SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact(
|
|||
unsigned char *output64,
|
||||
int *recid,
|
||||
const secp256k1_ecdsa_recoverable_signature* sig
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Create a recoverable ECDSA signature.
|
||||
*
|
||||
|
@ -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)
|
||||
*/
|
||||
|
|
|
@ -1,173 +0,0 @@
|
|||
#ifndef _SECP256K1_SCHNORR_
|
||||
# define _SECP256K1_SCHNORR_
|
||||
|
||||
# include "secp256k1.h"
|
||||
|
||||
# ifdef __cplusplus
|
||||
extern "C" {
|
||||
# endif
|
||||
|
||||
/** Create a signature using a custom EC-Schnorr-SHA256 construction. It
|
||||
* produces non-malleable 64-byte signatures which support public key recovery
|
||||
* batch validation, and multiparty signing.
|
||||
* Returns: 1: signature created
|
||||
* 0: the nonce generation function failed, or the private key was
|
||||
* invalid.
|
||||
* Args: ctx: pointer to a context object, initialized for signing
|
||||
* (cannot be NULL)
|
||||
* Out: sig64: pointer to a 64-byte array where the signature will be
|
||||
* placed (cannot be NULL)
|
||||
* In: msg32: the 32-byte message hash being signed (cannot be NULL)
|
||||
* seckey: pointer to a 32-byte secret key (cannot be NULL)
|
||||
* noncefp:pointer to a nonce generation function. If NULL,
|
||||
* secp256k1_nonce_function_default is used
|
||||
* ndata: pointer to arbitrary data used by the nonce generation
|
||||
* function (can be NULL)
|
||||
*/
|
||||
SECP256K1_API int secp256k1_schnorr_sign(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *sig64,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *seckey,
|
||||
secp256k1_nonce_function noncefp,
|
||||
const void *ndata
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Verify a signature created by secp256k1_schnorr_sign.
|
||||
* Returns: 1: correct signature
|
||||
* 0: incorrect signature
|
||||
* Args: ctx: a secp256k1 context object, initialized for verification.
|
||||
* In: sig64: the 64-byte signature being verified (cannot be NULL)
|
||||
* msg32: the 32-byte message hash being verified (cannot be NULL)
|
||||
* pubkey: the public key to verify with (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify(
|
||||
const secp256k1_context* ctx,
|
||||
const unsigned char *sig64,
|
||||
const unsigned char *msg32,
|
||||
const secp256k1_pubkey *pubkey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Recover an EC public key from a Schnorr signature created using
|
||||
* secp256k1_schnorr_sign.
|
||||
* 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 a pubkey to set to the recovered public key
|
||||
* (cannot be NULL).
|
||||
* In: sig64: signature as 64 byte array (cannot be NULL)
|
||||
* msg32: the 32-byte message hash assumed to be signed (cannot
|
||||
* be NULL)
|
||||
*/
|
||||
SECP256K1_API int secp256k1_schnorr_recover(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_pubkey *pubkey,
|
||||
const unsigned char *sig64,
|
||||
const unsigned char *msg32
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
|
||||
|
||||
/** Generate a nonce pair deterministically for use with
|
||||
* secp256k1_schnorr_partial_sign.
|
||||
* Returns: 1: valid nonce pair was generated.
|
||||
* 0: otherwise (nonce generation function failed)
|
||||
* Args: ctx: pointer to a context object, initialized for signing
|
||||
* (cannot be NULL)
|
||||
* Out: pubnonce: public side of the nonce (cannot be NULL)
|
||||
* privnonce32: private side of the nonce (32 byte) (cannot be NULL)
|
||||
* In: msg32: the 32-byte message hash assumed to be signed (cannot
|
||||
* be NULL)
|
||||
* sec32: the 32-byte private key (cannot be NULL)
|
||||
* noncefp: pointer to a nonce generation function. If NULL,
|
||||
* secp256k1_nonce_function_default is used
|
||||
* noncedata: pointer to arbitrary data used by the nonce generation
|
||||
* function (can be NULL)
|
||||
*
|
||||
* Do not use the output as a private/public key pair for signing/validation.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_schnorr_generate_nonce_pair(
|
||||
const secp256k1_context* ctx,
|
||||
secp256k1_pubkey *pubnonce,
|
||||
unsigned char *privnonce32,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *sec32,
|
||||
secp256k1_nonce_function noncefp,
|
||||
const void* noncedata
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
/** 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 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.
|
||||
* Args: ctx: pointer to context object, initialized for signing (cannot
|
||||
* be NULL)
|
||||
* Out: sig64: pointer to 64-byte array to put partial signature in
|
||||
* In: msg32: pointer to 32-byte message to sign
|
||||
* sec32: pointer to 32-byte private key
|
||||
* pubnonce_others: pointer to pubkey containing the sum of the other's
|
||||
* nonces (see secp256k1_ec_pubkey_combine)
|
||||
* secnonce32: pointer to 32-byte array containing our nonce
|
||||
*
|
||||
* The intended procedure for creating a multiparty signature is:
|
||||
* - Each signer S[i] with private key x[i] and public key Q[i] runs
|
||||
* secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of
|
||||
* private/public nonces.
|
||||
* - All signers communicate their public nonces to each other (revealing your
|
||||
* private nonce can lead to discovery of your private key, so it should be
|
||||
* considered secret).
|
||||
* - All signers combine all the public nonces they received (excluding their
|
||||
* own) using secp256k1_ec_pubkey_combine to obtain an
|
||||
* Rall[i] = sum(R[0..i-1,i+1..n]).
|
||||
* - All signers produce a partial signature using
|
||||
* secp256k1_schnorr_partial_sign, passing in their own private key x[i],
|
||||
* their own private nonce k[i], and the sum of the others' public nonces
|
||||
* Rall[i].
|
||||
* - All signers communicate their partial signatures to each other.
|
||||
* - Someone combines all partial signatures using
|
||||
* secp256k1_schnorr_partial_combine, to obtain a full signature.
|
||||
* - The resulting signature is validatable using secp256k1_schnorr_verify, with
|
||||
* public key equal to the result of secp256k1_ec_pubkey_combine of the
|
||||
* signers' public keys (sum(Q[0..n])).
|
||||
*
|
||||
* Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine
|
||||
* function take their arguments in any order, and it is possible to
|
||||
* pre-combine several inputs already with one call, and add more inputs later
|
||||
* by calling the function again (they are commutative and associative).
|
||||
*/
|
||||
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign(
|
||||
const secp256k1_context* ctx,
|
||||
unsigned char *sig64,
|
||||
const unsigned char *msg32,
|
||||
const unsigned char *sec32,
|
||||
const secp256k1_pubkey *pubnonce_others,
|
||||
const unsigned char *secnonce32
|
||||
) 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 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
|
||||
* Args: ctx: pointer to a context object
|
||||
* Out: sig64: pointer to a 64-byte array to place the combined signature
|
||||
* (cannot be NULL)
|
||||
* In: sig64sin: pointer to an array of n pointers to 64-byte input
|
||||
* signatures
|
||||
* n: the number of signatures to combine (at least 1)
|
||||
*/
|
||||
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
|
||||
) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
||||
#endif
|
|
@ -5,7 +5,7 @@ includedir=@includedir@
|
|||
|
||||
Name: libsecp256k1
|
||||
Description: Optimized C library for EC operations on curve secp256k1
|
||||
URL: https://github.com/bitcoin/secp256k1
|
||||
URL: https://github.com/bitcoin-core/secp256k1
|
||||
Version: @PACKAGE_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs.private: @SECP_LIBS@
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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
|
|
@ -0,0 +1,919 @@
|
|||
@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm:
|
||||
/**********************************************************************
|
||||
* Copyright (c) 2014 Wladimir J. van der Laan *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
/*
|
||||
ARM implementation of field_10x26 inner loops.
|
||||
|
||||
Note:
|
||||
|
||||
- To avoid unnecessary loads and make use of available registers, two
|
||||
'passes' have every time been interleaved, with the odd passes accumulating c' and d'
|
||||
which will be added to c and d respectively in the the even passes
|
||||
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.arch armv7-a
|
||||
@ eabi attributes - see readelf -A
|
||||
.eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes
|
||||
.eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no
|
||||
.eabi_attribute 10, 0 @ Tag_FP_arch = none
|
||||
.eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte
|
||||
.eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP
|
||||
.eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed
|
||||
.eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6
|
||||
.text
|
||||
|
||||
@ Field constants
|
||||
.set field_R0, 0x3d10
|
||||
.set field_R1, 0x400
|
||||
.set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff
|
||||
|
||||
.align 2
|
||||
.global secp256k1_fe_mul_inner
|
||||
.type secp256k1_fe_mul_inner, %function
|
||||
@ Arguments:
|
||||
@ r0 r Restrict: can overlap with a, not with b
|
||||
@ r1 a
|
||||
@ r2 b
|
||||
@ Stack (total 4+10*4 = 44)
|
||||
@ sp + #0 saved 'r' pointer
|
||||
@ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
|
||||
secp256k1_fe_mul_inner:
|
||||
stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
|
||||
sub sp, sp, #48 @ frame=44 + alignment
|
||||
str r0, [sp, #0] @ save result address, we need it only at the end
|
||||
|
||||
/******************************************
|
||||
* Main computation code.
|
||||
******************************************
|
||||
|
||||
Allocation:
|
||||
r0,r14,r7,r8 scratch
|
||||
r1 a (pointer)
|
||||
r2 b (pointer)
|
||||
r3:r4 c
|
||||
r5:r6 d
|
||||
r11:r12 c'
|
||||
r9:r10 d'
|
||||
|
||||
Note: do not write to r[] here, it may overlap with a[]
|
||||
*/
|
||||
|
||||
/* A - interleaved with B */
|
||||
ldr r7, [r1, #0*4] @ a[0]
|
||||
ldr r8, [r2, #9*4] @ b[9]
|
||||
ldr r0, [r1, #1*4] @ a[1]
|
||||
umull r5, r6, r7, r8 @ d = a[0] * b[9]
|
||||
ldr r14, [r2, #8*4] @ b[8]
|
||||
umull r9, r10, r0, r8 @ d' = a[1] * b[9]
|
||||
ldr r7, [r1, #2*4] @ a[2]
|
||||
umlal r5, r6, r0, r14 @ d += a[1] * b[8]
|
||||
ldr r8, [r2, #7*4] @ b[7]
|
||||
umlal r9, r10, r7, r14 @ d' += a[2] * b[8]
|
||||
ldr r0, [r1, #3*4] @ a[3]
|
||||
umlal r5, r6, r7, r8 @ d += a[2] * b[7]
|
||||
ldr r14, [r2, #6*4] @ b[6]
|
||||
umlal r9, r10, r0, r8 @ d' += a[3] * b[7]
|
||||
ldr r7, [r1, #4*4] @ a[4]
|
||||
umlal r5, r6, r0, r14 @ d += a[3] * b[6]
|
||||
ldr r8, [r2, #5*4] @ b[5]
|
||||
umlal r9, r10, r7, r14 @ d' += a[4] * b[6]
|
||||
ldr r0, [r1, #5*4] @ a[5]
|
||||
umlal r5, r6, r7, r8 @ d += a[4] * b[5]
|
||||
ldr r14, [r2, #4*4] @ b[4]
|
||||
umlal r9, r10, r0, r8 @ d' += a[5] * b[5]
|
||||
ldr r7, [r1, #6*4] @ a[6]
|
||||
umlal r5, r6, r0, r14 @ d += a[5] * b[4]
|
||||
ldr r8, [r2, #3*4] @ b[3]
|
||||
umlal r9, r10, r7, r14 @ d' += a[6] * b[4]
|
||||
ldr r0, [r1, #7*4] @ a[7]
|
||||
umlal r5, r6, r7, r8 @ d += a[6] * b[3]
|
||||
ldr r14, [r2, #2*4] @ b[2]
|
||||
umlal r9, r10, r0, r8 @ d' += a[7] * b[3]
|
||||
ldr r7, [r1, #8*4] @ a[8]
|
||||
umlal r5, r6, r0, r14 @ d += a[7] * b[2]
|
||||
ldr r8, [r2, #1*4] @ b[1]
|
||||
umlal r9, r10, r7, r14 @ d' += a[8] * b[2]
|
||||
ldr r0, [r1, #9*4] @ a[9]
|
||||
umlal r5, r6, r7, r8 @ d += a[8] * b[1]
|
||||
ldr r14, [r2, #0*4] @ b[0]
|
||||
umlal r9, r10, r0, r8 @ d' += a[9] * b[1]
|
||||
ldr r7, [r1, #0*4] @ a[0]
|
||||
umlal r5, r6, r0, r14 @ d += a[9] * b[0]
|
||||
@ r7,r14 used in B
|
||||
|
||||
bic r0, r5, field_not_M @ t9 = d & M
|
||||
str r0, [sp, #4 + 4*9]
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
|
||||
/* B */
|
||||
umull r3, r4, r7, r14 @ c = a[0] * b[0]
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u0 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u0 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
bic r14, r3, field_not_M @ t0 = c & M
|
||||
str r14, [sp, #4 + 0*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u0 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* C - interleaved with D */
|
||||
ldr r7, [r1, #0*4] @ a[0]
|
||||
ldr r8, [r2, #2*4] @ b[2]
|
||||
ldr r14, [r2, #1*4] @ b[1]
|
||||
umull r11, r12, r7, r8 @ c' = a[0] * b[2]
|
||||
ldr r0, [r1, #1*4] @ a[1]
|
||||
umlal r3, r4, r7, r14 @ c += a[0] * b[1]
|
||||
ldr r8, [r2, #0*4] @ b[0]
|
||||
umlal r11, r12, r0, r14 @ c' += a[1] * b[1]
|
||||
ldr r7, [r1, #2*4] @ a[2]
|
||||
umlal r3, r4, r0, r8 @ c += a[1] * b[0]
|
||||
ldr r14, [r2, #9*4] @ b[9]
|
||||
umlal r11, r12, r7, r8 @ c' += a[2] * b[0]
|
||||
ldr r0, [r1, #3*4] @ a[3]
|
||||
umlal r5, r6, r7, r14 @ d += a[2] * b[9]
|
||||
ldr r8, [r2, #8*4] @ b[8]
|
||||
umull r9, r10, r0, r14 @ d' = a[3] * b[9]
|
||||
ldr r7, [r1, #4*4] @ a[4]
|
||||
umlal r5, r6, r0, r8 @ d += a[3] * b[8]
|
||||
ldr r14, [r2, #7*4] @ b[7]
|
||||
umlal r9, r10, r7, r8 @ d' += a[4] * b[8]
|
||||
ldr r0, [r1, #5*4] @ a[5]
|
||||
umlal r5, r6, r7, r14 @ d += a[4] * b[7]
|
||||
ldr r8, [r2, #6*4] @ b[6]
|
||||
umlal r9, r10, r0, r14 @ d' += a[5] * b[7]
|
||||
ldr r7, [r1, #6*4] @ a[6]
|
||||
umlal r5, r6, r0, r8 @ d += a[5] * b[6]
|
||||
ldr r14, [r2, #5*4] @ b[5]
|
||||
umlal r9, r10, r7, r8 @ d' += a[6] * b[6]
|
||||
ldr r0, [r1, #7*4] @ a[7]
|
||||
umlal r5, r6, r7, r14 @ d += a[6] * b[5]
|
||||
ldr r8, [r2, #4*4] @ b[4]
|
||||
umlal r9, r10, r0, r14 @ d' += a[7] * b[5]
|
||||
ldr r7, [r1, #8*4] @ a[8]
|
||||
umlal r5, r6, r0, r8 @ d += a[7] * b[4]
|
||||
ldr r14, [r2, #3*4] @ b[3]
|
||||
umlal r9, r10, r7, r8 @ d' += a[8] * b[4]
|
||||
ldr r0, [r1, #9*4] @ a[9]
|
||||
umlal r5, r6, r7, r14 @ d += a[8] * b[3]
|
||||
ldr r8, [r2, #2*4] @ b[2]
|
||||
umlal r9, r10, r0, r14 @ d' += a[9] * b[3]
|
||||
umlal r5, r6, r0, r8 @ d += a[9] * b[2]
|
||||
|
||||
bic r0, r5, field_not_M @ u1 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u1 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
bic r14, r3, field_not_M @ t1 = c & M
|
||||
str r14, [sp, #4 + 1*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u1 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* D */
|
||||
adds r3, r3, r11 @ c += c'
|
||||
adc r4, r4, r12
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u2 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u2 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
bic r14, r3, field_not_M @ t2 = c & M
|
||||
str r14, [sp, #4 + 2*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u2 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* E - interleaved with F */
|
||||
ldr r7, [r1, #0*4] @ a[0]
|
||||
ldr r8, [r2, #4*4] @ b[4]
|
||||
umull r11, r12, r7, r8 @ c' = a[0] * b[4]
|
||||
ldr r8, [r2, #3*4] @ b[3]
|
||||
umlal r3, r4, r7, r8 @ c += a[0] * b[3]
|
||||
ldr r7, [r1, #1*4] @ a[1]
|
||||
umlal r11, r12, r7, r8 @ c' += a[1] * b[3]
|
||||
ldr r8, [r2, #2*4] @ b[2]
|
||||
umlal r3, r4, r7, r8 @ c += a[1] * b[2]
|
||||
ldr r7, [r1, #2*4] @ a[2]
|
||||
umlal r11, r12, r7, r8 @ c' += a[2] * b[2]
|
||||
ldr r8, [r2, #1*4] @ b[1]
|
||||
umlal r3, r4, r7, r8 @ c += a[2] * b[1]
|
||||
ldr r7, [r1, #3*4] @ a[3]
|
||||
umlal r11, r12, r7, r8 @ c' += a[3] * b[1]
|
||||
ldr r8, [r2, #0*4] @ b[0]
|
||||
umlal r3, r4, r7, r8 @ c += a[3] * b[0]
|
||||
ldr r7, [r1, #4*4] @ a[4]
|
||||
umlal r11, r12, r7, r8 @ c' += a[4] * b[0]
|
||||
ldr r8, [r2, #9*4] @ b[9]
|
||||
umlal r5, r6, r7, r8 @ d += a[4] * b[9]
|
||||
ldr r7, [r1, #5*4] @ a[5]
|
||||
umull r9, r10, r7, r8 @ d' = a[5] * b[9]
|
||||
ldr r8, [r2, #8*4] @ b[8]
|
||||
umlal r5, r6, r7, r8 @ d += a[5] * b[8]
|
||||
ldr r7, [r1, #6*4] @ a[6]
|
||||
umlal r9, r10, r7, r8 @ d' += a[6] * b[8]
|
||||
ldr r8, [r2, #7*4] @ b[7]
|
||||
umlal r5, r6, r7, r8 @ d += a[6] * b[7]
|
||||
ldr r7, [r1, #7*4] @ a[7]
|
||||
umlal r9, r10, r7, r8 @ d' += a[7] * b[7]
|
||||
ldr r8, [r2, #6*4] @ b[6]
|
||||
umlal r5, r6, r7, r8 @ d += a[7] * b[6]
|
||||
ldr r7, [r1, #8*4] @ a[8]
|
||||
umlal r9, r10, r7, r8 @ d' += a[8] * b[6]
|
||||
ldr r8, [r2, #5*4] @ b[5]
|
||||
umlal r5, r6, r7, r8 @ d += a[8] * b[5]
|
||||
ldr r7, [r1, #9*4] @ a[9]
|
||||
umlal r9, r10, r7, r8 @ d' += a[9] * b[5]
|
||||
ldr r8, [r2, #4*4] @ b[4]
|
||||
umlal r5, r6, r7, r8 @ d += a[9] * b[4]
|
||||
|
||||
bic r0, r5, field_not_M @ u3 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u3 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
bic r14, r3, field_not_M @ t3 = c & M
|
||||
str r14, [sp, #4 + 3*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u3 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* F */
|
||||
adds r3, r3, r11 @ c += c'
|
||||
adc r4, r4, r12
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u4 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u4 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
bic r14, r3, field_not_M @ t4 = c & M
|
||||
str r14, [sp, #4 + 4*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u4 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* G - interleaved with H */
|
||||
ldr r7, [r1, #0*4] @ a[0]
|
||||
ldr r8, [r2, #6*4] @ b[6]
|
||||
ldr r14, [r2, #5*4] @ b[5]
|
||||
umull r11, r12, r7, r8 @ c' = a[0] * b[6]
|
||||
ldr r0, [r1, #1*4] @ a[1]
|
||||
umlal r3, r4, r7, r14 @ c += a[0] * b[5]
|
||||
ldr r8, [r2, #4*4] @ b[4]
|
||||
umlal r11, r12, r0, r14 @ c' += a[1] * b[5]
|
||||
ldr r7, [r1, #2*4] @ a[2]
|
||||
umlal r3, r4, r0, r8 @ c += a[1] * b[4]
|
||||
ldr r14, [r2, #3*4] @ b[3]
|
||||
umlal r11, r12, r7, r8 @ c' += a[2] * b[4]
|
||||
ldr r0, [r1, #3*4] @ a[3]
|
||||
umlal r3, r4, r7, r14 @ c += a[2] * b[3]
|
||||
ldr r8, [r2, #2*4] @ b[2]
|
||||
umlal r11, r12, r0, r14 @ c' += a[3] * b[3]
|
||||
ldr r7, [r1, #4*4] @ a[4]
|
||||
umlal r3, r4, r0, r8 @ c += a[3] * b[2]
|
||||
ldr r14, [r2, #1*4] @ b[1]
|
||||
umlal r11, r12, r7, r8 @ c' += a[4] * b[2]
|
||||
ldr r0, [r1, #5*4] @ a[5]
|
||||
umlal r3, r4, r7, r14 @ c += a[4] * b[1]
|
||||
ldr r8, [r2, #0*4] @ b[0]
|
||||
umlal r11, r12, r0, r14 @ c' += a[5] * b[1]
|
||||
ldr r7, [r1, #6*4] @ a[6]
|
||||
umlal r3, r4, r0, r8 @ c += a[5] * b[0]
|
||||
ldr r14, [r2, #9*4] @ b[9]
|
||||
umlal r11, r12, r7, r8 @ c' += a[6] * b[0]
|
||||
ldr r0, [r1, #7*4] @ a[7]
|
||||
umlal r5, r6, r7, r14 @ d += a[6] * b[9]
|
||||
ldr r8, [r2, #8*4] @ b[8]
|
||||
umull r9, r10, r0, r14 @ d' = a[7] * b[9]
|
||||
ldr r7, [r1, #8*4] @ a[8]
|
||||
umlal r5, r6, r0, r8 @ d += a[7] * b[8]
|
||||
ldr r14, [r2, #7*4] @ b[7]
|
||||
umlal r9, r10, r7, r8 @ d' += a[8] * b[8]
|
||||
ldr r0, [r1, #9*4] @ a[9]
|
||||
umlal r5, r6, r7, r14 @ d += a[8] * b[7]
|
||||
ldr r8, [r2, #6*4] @ b[6]
|
||||
umlal r9, r10, r0, r14 @ d' += a[9] * b[7]
|
||||
umlal r5, r6, r0, r8 @ d += a[9] * b[6]
|
||||
|
||||
bic r0, r5, field_not_M @ u5 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u5 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
bic r14, r3, field_not_M @ t5 = c & M
|
||||
str r14, [sp, #4 + 5*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u5 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* H */
|
||||
adds r3, r3, r11 @ c += c'
|
||||
adc r4, r4, r12
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u6 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u6 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
bic r14, r3, field_not_M @ t6 = c & M
|
||||
str r14, [sp, #4 + 6*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u6 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* I - interleaved with J */
|
||||
ldr r8, [r2, #8*4] @ b[8]
|
||||
ldr r7, [r1, #0*4] @ a[0]
|
||||
ldr r14, [r2, #7*4] @ b[7]
|
||||
umull r11, r12, r7, r8 @ c' = a[0] * b[8]
|
||||
ldr r0, [r1, #1*4] @ a[1]
|
||||
umlal r3, r4, r7, r14 @ c += a[0] * b[7]
|
||||
ldr r8, [r2, #6*4] @ b[6]
|
||||
umlal r11, r12, r0, r14 @ c' += a[1] * b[7]
|
||||
ldr r7, [r1, #2*4] @ a[2]
|
||||
umlal r3, r4, r0, r8 @ c += a[1] * b[6]
|
||||
ldr r14, [r2, #5*4] @ b[5]
|
||||
umlal r11, r12, r7, r8 @ c' += a[2] * b[6]
|
||||
ldr r0, [r1, #3*4] @ a[3]
|
||||
umlal r3, r4, r7, r14 @ c += a[2] * b[5]
|
||||
ldr r8, [r2, #4*4] @ b[4]
|
||||
umlal r11, r12, r0, r14 @ c' += a[3] * b[5]
|
||||
ldr r7, [r1, #4*4] @ a[4]
|
||||
umlal r3, r4, r0, r8 @ c += a[3] * b[4]
|
||||
ldr r14, [r2, #3*4] @ b[3]
|
||||
umlal r11, r12, r7, r8 @ c' += a[4] * b[4]
|
||||
ldr r0, [r1, #5*4] @ a[5]
|
||||
umlal r3, r4, r7, r14 @ c += a[4] * b[3]
|
||||
ldr r8, [r2, #2*4] @ b[2]
|
||||
umlal r11, r12, r0, r14 @ c' += a[5] * b[3]
|
||||
ldr r7, [r1, #6*4] @ a[6]
|
||||
umlal r3, r4, r0, r8 @ c += a[5] * b[2]
|
||||
ldr r14, [r2, #1*4] @ b[1]
|
||||
umlal r11, r12, r7, r8 @ c' += a[6] * b[2]
|
||||
ldr r0, [r1, #7*4] @ a[7]
|
||||
umlal r3, r4, r7, r14 @ c += a[6] * b[1]
|
||||
ldr r8, [r2, #0*4] @ b[0]
|
||||
umlal r11, r12, r0, r14 @ c' += a[7] * b[1]
|
||||
ldr r7, [r1, #8*4] @ a[8]
|
||||
umlal r3, r4, r0, r8 @ c += a[7] * b[0]
|
||||
ldr r14, [r2, #9*4] @ b[9]
|
||||
umlal r11, r12, r7, r8 @ c' += a[8] * b[0]
|
||||
ldr r0, [r1, #9*4] @ a[9]
|
||||
umlal r5, r6, r7, r14 @ d += a[8] * b[9]
|
||||
ldr r8, [r2, #8*4] @ b[8]
|
||||
umull r9, r10, r0, r14 @ d' = a[9] * b[9]
|
||||
umlal r5, r6, r0, r8 @ d += a[9] * b[8]
|
||||
|
||||
bic r0, r5, field_not_M @ u7 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u7 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
bic r14, r3, field_not_M @ t7 = c & M
|
||||
str r14, [sp, #4 + 7*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u7 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* J */
|
||||
adds r3, r3, r11 @ c += c'
|
||||
adc r4, r4, r12
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u8 = d & M
|
||||
str r0, [sp, #4 + 8*4]
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u8 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/******************************************
|
||||
* compute and write back result
|
||||
******************************************
|
||||
Allocation:
|
||||
r0 r
|
||||
r3:r4 c
|
||||
r5:r6 d
|
||||
r7 t0
|
||||
r8 t1
|
||||
r9 t2
|
||||
r11 u8
|
||||
r12 t9
|
||||
r1,r2,r10,r14 scratch
|
||||
|
||||
Note: do not read from a[] after here, it may overlap with r[]
|
||||
*/
|
||||
ldr r0, [sp, #0]
|
||||
add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
|
||||
ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
|
||||
add r1, r0, #3*4
|
||||
stmia r1, {r2,r7,r8,r9,r10}
|
||||
|
||||
bic r2, r3, field_not_M @ r[8] = c & M
|
||||
str r2, [r0, #8*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u8 * R1
|
||||
umlal r3, r4, r11, r14
|
||||
movw r14, field_R0 @ c += d * R0
|
||||
umlal r3, r4, r5, r14
|
||||
adds r3, r3, r12 @ c += t9
|
||||
adc r4, r4, #0
|
||||
|
||||
add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
|
||||
ldmia r1, {r7,r8,r9}
|
||||
|
||||
ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
|
||||
str r2, [r0, #9*4]
|
||||
mov r3, r3, lsr #22 @ c >>= 22
|
||||
orr r3, r3, r4, asl #10
|
||||
mov r4, r4, lsr #22
|
||||
movw r14, field_R1 << 4 @ c += d * (R1 << 4)
|
||||
umlal r3, r4, r5, r14
|
||||
|
||||
movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
|
||||
umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
|
||||
adds r5, r5, r7 @ d.lo += t0
|
||||
mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
|
||||
adc r6, r6, 0 @ d.hi += carry
|
||||
|
||||
bic r2, r5, field_not_M @ r[0] = d & M
|
||||
str r2, [r0, #0*4]
|
||||
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
|
||||
movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
|
||||
umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
|
||||
adds r5, r5, r8 @ d.lo += t1
|
||||
adc r6, r6, #0 @ d.hi += carry
|
||||
adds r5, r5, r1 @ d.lo += tmp.lo
|
||||
mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
|
||||
adc r6, r6, r2 @ d.hi += carry + tmp.hi
|
||||
|
||||
bic r2, r5, field_not_M @ r[1] = d & M
|
||||
str r2, [r0, #1*4]
|
||||
mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
|
||||
orr r5, r5, r6, asl #6
|
||||
|
||||
add r5, r5, r9 @ d += t2
|
||||
str r5, [r0, #2*4] @ r[2] = d
|
||||
|
||||
add sp, sp, #48
|
||||
ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
|
||||
.size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner
|
||||
|
||||
.align 2
|
||||
.global secp256k1_fe_sqr_inner
|
||||
.type secp256k1_fe_sqr_inner, %function
|
||||
@ Arguments:
|
||||
@ r0 r Can overlap with a
|
||||
@ r1 a
|
||||
@ Stack (total 4+10*4 = 44)
|
||||
@ sp + #0 saved 'r' pointer
|
||||
@ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9
|
||||
secp256k1_fe_sqr_inner:
|
||||
stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14}
|
||||
sub sp, sp, #48 @ frame=44 + alignment
|
||||
str r0, [sp, #0] @ save result address, we need it only at the end
|
||||
/******************************************
|
||||
* Main computation code.
|
||||
******************************************
|
||||
|
||||
Allocation:
|
||||
r0,r14,r2,r7,r8 scratch
|
||||
r1 a (pointer)
|
||||
r3:r4 c
|
||||
r5:r6 d
|
||||
r11:r12 c'
|
||||
r9:r10 d'
|
||||
|
||||
Note: do not write to r[] here, it may overlap with a[]
|
||||
*/
|
||||
/* A interleaved with B */
|
||||
ldr r0, [r1, #1*4] @ a[1]*2
|
||||
ldr r7, [r1, #0*4] @ a[0]
|
||||
mov r0, r0, asl #1
|
||||
ldr r14, [r1, #9*4] @ a[9]
|
||||
umull r3, r4, r7, r7 @ c = a[0] * a[0]
|
||||
ldr r8, [r1, #8*4] @ a[8]
|
||||
mov r7, r7, asl #1
|
||||
umull r5, r6, r7, r14 @ d = a[0]*2 * a[9]
|
||||
ldr r7, [r1, #2*4] @ a[2]*2
|
||||
umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9]
|
||||
ldr r14, [r1, #7*4] @ a[7]
|
||||
umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8]
|
||||
mov r7, r7, asl #1
|
||||
ldr r0, [r1, #3*4] @ a[3]*2
|
||||
umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8]
|
||||
ldr r8, [r1, #6*4] @ a[6]
|
||||
umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7]
|
||||
mov r0, r0, asl #1
|
||||
ldr r7, [r1, #4*4] @ a[4]*2
|
||||
umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7]
|
||||
ldr r14, [r1, #5*4] @ a[5]
|
||||
mov r7, r7, asl #1
|
||||
umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6]
|
||||
umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6]
|
||||
umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5]
|
||||
umlal r9, r10, r14, r14 @ d' += a[5] * a[5]
|
||||
|
||||
bic r0, r5, field_not_M @ t9 = d & M
|
||||
str r0, [sp, #4 + 9*4]
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
|
||||
/* B */
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u0 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u0 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
bic r14, r3, field_not_M @ t0 = c & M
|
||||
str r14, [sp, #4 + 0*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u0 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* C interleaved with D */
|
||||
ldr r0, [r1, #0*4] @ a[0]*2
|
||||
ldr r14, [r1, #1*4] @ a[1]
|
||||
mov r0, r0, asl #1
|
||||
ldr r8, [r1, #2*4] @ a[2]
|
||||
umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1]
|
||||
mov r7, r8, asl #1 @ a[2]*2
|
||||
umull r11, r12, r14, r14 @ c' = a[1] * a[1]
|
||||
ldr r14, [r1, #9*4] @ a[9]
|
||||
umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2]
|
||||
ldr r0, [r1, #3*4] @ a[3]*2
|
||||
ldr r8, [r1, #8*4] @ a[8]
|
||||
umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9]
|
||||
mov r0, r0, asl #1
|
||||
ldr r7, [r1, #4*4] @ a[4]*2
|
||||
umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9]
|
||||
ldr r14, [r1, #7*4] @ a[7]
|
||||
umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8]
|
||||
mov r7, r7, asl #1
|
||||
ldr r0, [r1, #5*4] @ a[5]*2
|
||||
umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8]
|
||||
ldr r8, [r1, #6*4] @ a[6]
|
||||
mov r0, r0, asl #1
|
||||
umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7]
|
||||
umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7]
|
||||
umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6]
|
||||
umlal r9, r10, r8, r8 @ d' += a[6] * a[6]
|
||||
|
||||
bic r0, r5, field_not_M @ u1 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u1 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
bic r14, r3, field_not_M @ t1 = c & M
|
||||
str r14, [sp, #4 + 1*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u1 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* D */
|
||||
adds r3, r3, r11 @ c += c'
|
||||
adc r4, r4, r12
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u2 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u2 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
bic r14, r3, field_not_M @ t2 = c & M
|
||||
str r14, [sp, #4 + 2*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u2 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* E interleaved with F */
|
||||
ldr r7, [r1, #0*4] @ a[0]*2
|
||||
ldr r0, [r1, #1*4] @ a[1]*2
|
||||
ldr r14, [r1, #2*4] @ a[2]
|
||||
mov r7, r7, asl #1
|
||||
ldr r8, [r1, #3*4] @ a[3]
|
||||
ldr r2, [r1, #4*4]
|
||||
umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3]
|
||||
mov r0, r0, asl #1
|
||||
umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4]
|
||||
mov r2, r2, asl #1 @ a[4]*2
|
||||
umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3]
|
||||
ldr r8, [r1, #9*4] @ a[9]
|
||||
umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2]
|
||||
ldr r0, [r1, #5*4] @ a[5]*2
|
||||
umlal r11, r12, r14, r14 @ c' += a[2] * a[2]
|
||||
ldr r14, [r1, #8*4] @ a[8]
|
||||
mov r0, r0, asl #1
|
||||
umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9]
|
||||
ldr r7, [r1, #6*4] @ a[6]*2
|
||||
umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9]
|
||||
mov r7, r7, asl #1
|
||||
ldr r8, [r1, #7*4] @ a[7]
|
||||
umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8]
|
||||
umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8]
|
||||
umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7]
|
||||
umlal r9, r10, r8, r8 @ d' += a[7] * a[7]
|
||||
|
||||
bic r0, r5, field_not_M @ u3 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u3 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
bic r14, r3, field_not_M @ t3 = c & M
|
||||
str r14, [sp, #4 + 3*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u3 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* F */
|
||||
adds r3, r3, r11 @ c += c'
|
||||
adc r4, r4, r12
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u4 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u4 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
bic r14, r3, field_not_M @ t4 = c & M
|
||||
str r14, [sp, #4 + 4*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u4 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* G interleaved with H */
|
||||
ldr r7, [r1, #0*4] @ a[0]*2
|
||||
ldr r0, [r1, #1*4] @ a[1]*2
|
||||
mov r7, r7, asl #1
|
||||
ldr r8, [r1, #5*4] @ a[5]
|
||||
ldr r2, [r1, #6*4] @ a[6]
|
||||
umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5]
|
||||
ldr r14, [r1, #4*4] @ a[4]
|
||||
mov r0, r0, asl #1
|
||||
umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6]
|
||||
ldr r7, [r1, #2*4] @ a[2]*2
|
||||
umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5]
|
||||
mov r7, r7, asl #1
|
||||
ldr r8, [r1, #3*4] @ a[3]
|
||||
umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4]
|
||||
mov r0, r2, asl #1 @ a[6]*2
|
||||
umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4]
|
||||
ldr r14, [r1, #9*4] @ a[9]
|
||||
umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3]
|
||||
ldr r7, [r1, #7*4] @ a[7]*2
|
||||
umlal r11, r12, r8, r8 @ c' += a[3] * a[3]
|
||||
mov r7, r7, asl #1
|
||||
ldr r8, [r1, #8*4] @ a[8]
|
||||
umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9]
|
||||
umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9]
|
||||
umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8]
|
||||
umlal r9, r10, r8, r8 @ d' += a[8] * a[8]
|
||||
|
||||
bic r0, r5, field_not_M @ u5 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u5 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
bic r14, r3, field_not_M @ t5 = c & M
|
||||
str r14, [sp, #4 + 5*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u5 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* H */
|
||||
adds r3, r3, r11 @ c += c'
|
||||
adc r4, r4, r12
|
||||
adds r5, r5, r9 @ d += d'
|
||||
adc r6, r6, r10
|
||||
|
||||
bic r0, r5, field_not_M @ u6 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u6 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
bic r14, r3, field_not_M @ t6 = c & M
|
||||
str r14, [sp, #4 + 6*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u6 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* I interleaved with J */
|
||||
ldr r7, [r1, #0*4] @ a[0]*2
|
||||
ldr r0, [r1, #1*4] @ a[1]*2
|
||||
mov r7, r7, asl #1
|
||||
ldr r8, [r1, #7*4] @ a[7]
|
||||
ldr r2, [r1, #8*4] @ a[8]
|
||||
umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7]
|
||||
ldr r14, [r1, #6*4] @ a[6]
|
||||
mov r0, r0, asl #1
|
||||
umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8]
|
||||
ldr r7, [r1, #2*4] @ a[2]*2
|
||||
umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7]
|
||||
ldr r8, [r1, #5*4] @ a[5]
|
||||
umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6]
|
||||
ldr r0, [r1, #3*4] @ a[3]*2
|
||||
mov r7, r7, asl #1
|
||||
umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6]
|
||||
ldr r14, [r1, #4*4] @ a[4]
|
||||
mov r0, r0, asl #1
|
||||
umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5]
|
||||
mov r2, r2, asl #1 @ a[8]*2
|
||||
umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5]
|
||||
umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4]
|
||||
umlal r11, r12, r14, r14 @ c' += a[4] * a[4]
|
||||
ldr r8, [r1, #9*4] @ a[9]
|
||||
umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9]
|
||||
@ r8 will be used in J
|
||||
|
||||
bic r0, r5, field_not_M @ u7 = d & M
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u7 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
bic r14, r3, field_not_M @ t7 = c & M
|
||||
str r14, [sp, #4 + 7*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u7 * R1
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/* J */
|
||||
adds r3, r3, r11 @ c += c'
|
||||
adc r4, r4, r12
|
||||
umlal r5, r6, r8, r8 @ d += a[9] * a[9]
|
||||
|
||||
bic r0, r5, field_not_M @ u8 = d & M
|
||||
str r0, [sp, #4 + 8*4]
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
movw r14, field_R0 @ c += u8 * R0
|
||||
umlal r3, r4, r0, r14
|
||||
|
||||
/******************************************
|
||||
* compute and write back result
|
||||
******************************************
|
||||
Allocation:
|
||||
r0 r
|
||||
r3:r4 c
|
||||
r5:r6 d
|
||||
r7 t0
|
||||
r8 t1
|
||||
r9 t2
|
||||
r11 u8
|
||||
r12 t9
|
||||
r1,r2,r10,r14 scratch
|
||||
|
||||
Note: do not read from a[] after here, it may overlap with r[]
|
||||
*/
|
||||
ldr r0, [sp, #0]
|
||||
add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9
|
||||
ldmia r1, {r2,r7,r8,r9,r10,r11,r12}
|
||||
add r1, r0, #3*4
|
||||
stmia r1, {r2,r7,r8,r9,r10}
|
||||
|
||||
bic r2, r3, field_not_M @ r[8] = c & M
|
||||
str r2, [r0, #8*4]
|
||||
mov r3, r3, lsr #26 @ c >>= 26
|
||||
orr r3, r3, r4, asl #6
|
||||
mov r4, r4, lsr #26
|
||||
mov r14, field_R1 @ c += u8 * R1
|
||||
umlal r3, r4, r11, r14
|
||||
movw r14, field_R0 @ c += d * R0
|
||||
umlal r3, r4, r5, r14
|
||||
adds r3, r3, r12 @ c += t9
|
||||
adc r4, r4, #0
|
||||
|
||||
add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2
|
||||
ldmia r1, {r7,r8,r9}
|
||||
|
||||
ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4)
|
||||
str r2, [r0, #9*4]
|
||||
mov r3, r3, lsr #22 @ c >>= 22
|
||||
orr r3, r3, r4, asl #10
|
||||
mov r4, r4, lsr #22
|
||||
movw r14, field_R1 << 4 @ c += d * (R1 << 4)
|
||||
umlal r3, r4, r5, r14
|
||||
|
||||
movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add)
|
||||
umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4)
|
||||
adds r5, r5, r7 @ d.lo += t0
|
||||
mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4)
|
||||
adc r6, r6, 0 @ d.hi += carry
|
||||
|
||||
bic r2, r5, field_not_M @ r[0] = d & M
|
||||
str r2, [r0, #0*4]
|
||||
|
||||
mov r5, r5, lsr #26 @ d >>= 26
|
||||
orr r5, r5, r6, asl #6
|
||||
mov r6, r6, lsr #26
|
||||
|
||||
movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add)
|
||||
umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4)
|
||||
adds r5, r5, r8 @ d.lo += t1
|
||||
adc r6, r6, #0 @ d.hi += carry
|
||||
adds r5, r5, r1 @ d.lo += tmp.lo
|
||||
mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4)
|
||||
adc r6, r6, r2 @ d.hi += carry + tmp.hi
|
||||
|
||||
bic r2, r5, field_not_M @ r[1] = d & M
|
||||
str r2, [r0, #1*4]
|
||||
mov r5, r5, lsr #26 @ d >>= 26 (ignore hi)
|
||||
orr r5, r5, r6, asl #6
|
||||
|
||||
add r5, r5, r9 @ d += t2
|
||||
str r5, [r0, #2*4] @ r[2] = d
|
||||
|
||||
add sp, sp, #48
|
||||
ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
|
||||
.size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner
|
||||
|
|
@ -28,7 +28,8 @@ static void bench_ecdh_setup(void* arg) {
|
|||
0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f
|
||||
};
|
||||
|
||||
data->ctx = secp256k1_context_create(0);
|
||||
/* create a context with no capabilities */
|
||||
data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT);
|
||||
for (i = 0; i < 32; i++) {
|
||||
data->scalar[i] = i + 1;
|
||||
}
|
||||
|
|
|
@ -181,12 +181,12 @@ void bench_field_inverse_var(void* arg) {
|
|||
}
|
||||
}
|
||||
|
||||
void bench_field_sqrt_var(void* arg) {
|
||||
void bench_field_sqrt(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_fe_sqrt_var(&data->fe_x, &data->fe_x);
|
||||
secp256k1_fe_sqrt(&data->fe_x, &data->fe_x);
|
||||
secp256k1_fe_add(&data->fe_x, &data->fe_y);
|
||||
}
|
||||
}
|
||||
|
@ -227,6 +227,15 @@ void bench_group_add_affine_var(void* arg) {
|
|||
}
|
||||
}
|
||||
|
||||
void bench_group_jacobi_var(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
||||
for (i = 0; i < 20000; i++) {
|
||||
secp256k1_gej_has_quad_y_var(&data->gej_x);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_ecmult_wnaf(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
|
@ -299,6 +308,21 @@ void bench_context_sign(void* arg) {
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef USE_NUM_NONE
|
||||
void bench_num_jacobi(void* arg) {
|
||||
int i;
|
||||
bench_inv_t *data = (bench_inv_t*)arg;
|
||||
secp256k1_num nx, norder;
|
||||
|
||||
secp256k1_scalar_get_num(&nx, &data->scalar_x);
|
||||
secp256k1_scalar_order_get_num(&norder);
|
||||
secp256k1_scalar_get_num(&norder, &data->scalar_y);
|
||||
|
||||
for (i = 0; i < 200000; i++) {
|
||||
secp256k1_num_jacobi(&nx, &norder);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int have_flag(int argc, char** argv, char *flag) {
|
||||
char** argm = argv + argc;
|
||||
|
@ -333,12 +357,13 @@ int main(int argc, char **argv) {
|
|||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt_var", bench_field_sqrt_var, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000);
|
||||
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000);
|
||||
if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000);
|
||||
|
||||
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000);
|
||||
if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000);
|
||||
|
@ -350,5 +375,8 @@ int main(int argc, char **argv) {
|
|||
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20);
|
||||
if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200);
|
||||
|
||||
#ifndef USE_NUM_NONE
|
||||
if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -17,6 +17,5 @@ static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *r, secp256k1_scalar *s, c
|
|||
static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar *r, const secp256k1_scalar *s);
|
||||
static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message);
|
||||
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid);
|
||||
static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Copyright (c) 2013-2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
@ -46,66 +46,133 @@ static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CON
|
|||
0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL
|
||||
);
|
||||
|
||||
static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) {
|
||||
int lenleft, b1;
|
||||
size_t ret = 0;
|
||||
if (*sigp >= sigend) {
|
||||
return -1;
|
||||
}
|
||||
b1 = *((*sigp)++);
|
||||
if (b1 == 0xFF) {
|
||||
/* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */
|
||||
return -1;
|
||||
}
|
||||
if ((b1 & 0x80) == 0) {
|
||||
/* X.690-0207 8.1.3.4 short form length octets */
|
||||
return b1;
|
||||
}
|
||||
if (b1 == 0x80) {
|
||||
/* Indefinite length is not allowed in DER. */
|
||||
return -1;
|
||||
}
|
||||
/* X.690-207 8.1.3.5 long form length octets */
|
||||
lenleft = b1 & 0x7F;
|
||||
if (lenleft > sigend - *sigp) {
|
||||
return -1;
|
||||
}
|
||||
if (**sigp == 0) {
|
||||
/* Not the shortest possible length encoding. */
|
||||
return -1;
|
||||
}
|
||||
if ((size_t)lenleft > sizeof(size_t)) {
|
||||
/* The resulting length would exceed the range of a size_t, so
|
||||
* certainly longer than the passed array size.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
while (lenleft > 0) {
|
||||
if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) {
|
||||
}
|
||||
ret = (ret << 8) | **sigp;
|
||||
if (ret + lenleft > (size_t)(sigend - *sigp)) {
|
||||
/* Result exceeds the length of the passed array. */
|
||||
return -1;
|
||||
}
|
||||
(*sigp)++;
|
||||
lenleft--;
|
||||
}
|
||||
if (ret < 128) {
|
||||
/* Not the shortest possible length encoding. */
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) {
|
||||
int overflow = 0;
|
||||
unsigned char ra[32] = {0};
|
||||
int rlen;
|
||||
|
||||
if (*sig == sigend || **sig != 0x02) {
|
||||
/* Not a primitive integer (X.690-0207 8.3.1). */
|
||||
return 0;
|
||||
}
|
||||
(*sig)++;
|
||||
rlen = secp256k1_der_read_len(sig, sigend);
|
||||
if (rlen <= 0 || (*sig) + rlen > sigend) {
|
||||
/* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */
|
||||
return 0;
|
||||
}
|
||||
if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) {
|
||||
/* Excessive 0x00 padding. */
|
||||
return 0;
|
||||
}
|
||||
if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) {
|
||||
/* Excessive 0xFF padding. */
|
||||
return 0;
|
||||
}
|
||||
if ((**sig & 0x80) == 0x80) {
|
||||
/* Negative. */
|
||||
overflow = 1;
|
||||
}
|
||||
while (rlen > 0 && **sig == 0) {
|
||||
/* Skip leading zero bytes */
|
||||
rlen--;
|
||||
(*sig)++;
|
||||
}
|
||||
if (rlen > 32) {
|
||||
overflow = 1;
|
||||
}
|
||||
if (!overflow) {
|
||||
memcpy(ra + 32 - rlen, *sig, rlen);
|
||||
secp256k1_scalar_set_b32(r, ra, &overflow);
|
||||
}
|
||||
if (overflow) {
|
||||
secp256k1_scalar_set_int(r, 0);
|
||||
}
|
||||
(*sig) += rlen;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) {
|
||||
unsigned char ra[32] = {0}, sa[32] = {0};
|
||||
const unsigned char *rp;
|
||||
const unsigned char *sp;
|
||||
size_t lenr;
|
||||
size_t lens;
|
||||
int overflow;
|
||||
if (sig[0] != 0x30) {
|
||||
const unsigned char *sigend = sig + size;
|
||||
int rlen;
|
||||
if (sig == sigend || *(sig++) != 0x30) {
|
||||
/* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */
|
||||
return 0;
|
||||
}
|
||||
lenr = sig[3];
|
||||
if (5+lenr >= size) {
|
||||
rlen = secp256k1_der_read_len(&sig, sigend);
|
||||
if (rlen < 0 || sig + rlen > sigend) {
|
||||
/* Tuple exceeds bounds */
|
||||
return 0;
|
||||
}
|
||||
lens = sig[lenr+5];
|
||||
if (sig[1] != lenr+lens+4) {
|
||||
if (sig + rlen != sigend) {
|
||||
/* Garbage after tuple. */
|
||||
return 0;
|
||||
}
|
||||
if (lenr+lens+6 > size) {
|
||||
|
||||
if (!secp256k1_der_parse_integer(rr, &sig, sigend)) {
|
||||
return 0;
|
||||
}
|
||||
if (sig[2] != 0x02) {
|
||||
if (!secp256k1_der_parse_integer(rs, &sig, sigend)) {
|
||||
return 0;
|
||||
}
|
||||
if (lenr == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (sig[lenr+4] != 0x02) {
|
||||
return 0;
|
||||
}
|
||||
if (lens == 0) {
|
||||
return 0;
|
||||
}
|
||||
sp = sig + 6 + lenr;
|
||||
while (lens > 0 && sp[0] == 0) {
|
||||
lens--;
|
||||
sp++;
|
||||
}
|
||||
if (lens > 32) {
|
||||
return 0;
|
||||
}
|
||||
rp = sig + 4;
|
||||
while (lenr > 0 && rp[0] == 0) {
|
||||
lenr--;
|
||||
rp++;
|
||||
}
|
||||
if (lenr > 32) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(ra + 32 - lenr, rp, lenr);
|
||||
memcpy(sa + 32 - lens, sp, lens);
|
||||
overflow = 0;
|
||||
secp256k1_scalar_set_b32(rr, ra, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_set_b32(rs, sa, &overflow);
|
||||
if (overflow) {
|
||||
|
||||
if (sig != sigend) {
|
||||
/* Trailing garbage inside tuple. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -136,7 +203,9 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const
|
|||
static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) {
|
||||
unsigned char c[32];
|
||||
secp256k1_scalar sn, u1, u2;
|
||||
#if !defined(EXHAUSTIVE_TEST_ORDER)
|
||||
secp256k1_fe xr;
|
||||
#endif
|
||||
secp256k1_gej pubkeyj;
|
||||
secp256k1_gej pr;
|
||||
|
||||
|
@ -152,6 +221,19 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
|
|||
if (secp256k1_gej_is_infinity(&pr)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
{
|
||||
secp256k1_scalar computed_r;
|
||||
secp256k1_ge pr_ge;
|
||||
secp256k1_ge_set_gej(&pr_ge, &pr);
|
||||
secp256k1_fe_normalize(&pr_ge.x);
|
||||
|
||||
secp256k1_fe_get_b32(c, &pr_ge.x);
|
||||
secp256k1_scalar_set_b32(&computed_r, c, NULL);
|
||||
return secp256k1_scalar_eq(sigr, &computed_r);
|
||||
}
|
||||
#else
|
||||
secp256k1_scalar_get_b32(c, sigr);
|
||||
secp256k1_fe_set_b32(&xr, c);
|
||||
|
||||
|
@ -172,11 +254,11 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
|
|||
* secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test.
|
||||
*/
|
||||
if (secp256k1_gej_eq_x_var(&xr, &pr)) {
|
||||
/* xr.x == xr * xr.z^2 mod p, so the signature is valid. */
|
||||
/* xr * pr.z^2 mod p == pr.x, so the signature is valid. */
|
||||
return 1;
|
||||
}
|
||||
if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) {
|
||||
/* xr + p >= n, so we can skip testing the second case. */
|
||||
/* xr + n >= p, so we can skip testing the second case. */
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe);
|
||||
|
@ -185,39 +267,7 @@ static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const
|
|||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) {
|
||||
unsigned char brx[32];
|
||||
secp256k1_fe fx;
|
||||
secp256k1_ge x;
|
||||
secp256k1_gej xj;
|
||||
secp256k1_scalar rn, u1, u2;
|
||||
secp256k1_gej qj;
|
||||
|
||||
if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_get_b32(brx, sigr);
|
||||
VERIFY_CHECK(secp256k1_fe_set_b32(&fx, brx)); /* brx comes from a scalar, so is less than the order; certainly less than p */
|
||||
if (recid & 2) {
|
||||
if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe);
|
||||
}
|
||||
if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_gej_set_ge(&xj, &x);
|
||||
secp256k1_scalar_inverse_var(&rn, sigr);
|
||||
secp256k1_scalar_mul(&u1, &rn, message);
|
||||
secp256k1_scalar_negate(&u1, &u1);
|
||||
secp256k1_scalar_mul(&u2, &rn, sigs);
|
||||
secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1);
|
||||
secp256k1_ge_set_gej_var(pubkey, &qj);
|
||||
return !secp256k1_gej_is_infinity(&qj);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) {
|
||||
|
@ -233,13 +283,14 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
|
|||
secp256k1_fe_normalize(&r.y);
|
||||
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. */
|
||||
secp256k1_gej_clear(&rp);
|
||||
secp256k1_ge_clear(&r);
|
||||
return 0;
|
||||
}
|
||||
/* These two conditions should be checked before calling */
|
||||
VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr));
|
||||
VERIFY_CHECK(overflow == 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);
|
||||
|
|
|
@ -15,10 +15,7 @@
|
|||
#include "ecmult_gen.h"
|
||||
|
||||
static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size);
|
||||
static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, unsigned int flags);
|
||||
|
||||
static int secp256k1_eckey_privkey_parse(secp256k1_scalar *key, const unsigned char *privkey, size_t privkeylen);
|
||||
static int secp256k1_eckey_privkey_serialize(const secp256k1_ecmult_gen_context *ctx, unsigned char *privkey, size_t *privkeylen, const secp256k1_scalar *key, unsigned int flags);
|
||||
static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed);
|
||||
|
||||
static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak);
|
||||
static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak);
|
||||
|
|
|
@ -33,14 +33,14 @@ static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char
|
|||
}
|
||||
}
|
||||
|
||||
static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, unsigned int flags) {
|
||||
static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) {
|
||||
if (secp256k1_ge_is_infinity(elem)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_normalize_var(&elem->x);
|
||||
secp256k1_fe_normalize_var(&elem->y);
|
||||
secp256k1_fe_get_b32(&pub[1], &elem->x);
|
||||
if (flags & SECP256K1_EC_COMPRESSED) {
|
||||
if (compressed) {
|
||||
*size = 33;
|
||||
pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00);
|
||||
} else {
|
||||
|
@ -51,109 +51,6 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p
|
|||
return 1;
|
||||
}
|
||||
|
||||
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, unsigned int flags) {
|
||||
secp256k1_gej rp;
|
||||
secp256k1_ge r;
|
||||
size_t pubkeylen = 0;
|
||||
secp256k1_ecmult_gen(ctx, &rp, key);
|
||||
secp256k1_ge_set_gej(&r, &rp);
|
||||
if (flags & SECP256K1_EC_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_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
|
||||
secp256k1_scalar_add(key, key, tweak);
|
||||
if (secp256k1_scalar_is_zero(key)) {
|
||||
|
|
|
@ -58,25 +58,27 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
|
|||
int global_sign;
|
||||
int skew = 0;
|
||||
int word = 0;
|
||||
|
||||
/* 1 2 3 */
|
||||
int u_last;
|
||||
int u;
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
int flip;
|
||||
int bit;
|
||||
secp256k1_scalar neg_s;
|
||||
int not_neg_one;
|
||||
/* If we are using the endomorphism, we cannot handle even numbers by negating
|
||||
* them, since we are working with 128-bit numbers whose negations would be 256
|
||||
* bits, eliminating the performance advantage. Instead we use a technique from
|
||||
/* Note that we cannot handle even numbers by negating them to be odd, as is
|
||||
* done in other implementations, since if our scalars were specified to have
|
||||
* width < 256 for performance reasons, their negations would have width 256
|
||||
* and we'd lose any performance benefit. Instead, we use a technique from
|
||||
* Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even)
|
||||
* or 2 (for odd) to the number we are encoding, then compensating after the
|
||||
* multiplication. */
|
||||
/* Negative 128-bit numbers will be negated, since otherwise they are 256-bit */
|
||||
* or 2 (for odd) to the number we are encoding, returning a skew value indicating
|
||||
* this, and having the caller compensate after doing the multiplication. */
|
||||
|
||||
/* Negative numbers will be negated to keep their bit representation below the maximum width */
|
||||
flip = secp256k1_scalar_is_high(&s);
|
||||
/* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
|
||||
bit = flip ^ (s.d[0] & 1);
|
||||
bit = flip ^ !secp256k1_scalar_is_even(&s);
|
||||
/* We check for negative one, since adding 2 to it will cause an overflow */
|
||||
secp256k1_scalar_negate(&neg_s, &s);
|
||||
not_neg_one = !secp256k1_scalar_is_one(&neg_s);
|
||||
|
@ -89,11 +91,6 @@ static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) {
|
|||
global_sign = secp256k1_scalar_cond_negate(&s, flip);
|
||||
global_sign *= not_neg_one * 2 - 1;
|
||||
skew = 1 << bit;
|
||||
#else
|
||||
/* Otherwise, we just negate to force oddness */
|
||||
int is_even = secp256k1_scalar_is_even(&s);
|
||||
global_sign = secp256k1_scalar_cond_negate(&s, is_even);
|
||||
#endif
|
||||
|
||||
/* 4 */
|
||||
u_last = secp256k1_scalar_shr_int(&s, w);
|
||||
|
@ -127,15 +124,13 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
|||
secp256k1_ge tmpa;
|
||||
secp256k1_fe Z;
|
||||
|
||||
int skew_1;
|
||||
int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
|
||||
int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)];
|
||||
int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)];
|
||||
int skew_1;
|
||||
int skew_lam;
|
||||
secp256k1_scalar q_1, q_lam;
|
||||
#else
|
||||
int wnaf[1 + WNAF_SIZE(WINDOW_A - 1)];
|
||||
#endif
|
||||
|
||||
int i;
|
||||
|
@ -145,18 +140,10 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
|||
#ifdef USE_ENDOMORPHISM
|
||||
/* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */
|
||||
secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc);
|
||||
/* no need for zero correction when using endomorphism since even
|
||||
* numbers have one added to them anyway */
|
||||
skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1);
|
||||
skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1);
|
||||
#else
|
||||
int is_zero = secp256k1_scalar_is_zero(scalar);
|
||||
/* the wNAF ladder cannot handle zero, so bump this to one .. we will
|
||||
* correct the result after the fact */
|
||||
sc.d[0] += is_zero;
|
||||
VERIFY_CHECK(!secp256k1_scalar_is_zero(&sc));
|
||||
|
||||
secp256k1_wnaf_const(wnaf, sc, WINDOW_A - 1);
|
||||
skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1);
|
||||
#endif
|
||||
|
||||
/* Calculate odd multiples of a.
|
||||
|
@ -179,21 +166,15 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
|||
/* first loop iteration (separated out so we can directly set r, rather
|
||||
* than having it start at infinity, get doubled several times, then have
|
||||
* its new value added to it) */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)];
|
||||
VERIFY_CHECK(i != 0);
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
|
||||
secp256k1_gej_set_ge(r, &tmpa);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)];
|
||||
VERIFY_CHECK(i != 0);
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A);
|
||||
secp256k1_gej_add_ge(r, r, &tmpa);
|
||||
#else
|
||||
i = wnaf[WNAF_SIZE(WINDOW_A - 1)];
|
||||
VERIFY_CHECK(i != 0);
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A);
|
||||
secp256k1_gej_set_ge(r, &tmpa);
|
||||
#endif
|
||||
/* remaining loop iterations */
|
||||
for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) {
|
||||
|
@ -202,59 +183,57 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
|
|||
for (j = 0; j < WINDOW_A - 1; ++j) {
|
||||
secp256k1_gej_double_nonzero(r, r, NULL);
|
||||
}
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
|
||||
n = wnaf_1[i];
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
|
||||
VERIFY_CHECK(n != 0);
|
||||
secp256k1_gej_add_ge(r, r, &tmpa);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
n = wnaf_lam[i];
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A);
|
||||
VERIFY_CHECK(n != 0);
|
||||
secp256k1_gej_add_ge(r, r, &tmpa);
|
||||
#else
|
||||
n = wnaf[i];
|
||||
VERIFY_CHECK(n != 0);
|
||||
ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A);
|
||||
secp256k1_gej_add_ge(r, r, &tmpa);
|
||||
#endif
|
||||
}
|
||||
|
||||
secp256k1_fe_mul(&r->z, &r->z, &Z);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
{
|
||||
/* Correct for wNAF skew */
|
||||
secp256k1_ge correction = *a;
|
||||
secp256k1_ge_storage correction_1_stor;
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge_storage correction_lam_stor;
|
||||
#endif
|
||||
secp256k1_ge_storage a2_stor;
|
||||
secp256k1_gej tmpj;
|
||||
secp256k1_gej_set_ge(&tmpj, &correction);
|
||||
secp256k1_gej_double_var(&tmpj, &tmpj, NULL);
|
||||
secp256k1_ge_set_gej(&correction, &tmpj);
|
||||
secp256k1_ge_to_storage(&correction_1_stor, a);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge_to_storage(&correction_lam_stor, a);
|
||||
#endif
|
||||
secp256k1_ge_to_storage(&a2_stor, &correction);
|
||||
|
||||
/* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
|
||||
secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
|
||||
#endif
|
||||
|
||||
/* Apply the correction */
|
||||
secp256k1_ge_from_storage(&correction, &correction_1_stor);
|
||||
secp256k1_ge_neg(&correction, &correction);
|
||||
secp256k1_gej_add_ge(r, r, &correction);
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
secp256k1_ge_from_storage(&correction, &correction_lam_stor);
|
||||
secp256k1_ge_neg(&correction, &correction);
|
||||
secp256k1_ge_mul_lambda(&correction, &correction);
|
||||
secp256k1_gej_add_ge(r, r, &correction);
|
||||
}
|
||||
#else
|
||||
/* correct for zero */
|
||||
r->infinity |= is_zero;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,8 +40,13 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
|
|||
static const unsigned char nums_b32[33] = "The scalar for this x is unknown";
|
||||
secp256k1_fe nums_x;
|
||||
secp256k1_ge nums_ge;
|
||||
VERIFY_CHECK(secp256k1_fe_set_b32(&nums_x, nums_b32));
|
||||
VERIFY_CHECK(secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0));
|
||||
int r;
|
||||
r = secp256k1_fe_set_b32(&nums_x, nums_b32);
|
||||
(void)r;
|
||||
VERIFY_CHECK(r);
|
||||
r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0);
|
||||
(void)r;
|
||||
VERIFY_CHECK(r);
|
||||
secp256k1_gej_set_ge(&nums_gej, &nums_ge);
|
||||
/* Add G to make the bits in x uniformly distributed. */
|
||||
secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL);
|
||||
|
@ -72,7 +77,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx
|
|||
secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL);
|
||||
}
|
||||
}
|
||||
secp256k1_ge_set_all_gej_var(1024, prec, precj, cb);
|
||||
secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb);
|
||||
}
|
||||
for (j = 0; j < 64; j++) {
|
||||
for (i = 0; i < 16; i++) {
|
||||
|
@ -182,7 +187,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
|
|||
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
||||
retry = !secp256k1_fe_set_b32(&s, nonce32);
|
||||
retry |= secp256k1_fe_is_zero(&s);
|
||||
} while (retry);
|
||||
} while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */
|
||||
/* Randomize the projection to defend against multiplier sidechannels. */
|
||||
secp256k1_gej_rescale(&ctx->initial, &s);
|
||||
secp256k1_fe_clear(&s);
|
||||
|
@ -191,7 +196,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
|
|||
secp256k1_scalar_set_b32(&b, nonce32, &retry);
|
||||
/* A blinding value of 0 works, but would undermine the projection hardening. */
|
||||
retry |= secp256k1_scalar_is_zero(&b);
|
||||
} while (retry);
|
||||
} while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */
|
||||
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
|
||||
memset(nonce32, 0, 32);
|
||||
secp256k1_ecmult_gen(ctx, &gb, &b);
|
||||
|
|
|
@ -7,13 +7,29 @@
|
|||
#ifndef _SECP256K1_ECMULT_IMPL_H_
|
||||
#define _SECP256K1_ECMULT_IMPL_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "group.h"
|
||||
#include "scalar.h"
|
||||
#include "ecmult.h"
|
||||
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
/* We need to lower these values for exhaustive tests because
|
||||
* the tables cannot have infinities in them (this breaks the
|
||||
* affine-isomorphism stuff which tracks z-ratios) */
|
||||
# if EXHAUSTIVE_TEST_ORDER > 128
|
||||
# define WINDOW_A 5
|
||||
# define WINDOW_G 8
|
||||
# elif EXHAUSTIVE_TEST_ORDER > 8
|
||||
# define WINDOW_A 4
|
||||
# define WINDOW_G 4
|
||||
# else
|
||||
# define WINDOW_A 2
|
||||
# define WINDOW_G 2
|
||||
# endif
|
||||
#else
|
||||
/* optimal for 128-bit and 256-bit exponents. */
|
||||
#define WINDOW_A 5
|
||||
|
||||
/** larger numbers may result in slightly better performance, at the cost of
|
||||
exponentially larger precomputed tables. */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
|
@ -23,6 +39,7 @@
|
|||
/** One table for window size 16: 1.375 MiB. */
|
||||
#define WINDOW_G 16
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** The number of entries a table with precomputed multiples needs to have. */
|
||||
#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2))
|
||||
|
@ -101,7 +118,7 @@ static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge
|
|||
/* Compute the odd multiples in Jacobian form. */
|
||||
secp256k1_ecmult_odd_multiples_table(n, prej, zr, a);
|
||||
/* Convert them in batch to affine coordinates. */
|
||||
secp256k1_ge_set_table_gej_var(n, prea, prej, zr);
|
||||
secp256k1_ge_set_table_gej_var(prea, prej, zr, n);
|
||||
/* Convert them to compact storage form. */
|
||||
for (i = 0; i < n; i++) {
|
||||
secp256k1_ge_to_storage(&pre[i], &prea[i]);
|
||||
|
|
|
@ -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
|
||||
|
@ -30,6 +30,8 @@
|
|||
#error "Please select field implementation"
|
||||
#endif
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/** Normalize a field element. */
|
||||
static void secp256k1_fe_normalize(secp256k1_fe *r);
|
||||
|
||||
|
@ -50,6 +52,9 @@ static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r);
|
|||
/** Set a field element equal to a small integer. Resulting field element is normalized. */
|
||||
static void secp256k1_fe_set_int(secp256k1_fe *r, int a);
|
||||
|
||||
/** Sets a field element equal to zero, initializing all fields. */
|
||||
static void secp256k1_fe_clear(secp256k1_fe *a);
|
||||
|
||||
/** Verify whether a field element is zero. Requires the input to be normalized. */
|
||||
static int secp256k1_fe_is_zero(const secp256k1_fe *a);
|
||||
|
||||
|
@ -57,6 +62,9 @@ static int secp256k1_fe_is_zero(const secp256k1_fe *a);
|
|||
static int secp256k1_fe_is_odd(const secp256k1_fe *a);
|
||||
|
||||
/** Compare two field elements. Requires magnitude-1 inputs. */
|
||||
static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b);
|
||||
|
||||
/** Same as secp256k1_fe_equal, but may be variable time. */
|
||||
static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b);
|
||||
|
||||
/** Compare two field elements. Requires both inputs to be normalized */
|
||||
|
@ -87,10 +95,15 @@ 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. */
|
||||
static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a);
|
||||
/** 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(secp256k1_fe *r, const secp256k1_fe *a);
|
||||
|
||||
/** Checks whether a field element is a quadratic residue. */
|
||||
static int secp256k1_fe_is_quad_var(const secp256k1_fe *a);
|
||||
|
||||
/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be
|
||||
* at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */
|
||||
|
@ -102,7 +115,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a);
|
|||
/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be
|
||||
* at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and
|
||||
* outputs must not overlap in memory. */
|
||||
static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k1_fe *a);
|
||||
static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len);
|
||||
|
||||
/** Convert a field element to the storage type. */
|
||||
static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a);
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#ifndef _SECP256K1_FIELD_REPR_IMPL_H_
|
||||
#define _SECP256K1_FIELD_REPR_IMPL_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
#include "num.h"
|
||||
#include "field.h"
|
||||
|
@ -40,10 +38,6 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
|
|||
}
|
||||
VERIFY_CHECK(r == 1);
|
||||
}
|
||||
#else
|
||||
static void secp256k1_fe_verify(const secp256k1_fe *a) {
|
||||
(void)a;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void secp256k1_fe_normalize(secp256k1_fe *r) {
|
||||
|
@ -429,6 +423,14 @@ SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_f
|
|||
#endif
|
||||
}
|
||||
|
||||
#if defined(USE_EXTERNAL_ASM)
|
||||
|
||||
/* External assembler implementation */
|
||||
void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b);
|
||||
void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a);
|
||||
|
||||
#else
|
||||
|
||||
#ifdef VERIFY
|
||||
#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0)
|
||||
#else
|
||||
|
@ -1037,7 +1039,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t
|
|||
VERIFY_BITS(r[2], 27);
|
||||
/* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) {
|
||||
#ifdef VERIFY
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "libsecp256k1-config.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include "util.h"
|
||||
#include "num.h"
|
||||
#include "field.h"
|
||||
|
@ -50,10 +49,6 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
|
|||
}
|
||||
VERIFY_CHECK(r == 1);
|
||||
}
|
||||
#else
|
||||
static void secp256k1_fe_verify(const secp256k1_fe *a) {
|
||||
(void)a;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void secp256k1_fe_normalize(secp256k1_fe *r) {
|
||||
|
|
|
@ -137,7 +137,7 @@ SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t
|
|||
VERIFY_BITS(r[2], 52);
|
||||
VERIFY_BITS(c, 63);
|
||||
/* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
|
||||
c += d * R + t3;;
|
||||
c += d * R + t3;
|
||||
VERIFY_BITS(c, 100);
|
||||
/* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
|
||||
r[3] = c & M; c >>= 52;
|
||||
|
@ -259,7 +259,7 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t
|
|||
VERIFY_BITS(c, 63);
|
||||
/* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
|
||||
|
||||
c += d * R + t3;;
|
||||
c += d * R + t3;
|
||||
VERIFY_BITS(c, 100);
|
||||
/* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */
|
||||
r[3] = c & M; c >>= 52;
|
||||
|
|
|
@ -21,6 +21,13 @@
|
|||
#error "Please select field implementation"
|
||||
#endif
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) {
|
||||
secp256k1_fe na;
|
||||
secp256k1_fe_negate(&na, a, 1);
|
||||
secp256k1_fe_add(&na, b);
|
||||
return secp256k1_fe_normalizes_to_zero(&na);
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) {
|
||||
secp256k1_fe na;
|
||||
secp256k1_fe_negate(&na, a, 1);
|
||||
|
@ -28,7 +35,16 @@ SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const
|
|||
return secp256k1_fe_normalizes_to_zero_var(&na);
|
||||
}
|
||||
|
||||
static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) {
|
||||
static int secp256k1_fe_sqrt(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;
|
||||
|
||||
|
@ -114,7 +130,7 @@ static int secp256k1_fe_sqrt_var(secp256k1_fe *r, const secp256k1_fe *a) {
|
|||
/* Check that a square root was actually calculated */
|
||||
|
||||
secp256k1_fe_sqr(&t1, r);
|
||||
return secp256k1_fe_equal_var(&t1, a);
|
||||
return secp256k1_fe_equal(&t1, a);
|
||||
}
|
||||
|
||||
static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) {
|
||||
|
@ -224,6 +240,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
|
|||
0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F
|
||||
};
|
||||
unsigned char b[32];
|
||||
int res;
|
||||
secp256k1_fe c = *a;
|
||||
secp256k1_fe_normalize_var(&c);
|
||||
secp256k1_fe_get_b32(b, &c);
|
||||
|
@ -231,7 +248,9 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
|
|||
secp256k1_num_set_bin(&m, prime, 32);
|
||||
secp256k1_num_mod_inverse(&n, &n, &m);
|
||||
secp256k1_num_get_bin(b, 32, &n);
|
||||
VERIFY_CHECK(secp256k1_fe_set_b32(r, b));
|
||||
res = secp256k1_fe_set_b32(r, b);
|
||||
(void)res;
|
||||
VERIFY_CHECK(res);
|
||||
/* Verify the result is the (unique) valid inverse using non-GMP code. */
|
||||
secp256k1_fe_mul(&c, &c, r);
|
||||
secp256k1_fe_add(&c, &negone);
|
||||
|
@ -241,7 +260,7 @@ static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k1_fe *a) {
|
||||
static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) {
|
||||
secp256k1_fe u;
|
||||
size_t i;
|
||||
if (len < 1) {
|
||||
|
@ -268,4 +287,29 @@ static void secp256k1_fe_inv_all_var(size_t len, secp256k1_fe *r, const secp256k
|
|||
r[0] = u;
|
||||
}
|
||||
|
||||
static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
|
||||
#ifndef USE_NUM_NONE
|
||||
unsigned char b[32];
|
||||
secp256k1_num n;
|
||||
secp256k1_num m;
|
||||
/* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
|
||||
static const unsigned char prime[32] = {
|
||||
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
|
||||
};
|
||||
|
||||
secp256k1_fe c = *a;
|
||||
secp256k1_fe_normalize_var(&c);
|
||||
secp256k1_fe_get_b32(b, &c);
|
||||
secp256k1_num_set_bin(&n, b, 32);
|
||||
secp256k1_num_set_bin(&m, prime, 32);
|
||||
return secp256k1_num_jacobi(&n, &m) >= 0;
|
||||
#else
|
||||
secp256k1_fe r;
|
||||
return secp256k1_fe_sqrt(&r, a);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,12 +40,15 @@ typedef struct {
|
|||
|
||||
#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y)
|
||||
|
||||
/** Set a group element equal to the point at infinity */
|
||||
static void secp256k1_ge_set_infinity(secp256k1_ge *r);
|
||||
|
||||
/** 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(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);
|
||||
|
@ -62,12 +65,12 @@ static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a);
|
|||
static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a);
|
||||
|
||||
/** Set a batch of group elements equal to the inputs given in jacobian coordinates */
|
||||
static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_callback *cb);
|
||||
static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb);
|
||||
|
||||
/** Set a batch of group elements equal to the inputs given in jacobian
|
||||
* coordinates (with known z-ratios). zr must contain the known z-ratios such
|
||||
* that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */
|
||||
static void secp256k1_ge_set_table_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr);
|
||||
static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len);
|
||||
|
||||
/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to
|
||||
* the same global z "denominator". zr must contain the known z-ratios such
|
||||
|
@ -79,9 +82,6 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
|
|||
/** Set a group element (jacobian) equal to the point at infinity. */
|
||||
static void secp256k1_gej_set_infinity(secp256k1_gej *r);
|
||||
|
||||
/** Set a group element (jacobian) equal to the point with given X and Y coordinates. */
|
||||
static void secp256k1_gej_set_xy(secp256k1_gej *r, const secp256k1_fe *x, const secp256k1_fe *y);
|
||||
|
||||
/** Set a group element (jacobian) equal to another which is given in affine coordinates. */
|
||||
static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a);
|
||||
|
||||
|
@ -94,6 +94,9 @@ static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a);
|
|||
/** Check whether a group element is the point at infinity. */
|
||||
static int secp256k1_gej_is_infinity(const secp256k1_gej *a);
|
||||
|
||||
/** Check whether a group element's y coordinate is a quadratic residue. */
|
||||
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a);
|
||||
|
||||
/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0).
|
||||
* a may not be zero. Constant time. */
|
||||
static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr);
|
||||
|
|
|
@ -7,12 +7,57 @@
|
|||
#ifndef _SECP256K1_GROUP_IMPL_H_
|
||||
#define _SECP256K1_GROUP_IMPL_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "num.h"
|
||||
#include "field.h"
|
||||
#include "group.h"
|
||||
|
||||
/* These points can be generated in sage as follows:
|
||||
*
|
||||
* 0. Setup a worksheet with the following parameters.
|
||||
* b = 4 # whatever CURVE_B will be set to
|
||||
* F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F)
|
||||
* C = EllipticCurve ([F (0), F (b)])
|
||||
*
|
||||
* 1. Determine all the small orders available to you. (If there are
|
||||
* no satisfactory ones, go back and change b.)
|
||||
* print C.order().factor(limit=1000)
|
||||
*
|
||||
* 2. Choose an order as one of the prime factors listed in the above step.
|
||||
* (You can also multiply some to get a composite order, though the
|
||||
* tests will crash trying to invert scalars during signing.) We take a
|
||||
* random point and scale it to drop its order to the desired value.
|
||||
* There is some probability this won't work; just try again.
|
||||
* order = 199
|
||||
* P = C.random_point()
|
||||
* P = (int(P.order()) / int(order)) * P
|
||||
* assert(P.order() == order)
|
||||
*
|
||||
* 3. Print the values. You'll need to use a vim macro or something to
|
||||
* split the hex output into 4-byte chunks.
|
||||
* print "%x %x" % P.xy()
|
||||
*/
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
# if EXHAUSTIVE_TEST_ORDER == 199
|
||||
const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
|
||||
0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069,
|
||||
0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18,
|
||||
0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868,
|
||||
0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED
|
||||
);
|
||||
|
||||
const int CURVE_B = 4;
|
||||
# elif EXHAUSTIVE_TEST_ORDER == 13
|
||||
const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
|
||||
0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0,
|
||||
0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15,
|
||||
0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e,
|
||||
0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac
|
||||
);
|
||||
const int CURVE_B = 2;
|
||||
# else
|
||||
# error No known generator for the specified exhaustive test group order.
|
||||
# endif
|
||||
#else
|
||||
/** Generator for secp256k1, value 'g' defined in
|
||||
* "Standards for Efficient Cryptography" (SEC2) 2.7.1.
|
||||
*/
|
||||
|
@ -23,8 +68,11 @@ static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST(
|
|||
0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL
|
||||
);
|
||||
|
||||
const int CURVE_B = 7;
|
||||
#endif
|
||||
|
||||
static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) {
|
||||
secp256k1_fe zi2;
|
||||
secp256k1_fe zi2;
|
||||
secp256k1_fe zi3;
|
||||
secp256k1_fe_sqr(&zi2, zi);
|
||||
secp256k1_fe_mul(&zi3, &zi2, zi);
|
||||
|
@ -33,10 +81,6 @@ static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, c
|
|||
r->infinity = a->infinity;
|
||||
}
|
||||
|
||||
static void secp256k1_ge_set_infinity(secp256k1_ge *r) {
|
||||
r->infinity = 1;
|
||||
}
|
||||
|
||||
static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) {
|
||||
r->infinity = 0;
|
||||
r->x = *x;
|
||||
|
@ -82,7 +126,7 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) {
|
|||
r->y = a->y;
|
||||
}
|
||||
|
||||
static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_callback *cb) {
|
||||
static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) {
|
||||
secp256k1_fe *az;
|
||||
secp256k1_fe *azi;
|
||||
size_t i;
|
||||
|
@ -95,7 +139,7 @@ static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp
|
|||
}
|
||||
|
||||
azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count);
|
||||
secp256k1_fe_inv_all_var(count, azi, az);
|
||||
secp256k1_fe_inv_all_var(azi, az, count);
|
||||
free(az);
|
||||
|
||||
count = 0;
|
||||
|
@ -108,7 +152,7 @@ static void secp256k1_ge_set_all_gej_var(size_t len, secp256k1_ge *r, const secp
|
|||
free(azi);
|
||||
}
|
||||
|
||||
static void secp256k1_ge_set_table_gej_var(size_t len, secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr) {
|
||||
static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) {
|
||||
size_t i = len - 1;
|
||||
secp256k1_fe zi;
|
||||
|
||||
|
@ -151,16 +195,9 @@ static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp
|
|||
|
||||
static void secp256k1_gej_set_infinity(secp256k1_gej *r) {
|
||||
r->infinity = 1;
|
||||
secp256k1_fe_set_int(&r->x, 0);
|
||||
secp256k1_fe_set_int(&r->y, 0);
|
||||
secp256k1_fe_set_int(&r->z, 0);
|
||||
}
|
||||
|
||||
static void secp256k1_gej_set_xy(secp256k1_gej *r, const secp256k1_fe *x, const secp256k1_fe *y) {
|
||||
r->infinity = 0;
|
||||
r->x = *x;
|
||||
r->y = *y;
|
||||
secp256k1_fe_set_int(&r->z, 1);
|
||||
secp256k1_fe_clear(&r->x);
|
||||
secp256k1_fe_clear(&r->y);
|
||||
secp256k1_fe_clear(&r->z);
|
||||
}
|
||||
|
||||
static void secp256k1_gej_clear(secp256k1_gej *r) {
|
||||
|
@ -176,15 +213,19 @@ 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(secp256k1_ge *r, const secp256k1_fe *x) {
|
||||
secp256k1_fe x2, x3, c;
|
||||
r->x = *x;
|
||||
secp256k1_fe_sqr(&x2, x);
|
||||
secp256k1_fe_mul(&x3, x, &x2);
|
||||
r->infinity = 0;
|
||||
secp256k1_fe_set_int(&c, 7);
|
||||
secp256k1_fe_set_int(&c, CURVE_B);
|
||||
secp256k1_fe_add(&c, &x3);
|
||||
if (!secp256k1_fe_sqrt_var(&r->y, &c)) {
|
||||
return secp256k1_fe_sqrt(&r->y, &c);
|
||||
}
|
||||
|
||||
static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) {
|
||||
if (!secp256k1_ge_set_xquad(r, x)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_normalize_var(&r->y);
|
||||
|
@ -192,6 +233,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) {
|
||||
|
@ -236,7 +278,7 @@ static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) {
|
|||
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
|
||||
secp256k1_fe_sqr(&z2, &a->z);
|
||||
secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2);
|
||||
secp256k1_fe_mul_int(&z6, 7);
|
||||
secp256k1_fe_mul_int(&z6, CURVE_B);
|
||||
secp256k1_fe_add(&x3, &z6);
|
||||
secp256k1_fe_normalize_weak(&x3);
|
||||
return secp256k1_fe_equal_var(&y2, &x3);
|
||||
|
@ -250,18 +292,30 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
|
|||
/* y^2 = x^3 + 7 */
|
||||
secp256k1_fe_sqr(&y2, &a->y);
|
||||
secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x);
|
||||
secp256k1_fe_set_int(&c, 7);
|
||||
secp256k1_fe_set_int(&c, CURVE_B);
|
||||
secp256k1_fe_add(&x3, &c);
|
||||
secp256k1_fe_normalize_weak(&x3);
|
||||
return secp256k1_fe_equal_var(&y2, &x3);
|
||||
}
|
||||
|
||||
static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
|
||||
/* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate */
|
||||
/* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
|
||||
*
|
||||
* Note that there is an implementation described at
|
||||
* https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
|
||||
* which trades a multiply for a square, but in practice this is actually slower,
|
||||
* mainly because it requires more normalizations.
|
||||
*/
|
||||
secp256k1_fe t1,t2,t3,t4;
|
||||
/** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity,
|
||||
* Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have
|
||||
* y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p.
|
||||
*
|
||||
* Having said this, if this function receives a point on a sextic twist, e.g. by
|
||||
* a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6,
|
||||
* since -6 does have a cube root mod p. For this point, this function will not set
|
||||
* the infinity flag even though the point doubles to infinity, and the result
|
||||
* point will be gibberish (z = 0 but infinity = 0).
|
||||
*/
|
||||
r->infinity = a->infinity;
|
||||
if (r->infinity) {
|
||||
|
@ -629,4 +683,18 @@ static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
|
|||
}
|
||||
#endif
|
||||
|
||||
static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) {
|
||||
secp256k1_fe yz;
|
||||
|
||||
if (a->infinity) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as
|
||||
* that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z
|
||||
is */
|
||||
secp256k1_fe_mul(&yz, &a->y, &a->z);
|
||||
return secp256k1_fe_is_quad_var(&yz);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t s[32];
|
||||
uint32_t s[8];
|
||||
uint32_t buf[16]; /* In big endian */
|
||||
size_t bytes;
|
||||
} secp256k1_sha256_t;
|
||||
|
|
|
@ -269,15 +269,13 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256
|
|||
rng->retry = 0;
|
||||
}
|
||||
|
||||
|
||||
#undef BE32
|
||||
#undef Round
|
||||
#undef sigma0
|
||||
#undef sigma1
|
||||
#undef Sigma0
|
||||
#undef sigma0
|
||||
#undef Sigma1
|
||||
#undef Ch
|
||||
#undef Sigma0
|
||||
#undef Maj
|
||||
#undef ReadBE32
|
||||
#undef WriteBE32
|
||||
#undef Ch
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,60 +1,446 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoin;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import static org.bitcoin.NativeSecp256k1Util.*;
|
||||
|
||||
/**
|
||||
* This class holds native methods to handle ECDSA verification.
|
||||
* You can find an example library that can be used for this at
|
||||
* https://github.com/sipa/secp256k1
|
||||
* <p>This class holds native methods to handle ECDSA verification.</p>
|
||||
*
|
||||
* <p>You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1</p>
|
||||
*
|
||||
* <p>To build secp256k1 for use with bitcoinj, run
|
||||
* `./configure --enable-jni --enable-experimental --enable-module-ecdh`
|
||||
* and `make` then copy `.libs/libsecp256k1.so` to your system library path
|
||||
* or point the JVM to the folder containing it with -Djava.library.path
|
||||
* </p>
|
||||
*/
|
||||
public class NativeSecp256k1 {
|
||||
public static final boolean enabled;
|
||||
static {
|
||||
boolean isEnabled = true;
|
||||
try {
|
||||
System.loadLibrary("javasecp256k1");
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
isEnabled = false;
|
||||
}
|
||||
enabled = isEnabled;
|
||||
}
|
||||
|
||||
|
||||
private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
|
||||
private static final Lock r = rwl.readLock();
|
||||
private static final Lock w = rwl.writeLock();
|
||||
private static ThreadLocal<ByteBuffer> nativeECDSABuffer = new ThreadLocal<ByteBuffer>();
|
||||
/**
|
||||
* Verifies the given secp256k1 signature in native code.
|
||||
* Calling when enabled == false is undefined (probably library not loaded)
|
||||
*
|
||||
*
|
||||
* @param data The data which was signed, must be exactly 32 bytes
|
||||
* @param signature The signature
|
||||
* @param pub The public key which did the signing
|
||||
*/
|
||||
public static boolean verify(byte[] data, byte[] signature, byte[] pub) {
|
||||
public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{
|
||||
Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null) {
|
||||
byteBuff = ByteBuffer.allocateDirect(32 + 8 + 520 + 520);
|
||||
if (byteBuff == null || byteBuff.capacity() < 520) {
|
||||
byteBuff = ByteBuffer.allocateDirect(520);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(data);
|
||||
byteBuff.putInt(signature.length);
|
||||
byteBuff.putInt(pub.length);
|
||||
byteBuff.put(signature);
|
||||
byteBuff.put(pub);
|
||||
return secp256k1_ecdsa_verify(byteBuff) == 1;
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1;
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param byteBuff signature format is byte[32] data,
|
||||
* native-endian int signatureLength, native-endian int pubkeyLength,
|
||||
* byte[signatureLength] signature, byte[pubkeyLength] pub
|
||||
* @returns 1 for valid signature, anything else for invalid
|
||||
* libsecp256k1 Create an ECDSA signature.
|
||||
*
|
||||
* @param data Message hash, 32 bytes
|
||||
* @param key Secret key, 32 bytes
|
||||
*
|
||||
* Return values
|
||||
* @param sig byte array of signature
|
||||
*/
|
||||
private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff);
|
||||
public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{
|
||||
Preconditions.checkArgument(data.length == 32 && sec.length <= 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 32 + 32) {
|
||||
byteBuff = ByteBuffer.allocateDirect(32 + 32);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(data);
|
||||
byteBuff.put(sec);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] sigArr = retByteArray[0];
|
||||
int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(sigArr.length, sigLen, "Got bad signature length.");
|
||||
|
||||
return retVal == 0 ? new byte[0] : sigArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid
|
||||
*
|
||||
* @param seckey ECDSA Secret key, 32 bytes
|
||||
*/
|
||||
public static boolean secKeyVerify(byte[] seckey) {
|
||||
Preconditions.checkArgument(seckey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seckey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seckey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1;
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* libsecp256k1 Compute Pubkey - computes public key from secret key
|
||||
*
|
||||
* @param seckey ECDSA Secret key, 32 bytes
|
||||
*
|
||||
* Return values
|
||||
* @param pubkey ECDSA Public key, 33 or 65 bytes
|
||||
*/
|
||||
//TODO add a 'compressed' arg
|
||||
public static byte[] computePubkey(byte[] seckey) throws AssertFailException{
|
||||
Preconditions.checkArgument(seckey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seckey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seckey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
|
||||
byte[][] retByteArray;
|
||||
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
return retVal == 0 ? new byte[0]: pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 Cleanup - This destroys the secp256k1 context object
|
||||
* This should be called at the end of the program for proper cleanup of the context.
|
||||
*/
|
||||
public static synchronized void cleanup() {
|
||||
w.lock();
|
||||
try {
|
||||
secp256k1_destroy_context(Secp256k1Context.getContext());
|
||||
} finally {
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static long cloneContext() {
|
||||
r.lock();
|
||||
try {
|
||||
return secp256k1_ctx_clone(Secp256k1Context.getContext());
|
||||
} finally { r.unlock(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param seckey 32-byte seckey
|
||||
*/
|
||||
public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{
|
||||
Preconditions.checkArgument(privkey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(privkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] privArr = retByteArray[0];
|
||||
|
||||
int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(privArr.length, privLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return privArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param seckey 32-byte seckey
|
||||
*/
|
||||
public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{
|
||||
Preconditions.checkArgument(privkey.length == 32);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(privkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext());
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] privArr = retByteArray[0];
|
||||
|
||||
int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(privArr.length, privLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return privArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param pubkey 32-byte seckey
|
||||
*/
|
||||
public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{
|
||||
Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(pubkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
|
||||
int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it
|
||||
*
|
||||
* @param tweak some bytes to tweak with
|
||||
* @param pubkey 32-byte seckey
|
||||
*/
|
||||
public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{
|
||||
Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(pubkey);
|
||||
byteBuff.put(tweak);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] pubArr = retByteArray[0];
|
||||
|
||||
int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF;
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue();
|
||||
|
||||
assertEquals(pubArr.length, pubLen, "Got bad pubkey length.");
|
||||
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return pubArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 create ECDH secret - constant time ECDH calculation
|
||||
*
|
||||
* @param seckey byte array of secret key used in exponentiaion
|
||||
* @param pubkey byte array of public key used in exponentiaion
|
||||
*/
|
||||
public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{
|
||||
Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seckey);
|
||||
byteBuff.put(pubkey);
|
||||
|
||||
byte[][] retByteArray;
|
||||
r.lock();
|
||||
try {
|
||||
retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length);
|
||||
} finally {
|
||||
r.unlock();
|
||||
}
|
||||
|
||||
byte[] resArr = retByteArray[0];
|
||||
int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue();
|
||||
|
||||
assertEquals(resArr.length, 32, "Got bad result length.");
|
||||
assertEquals(retVal, 1, "Failed return value check.");
|
||||
|
||||
return resArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* libsecp256k1 randomize - updates the context randomization
|
||||
*
|
||||
* @param seed 32-byte random seed
|
||||
*/
|
||||
public static synchronized boolean randomize(byte[] seed) throws AssertFailException{
|
||||
Preconditions.checkArgument(seed.length == 32 || seed == null);
|
||||
|
||||
ByteBuffer byteBuff = nativeECDSABuffer.get();
|
||||
if (byteBuff == null || byteBuff.capacity() < seed.length) {
|
||||
byteBuff = ByteBuffer.allocateDirect(seed.length);
|
||||
byteBuff.order(ByteOrder.nativeOrder());
|
||||
nativeECDSABuffer.set(byteBuff);
|
||||
}
|
||||
byteBuff.rewind();
|
||||
byteBuff.put(seed);
|
||||
|
||||
w.lock();
|
||||
try {
|
||||
return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1;
|
||||
} finally {
|
||||
w.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static native long secp256k1_ctx_clone(long context);
|
||||
|
||||
private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen);
|
||||
|
||||
private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen);
|
||||
|
||||
private static native void secp256k1_destroy_context(long context);
|
||||
|
||||
private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen);
|
||||
|
||||
private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context);
|
||||
|
||||
private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen);
|
||||
|
||||
private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
package org.bitcoin;
|
||||
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import java.util.Arrays;
|
||||
import java.math.BigInteger;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import static org.bitcoin.NativeSecp256k1Util.*;
|
||||
|
||||
/**
|
||||
* This class holds test cases defined for testing this library.
|
||||
*/
|
||||
public class NativeSecp256k1Test {
|
||||
|
||||
//TODO improve comments/add more tests
|
||||
/**
|
||||
* This tests verify() for a valid signature
|
||||
*/
|
||||
public static void testVerifyPos() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.verify( data, sig, pub);
|
||||
assertEquals( result, true , "testVerifyPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests verify() for a non-valid signature
|
||||
*/
|
||||
public static void testVerifyNeg() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.verify( data, sig, pub);
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, false , "testVerifyNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests secret key verify() for a valid secretkey
|
||||
*/
|
||||
public static void testSecKeyVerifyPos() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.secKeyVerify( sec );
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, true , "testSecKeyVerifyPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests secret key verify() for a invalid secretkey
|
||||
*/
|
||||
public static void testSecKeyVerifyNeg() throws AssertFailException{
|
||||
boolean result = false;
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
result = NativeSecp256k1.secKeyVerify( sec );
|
||||
//System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16));
|
||||
assertEquals( result, false , "testSecKeyVerifyNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests public key create() for a valid secretkey
|
||||
*/
|
||||
public static void testPubKeyCreatePos() throws AssertFailException{
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.computePubkey( sec);
|
||||
String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests public key create() for a invalid secretkey
|
||||
*/
|
||||
public static void testPubKeyCreateNeg() throws AssertFailException{
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.computePubkey( sec);
|
||||
String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( pubkeyString, "" , "testPubKeyCreateNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests sign() for a valid secretkey
|
||||
*/
|
||||
public static void testSignPos() throws AssertFailException{
|
||||
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.sign(data, sec);
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests sign() for a invalid secretkey
|
||||
*/
|
||||
public static void testSignNeg() throws AssertFailException{
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing"
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.sign(data, sec);
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString, "" , "testSignNeg");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-add
|
||||
*/
|
||||
public static void testPrivKeyTweakAdd_1() throws AssertFailException {
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-mul
|
||||
*/
|
||||
public static void testPrivKeyTweakMul_1() throws AssertFailException {
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-add uncompressed
|
||||
*/
|
||||
public static void testPrivKeyTweakAdd_2() throws AssertFailException {
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests private key tweak-mul uncompressed
|
||||
*/
|
||||
public static void testPrivKeyTweakMul_2() throws AssertFailException {
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak"
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data );
|
||||
String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2");
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests seed randomization
|
||||
*/
|
||||
public static void testRandomize() throws AssertFailException {
|
||||
byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random"
|
||||
boolean result = NativeSecp256k1.randomize(seed);
|
||||
assertEquals( result, true, "testRandomize");
|
||||
}
|
||||
|
||||
public static void testCreateECDHSecret() throws AssertFailException{
|
||||
|
||||
byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase());
|
||||
byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase());
|
||||
|
||||
byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub);
|
||||
String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr);
|
||||
assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws AssertFailException{
|
||||
|
||||
|
||||
System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n");
|
||||
|
||||
assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" );
|
||||
|
||||
//Test verify() success/fail
|
||||
testVerifyPos();
|
||||
testVerifyNeg();
|
||||
|
||||
//Test secKeyVerify() success/fail
|
||||
testSecKeyVerifyPos();
|
||||
testSecKeyVerifyNeg();
|
||||
|
||||
//Test computePubkey() success/fail
|
||||
testPubKeyCreatePos();
|
||||
testPubKeyCreateNeg();
|
||||
|
||||
//Test sign() success/fail
|
||||
testSignPos();
|
||||
testSignNeg();
|
||||
|
||||
//Test privKeyTweakAdd() 1
|
||||
testPrivKeyTweakAdd_1();
|
||||
|
||||
//Test privKeyTweakMul() 2
|
||||
testPrivKeyTweakMul_1();
|
||||
|
||||
//Test privKeyTweakAdd() 3
|
||||
testPrivKeyTweakAdd_2();
|
||||
|
||||
//Test privKeyTweakMul() 4
|
||||
testPrivKeyTweakMul_2();
|
||||
|
||||
//Test randomize()
|
||||
testRandomize();
|
||||
|
||||
//Test ECDH
|
||||
testCreateECDHSecret();
|
||||
|
||||
NativeSecp256k1.cleanup();
|
||||
|
||||
System.out.println(" All tests passed." );
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoin;
|
||||
|
||||
public class NativeSecp256k1Util{
|
||||
|
||||
public static void assertEquals( int val, int val2, String message ) throws AssertFailException{
|
||||
if( val != val2 )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
}
|
||||
|
||||
public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{
|
||||
if( val != val2 )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
else
|
||||
System.out.println("PASS: " + message);
|
||||
}
|
||||
|
||||
public static void assertEquals( String val, String val2, String message ) throws AssertFailException{
|
||||
if( !val.equals(val2) )
|
||||
throw new AssertFailException("FAIL: " + message);
|
||||
else
|
||||
System.out.println("PASS: " + message);
|
||||
}
|
||||
|
||||
public static class AssertFailException extends Exception {
|
||||
public AssertFailException(String message) {
|
||||
super( message );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2014-2016 the libsecp256k1 contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.bitcoin;
|
||||
|
||||
/**
|
||||
* This class holds the context reference used in native methods
|
||||
* to handle ECDSA operations.
|
||||
*/
|
||||
public class Secp256k1Context {
|
||||
private static final boolean enabled; //true if the library is loaded
|
||||
private static final long context; //ref to pointer to context obj
|
||||
|
||||
static { //static initializer
|
||||
boolean isEnabled = true;
|
||||
long contextRef = -1;
|
||||
try {
|
||||
System.loadLibrary("secp256k1");
|
||||
contextRef = secp256k1_init_context();
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
System.out.println("UnsatisfiedLinkError: " + e.toString());
|
||||
isEnabled = false;
|
||||
}
|
||||
enabled = isEnabled;
|
||||
context = contextRef;
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public static long getContext() {
|
||||
if(!enabled) return -1; //sanity check
|
||||
return context;
|
||||
}
|
||||
|
||||
private static native long secp256k1_init_context();
|
||||
}
|
|
@ -1,23 +1,377 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "org_bitcoin_NativeSecp256k1.h"
|
||||
#include "include/secp256k1.h"
|
||||
#include "include/secp256k1_ecdh.h"
|
||||
#include "include/secp256k1_recovery.h"
|
||||
|
||||
JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject)
|
||||
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
|
||||
(JNIEnv* env, jclass classObject, jlong ctx_l)
|
||||
{
|
||||
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
int sigLen = *((int*)(data + 32));
|
||||
int pubLen = *((int*)(data + 32 + 4));
|
||||
const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
|
||||
jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx);
|
||||
|
||||
(void)classObject;(void)env;
|
||||
|
||||
return ctx_clone_l;
|
||||
|
||||
return secp256k1_ecdsa_verify(data, 32, data+32+8, sigLen, data+32+8+sigLen, pubLen);
|
||||
}
|
||||
|
||||
static void __javasecp256k1_attach(void) __attribute__((constructor));
|
||||
static void __javasecp256k1_detach(void) __attribute__((destructor));
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
|
||||
const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return secp256k1_context_randomize(ctx, seed);
|
||||
|
||||
static void __javasecp256k1_attach(void) {
|
||||
secp256k1_start(SECP256K1_START_VERIFY);
|
||||
}
|
||||
|
||||
static void __javasecp256k1_detach(void) {
|
||||
secp256k1_stop();
|
||||
SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
|
||||
(JNIEnv* env, jclass classObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
|
||||
secp256k1_context_destroy(ctx);
|
||||
|
||||
(void)classObject;(void)env;
|
||||
}
|
||||
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
|
||||
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* sigdata = { (unsigned char*) (data + 32) };
|
||||
const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) };
|
||||
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen);
|
||||
|
||||
if( ret ) {
|
||||
ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
|
||||
|
||||
if( ret ) {
|
||||
ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey);
|
||||
}
|
||||
}
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
unsigned char* secKey = (unsigned char*) (data + 32);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray sigArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
|
||||
secp256k1_ecdsa_signature sig[72];
|
||||
|
||||
int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL );
|
||||
|
||||
unsigned char outputSer[72];
|
||||
size_t outputLen = 72;
|
||||
|
||||
if( ret ) {
|
||||
int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2;
|
||||
}
|
||||
|
||||
intsarray[0] = outputLen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
sigArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, sigArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return secp256k1_ec_seckey_verify(ctx, secKey);
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
|
||||
secp256k1_pubkey pubkey;
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray pubkeyArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
|
||||
int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey);
|
||||
|
||||
unsigned char outputSer[65];
|
||||
size_t outputLen = 65;
|
||||
|
||||
if( ret ) {
|
||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
||||
}
|
||||
|
||||
intsarray[0] = outputLen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
pubkeyArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* tweak = (unsigned char*) (privkey + 32);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray privArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
|
||||
int privkeylen = 32;
|
||||
|
||||
int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak);
|
||||
|
||||
intsarray[0] = privkeylen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
privArray = (*env)->NewByteArray(env, privkeylen);
|
||||
(*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, privArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* tweak = (unsigned char*) (privkey + 32);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray privArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
|
||||
int privkeylen = 32;
|
||||
|
||||
int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak);
|
||||
|
||||
intsarray[0] = privkeylen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
privArray = (*env)->NewByteArray(env, privkeylen);
|
||||
(*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, privArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/
|
||||
unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* tweak = (unsigned char*) (pkey + publen);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray pubArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
unsigned char outputSer[65];
|
||||
size_t outputLen = 65;
|
||||
|
||||
secp256k1_pubkey pubkey;
|
||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
|
||||
|
||||
if( ret ) {
|
||||
ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak);
|
||||
}
|
||||
|
||||
if( ret ) {
|
||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
||||
}
|
||||
|
||||
intsarray[0] = outputLen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
pubArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* tweak = (unsigned char*) (pkey + publen);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray pubArray, intsByteArray;
|
||||
unsigned char intsarray[2];
|
||||
unsigned char outputSer[65];
|
||||
size_t outputLen = 65;
|
||||
|
||||
secp256k1_pubkey pubkey;
|
||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen);
|
||||
|
||||
if ( ret ) {
|
||||
ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak);
|
||||
}
|
||||
|
||||
if( ret ) {
|
||||
int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2;
|
||||
}
|
||||
|
||||
intsarray[0] = outputLen;
|
||||
intsarray[1] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
pubArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, pubArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 2);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine
|
||||
(JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys)
|
||||
{
|
||||
(void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen)
|
||||
{
|
||||
secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l;
|
||||
const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject);
|
||||
const unsigned char* pubdata = (const unsigned char*) (secdata + 32);
|
||||
|
||||
jobjectArray retArray;
|
||||
jbyteArray outArray, intsByteArray;
|
||||
unsigned char intsarray[1];
|
||||
secp256k1_pubkey pubkey;
|
||||
unsigned char nonce_res[32];
|
||||
size_t outputLen = 32;
|
||||
|
||||
int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen);
|
||||
|
||||
if (ret) {
|
||||
ret = secp256k1_ecdh(
|
||||
ctx,
|
||||
nonce_res,
|
||||
&pubkey,
|
||||
secdata
|
||||
);
|
||||
}
|
||||
|
||||
intsarray[0] = ret;
|
||||
|
||||
retArray = (*env)->NewObjectArray(env, 2,
|
||||
(*env)->FindClass(env, "[B"),
|
||||
(*env)->NewByteArray(env, 1));
|
||||
|
||||
outArray = (*env)->NewByteArray(env, outputLen);
|
||||
(*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 0, outArray);
|
||||
|
||||
intsByteArray = (*env)->NewByteArray(env, 1);
|
||||
(*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray);
|
||||
(*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray);
|
||||
|
||||
(void)classObject;
|
||||
|
||||
return retArray;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
#include "include/secp256k1.h"
|
||||
/* Header for class org_bitcoin_NativeSecp256k1 */
|
||||
|
||||
#ifndef _Included_org_bitcoin_NativeSecp256k1
|
||||
|
@ -9,11 +10,108 @@ extern "C" {
|
|||
#endif
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ecdsa_verify
|
||||
* Signature: (Ljava/nio/ByteBuffer;)I
|
||||
* Method: secp256k1_ctx_clone
|
||||
* Signature: (J)J
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
|
||||
(JNIEnv *, jclass, jobject);
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_context_randomize
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)I
|
||||
*/
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_privkey_tweak_add
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_privkey_tweak_mul
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_pubkey_tweak_add
|
||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add
|
||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_pubkey_tweak_mul
|
||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul
|
||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_destroy_context
|
||||
* Signature: (J)V
|
||||
*/
|
||||
SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ecdsa_verify
|
||||
* Signature: (Ljava/nio/ByteBuffer;JII)I
|
||||
*/
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify
|
||||
(JNIEnv *, jclass, jobject, jlong, jint, jint);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ecdsa_sign
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ec_seckey_verify
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)I
|
||||
*/
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ec_pubkey_create
|
||||
* Signature: (Ljava/nio/ByteBuffer;J)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create
|
||||
(JNIEnv *, jclass, jobject, jlong);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ec_pubkey_parse
|
||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse
|
||||
(JNIEnv *, jclass, jobject, jlong, jint);
|
||||
|
||||
/*
|
||||
* Class: org_bitcoin_NativeSecp256k1
|
||||
* Method: secp256k1_ecdh
|
||||
* Signature: (Ljava/nio/ByteBuffer;JI)[[B
|
||||
*/
|
||||
SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh
|
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "org_bitcoin_Secp256k1Context.h"
|
||||
#include "include/secp256k1.h"
|
||||
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
|
||||
(JNIEnv* env, jclass classObject)
|
||||
{
|
||||
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
(void)classObject;(void)env;
|
||||
|
||||
return (uintptr_t)ctx;
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
#include "include/secp256k1.h"
|
||||
/* Header for class org_bitcoin_Secp256k1Context */
|
||||
|
||||
#ifndef _Included_org_bitcoin_Secp256k1Context
|
||||
#define _Included_org_bitcoin_Secp256k1Context
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: org_bitcoin_Secp256k1Context
|
||||
* Method: secp256k1_init_context
|
||||
* Signature: ()J
|
||||
*/
|
||||
SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context
|
||||
(JNIEnv *, jclass);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -4,6 +4,5 @@ noinst_HEADERS += src/modules/ecdh/tests_impl.h
|
|||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_ecdh
|
||||
bench_ecdh_SOURCES = src/bench_ecdh.c
|
||||
bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
bench_ecdh_LDFLAGS = -static
|
||||
bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
|
||||
endif
|
||||
|
|
|
@ -16,10 +16,10 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se
|
|||
secp256k1_gej res;
|
||||
secp256k1_ge pt;
|
||||
secp256k1_scalar s;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(result != NULL);
|
||||
ARG_CHECK(point != NULL);
|
||||
ARG_CHECK(scalar != NULL);
|
||||
(void)ctx;
|
||||
|
||||
secp256k1_pubkey_load(ctx, &pt, point);
|
||||
secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
||||
|
|
|
@ -7,6 +7,35 @@
|
|||
#ifndef _SECP256K1_MODULE_ECDH_TESTS_
|
||||
#define _SECP256K1_MODULE_ECDH_TESTS_
|
||||
|
||||
void test_ecdh_api(void) {
|
||||
/* Setup context that just counts errors */
|
||||
secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
secp256k1_pubkey point;
|
||||
unsigned char res[32];
|
||||
unsigned char s_one[32] = { 0 };
|
||||
int32_t ecount = 0;
|
||||
s_one[31] = 1;
|
||||
|
||||
secp256k1_context_set_error_callback(tctx, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(tctx, counting_illegal_callback_fn, &ecount);
|
||||
CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1);
|
||||
|
||||
/* Check all NULLs are detected */
|
||||
CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
|
||||
CHECK(ecount == 0);
|
||||
CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1);
|
||||
CHECK(ecount == 3);
|
||||
|
||||
/* Cleanup */
|
||||
secp256k1_context_destroy(tctx);
|
||||
}
|
||||
|
||||
void test_ecdh_generator_basepoint(void) {
|
||||
unsigned char s_one[32] = { 0 };
|
||||
secp256k1_pubkey point[2];
|
||||
|
@ -68,6 +97,7 @@ void test_bad_scalar(void) {
|
|||
}
|
||||
|
||||
void run_ecdh_tests(void) {
|
||||
test_ecdh_api();
|
||||
test_ecdh_generator_basepoint();
|
||||
test_bad_scalar();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,5 @@ noinst_HEADERS += src/modules/recovery/tests_impl.h
|
|||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_recover
|
||||
bench_recover_SOURCES = src/bench_recover.c
|
||||
bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
bench_recover_LDFLAGS = -static
|
||||
bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB)
|
||||
endif
|
||||
|
|
|
@ -63,6 +63,7 @@ int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_cont
|
|||
(void)ctx;
|
||||
ARG_CHECK(output64 != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(recid != NULL);
|
||||
|
||||
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, recid, sig);
|
||||
secp256k1_scalar_get_b32(&output64[0], &r);
|
||||
|
@ -83,6 +84,42 @@ int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) {
|
||||
unsigned char brx[32];
|
||||
secp256k1_fe fx;
|
||||
secp256k1_ge x;
|
||||
secp256k1_gej xj;
|
||||
secp256k1_scalar rn, u1, u2;
|
||||
secp256k1_gej qj;
|
||||
int r;
|
||||
|
||||
if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_scalar_get_b32(brx, sigr);
|
||||
r = secp256k1_fe_set_b32(&fx, brx);
|
||||
(void)r;
|
||||
VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */
|
||||
if (recid & 2) {
|
||||
if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe);
|
||||
}
|
||||
if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_gej_set_ge(&xj, &x);
|
||||
secp256k1_scalar_inverse_var(&rn, sigr);
|
||||
secp256k1_scalar_mul(&u1, &rn, message);
|
||||
secp256k1_scalar_negate(&u1, &u1);
|
||||
secp256k1_scalar_mul(&u2, &rn, sigs);
|
||||
secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1);
|
||||
secp256k1_ge_set_gej_var(pubkey, &qj);
|
||||
return !secp256k1_gej_is_infinity(&qj);
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
|
||||
secp256k1_scalar r, s;
|
||||
secp256k1_scalar sec, non, msg;
|
||||
|
@ -101,16 +138,15 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
|
|||
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
|
||||
/* Fail if the secret key is invalid. */
|
||||
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
|
||||
unsigned char nonce32[32];
|
||||
unsigned int count = 0;
|
||||
secp256k1_scalar_set_b32(&msg, msg32, NULL);
|
||||
while (1) {
|
||||
unsigned char nonce32[32];
|
||||
ret = noncefp(nonce32, seckey, msg32, NULL, (void*)noncedata, count);
|
||||
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
|
||||
memset(nonce32, 0, 32);
|
||||
if (!secp256k1_scalar_is_zero(&non) && !overflow) {
|
||||
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) {
|
||||
break;
|
||||
|
@ -118,6 +154,7 @@ int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecd
|
|||
}
|
||||
count++;
|
||||
}
|
||||
memset(nonce32, 0, 32);
|
||||
secp256k1_scalar_clear(&msg);
|
||||
secp256k1_scalar_clear(&non);
|
||||
secp256k1_scalar_clear(&sec);
|
||||
|
@ -142,7 +179,7 @@ int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubk
|
|||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature);
|
||||
ARG_CHECK(recid >= 0 && recid < 4);
|
||||
VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */
|
||||
secp256k1_scalar_set_b32(&m, msg32, NULL);
|
||||
if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) {
|
||||
secp256k1_pubkey_save(pubkey, &q);
|
||||
|
|
|
@ -7,6 +7,146 @@
|
|||
#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_
|
||||
#define _SECP256K1_MODULE_RECOVERY_TESTS_
|
||||
|
||||
static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) {
|
||||
(void) msg32;
|
||||
(void) key32;
|
||||
(void) algo16;
|
||||
(void) data;
|
||||
|
||||
/* On the first run, return 0 to force a second run */
|
||||
if (counter == 0) {
|
||||
memset(nonce32, 0, 32);
|
||||
return 1;
|
||||
}
|
||||
/* On the second run, return an overflow to force a third run */
|
||||
if (counter == 1) {
|
||||
memset(nonce32, 0xff, 32);
|
||||
return 1;
|
||||
}
|
||||
/* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */
|
||||
memset(nonce32, 1, 32);
|
||||
return secp256k1_rand_bits(1);
|
||||
}
|
||||
|
||||
void test_ecdsa_recovery_api(void) {
|
||||
/* Setup contexts that just count errors */
|
||||
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
|
||||
secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY);
|
||||
secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
secp256k1_pubkey pubkey;
|
||||
secp256k1_pubkey recpubkey;
|
||||
secp256k1_ecdsa_signature normal_sig;
|
||||
secp256k1_ecdsa_recoverable_signature recsig;
|
||||
unsigned char privkey[32] = { 1 };
|
||||
unsigned char message[32] = { 2 };
|
||||
int32_t ecount = 0;
|
||||
int recid = 0;
|
||||
unsigned char sig[74];
|
||||
unsigned char zero_privkey[32] = { 0 };
|
||||
unsigned char over_privkey[32] = { 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, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount);
|
||||
secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount);
|
||||
|
||||
/* Construct and verify corresponding public key. */
|
||||
CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
|
||||
|
||||
/* Check bad contexts and NULLs for signing */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(none, &recsig, message, privkey, NULL, NULL) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(sign, &recsig, message, privkey, NULL, NULL) == 1);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(vrfy, &recsig, message, privkey, NULL, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(both, NULL, message, privkey, NULL, NULL) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, NULL, privkey, NULL, NULL) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, NULL, NULL, NULL) == 0);
|
||||
CHECK(ecount == 5);
|
||||
/* This will fail or succeed randomly, and in either case will not ARG_CHECK failure */
|
||||
secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, recovery_test_nonce_function, NULL);
|
||||
CHECK(ecount == 5);
|
||||
/* These will all fail, but not in ARG_CHECK way */
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, zero_privkey, NULL, NULL) == 0);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, over_privkey, NULL, NULL) == 0);
|
||||
/* This one will succeed. */
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1);
|
||||
CHECK(ecount == 5);
|
||||
|
||||
/* Check signing with a goofy nonce function */
|
||||
|
||||
/* Check bad contexts and NULLs for recovery */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_ecdsa_recover(none, &recpubkey, &recsig, message) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_recover(sign, &recpubkey, &recsig, message) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_recover(vrfy, &recpubkey, &recsig, message) == 1);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, message) == 1);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_recover(both, NULL, &recsig, message) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_ecdsa_recover(both, &recpubkey, NULL, message) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, NULL) == 0);
|
||||
CHECK(ecount == 5);
|
||||
|
||||
/* Check NULLs for conversion */
|
||||
CHECK(secp256k1_ecdsa_sign(both, &normal_sig, message, privkey, NULL, NULL) == 1);
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, NULL, &recsig) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, NULL) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, &recsig) == 1);
|
||||
|
||||
/* Check NULLs for de/serialization */
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1);
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, NULL, &recid, &recsig) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, NULL, &recsig) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, NULL) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, &recsig) == 1);
|
||||
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, NULL, sig, recid) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, NULL, recid) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, -1) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, 5) == 0);
|
||||
CHECK(ecount == 7);
|
||||
/* overflow in signature will fail but not affect ecount */
|
||||
memcpy(sig, over_privkey, 32);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, recid) == 0);
|
||||
CHECK(ecount == 7);
|
||||
|
||||
/* cleanup */
|
||||
secp256k1_context_destroy(none);
|
||||
secp256k1_context_destroy(sign);
|
||||
secp256k1_context_destroy(vrfy);
|
||||
secp256k1_context_destroy(both);
|
||||
}
|
||||
|
||||
void test_ecdsa_recovery_end_to_end(void) {
|
||||
unsigned char extra[32] = {0x00};
|
||||
unsigned char privkey[32];
|
||||
|
@ -34,6 +174,7 @@ void test_ecdsa_recovery_end_to_end(void) {
|
|||
/* Serialize/parse compact and verify/recover. */
|
||||
extra[0] = 0;
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[0], message, privkey, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[4], message, privkey, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[1], message, privkey, NULL, extra) == 1);
|
||||
extra[31] = 1;
|
||||
|
@ -43,6 +184,7 @@ void test_ecdsa_recovery_end_to_end(void) {
|
|||
CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1);
|
||||
CHECK(memcmp(&signature[4], &signature[0], 64) == 0);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1);
|
||||
memset(&rsignature[4], 0, sizeof(rsignature[4]));
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
|
||||
|
@ -54,7 +196,7 @@ void test_ecdsa_recovery_end_to_end(void) {
|
|||
CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
|
||||
/* Serialize/destroy/parse signature and verify again. */
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1);
|
||||
sig[secp256k1_rand32() % 64] += 1 + (secp256k1_rand32() % 255);
|
||||
sig[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1);
|
||||
CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0);
|
||||
|
@ -161,25 +303,24 @@ void test_ecdsa_recovery_edge_cases(void) {
|
|||
CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid2) == 1);
|
||||
CHECK(secp256k1_ecdsa_recover(ctx, &pubkey2b, &rsig, msg32) == 1);
|
||||
/* Verifying with (order + r,4) should always fail. */
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 0);
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0);
|
||||
}
|
||||
/* DER parsing tests. */
|
||||
/* Zero length r/s. */
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0);
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0);
|
||||
/* Leading zeros. */
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1);
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1);
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1);
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1);
|
||||
sigbderalt3[4] = 1;
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0);
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0);
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0);
|
||||
sigbderalt4[7] = 1;
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0);
|
||||
sigbderalt3[4] = 1;
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0);
|
||||
sigbderalt4[7] = 1;
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1);
|
||||
CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0);
|
||||
/* Damage signature. */
|
||||
sigbder[7]++;
|
||||
CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1);
|
||||
|
@ -240,6 +381,9 @@ void test_ecdsa_recovery_edge_cases(void) {
|
|||
|
||||
void run_recovery_tests(void) {
|
||||
int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
test_ecdsa_recovery_api();
|
||||
}
|
||||
for (i = 0; i < 64*count; i++) {
|
||||
test_ecdsa_recovery_end_to_end();
|
||||
}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
include_HEADERS += include/secp256k1_schnorr.h
|
||||
noinst_HEADERS += src/modules/schnorr/main_impl.h
|
||||
noinst_HEADERS += src/modules/schnorr/schnorr.h
|
||||
noinst_HEADERS += src/modules/schnorr/schnorr_impl.h
|
||||
noinst_HEADERS += src/modules/schnorr/tests_impl.h
|
||||
if USE_BENCHMARK
|
||||
noinst_PROGRAMS += bench_schnorr_verify
|
||||
bench_schnorr_verify_SOURCES = src/bench_schnorr_verify.c
|
||||
bench_schnorr_verify_LDADD = libsecp256k1.la $(SECP_LIBS)
|
||||
bench_schnorr_verify_LDFLAGS = -static
|
||||
endif
|
|
@ -1,164 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_SCHNORR_MAIN
|
||||
#define SECP256K1_MODULE_SCHNORR_MAIN
|
||||
|
||||
#include "include/secp256k1_schnorr.h"
|
||||
#include "modules/schnorr/schnorr_impl.h"
|
||||
|
||||
static void secp256k1_schnorr_msghash_sha256(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
|
||||
secp256k1_sha256_t sha;
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, r32, 32);
|
||||
secp256k1_sha256_write(&sha, msg32, 32);
|
||||
secp256k1_sha256_finalize(&sha, h32);
|
||||
}
|
||||
|
||||
static const unsigned char secp256k1_schnorr_algo16[17] = "Schnorr+SHA256 ";
|
||||
|
||||
int secp256k1_schnorr_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) {
|
||||
secp256k1_scalar sec, non;
|
||||
int ret = 0;
|
||||
int overflow = 0;
|
||||
unsigned int count = 0;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
if (noncefp == NULL) {
|
||||
noncefp = secp256k1_nonce_function_default;
|
||||
}
|
||||
|
||||
secp256k1_scalar_set_b32(&sec, seckey, NULL);
|
||||
while (1) {
|
||||
unsigned char nonce32[32];
|
||||
ret = noncefp(nonce32, msg32, seckey, secp256k1_schnorr_algo16, (void*)noncedata, count);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
|
||||
memset(nonce32, 0, 32);
|
||||
if (!secp256k1_scalar_is_zero(&non) && !overflow) {
|
||||
if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64, &sec, &non, NULL, secp256k1_schnorr_msghash_sha256, msg32)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (!ret) {
|
||||
memset(sig64, 0, 64);
|
||||
}
|
||||
secp256k1_scalar_clear(&non);
|
||||
secp256k1_scalar_clear(&sec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_verify(const secp256k1_context* ctx, const unsigned char *sig64, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
|
||||
secp256k1_ge q;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
secp256k1_pubkey_load(ctx, &q, pubkey);
|
||||
return secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32);
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *sig64, const unsigned char *msg32) {
|
||||
secp256k1_ge q;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
|
||||
if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &q, secp256k1_schnorr_msghash_sha256, msg32)) {
|
||||
secp256k1_pubkey_save(pubkey, &q);
|
||||
return 1;
|
||||
} else {
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_generate_nonce_pair(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, unsigned char *privnonce32, const unsigned char *sec32, const unsigned char *msg32, secp256k1_nonce_function noncefp, const void* noncedata) {
|
||||
int count = 0;
|
||||
int ret = 1;
|
||||
secp256k1_gej Qj;
|
||||
secp256k1_ge Q;
|
||||
secp256k1_scalar sec;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sec32 != NULL);
|
||||
ARG_CHECK(pubnonce != NULL);
|
||||
ARG_CHECK(privnonce32 != NULL);
|
||||
|
||||
if (noncefp == NULL) {
|
||||
noncefp = secp256k1_nonce_function_default;
|
||||
}
|
||||
|
||||
do {
|
||||
int overflow;
|
||||
ret = noncefp(privnonce32, sec32, msg32, secp256k1_schnorr_algo16, (void*)noncedata, count++);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&sec, privnonce32, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&sec)) {
|
||||
continue;
|
||||
}
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sec);
|
||||
secp256k1_ge_set_gej(&Q, &Qj);
|
||||
|
||||
secp256k1_pubkey_save(pubnonce, &Q);
|
||||
break;
|
||||
} while(1);
|
||||
|
||||
secp256k1_scalar_clear(&sec);
|
||||
if (!ret) {
|
||||
memset(pubnonce, 0, sizeof(*pubnonce));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_schnorr_partial_sign(const secp256k1_context* ctx, unsigned char *sig64, const unsigned char *msg32, const unsigned char *sec32, const secp256k1_pubkey *pubnonce_others, const unsigned char *secnonce32) {
|
||||
int overflow = 0;
|
||||
secp256k1_scalar sec, non;
|
||||
secp256k1_ge pubnon;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
ARG_CHECK(msg32 != NULL);
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(sec32 != NULL);
|
||||
ARG_CHECK(secnonce32 != NULL);
|
||||
ARG_CHECK(pubnonce_others != NULL);
|
||||
|
||||
secp256k1_scalar_set_b32(&sec, sec32, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&sec)) {
|
||||
return -1;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&non, secnonce32, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&non)) {
|
||||
return -1;
|
||||
}
|
||||
secp256k1_pubkey_load(ctx, &pubnon, pubnonce_others);
|
||||
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) {
|
||||
ARG_CHECK(sig64 != NULL);
|
||||
ARG_CHECK(n >= 1);
|
||||
ARG_CHECK(sig64sin != NULL);
|
||||
return secp256k1_schnorr_sig_combine(sig64, n, sig64sin);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,20 +0,0 @@
|
|||
/***********************************************************************
|
||||
* 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. *
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_MODULE_SCHNORR_H_
|
||||
#define _SECP256K1_MODULE_SCHNORR_H_
|
||||
|
||||
#include "scalar.h"
|
||||
#include "group.h"
|
||||
|
||||
typedef void (*secp256k1_schnorr_msghash)(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
|
@ -1,207 +0,0 @@
|
|||
/***********************************************************************
|
||||
* 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. *
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_SCHNORR_IMPL_H_
|
||||
#define _SECP256K1_SCHNORR_IMPL_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "schnorr.h"
|
||||
#include "num.h"
|
||||
#include "field.h"
|
||||
#include "group.h"
|
||||
#include "ecmult.h"
|
||||
#include "ecmult_gen.h"
|
||||
|
||||
/**
|
||||
* Custom Schnorr-based signature scheme. They support multiparty signing, public key
|
||||
* recovery and batch validation.
|
||||
*
|
||||
* Rationale for verifying R's y coordinate:
|
||||
* In order to support batch validation and public key recovery, the full R point must
|
||||
* be known to verifiers, rather than just its x coordinate. In order to not risk
|
||||
* being more strict in batch validation than normal validation, validators must be
|
||||
* required to reject signatures with incorrect y coordinate. This is only possible
|
||||
* by including a (relatively slow) field inverse, or a field square root. However,
|
||||
* batch validation offers potentially much higher benefits than this cost.
|
||||
*
|
||||
* Rationale for having an implicit y coordinate oddness:
|
||||
* If we commit to having the full R point known to verifiers, there are two mechanism.
|
||||
* Either include its oddness in the signature, or give it an implicit fixed value.
|
||||
* As the R y coordinate can be flipped by a simple negation of the nonce, we choose the
|
||||
* latter, as it comes with nearly zero impact on signing or validation performance, and
|
||||
* saves a byte in the signature.
|
||||
*
|
||||
* Signing:
|
||||
* Inputs: 32-byte message m, 32-byte scalar key x (!=0), 32-byte scalar nonce k (!=0)
|
||||
*
|
||||
* Compute point R = k * G. Reject nonce if R's y coordinate is odd (or negate nonce).
|
||||
* Compute 32-byte r, the serialization of R's x coordinate.
|
||||
* Compute scalar h = Hash(r || m). Reject nonce if h == 0 or h >= order.
|
||||
* Compute scalar s = k - h * x.
|
||||
* The signature is (r, s).
|
||||
*
|
||||
*
|
||||
* Verification:
|
||||
* Inputs: 32-byte message m, public key point Q, signature: (32-byte r, scalar s)
|
||||
*
|
||||
* Signature is invalid if s >= order.
|
||||
* Signature is invalid if r >= p.
|
||||
* Compute scalar h = Hash(r || m). Signature is invalid if h == 0 or h >= order.
|
||||
* Option 1 (faster for single verification):
|
||||
* Compute point R = h * Q + s * G. Signature is invalid if R is infinity or R's y coordinate is odd.
|
||||
* Signature is valid if the serialization of R's x coordinate equals r.
|
||||
* Option 2 (allows batch validation and pubkey recovery):
|
||||
* Decompress x coordinate r into point R, with odd y coordinate. Fail if R is not on the curve.
|
||||
* Signature is valid if R + h * Q + s * G == 0.
|
||||
*/
|
||||
|
||||
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) {
|
||||
secp256k1_gej Rj;
|
||||
secp256k1_ge Ra;
|
||||
unsigned char h32[32];
|
||||
secp256k1_scalar h, s;
|
||||
int overflow;
|
||||
secp256k1_scalar n;
|
||||
|
||||
if (secp256k1_scalar_is_zero(key) || secp256k1_scalar_is_zero(nonce)) {
|
||||
return 0;
|
||||
}
|
||||
n = *nonce;
|
||||
|
||||
secp256k1_ecmult_gen(ctx, &Rj, &n);
|
||||
if (pubnonce != NULL) {
|
||||
secp256k1_gej_add_ge(&Rj, &Rj, pubnonce);
|
||||
}
|
||||
secp256k1_ge_set_gej(&Ra, &Rj);
|
||||
secp256k1_fe_normalize(&Ra.y);
|
||||
if (secp256k1_fe_is_odd(&Ra.y)) {
|
||||
/* R's y coordinate is odd, which is not allowed (see rationale above).
|
||||
Force it to be even by negating the nonce. Note that this even works
|
||||
for multiparty signing, as the R point is known to all participants,
|
||||
which can all decide to flip the sign in unison, resulting in the
|
||||
overall R point to be negated too. */
|
||||
secp256k1_scalar_negate(&n, &n);
|
||||
}
|
||||
secp256k1_fe_normalize(&Ra.x);
|
||||
secp256k1_fe_get_b32(sig64, &Ra.x);
|
||||
hash(h32, sig64, msg32);
|
||||
overflow = 0;
|
||||
secp256k1_scalar_set_b32(&h, h32, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&h)) {
|
||||
secp256k1_scalar_clear(&n);
|
||||
return 0;
|
||||
}
|
||||
secp256k1_scalar_mul(&s, &h, key);
|
||||
secp256k1_scalar_negate(&s, &s);
|
||||
secp256k1_scalar_add(&s, &s, &n);
|
||||
secp256k1_scalar_clear(&n);
|
||||
secp256k1_scalar_get_b32(sig64 + 32, &s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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) {
|
||||
secp256k1_gej Qj, Rj;
|
||||
secp256k1_ge Ra;
|
||||
secp256k1_fe Rx;
|
||||
secp256k1_scalar h, s;
|
||||
unsigned char hh[32];
|
||||
int overflow;
|
||||
|
||||
if (secp256k1_ge_is_infinity(pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
hash(hh, sig64, msg32);
|
||||
overflow = 0;
|
||||
secp256k1_scalar_set_b32(&h, hh, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&h)) {
|
||||
return 0;
|
||||
}
|
||||
overflow = 0;
|
||||
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_fe_set_b32(&Rx, sig64)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_gej_set_ge(&Qj, pubkey);
|
||||
secp256k1_ecmult(ctx, &Rj, &Qj, &h, &s);
|
||||
if (secp256k1_gej_is_infinity(&Rj)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_gej_var(&Ra, &Rj);
|
||||
secp256k1_fe_normalize_var(&Ra.y);
|
||||
if (secp256k1_fe_is_odd(&Ra.y)) {
|
||||
return 0;
|
||||
}
|
||||
return secp256k1_fe_equal_var(&Rx, &Ra.x);
|
||||
}
|
||||
|
||||
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) {
|
||||
secp256k1_gej Qj, Rj;
|
||||
secp256k1_ge Ra;
|
||||
secp256k1_fe Rx;
|
||||
secp256k1_scalar h, s;
|
||||
unsigned char hh[32];
|
||||
int overflow;
|
||||
|
||||
hash(hh, sig64, msg32);
|
||||
overflow = 0;
|
||||
secp256k1_scalar_set_b32(&h, hh, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&h)) {
|
||||
return 0;
|
||||
}
|
||||
overflow = 0;
|
||||
secp256k1_scalar_set_b32(&s, sig64 + 32, &overflow);
|
||||
if (overflow) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_fe_set_b32(&Rx, sig64)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_ge_set_xo_var(&Ra, &Rx, 0)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_gej_set_ge(&Rj, &Ra);
|
||||
secp256k1_scalar_inverse_var(&h, &h);
|
||||
secp256k1_scalar_negate(&s, &s);
|
||||
secp256k1_scalar_mul(&s, &s, &h);
|
||||
secp256k1_ecmult(ctx, &Qj, &Rj, &h, &s);
|
||||
if (secp256k1_gej_is_infinity(&Qj)) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_gej(pubkey, &Qj);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int secp256k1_schnorr_sig_combine(unsigned char *sig64, int n, const unsigned char * const *sig64ins) {
|
||||
secp256k1_scalar s = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
secp256k1_scalar si;
|
||||
int overflow;
|
||||
secp256k1_scalar_set_b32(&si, sig64ins[i] + 32, &overflow);
|
||||
if (overflow) {
|
||||
return -1;
|
||||
}
|
||||
if (i) {
|
||||
if (memcmp(sig64ins[i - 1], sig64ins[i], 32) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
secp256k1_scalar_add(&s, &s, &si);
|
||||
}
|
||||
if (secp256k1_scalar_is_zero(&s)) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(sig64, sig64ins[0], 32);
|
||||
secp256k1_scalar_get_b32(sig64 + 32, &s);
|
||||
secp256k1_scalar_clear(&s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,175 +0,0 @@
|
|||
/**********************************************************************
|
||||
* 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.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_MODULE_SCHNORR_TESTS
|
||||
#define SECP256K1_MODULE_SCHNORR_TESTS
|
||||
|
||||
#include "include/secp256k1_schnorr.h"
|
||||
|
||||
void test_schnorr_end_to_end(void) {
|
||||
unsigned char privkey[32];
|
||||
unsigned char message[32];
|
||||
unsigned char schnorr_signature[64];
|
||||
secp256k1_pubkey pubkey, recpubkey;
|
||||
|
||||
/* Generate a random key and message. */
|
||||
{
|
||||
secp256k1_scalar key;
|
||||
random_scalar_order_test(&key);
|
||||
secp256k1_scalar_get_b32(privkey, &key);
|
||||
secp256k1_rand256_test(message);
|
||||
}
|
||||
|
||||
/* Construct and verify corresponding public key. */
|
||||
CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1);
|
||||
|
||||
/* Schnorr sign. */
|
||||
CHECK(secp256k1_schnorr_sign(ctx, schnorr_signature, message, privkey, NULL, NULL) == 1);
|
||||
CHECK(secp256k1_schnorr_verify(ctx, schnorr_signature, message, &pubkey) == 1);
|
||||
CHECK(secp256k1_schnorr_recover(ctx, &recpubkey, schnorr_signature, message) == 1);
|
||||
CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0);
|
||||
/* Destroy signature and verify again. */
|
||||
schnorr_signature[secp256k1_rand32() % 64] += 1 + (secp256k1_rand32() % 255);
|
||||
CHECK(secp256k1_schnorr_verify(ctx, schnorr_signature, message, &pubkey) == 0);
|
||||
CHECK(secp256k1_schnorr_recover(ctx, &recpubkey, schnorr_signature, message) != 1 ||
|
||||
memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0);
|
||||
}
|
||||
|
||||
/** Horribly broken hash function. Do not use for anything but tests. */
|
||||
void test_schnorr_hash(unsigned char *h32, const unsigned char *r32, const unsigned char *msg32) {
|
||||
int i;
|
||||
for (i = 0; i < 32; i++) {
|
||||
h32[i] = r32[i] ^ msg32[i];
|
||||
}
|
||||
}
|
||||
|
||||
void test_schnorr_sign_verify(void) {
|
||||
unsigned char msg32[32];
|
||||
unsigned char sig64[3][64];
|
||||
secp256k1_gej pubkeyj[3];
|
||||
secp256k1_ge pubkey[3];
|
||||
secp256k1_scalar nonce[3], key[3];
|
||||
int i = 0;
|
||||
int k;
|
||||
|
||||
secp256k1_rand256_test(msg32);
|
||||
|
||||
for (k = 0; k < 3; k++) {
|
||||
random_scalar_order_test(&key[k]);
|
||||
|
||||
do {
|
||||
random_scalar_order_test(&nonce[k]);
|
||||
if (secp256k1_schnorr_sig_sign(&ctx->ecmult_gen_ctx, sig64[k], &key[k], &nonce[k], NULL, &test_schnorr_hash, msg32)) {
|
||||
break;
|
||||
}
|
||||
} while(1);
|
||||
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubkeyj[k], &key[k]);
|
||||
secp256k1_ge_set_gej_var(&pubkey[k], &pubkeyj[k]);
|
||||
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32));
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
int pos = secp256k1_rand32() % 64;
|
||||
int mod = 1 + (secp256k1_rand32() % 255);
|
||||
sig64[k][pos] ^= mod;
|
||||
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64[k], &pubkey[k], &test_schnorr_hash, msg32) == 0);
|
||||
sig64[k][pos] ^= mod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_schnorr_threshold(void) {
|
||||
unsigned char msg[32];
|
||||
unsigned char sec[5][32];
|
||||
secp256k1_pubkey pub[5];
|
||||
unsigned char nonce[5][32];
|
||||
secp256k1_pubkey pubnonce[5];
|
||||
unsigned char sig[5][64];
|
||||
const unsigned char* sigs[5];
|
||||
unsigned char allsig[64];
|
||||
const secp256k1_pubkey* pubs[5];
|
||||
secp256k1_pubkey allpub;
|
||||
int n, i;
|
||||
int damage;
|
||||
int ret = 0;
|
||||
|
||||
damage = (secp256k1_rand32() % 2) ? (1 + (secp256k1_rand32() % 4)) : 0;
|
||||
secp256k1_rand256_test(msg);
|
||||
n = 2 + (secp256k1_rand32() % 4);
|
||||
for (i = 0; i < n; i++) {
|
||||
do {
|
||||
secp256k1_rand256_test(sec[i]);
|
||||
} while (!secp256k1_ec_seckey_verify(ctx, sec[i]));
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pub[i], sec[i]));
|
||||
CHECK(secp256k1_schnorr_generate_nonce_pair(ctx, &pubnonce[i], nonce[i], msg, sec[i], NULL, NULL));
|
||||
pubs[i] = &pub[i];
|
||||
}
|
||||
if (damage == 1) {
|
||||
nonce[secp256k1_rand32() % n][secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
|
||||
} else if (damage == 2) {
|
||||
sec[secp256k1_rand32() % n][secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
|
||||
}
|
||||
for (i = 0; i < n; i++) {
|
||||
secp256k1_pubkey allpubnonce;
|
||||
const secp256k1_pubkey *pubnonces[4];
|
||||
int j;
|
||||
for (j = 0; j < i; j++) {
|
||||
pubnonces[j] = &pubnonce[j];
|
||||
}
|
||||
for (j = i + 1; j < n; j++) {
|
||||
pubnonces[j - 1] = &pubnonce[j];
|
||||
}
|
||||
CHECK(secp256k1_ec_pubkey_combine(ctx, &allpubnonce, pubnonces, n - 1));
|
||||
ret |= (secp256k1_schnorr_partial_sign(ctx, sig[i], msg, sec[i], &allpubnonce, nonce[i]) != 1) * 1;
|
||||
sigs[i] = sig[i];
|
||||
}
|
||||
if (damage == 3) {
|
||||
sig[secp256k1_rand32() % n][secp256k1_rand32() % 64] ^= 1 + (secp256k1_rand32() % 255);
|
||||
}
|
||||
ret |= (secp256k1_ec_pubkey_combine(ctx, &allpub, pubs, n) != 1) * 2;
|
||||
if ((ret & 1) == 0) {
|
||||
ret |= (secp256k1_schnorr_partial_combine(ctx, allsig, sigs, n) != 1) * 4;
|
||||
}
|
||||
if (damage == 4) {
|
||||
allsig[secp256k1_rand32() % 32] ^= 1 + (secp256k1_rand32() % 255);
|
||||
}
|
||||
if ((ret & 7) == 0) {
|
||||
ret |= (secp256k1_schnorr_verify(ctx, allsig, msg, &allpub) != 1) * 8;
|
||||
}
|
||||
CHECK((ret == 0) == (damage == 0));
|
||||
}
|
||||
|
||||
void test_schnorr_recovery(void) {
|
||||
unsigned char msg32[32];
|
||||
unsigned char sig64[64];
|
||||
secp256k1_ge Q;
|
||||
|
||||
secp256k1_rand256_test(msg32);
|
||||
secp256k1_rand256_test(sig64);
|
||||
secp256k1_rand256_test(sig64 + 32);
|
||||
if (secp256k1_schnorr_sig_recover(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1) {
|
||||
CHECK(secp256k1_schnorr_sig_verify(&ctx->ecmult_ctx, sig64, &Q, &test_schnorr_hash, msg32) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
void run_schnorr_tests(void) {
|
||||
int i;
|
||||
for (i = 0; i < 32*count; i++) {
|
||||
test_schnorr_end_to_end();
|
||||
}
|
||||
for (i = 0; i < 32 * count; i++) {
|
||||
test_schnorr_sign_verify();
|
||||
}
|
||||
for (i = 0; i < 16 * count; i++) {
|
||||
test_schnorr_recovery();
|
||||
}
|
||||
for (i = 0; i < 10 * count; i++) {
|
||||
test_schnorr_threshold();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -32,6 +32,9 @@ static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsi
|
|||
/** Compute a modular inverse. The input must be less than the modulus. */
|
||||
static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m);
|
||||
|
||||
/** Compute the jacobi symbol (a|b). b must be positive and odd. */
|
||||
static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b);
|
||||
|
||||
/** Compare the absolute value of two numbers. */
|
||||
static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b);
|
||||
|
||||
|
@ -57,6 +60,9 @@ static void secp256k1_num_shift(secp256k1_num *r, int bits);
|
|||
/** Check whether a number is zero. */
|
||||
static int secp256k1_num_is_zero(const secp256k1_num *a);
|
||||
|
||||
/** Check whether a number is one. */
|
||||
static int secp256k1_num_is_one(const secp256k1_num *a);
|
||||
|
||||
/** Check whether a number is strictly negative. */
|
||||
static int secp256k1_num_is_neg(const secp256k1_num *a);
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ static void secp256k1_num_add_abs(secp256k1_num *r, const secp256k1_num *a, cons
|
|||
|
||||
static void secp256k1_num_sub_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) {
|
||||
mp_limb_t c = mpn_sub(r->data, a->data, a->limbs, b->data, b->limbs);
|
||||
(void)c;
|
||||
VERIFY_CHECK(c == 0);
|
||||
r->limbs = a->limbs;
|
||||
while (r->limbs > 1 && r->data[r->limbs-1]==0) {
|
||||
|
@ -125,6 +126,7 @@ static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a,
|
|||
}
|
||||
sn = NUM_LIMBS+1;
|
||||
gn = mpn_gcdext(g, r->data, &sn, u, m->limbs, v, m->limbs);
|
||||
(void)gn;
|
||||
VERIFY_CHECK(gn == 1);
|
||||
VERIFY_CHECK(g[0] == 1);
|
||||
r->neg = a->neg ^ m->neg;
|
||||
|
@ -142,6 +144,32 @@ static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a,
|
|||
memset(v, 0, sizeof(v));
|
||||
}
|
||||
|
||||
static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) {
|
||||
int ret;
|
||||
mpz_t ga, gb;
|
||||
secp256k1_num_sanity(a);
|
||||
secp256k1_num_sanity(b);
|
||||
VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1));
|
||||
|
||||
mpz_inits(ga, gb, NULL);
|
||||
|
||||
mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data);
|
||||
mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data);
|
||||
if (a->neg) {
|
||||
mpz_neg(ga, ga);
|
||||
}
|
||||
|
||||
ret = mpz_jacobi(ga, gb);
|
||||
|
||||
mpz_clears(ga, gb, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int secp256k1_num_is_one(const secp256k1_num *a) {
|
||||
return (a->limbs == 1 && a->data[0] == 1);
|
||||
}
|
||||
|
||||
static int secp256k1_num_is_zero(const secp256k1_num *a) {
|
||||
return (a->limbs == 1 && a->data[0] == 0);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
#include "libsecp256k1-config.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_SCALAR_4X64)
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
#include "scalar_low.h"
|
||||
#elif defined(USE_SCALAR_4X64)
|
||||
#include "scalar_4x64.h"
|
||||
#elif defined(USE_SCALAR_8X32)
|
||||
#include "scalar_8x32.h"
|
||||
|
|
|
@ -282,8 +282,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"movq 56(%%rsi), %%r14\n"
|
||||
/* Initialize r8,r9,r10 */
|
||||
"movq 0(%%rsi), %%r8\n"
|
||||
"movq $0, %%r9\n"
|
||||
"movq $0, %%r10\n"
|
||||
"xorq %%r9, %%r9\n"
|
||||
"xorq %%r10, %%r10\n"
|
||||
/* (r8,r9) += n0 * c0 */
|
||||
"movq %8, %%rax\n"
|
||||
"mulq %%r11\n"
|
||||
|
@ -291,7 +291,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"adcq %%rdx, %%r9\n"
|
||||
/* extract m0 */
|
||||
"movq %%r8, %q0\n"
|
||||
"movq $0, %%r8\n"
|
||||
"xorq %%r8, %%r8\n"
|
||||
/* (r9,r10) += l1 */
|
||||
"addq 8(%%rsi), %%r9\n"
|
||||
"adcq $0, %%r10\n"
|
||||
|
@ -309,7 +309,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"adcq $0, %%r8\n"
|
||||
/* extract m1 */
|
||||
"movq %%r9, %q1\n"
|
||||
"movq $0, %%r9\n"
|
||||
"xorq %%r9, %%r9\n"
|
||||
/* (r10,r8,r9) += l2 */
|
||||
"addq 16(%%rsi), %%r10\n"
|
||||
"adcq $0, %%r8\n"
|
||||
|
@ -332,7 +332,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"adcq $0, %%r9\n"
|
||||
/* extract m2 */
|
||||
"movq %%r10, %q2\n"
|
||||
"movq $0, %%r10\n"
|
||||
"xorq %%r10, %%r10\n"
|
||||
/* (r8,r9,r10) += l3 */
|
||||
"addq 24(%%rsi), %%r8\n"
|
||||
"adcq $0, %%r9\n"
|
||||
|
@ -355,7 +355,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"adcq $0, %%r10\n"
|
||||
/* extract m3 */
|
||||
"movq %%r8, %q3\n"
|
||||
"movq $0, %%r8\n"
|
||||
"xorq %%r8, %%r8\n"
|
||||
/* (r9,r10,r8) += n3 * c1 */
|
||||
"movq %9, %%rax\n"
|
||||
"mulq %%r14\n"
|
||||
|
@ -387,8 +387,8 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"movq %q11, %%r13\n"
|
||||
/* Initialize (r8,r9,r10) */
|
||||
"movq %q5, %%r8\n"
|
||||
"movq $0, %%r9\n"
|
||||
"movq $0, %%r10\n"
|
||||
"xorq %%r9, %%r9\n"
|
||||
"xorq %%r10, %%r10\n"
|
||||
/* (r8,r9) += m4 * c0 */
|
||||
"movq %12, %%rax\n"
|
||||
"mulq %%r11\n"
|
||||
|
@ -396,7 +396,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"adcq %%rdx, %%r9\n"
|
||||
/* extract p0 */
|
||||
"movq %%r8, %q0\n"
|
||||
"movq $0, %%r8\n"
|
||||
"xorq %%r8, %%r8\n"
|
||||
/* (r9,r10) += m1 */
|
||||
"addq %q6, %%r9\n"
|
||||
"adcq $0, %%r10\n"
|
||||
|
@ -414,7 +414,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"adcq $0, %%r8\n"
|
||||
/* extract p1 */
|
||||
"movq %%r9, %q1\n"
|
||||
"movq $0, %%r9\n"
|
||||
"xorq %%r9, %%r9\n"
|
||||
/* (r10,r8,r9) += m2 */
|
||||
"addq %q7, %%r10\n"
|
||||
"adcq $0, %%r8\n"
|
||||
|
@ -472,7 +472,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"movq %%rax, 0(%q6)\n"
|
||||
/* Move to (r8,r9) */
|
||||
"movq %%rdx, %%r8\n"
|
||||
"movq $0, %%r9\n"
|
||||
"xorq %%r9, %%r9\n"
|
||||
/* (r8,r9) += p1 */
|
||||
"addq %q2, %%r8\n"
|
||||
"adcq $0, %%r9\n"
|
||||
|
@ -483,7 +483,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"adcq %%rdx, %%r9\n"
|
||||
/* Extract r1 */
|
||||
"movq %%r8, 8(%q6)\n"
|
||||
"movq $0, %%r8\n"
|
||||
"xorq %%r8, %%r8\n"
|
||||
/* (r9,r8) += p4 */
|
||||
"addq %%r10, %%r9\n"
|
||||
"adcq $0, %%r8\n"
|
||||
|
@ -492,7 +492,7 @@ static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l)
|
|||
"adcq $0, %%r8\n"
|
||||
/* Extract r2 */
|
||||
"movq %%r9, 16(%q6)\n"
|
||||
"movq $0, %%r9\n"
|
||||
"xorq %%r9, %%r9\n"
|
||||
/* (r8,r9) += p3 */
|
||||
"addq %q4, %%r8\n"
|
||||
"adcq $0, %%r9\n"
|
||||
|
@ -912,6 +912,7 @@ static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a)
|
|||
secp256k1_scalar_reduce_512(r, l);
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
|
||||
r1->d[0] = a->d[0];
|
||||
r1->d[1] = a->d[1];
|
||||
|
@ -922,6 +923,7 @@ static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r
|
|||
r2->d[2] = 0;
|
||||
r2->d[3] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
|
||||
return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0;
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#ifndef _SECP256K1_SCALAR_IMPL_H_
|
||||
#define _SECP256K1_SCALAR_IMPL_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "group.h"
|
||||
#include "scalar.h"
|
||||
|
||||
|
@ -16,7 +14,9 @@
|
|||
#include "libsecp256k1-config.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_SCALAR_4X64)
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
#include "scalar_low_impl.h"
|
||||
#elif defined(USE_SCALAR_4X64)
|
||||
#include "scalar_4x64_impl.h"
|
||||
#elif defined(USE_SCALAR_8X32)
|
||||
#include "scalar_8x32_impl.h"
|
||||
|
@ -33,17 +33,37 @@ static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a
|
|||
|
||||
/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */
|
||||
static void secp256k1_scalar_order_get_num(secp256k1_num *r) {
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
static const unsigned char order[32] = {
|
||||
0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER
|
||||
};
|
||||
#else
|
||||
static const unsigned char order[32] = {
|
||||
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
|
||||
};
|
||||
#endif
|
||||
secp256k1_num_set_bin(r, order, 32);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) {
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
int i;
|
||||
*r = 0;
|
||||
for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++)
|
||||
if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1)
|
||||
*r = i;
|
||||
/* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus
|
||||
* have a composite group order; fix it in exhaustive_tests.c). */
|
||||
VERIFY_CHECK(*r != 0);
|
||||
}
|
||||
#else
|
||||
secp256k1_scalar *t;
|
||||
int i;
|
||||
/* First compute x ^ (2^N - 1) for some values of N. */
|
||||
|
@ -235,9 +255,9 @@ static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar
|
|||
}
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
|
||||
/* d[0] is present and is the lowest word for all representations */
|
||||
return !(a->d[0] & 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) {
|
||||
#if defined(USE_SCALAR_INV_BUILTIN)
|
||||
|
@ -261,6 +281,18 @@ static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_sc
|
|||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
#if defined(EXHAUSTIVE_TEST_ORDER)
|
||||
/**
|
||||
* Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the
|
||||
* full case we don't bother making k1 and k2 be small, we just want them to be
|
||||
* nontrivial to get full test coverage for the exhaustive tests. We therefore
|
||||
* (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda.
|
||||
*/
|
||||
static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
|
||||
*r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER;
|
||||
*r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where
|
||||
* lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a,
|
||||
|
@ -333,5 +365,6 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar
|
|||
secp256k1_scalar_add(r1, r1, a);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2015 Andrew Poelstra *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_SCALAR_REPR_
|
||||
#define _SECP256K1_SCALAR_REPR_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** A scalar modulo the group order of the secp256k1 curve. */
|
||||
typedef uint32_t secp256k1_scalar;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,114 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2015 Andrew Poelstra *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_
|
||||
#define _SECP256K1_SCALAR_REPR_IMPL_H_
|
||||
|
||||
#include "scalar.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) {
|
||||
return !(*a & 1);
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; }
|
||||
SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; }
|
||||
|
||||
SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
|
||||
if (offset < 32)
|
||||
return ((*a >> offset) & ((((uint32_t)1) << count) - 1));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) {
|
||||
return secp256k1_scalar_get_bits(a, offset, count);
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; }
|
||||
|
||||
static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
|
||||
*r = (*a + *b) % EXHAUSTIVE_TEST_ORDER;
|
||||
return *r < *b;
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) {
|
||||
if (flag && bit < 32)
|
||||
*r += (1 << bit);
|
||||
#ifdef VERIFY
|
||||
VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) {
|
||||
const int base = 0x100 % EXHAUSTIVE_TEST_ORDER;
|
||||
int i;
|
||||
*r = 0;
|
||||
for (i = 0; i < 32; i++) {
|
||||
*r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
/* just deny overflow, it basically always happens */
|
||||
if (overflow) *overflow = 0;
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) {
|
||||
memset(bin, 0, 32);
|
||||
bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a;
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) {
|
||||
return *a == 0;
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) {
|
||||
if (*a == 0) {
|
||||
*r = 0;
|
||||
} else {
|
||||
*r = EXHAUSTIVE_TEST_ORDER - *a;
|
||||
}
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) {
|
||||
return *a == 1;
|
||||
}
|
||||
|
||||
static int secp256k1_scalar_is_high(const secp256k1_scalar *a) {
|
||||
return *a > EXHAUSTIVE_TEST_ORDER / 2;
|
||||
}
|
||||
|
||||
static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) {
|
||||
if (flag) secp256k1_scalar_negate(r, r);
|
||||
return flag ? -1 : 1;
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) {
|
||||
*r = (*a * *b) % EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
|
||||
static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) {
|
||||
int ret;
|
||||
VERIFY_CHECK(n > 0);
|
||||
VERIFY_CHECK(n < 16);
|
||||
ret = *r & ((1 << n) - 1);
|
||||
*r >>= n;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) {
|
||||
*r = (*a * *a) % EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
|
||||
static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) {
|
||||
*r1 = *a;
|
||||
*r2 = 0;
|
||||
}
|
||||
|
||||
SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) {
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -4,8 +4,6 @@
|
|||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#define SECP256K1_BUILD (1)
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
|
||||
#include "util.h"
|
||||
|
@ -62,13 +60,20 @@ secp256k1_context* secp256k1_context_create(unsigned int flags) {
|
|||
ret->illegal_callback = default_illegal_callback;
|
||||
ret->error_callback = default_error_callback;
|
||||
|
||||
if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) {
|
||||
secp256k1_callback_call(&ret->illegal_callback,
|
||||
"Invalid flags");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
secp256k1_ecmult_context_init(&ret->ecmult_ctx);
|
||||
secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx);
|
||||
|
||||
if (flags & SECP256K1_CONTEXT_SIGN) {
|
||||
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) {
|
||||
secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback);
|
||||
}
|
||||
if (flags & SECP256K1_CONTEXT_VERIFY) {
|
||||
if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) {
|
||||
secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback);
|
||||
}
|
||||
|
||||
|
@ -145,9 +150,11 @@ static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) {
|
|||
int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) {
|
||||
secp256k1_ge Q;
|
||||
|
||||
(void)ctx;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(pubkey != NULL);
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
ARG_CHECK(input != NULL);
|
||||
if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) {
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
return 0;
|
||||
}
|
||||
secp256k1_pubkey_save(pubkey, &Q);
|
||||
|
@ -157,10 +164,25 @@ 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;
|
||||
return (secp256k1_pubkey_load(ctx, &Q, pubkey) &&
|
||||
secp256k1_eckey_pubkey_serialize(&Q, output, outputlen, flags));
|
||||
VERIFY_CHECK(ctx != 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);
|
||||
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) {
|
||||
|
@ -190,7 +212,7 @@ static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const
|
|||
int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) {
|
||||
secp256k1_scalar r, s;
|
||||
|
||||
(void)ctx;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(input != NULL);
|
||||
|
||||
|
@ -203,10 +225,31 @@ int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_
|
|||
}
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64) {
|
||||
secp256k1_scalar r, s;
|
||||
int ret = 1;
|
||||
int overflow = 0;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
ARG_CHECK(input64 != NULL);
|
||||
|
||||
secp256k1_scalar_set_b32(&r, &input64[0], &overflow);
|
||||
ret &= !overflow;
|
||||
secp256k1_scalar_set_b32(&s, &input64[32], &overflow);
|
||||
ret &= !overflow;
|
||||
if (ret) {
|
||||
secp256k1_ecdsa_signature_save(sig, &r, &s);
|
||||
} else {
|
||||
memset(sig, 0, sizeof(*sig));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) {
|
||||
secp256k1_scalar r, s;
|
||||
|
||||
(void)ctx;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(output != NULL);
|
||||
ARG_CHECK(outputlen != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
|
@ -215,6 +258,38 @@ int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsign
|
|||
return secp256k1_ecdsa_sig_serialize(output, outputlen, &r, &s);
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) {
|
||||
secp256k1_scalar r, s;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(output64 != NULL);
|
||||
ARG_CHECK(sig != NULL);
|
||||
|
||||
secp256k1_ecdsa_signature_load(ctx, &r, &s, sig);
|
||||
secp256k1_scalar_get_b32(&output64[0], &r);
|
||||
secp256k1_scalar_get_b32(&output64[32], &s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) {
|
||||
secp256k1_scalar r, s;
|
||||
int ret = 0;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(sigin != NULL);
|
||||
|
||||
secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin);
|
||||
ret = secp256k1_scalar_is_high(&s);
|
||||
if (sigout != NULL) {
|
||||
if (ret) {
|
||||
secp256k1_scalar_negate(&s, &s);
|
||||
}
|
||||
secp256k1_ecdsa_signature_save(sigout, &r, &s);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
|
||||
secp256k1_ge q;
|
||||
secp256k1_scalar r, s;
|
||||
|
@ -227,7 +302,8 @@ int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_s
|
|||
|
||||
secp256k1_scalar_set_b32(&m, msg32, NULL);
|
||||
secp256k1_ecdsa_signature_load(ctx, &r, &s, sig);
|
||||
return (secp256k1_pubkey_load(ctx, &q, pubkey) &&
|
||||
return (!secp256k1_scalar_is_high(&s) &&
|
||||
secp256k1_pubkey_load(ctx, &q, pubkey) &&
|
||||
secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m));
|
||||
}
|
||||
|
||||
|
@ -239,8 +315,10 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
|
|||
/* We feed a byte array to the PRNG as input, consisting of:
|
||||
* - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d.
|
||||
* - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data.
|
||||
* - optionally 16 extra bytes with the algorithm name (the extra data bytes
|
||||
* are set to zeroes when not present, while the algorithm name is).
|
||||
* - optionally 16 extra bytes with the algorithm name.
|
||||
* Because the arguments have distinct fixed lengths it is not possible for
|
||||
* different argument mixtures to emulate each other and result in the same
|
||||
* nonces.
|
||||
*/
|
||||
memcpy(keydata, key32, 32);
|
||||
memcpy(keydata + 32, msg32, 32);
|
||||
|
@ -249,9 +327,8 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
|
|||
keylen = 96;
|
||||
}
|
||||
if (algo16 != NULL) {
|
||||
memset(keydata + keylen, 0, 96 - keylen);
|
||||
memcpy(keydata + 96, algo16, 16);
|
||||
keylen = 112;
|
||||
memcpy(keydata + keylen, algo16, 16);
|
||||
keylen += 16;
|
||||
}
|
||||
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen);
|
||||
memset(keydata, 0, sizeof(keydata));
|
||||
|
@ -282,16 +359,15 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
|
|||
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
|
||||
/* Fail if the secret key is invalid. */
|
||||
if (!overflow && !secp256k1_scalar_is_zero(&sec)) {
|
||||
unsigned char nonce32[32];
|
||||
unsigned int count = 0;
|
||||
secp256k1_scalar_set_b32(&msg, msg32, NULL);
|
||||
while (1) {
|
||||
unsigned char nonce32[32];
|
||||
ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count);
|
||||
if (!ret) {
|
||||
break;
|
||||
}
|
||||
secp256k1_scalar_set_b32(&non, nonce32, &overflow);
|
||||
memset(nonce32, 0, 32);
|
||||
if (!overflow && !secp256k1_scalar_is_zero(&non)) {
|
||||
if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) {
|
||||
break;
|
||||
|
@ -299,6 +375,7 @@ int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature
|
|||
}
|
||||
count++;
|
||||
}
|
||||
memset(nonce32, 0, 32);
|
||||
secp256k1_scalar_clear(&msg);
|
||||
secp256k1_scalar_clear(&non);
|
||||
secp256k1_scalar_clear(&sec);
|
||||
|
@ -317,7 +394,6 @@ int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char
|
|||
int overflow;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
(void)ctx;
|
||||
|
||||
secp256k1_scalar_set_b32(&sec, seckey, &overflow);
|
||||
ret = !overflow && !secp256k1_scalar_is_zero(&sec);
|
||||
|
@ -332,19 +408,19 @@ 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));
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
|
||||
secp256k1_ge_set_gej(&p, &pj);
|
||||
secp256k1_pubkey_save(pubkey, &p);
|
||||
secp256k1_scalar_clear(&sec);
|
||||
if (!ret) {
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
if (ret) {
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec);
|
||||
secp256k1_ge_set_gej(&p, &pj);
|
||||
secp256k1_pubkey_save(pubkey, &p);
|
||||
}
|
||||
secp256k1_scalar_clear(&sec);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -356,12 +432,12 @@ int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *
|
|||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
ARG_CHECK(tweak != NULL);
|
||||
(void)ctx;
|
||||
|
||||
secp256k1_scalar_set_b32(&term, tweak, &overflow);
|
||||
secp256k1_scalar_set_b32(&sec, seckey, NULL);
|
||||
|
||||
ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term);
|
||||
memset(seckey, 0, 32);
|
||||
if (ret) {
|
||||
secp256k1_scalar_get_b32(seckey, &sec);
|
||||
}
|
||||
|
@ -382,12 +458,13 @@ int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey
|
|||
ARG_CHECK(tweak != NULL);
|
||||
|
||||
secp256k1_scalar_set_b32(&term, tweak, &overflow);
|
||||
if (!overflow && secp256k1_pubkey_load(ctx, &p, pubkey)) {
|
||||
ret = secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term);
|
||||
if (ret) {
|
||||
ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey);
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
if (ret) {
|
||||
if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) {
|
||||
secp256k1_pubkey_save(pubkey, &p);
|
||||
} else {
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -402,11 +479,11 @@ int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *
|
|||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(seckey != NULL);
|
||||
ARG_CHECK(tweak != NULL);
|
||||
(void)ctx;
|
||||
|
||||
secp256k1_scalar_set_b32(&factor, tweak, &overflow);
|
||||
secp256k1_scalar_set_b32(&sec, seckey, NULL);
|
||||
ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor);
|
||||
memset(seckey, 0, 32);
|
||||
if (ret) {
|
||||
secp256k1_scalar_get_b32(seckey, &sec);
|
||||
}
|
||||
|
@ -427,48 +504,19 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey
|
|||
ARG_CHECK(tweak != NULL);
|
||||
|
||||
secp256k1_scalar_set_b32(&factor, tweak, &overflow);
|
||||
if (!overflow && secp256k1_pubkey_load(ctx, &p, pubkey)) {
|
||||
ret = secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor);
|
||||
if (ret) {
|
||||
ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey);
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
if (ret) {
|
||||
if (secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor)) {
|
||||
secp256k1_pubkey_save(pubkey, &p);
|
||||
} else {
|
||||
memset(pubkey, 0, sizeof(*pubkey));
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_ec_privkey_export(const secp256k1_context* ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *seckey, unsigned int flags) {
|
||||
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, flags);
|
||||
secp256k1_scalar_clear(&key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int secp256k1_ec_privkey_import(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;
|
||||
}
|
||||
|
||||
int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) {
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
|
||||
|
@ -476,12 +524,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);
|
||||
|
||||
|
@ -492,7 +541,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);
|
||||
|
|
|
@ -16,13 +16,23 @@
|
|||
/** Seed the pseudorandom number generator for testing. */
|
||||
SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16);
|
||||
|
||||
/** Generate a pseudorandom 32-bit number. */
|
||||
/** Generate a pseudorandom number in the range [0..2**32-1]. */
|
||||
static uint32_t secp256k1_rand32(void);
|
||||
|
||||
/** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or
|
||||
* more. */
|
||||
static uint32_t secp256k1_rand_bits(int bits);
|
||||
|
||||
/** Generate a pseudorandom number in the range [0..range-1]. */
|
||||
static uint32_t secp256k1_rand_int(uint32_t range);
|
||||
|
||||
/** Generate a pseudorandom 32-byte array. */
|
||||
static void secp256k1_rand256(unsigned char *b32);
|
||||
|
||||
/** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */
|
||||
static void secp256k1_rand256_test(unsigned char *b32);
|
||||
|
||||
/** Generate pseudorandom bytes with long sequences of zero and one bits. */
|
||||
static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**********************************************************************
|
||||
* Copyright (c) 2013, 2014 Pieter Wuille *
|
||||
* Copyright (c) 2013-2015 Pieter Wuille *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
@ -16,6 +16,8 @@
|
|||
static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng;
|
||||
static uint32_t secp256k1_test_rng_precomputed[8];
|
||||
static int secp256k1_test_rng_precomputed_used = 8;
|
||||
static uint64_t secp256k1_test_rng_integer;
|
||||
static int secp256k1_test_rng_integer_bits_left = 0;
|
||||
|
||||
SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) {
|
||||
secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16);
|
||||
|
@ -29,32 +31,80 @@ SECP256K1_INLINE static uint32_t secp256k1_rand32(void) {
|
|||
return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++];
|
||||
}
|
||||
|
||||
static uint32_t secp256k1_rand_bits(int bits) {
|
||||
uint32_t ret;
|
||||
if (secp256k1_test_rng_integer_bits_left < bits) {
|
||||
secp256k1_test_rng_integer |= (((uint64_t)secp256k1_rand32()) << secp256k1_test_rng_integer_bits_left);
|
||||
secp256k1_test_rng_integer_bits_left += 32;
|
||||
}
|
||||
ret = secp256k1_test_rng_integer;
|
||||
secp256k1_test_rng_integer >>= bits;
|
||||
secp256k1_test_rng_integer_bits_left -= bits;
|
||||
ret &= ((~((uint32_t)0)) >> (32 - bits));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t secp256k1_rand_int(uint32_t range) {
|
||||
/* We want a uniform integer between 0 and range-1, inclusive.
|
||||
* B is the smallest number such that range <= 2**B.
|
||||
* two mechanisms implemented here:
|
||||
* - generate B bits numbers until one below range is found, and return it
|
||||
* - find the largest multiple M of range that is <= 2**(B+A), generate B+A
|
||||
* bits numbers until one below M is found, and return it modulo range
|
||||
* The second mechanism consumes A more bits of entropy in every iteration,
|
||||
* but may need fewer iterations due to M being closer to 2**(B+A) then
|
||||
* range is to 2**B. The array below (indexed by B) contains a 0 when the
|
||||
* first mechanism is to be used, and the number A otherwise.
|
||||
*/
|
||||
static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0};
|
||||
uint32_t trange, mult;
|
||||
int bits = 0;
|
||||
if (range <= 1) {
|
||||
return 0;
|
||||
}
|
||||
trange = range - 1;
|
||||
while (trange > 0) {
|
||||
trange >>= 1;
|
||||
bits++;
|
||||
}
|
||||
if (addbits[bits]) {
|
||||
bits = bits + addbits[bits];
|
||||
mult = ((~((uint32_t)0)) >> (32 - bits)) / range;
|
||||
trange = range * mult;
|
||||
} else {
|
||||
trange = range;
|
||||
mult = 1;
|
||||
}
|
||||
while(1) {
|
||||
uint32_t x = secp256k1_rand_bits(bits);
|
||||
if (x < trange) {
|
||||
return (mult == 1) ? x : (x % range);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void secp256k1_rand256(unsigned char *b32) {
|
||||
secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32);
|
||||
}
|
||||
|
||||
static void secp256k1_rand256_test(unsigned char *b32) {
|
||||
int bits=0;
|
||||
uint64_t ent = 0;
|
||||
int entleft = 0;
|
||||
memset(b32, 0, 32);
|
||||
while (bits < 256) {
|
||||
static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) {
|
||||
size_t bits = 0;
|
||||
memset(bytes, 0, len);
|
||||
while (bits < len * 8) {
|
||||
int now;
|
||||
uint32_t val;
|
||||
if (entleft < 12) {
|
||||
ent |= ((uint64_t)secp256k1_rand32()) << entleft;
|
||||
entleft += 32;
|
||||
}
|
||||
now = 1 + ((ent % 64)*((ent >> 6) % 32)+16)/31;
|
||||
val = 1 & (ent >> 11);
|
||||
ent >>= 12;
|
||||
entleft -= 12;
|
||||
while (now > 0 && bits < 256) {
|
||||
b32[bits / 8] |= val << (bits % 8);
|
||||
now = 1 + (secp256k1_rand_bits(6) * secp256k1_rand_bits(5) + 16) / 31;
|
||||
val = secp256k1_rand_bits(1);
|
||||
while (now > 0 && bits < len * 8) {
|
||||
bytes[bits / 8] |= val << (bits % 8);
|
||||
now--;
|
||||
bits++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void secp256k1_rand256_test(unsigned char *b32) {
|
||||
secp256k1_rand_bytes_test(b32, 32);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,470 @@
|
|||
/***********************************************************************
|
||||
* Copyright (c) 2016 Andrew Poelstra *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
|
||||
**********************************************************************/
|
||||
|
||||
#if defined HAVE_CONFIG_H
|
||||
#include "libsecp256k1-config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#undef USE_ECMULT_STATIC_PRECOMPUTATION
|
||||
|
||||
#ifndef EXHAUSTIVE_TEST_ORDER
|
||||
/* see group_impl.h for allowable values */
|
||||
#define EXHAUSTIVE_TEST_ORDER 13
|
||||
#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */
|
||||
#endif
|
||||
|
||||
#include "include/secp256k1.h"
|
||||
#include "group.h"
|
||||
#include "secp256k1.c"
|
||||
#include "testrand_impl.h"
|
||||
|
||||
#ifdef ENABLE_MODULE_RECOVERY
|
||||
#include "src/modules/recovery/main_impl.h"
|
||||
#include "include/secp256k1_recovery.h"
|
||||
#endif
|
||||
|
||||
/** stolen from tests.c */
|
||||
void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) {
|
||||
CHECK(a->infinity == b->infinity);
|
||||
if (a->infinity) {
|
||||
return;
|
||||
}
|
||||
CHECK(secp256k1_fe_equal_var(&a->x, &b->x));
|
||||
CHECK(secp256k1_fe_equal_var(&a->y, &b->y));
|
||||
}
|
||||
|
||||
void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) {
|
||||
secp256k1_fe z2s;
|
||||
secp256k1_fe u1, u2, s1, s2;
|
||||
CHECK(a->infinity == b->infinity);
|
||||
if (a->infinity) {
|
||||
return;
|
||||
}
|
||||
/* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */
|
||||
secp256k1_fe_sqr(&z2s, &b->z);
|
||||
secp256k1_fe_mul(&u1, &a->x, &z2s);
|
||||
u2 = b->x; secp256k1_fe_normalize_weak(&u2);
|
||||
secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z);
|
||||
s2 = b->y; secp256k1_fe_normalize_weak(&s2);
|
||||
CHECK(secp256k1_fe_equal_var(&u1, &u2));
|
||||
CHECK(secp256k1_fe_equal_var(&s1, &s2));
|
||||
}
|
||||
|
||||
void random_fe(secp256k1_fe *x) {
|
||||
unsigned char bin[32];
|
||||
do {
|
||||
secp256k1_rand256(bin);
|
||||
if (secp256k1_fe_set_b32(x, bin)) {
|
||||
return;
|
||||
}
|
||||
} while(1);
|
||||
}
|
||||
/** END stolen from tests.c */
|
||||
|
||||
int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32,
|
||||
const unsigned char *key32, const unsigned char *algo16,
|
||||
void *data, unsigned int attempt) {
|
||||
secp256k1_scalar s;
|
||||
int *idata = data;
|
||||
(void)msg32;
|
||||
(void)key32;
|
||||
(void)algo16;
|
||||
/* Some nonces cannot be used because they'd cause s and/or r to be zero.
|
||||
* The signing function has retry logic here that just re-calls the nonce
|
||||
* function with an increased `attempt`. So if attempt > 0 this means we
|
||||
* need to change the nonce to avoid an infinite loop. */
|
||||
if (attempt > 0) {
|
||||
*idata = (*idata + 1) % EXHAUSTIVE_TEST_ORDER;
|
||||
}
|
||||
secp256k1_scalar_set_int(&s, *idata);
|
||||
secp256k1_scalar_get_b32(nonce32, &s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) {
|
||||
int i;
|
||||
for (i = 0; i < order; i++) {
|
||||
secp256k1_ge res;
|
||||
secp256k1_ge_mul_lambda(&res, &group[i]);
|
||||
ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
|
||||
int i, j;
|
||||
|
||||
/* Sanity-check (and check infinity functions) */
|
||||
CHECK(secp256k1_ge_is_infinity(&group[0]));
|
||||
CHECK(secp256k1_gej_is_infinity(&groupj[0]));
|
||||
for (i = 1; i < order; i++) {
|
||||
CHECK(!secp256k1_ge_is_infinity(&group[i]));
|
||||
CHECK(!secp256k1_gej_is_infinity(&groupj[i]));
|
||||
}
|
||||
|
||||
/* Check all addition formulae */
|
||||
for (j = 0; j < order; j++) {
|
||||
secp256k1_fe fe_inv;
|
||||
secp256k1_fe_inv(&fe_inv, &groupj[j].z);
|
||||
for (i = 0; i < order; i++) {
|
||||
secp256k1_ge zless_gej;
|
||||
secp256k1_gej tmp;
|
||||
/* add_var */
|
||||
secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL);
|
||||
ge_equals_gej(&group[(i + j) % order], &tmp);
|
||||
/* add_ge */
|
||||
if (j > 0) {
|
||||
secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]);
|
||||
ge_equals_gej(&group[(i + j) % order], &tmp);
|
||||
}
|
||||
/* add_ge_var */
|
||||
secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL);
|
||||
ge_equals_gej(&group[(i + j) % order], &tmp);
|
||||
/* add_zinv_var */
|
||||
zless_gej.infinity = groupj[j].infinity;
|
||||
zless_gej.x = groupj[j].x;
|
||||
zless_gej.y = groupj[j].y;
|
||||
secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv);
|
||||
ge_equals_gej(&group[(i + j) % order], &tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check doubling */
|
||||
for (i = 0; i < order; i++) {
|
||||
secp256k1_gej tmp;
|
||||
if (i > 0) {
|
||||
secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL);
|
||||
ge_equals_gej(&group[(2 * i) % order], &tmp);
|
||||
}
|
||||
secp256k1_gej_double_var(&tmp, &groupj[i], NULL);
|
||||
ge_equals_gej(&group[(2 * i) % order], &tmp);
|
||||
}
|
||||
|
||||
/* Check negation */
|
||||
for (i = 1; i < order; i++) {
|
||||
secp256k1_ge tmp;
|
||||
secp256k1_gej tmpj;
|
||||
secp256k1_ge_neg(&tmp, &group[i]);
|
||||
ge_equals_ge(&group[order - i], &tmp);
|
||||
secp256k1_gej_neg(&tmpj, &groupj[i]);
|
||||
ge_equals_gej(&group[order - i], &tmpj);
|
||||
}
|
||||
}
|
||||
|
||||
void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) {
|
||||
int i, j, r_log;
|
||||
for (r_log = 1; r_log < order; r_log++) {
|
||||
for (j = 0; j < order; j++) {
|
||||
for (i = 0; i < order; i++) {
|
||||
secp256k1_gej tmp;
|
||||
secp256k1_scalar na, ng;
|
||||
secp256k1_scalar_set_int(&na, i);
|
||||
secp256k1_scalar_set_int(&ng, j);
|
||||
|
||||
secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng);
|
||||
ge_equals_gej(&group[(i * r_log + j) % order], &tmp);
|
||||
|
||||
if (i > 0) {
|
||||
secp256k1_ecmult_const(&tmp, &group[i], &ng);
|
||||
ge_equals_gej(&group[(i * j) % order], &tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) {
|
||||
secp256k1_fe x;
|
||||
unsigned char x_bin[32];
|
||||
k %= EXHAUSTIVE_TEST_ORDER;
|
||||
x = group[k].x;
|
||||
secp256k1_fe_normalize(&x);
|
||||
secp256k1_fe_get_b32(x_bin, &x);
|
||||
secp256k1_scalar_set_b32(r, x_bin, NULL);
|
||||
}
|
||||
|
||||
void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
|
||||
int s, r, msg, key;
|
||||
for (s = 1; s < order; s++) {
|
||||
for (r = 1; r < order; r++) {
|
||||
for (msg = 1; msg < order; msg++) {
|
||||
for (key = 1; key < order; key++) {
|
||||
secp256k1_ge nonconst_ge;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_pubkey pk;
|
||||
secp256k1_scalar sk_s, msg_s, r_s, s_s;
|
||||
secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
|
||||
int k, should_verify;
|
||||
unsigned char msg32[32];
|
||||
|
||||
secp256k1_scalar_set_int(&s_s, s);
|
||||
secp256k1_scalar_set_int(&r_s, r);
|
||||
secp256k1_scalar_set_int(&msg_s, msg);
|
||||
secp256k1_scalar_set_int(&sk_s, key);
|
||||
|
||||
/* Verify by hand */
|
||||
/* Run through every k value that gives us this r and check that *one* works.
|
||||
* Note there could be none, there could be multiple, ECDSA is weird. */
|
||||
should_verify = 0;
|
||||
for (k = 0; k < order; k++) {
|
||||
secp256k1_scalar check_x_s;
|
||||
r_from_k(&check_x_s, group, k);
|
||||
if (r_s == check_x_s) {
|
||||
secp256k1_scalar_set_int(&s_times_k_s, k);
|
||||
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
|
||||
secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
|
||||
secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
|
||||
should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
|
||||
}
|
||||
}
|
||||
/* nb we have a "high s" rule */
|
||||
should_verify &= !secp256k1_scalar_is_high(&s_s);
|
||||
|
||||
/* Verify by calling verify */
|
||||
secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s);
|
||||
memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
|
||||
secp256k1_pubkey_save(&pk, &nonconst_ge);
|
||||
secp256k1_scalar_get_b32(msg32, &msg_s);
|
||||
CHECK(should_verify ==
|
||||
secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
|
||||
int i, j, k;
|
||||
|
||||
/* Loop */
|
||||
for (i = 1; i < order; i++) { /* message */
|
||||
for (j = 1; j < order; j++) { /* key */
|
||||
for (k = 1; k < order; k++) { /* nonce */
|
||||
const int starting_k = k;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_scalar sk, msg, r, s, expected_r;
|
||||
unsigned char sk32[32], msg32[32];
|
||||
secp256k1_scalar_set_int(&msg, i);
|
||||
secp256k1_scalar_set_int(&sk, j);
|
||||
secp256k1_scalar_get_b32(sk32, &sk);
|
||||
secp256k1_scalar_get_b32(msg32, &msg);
|
||||
|
||||
secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
|
||||
|
||||
secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
|
||||
/* Note that we compute expected_r *after* signing -- this is important
|
||||
* because our nonce-computing function function might change k during
|
||||
* signing. */
|
||||
r_from_k(&expected_r, group, k);
|
||||
CHECK(r == expected_r);
|
||||
CHECK((k * s) % order == (i + r * j) % order ||
|
||||
(k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
|
||||
|
||||
/* Overflow means we've tried every possible nonce */
|
||||
if (k < starting_k) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We would like to verify zero-knowledge here by counting how often every
|
||||
* possible (s, r) tuple appears, but because the group order is larger
|
||||
* than the field order, when coercing the x-values to scalar values, some
|
||||
* appear more often than others, so we are actually not zero-knowledge.
|
||||
* (This effect also appears in the real code, but the difference is on the
|
||||
* order of 1/2^128th the field order, so the deviation is not useful to a
|
||||
* computationally bounded attacker.)
|
||||
*/
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MODULE_RECOVERY
|
||||
void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
|
||||
int i, j, k;
|
||||
|
||||
/* Loop */
|
||||
for (i = 1; i < order; i++) { /* message */
|
||||
for (j = 1; j < order; j++) { /* key */
|
||||
for (k = 1; k < order; k++) { /* nonce */
|
||||
const int starting_k = k;
|
||||
secp256k1_fe r_dot_y_normalized;
|
||||
secp256k1_ecdsa_recoverable_signature rsig;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_scalar sk, msg, r, s, expected_r;
|
||||
unsigned char sk32[32], msg32[32];
|
||||
int expected_recid;
|
||||
int recid;
|
||||
secp256k1_scalar_set_int(&msg, i);
|
||||
secp256k1_scalar_set_int(&sk, j);
|
||||
secp256k1_scalar_get_b32(sk32, &sk);
|
||||
secp256k1_scalar_get_b32(msg32, &msg);
|
||||
|
||||
secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k);
|
||||
|
||||
/* Check directly */
|
||||
secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig);
|
||||
r_from_k(&expected_r, group, k);
|
||||
CHECK(r == expected_r);
|
||||
CHECK((k * s) % order == (i + r * j) % order ||
|
||||
(k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
|
||||
/* In computing the recid, there is an overflow condition that is disabled in
|
||||
* scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value
|
||||
* will exceed the group order, and our signing code always holds out for r
|
||||
* values that don't overflow, so with a proper overflow check the tests would
|
||||
* loop indefinitely. */
|
||||
r_dot_y_normalized = group[k].y;
|
||||
secp256k1_fe_normalize(&r_dot_y_normalized);
|
||||
/* Also the recovery id is flipped depending if we hit the low-s branch */
|
||||
if ((k * s) % order == (i + r * j) % order) {
|
||||
expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0;
|
||||
} else {
|
||||
expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1;
|
||||
}
|
||||
CHECK(recid == expected_recid);
|
||||
|
||||
/* Convert to a standard sig then check */
|
||||
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
|
||||
secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig);
|
||||
/* Note that we compute expected_r *after* signing -- this is important
|
||||
* because our nonce-computing function function might change k during
|
||||
* signing. */
|
||||
r_from_k(&expected_r, group, k);
|
||||
CHECK(r == expected_r);
|
||||
CHECK((k * s) % order == (i + r * j) % order ||
|
||||
(k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order);
|
||||
|
||||
/* Overflow means we've tried every possible nonce */
|
||||
if (k < starting_k) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) {
|
||||
/* This is essentially a copy of test_exhaustive_verify, with recovery added */
|
||||
int s, r, msg, key;
|
||||
for (s = 1; s < order; s++) {
|
||||
for (r = 1; r < order; r++) {
|
||||
for (msg = 1; msg < order; msg++) {
|
||||
for (key = 1; key < order; key++) {
|
||||
secp256k1_ge nonconst_ge;
|
||||
secp256k1_ecdsa_recoverable_signature rsig;
|
||||
secp256k1_ecdsa_signature sig;
|
||||
secp256k1_pubkey pk;
|
||||
secp256k1_scalar sk_s, msg_s, r_s, s_s;
|
||||
secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s;
|
||||
int recid = 0;
|
||||
int k, should_verify;
|
||||
unsigned char msg32[32];
|
||||
|
||||
secp256k1_scalar_set_int(&s_s, s);
|
||||
secp256k1_scalar_set_int(&r_s, r);
|
||||
secp256k1_scalar_set_int(&msg_s, msg);
|
||||
secp256k1_scalar_set_int(&sk_s, key);
|
||||
secp256k1_scalar_get_b32(msg32, &msg_s);
|
||||
|
||||
/* Verify by hand */
|
||||
/* Run through every k value that gives us this r and check that *one* works.
|
||||
* Note there could be none, there could be multiple, ECDSA is weird. */
|
||||
should_verify = 0;
|
||||
for (k = 0; k < order; k++) {
|
||||
secp256k1_scalar check_x_s;
|
||||
r_from_k(&check_x_s, group, k);
|
||||
if (r_s == check_x_s) {
|
||||
secp256k1_scalar_set_int(&s_times_k_s, k);
|
||||
secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s);
|
||||
secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s);
|
||||
secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s);
|
||||
should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s);
|
||||
}
|
||||
}
|
||||
/* nb we have a "high s" rule */
|
||||
should_verify &= !secp256k1_scalar_is_high(&s_s);
|
||||
|
||||
/* We would like to try recovering the pubkey and checking that it matches,
|
||||
* but pubkey recovery is impossible in the exhaustive tests (the reason
|
||||
* being that there are 12 nonzero r values, 12 nonzero points, and no
|
||||
* overlap between the sets, so there are no valid signatures). */
|
||||
|
||||
/* Verify by converting to a standard signature and calling verify */
|
||||
secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid);
|
||||
secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig);
|
||||
memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge));
|
||||
secp256k1_pubkey_save(&pk, &nonconst_ge);
|
||||
CHECK(should_verify ==
|
||||
secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
int i;
|
||||
secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER];
|
||||
secp256k1_ge group[EXHAUSTIVE_TEST_ORDER];
|
||||
|
||||
/* Build context */
|
||||
secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
|
||||
|
||||
/* TODO set z = 1, then do num_tests runs with random z values */
|
||||
|
||||
/* Generate the entire group */
|
||||
secp256k1_gej_set_infinity(&groupj[0]);
|
||||
secp256k1_ge_set_gej(&group[0], &groupj[0]);
|
||||
for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) {
|
||||
/* Set a different random z-value for each Jacobian point */
|
||||
secp256k1_fe z;
|
||||
random_fe(&z);
|
||||
|
||||
secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g);
|
||||
secp256k1_ge_set_gej(&group[i], &groupj[i]);
|
||||
secp256k1_gej_rescale(&groupj[i], &z);
|
||||
|
||||
/* Verify against ecmult_gen */
|
||||
{
|
||||
secp256k1_scalar scalar_i;
|
||||
secp256k1_gej generatedj;
|
||||
secp256k1_ge generated;
|
||||
|
||||
secp256k1_scalar_set_int(&scalar_i, i);
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i);
|
||||
secp256k1_ge_set_gej(&generated, &generatedj);
|
||||
|
||||
CHECK(group[i].infinity == 0);
|
||||
CHECK(generated.infinity == 0);
|
||||
CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x));
|
||||
CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y));
|
||||
}
|
||||
}
|
||||
|
||||
/* Run the tests */
|
||||
#ifdef USE_ENDOMORPHISM
|
||||
test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER);
|
||||
#endif
|
||||
test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER);
|
||||
test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER);
|
||||
test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
|
||||
test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
|
||||
|
||||
#ifdef ENABLE_MODULE_RECOVERY
|
||||
test_exhaustive_recovery_sign(ctx, group, EXHAUSTIVE_TEST_ORDER);
|
||||
test_exhaustive_recovery_verify(ctx, group, EXHAUSTIVE_TEST_ORDER);
|
||||
#endif
|
||||
|
||||
secp256k1_context_destroy(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -57,7 +57,10 @@ static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback *
|
|||
#endif
|
||||
|
||||
/* Like assert(), but when VERIFY is defined, and side-effect safe. */
|
||||
#ifdef VERIFY
|
||||
#if defined(COVERAGE)
|
||||
#define VERIFY_CHECK(check)
|
||||
#define VERIFY_SETUP(stmt)
|
||||
#elif defined(VERIFY)
|
||||
#define VERIFY_CHECK CHECK
|
||||
#define VERIFY_SETUP(stmt) do { stmt; } while(0)
|
||||
#else
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package secp256k1
|
||||
|
||||
/*
|
||||
<HaltingState> sipa, int secp256k1_ecdsa_pubkey_create(unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed);
|
||||
<HaltingState> is that how i generate private/public keys?
|
||||
<sipa> HaltingState: you pass in a random 32-byte string as seckey
|
||||
<sipa> HaltingState: if it is valid, the corresponding pubkey is put in pubkey
|
||||
<sipa> and true is returned
|
||||
<sipa> otherwise, false is returned
|
||||
<sipa> around 1 in 2^128 32-byte strings are invalid, so the odds of even ever seeing one is extremely rare
|
||||
|
||||
<sipa> private keys are mathematically numbers
|
||||
<sipa> each has a corresponding point on the curve as public key
|
||||
<sipa> a private key is just a number
|
||||
<sipa> a public key is a point with x/y coordinates
|
||||
<sipa> almost every 256-bit number is a valid private key (one with a point on the curve corresponding to it)
|
||||
<sipa> HaltingState: ok?
|
||||
|
||||
<sipa> more than half of random points are not on the curve
|
||||
<sipa> and actually, it is less than the square root, not less than half, sorry :)
|
||||
!!!
|
||||
<sipa> a private key is a NUMBER
|
||||
<sipa> a public key is a POINT
|
||||
<gmaxwell> half the x,y values in the field are not on the curve, a private key is an integer.
|
||||
|
||||
<sipa> HaltingState: yes, n,q = private keys; N,Q = corresponding public keys (N=n*G, Q=q*G); then it follows that n*Q = n*q*G = q*n*G = q*N
|
||||
<sipa> that's the reason ECDH works
|
||||
<sipa> multiplication is associative and commutativ
|
||||
*/
|
||||
|
||||
/*
|
||||
<HaltingState> sipa, ok; i am doing compact signatures and I want to know; can someone change the signature to get another valid signature for same message without the private key
|
||||
<HaltingState> because i know they can do that for the normal 72 byte signatures that openssl was putting out
|
||||
<sipa> HaltingState: if you don't enforce non-malleability, yes
|
||||
<sipa> HaltingState: if you force the highest bit of t
|
||||
|
||||
<sipa> it _creates_ signatures that already satisfy that condition
|
||||
<sipa> but it will accept ones that don't
|
||||
<sipa> maybe i should change that, and be strict
|
||||
<HaltingState> yes; i want some way to know signature is valid but fails malleability
|
||||
<sipa> well if the highest bit of S is 1, you can take its complement
|
||||
<sipa> and end up with a valid signature
|
||||
<sipa> that is canonical
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
<HaltingState> sipa, I am signing messages and highest bit of the compact signature is 1!!!
|
||||
<HaltingState> if (b & 0x80) == 0x80 {
|
||||
<HaltingState> log.Panic("b= %v b2= %v \n", b, b&0x80)
|
||||
<HaltingState> }
|
||||
<sipa> what bit?
|
||||
* Pengoo has quit (Ping timeout: 272 seconds)
|
||||
<HaltingState> the highest bit of the first byte of signature
|
||||
<sipa> it's the highest bit of S
|
||||
<sipa> so the 32nd byte
|
||||
<HaltingState> wtf
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
For instance, nonces are used in HTTP digest access authentication to calculate an MD5 digest
|
||||
of the password. The nonces are different each time the 401 authentication challenge
|
||||
response code is presented, thus making replay attacks virtually impossible.
|
||||
|
||||
can verify client/server match without sending password over network
|
||||
*/
|
||||
|
||||
/*
|
||||
<hanihani> one thing I dont get about armory for instance,
|
||||
is how the hot-wallet can generate new addresses without
|
||||
knowing the master key
|
||||
*/
|
||||
|
||||
/*
|
||||
<HaltingState> i am yelling at the telehash people for using secp256r1
|
||||
instead of secp256k1; they thing r1 is "more secure" despite fact that
|
||||
there is no implementation that works and wrapping it is now taking
|
||||
up massive time, lol
|
||||
<gmaxwell> ...
|
||||
|
||||
<gmaxwell> You know that the *r curves are selected via an undisclosed
|
||||
secret process, right?
|
||||
<gmaxwell> HaltingState: telehash is offtopic for this channel.
|
||||
*/
|
||||
/*
|
||||
For instance, nonces are used in HTTP digest access authentication to calculate an MD5 digest
|
||||
of the password. The nonces are different each time the 401 authentication challenge
|
||||
response code is presented, thus making replay attacks virtually impossible.
|
||||
|
||||
can verify client/server match without sending password over network
|
||||
*/
|
||||
|
||||
/*
|
||||
void secp256k1_start(void);
|
||||
void secp256k1_stop(void);
|
||||
|
||||
* Verify an ECDSA signature.
|
||||
* Returns: 1: correct signature
|
||||
* 0: incorrect signature
|
||||
* -1: invalid public key
|
||||
* -2: invalid signature
|
||||
*
|
||||
int secp256k1_ecdsa_verify(const unsigned char *msg, int msglen,
|
||||
const unsigned char *sig, int siglen,
|
||||
const unsigned char *pubkey, int pubkeylen);
|
||||
|
||||
http://www.nilsschneider.net/2013/01/28/recovering-bitcoin-private-keys.html
|
||||
|
||||
Why did this work? ECDSA requires a random number for each signature. If this random
|
||||
number is ever used twice with the same private key it can be recovered.
|
||||
This transaction was generated by a hardware bitcoin wallet using a pseudo-random number
|
||||
generator that was returning the same “random” number every time.
|
||||
|
||||
Nonce is 32 bytes?
|
||||
|
||||
* Create an ECDSA signature.
|
||||
* Returns: 1: signature created
|
||||
* 0: nonce invalid, try another one
|
||||
* In: msg: the message being signed
|
||||
* msglen: the length of the message being signed
|
||||
* seckey: pointer to a 32-byte secret key (assumed to be valid)
|
||||
* nonce: pointer to a 32-byte nonce (generated with a cryptographic PRNG)
|
||||
* Out: sig: pointer to a 72-byte array where the signature will be placed.
|
||||
* siglen: pointer to an int, which will be updated to the signature length (<=72).
|
||||
*
|
||||
int secp256k1_ecdsa_sign(const unsigned char *msg, int msglen,
|
||||
unsigned char *sig, int *siglen,
|
||||
const unsigned char *seckey,
|
||||
const unsigned char *nonce);
|
||||
|
||||
|
||||
* Create a compact ECDSA signature (64 byte + recovery id).
|
||||
* Returns: 1: signature created
|
||||
* 0: nonce invalid, try another one
|
||||
* In: msg: the message being signed
|
||||
* msglen: the length of the message being signed
|
||||
* seckey: pointer to a 32-byte secret key (assumed to be valid)
|
||||
* nonce: pointer to a 32-byte nonce (generated with a cryptographic PRNG)
|
||||
* Out: sig: pointer to a 64-byte array where the signature will be placed.
|
||||
* recid: pointer to an int, which will be updated to contain the recovery id.
|
||||
*
|
||||
int secp256k1_ecdsa_sign_compact(const unsigned char *msg, int msglen,
|
||||
unsigned char *sig64,
|
||||
const unsigned char *seckey,
|
||||
const unsigned char *nonce,
|
||||
int *recid);
|
||||
|
||||
* Recover an ECDSA public key from a compact signature.
|
||||
* Returns: 1: public key successfully recovered (which guarantees a correct signature).
|
||||
* 0: otherwise.
|
||||
* In: msg: the message assumed to be signed
|
||||
* msglen: the length of the message
|
||||
* compressed: whether to recover a compressed or uncompressed pubkey
|
||||
* recid: the recovery id (as returned by ecdsa_sign_compact)
|
||||
* Out: pubkey: pointer to a 33 or 65 byte array to put the pubkey.
|
||||
* pubkeylen: pointer to an int that will contain the pubkey length.
|
||||
*
|
||||
|
||||
recovery id is between 0 and 3
|
||||
|
||||
int secp256k1_ecdsa_recover_compact(const unsigned char *msg, int msglen,
|
||||
const unsigned char *sig64,
|
||||
unsigned char *pubkey, int *pubkeylen,
|
||||
int compressed, int recid);
|
||||
|
||||
|
||||
* Verify an ECDSA secret key.
|
||||
* Returns: 1: secret key is valid
|
||||
* 0: secret key is invalid
|
||||
* In: seckey: pointer to a 32-byte secret key
|
||||
*
|
||||
int secp256k1_ecdsa_seckey_verify(const unsigned char *seckey);
|
||||
|
||||
** Just validate a public key.
|
||||
* Returns: 1: valid public key
|
||||
* 0: invalid public key
|
||||
*
|
||||
int secp256k1_ecdsa_pubkey_verify(const unsigned char *pubkey, int pubkeylen);
|
||||
|
||||
** Compute the public key for a secret key.
|
||||
* In: compressed: whether the computed public key should be compressed
|
||||
* seckey: pointer to a 32-byte private key.
|
||||
* Out: pubkey: pointer to a 33-byte (if compressed) or 65-byte (if uncompressed)
|
||||
* area to store the public key.
|
||||
* pubkeylen: pointer to int that will be updated to contains the pubkey's
|
||||
* length.
|
||||
* Returns: 1: secret was valid, public key stores
|
||||
* 0: secret was invalid, try again.
|
||||
*
|
||||
int secp256k1_ecdsa_pubkey_create(unsigned char *pubkey, int *pubkeylen, const unsigned char *seckey, int compressed);
|
||||
*/
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library 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 Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/** Multiply point by scalar in constant time.
|
||||
* Returns: 1: multiplication was successful
|
||||
* 0: scalar was invalid (zero or overflow)
|
||||
* Args: ctx: pointer to a context object (cannot be NULL)
|
||||
* Out: point: the multiplied point (usually secret)
|
||||
* In: point: pointer to a 64-byte bytepublic point,
|
||||
encoded as two 256bit big-endian numbers.
|
||||
* scalar: a 32-byte scalar with which to multiply the point
|
||||
*/
|
||||
int secp256k1_pubkey_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) {
|
||||
int ret = 0;
|
||||
int overflow = 0;
|
||||
secp256k1_fe feX, feY;
|
||||
secp256k1_gej res;
|
||||
secp256k1_ge ge;
|
||||
secp256k1_scalar s;
|
||||
ARG_CHECK(point != NULL);
|
||||
ARG_CHECK(scalar != NULL);
|
||||
(void)ctx;
|
||||
|
||||
secp256k1_fe_set_b32(&feX, point);
|
||||
secp256k1_fe_set_b32(&feY, point+32);
|
||||
secp256k1_ge_set_xy(&ge, &feX, &feY);
|
||||
secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
||||
if (overflow || secp256k1_scalar_is_zero(&s)) {
|
||||
ret = 0;
|
||||
} else {
|
||||
secp256k1_ecmult_const(&res, &ge, &s);
|
||||
secp256k1_ge_set_gej(&ge, &res);
|
||||
/* Note: can't use secp256k1_pubkey_save here because it is not constant time. */
|
||||
secp256k1_fe_normalize(&ge.x);
|
||||
secp256k1_fe_normalize(&ge.y);
|
||||
secp256k1_fe_get_b32(point, &ge.x);
|
||||
secp256k1_fe_get_b32(point+32, &ge.y);
|
||||
ret = 1;
|
||||
}
|
||||
secp256k1_scalar_clear(&s);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -14,10 +14,9 @@
|
|||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package secp256k1 wraps the bitcoin secp256k1 C library.
|
||||
package secp256k1
|
||||
|
||||
// TODO: set USE_SCALAR_4X64 depending on platform?
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I./libsecp256k1
|
||||
#cgo CFLAGS: -I./libsecp256k1/src/
|
||||
|
@ -29,7 +28,7 @@ package secp256k1
|
|||
#define NDEBUG
|
||||
#include "./libsecp256k1/src/secp256k1.c"
|
||||
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
|
||||
#include "pubkey_scalar_mul.h"
|
||||
#include "ext.h"
|
||||
|
||||
typedef void (*callbackFunc) (const char* msg, void* data);
|
||||
extern void secp256k1GoPanicIllegal(const char* msg, void* data);
|
||||
|
@ -45,16 +44,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||
)
|
||||
|
||||
//#define USE_FIELD_5X64
|
||||
|
||||
/*
|
||||
TODO:
|
||||
> store private keys in buffer and shuffle (deters persistence on swap disc)
|
||||
> byte permutation (changing)
|
||||
> xor with chaning random block (to deter scanning memory for 0x63) (stream cipher?)
|
||||
*/
|
||||
|
||||
// holds ptr to secp256k1_context_struct (see secp256k1/include/secp256k1.h)
|
||||
var (
|
||||
context *C.secp256k1_context
|
||||
N *big.Int
|
||||
|
@ -67,127 +56,57 @@ func init() {
|
|||
HalfN, _ = new(big.Int).SetString("7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", 16)
|
||||
|
||||
// around 20 ms on a modern CPU.
|
||||
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
|
||||
context = C.secp256k1_context_create_sign_verify()
|
||||
C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil)
|
||||
C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil)
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidMsgLen = errors.New("invalid message length for signature recovery")
|
||||
ErrInvalidMsgLen = errors.New("invalid message length, need 32 bytes")
|
||||
ErrInvalidSignatureLen = errors.New("invalid signature length")
|
||||
ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
|
||||
ErrInvalidKey = errors.New("invalid private key")
|
||||
ErrSignFailed = errors.New("signing failed")
|
||||
ErrRecoverFailed = errors.New("recovery failed")
|
||||
)
|
||||
|
||||
func GenerateKeyPair() ([]byte, []byte) {
|
||||
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
|
||||
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
||||
var pubkey64 []byte = make([]byte, 64) // secp256k1_pubkey
|
||||
var pubkey65 []byte = make([]byte, 65) // 65 byte uncompressed pubkey
|
||||
pubkey64_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey64[0]))
|
||||
pubkey65_ptr := (*C.uchar)(unsafe.Pointer(&pubkey65[0]))
|
||||
|
||||
ret := C.secp256k1_ec_pubkey_create(
|
||||
context,
|
||||
pubkey64_ptr,
|
||||
seckey_ptr,
|
||||
)
|
||||
|
||||
if ret != C.int(1) {
|
||||
return GenerateKeyPair() // invalid secret, try again
|
||||
}
|
||||
|
||||
var output_len C.size_t
|
||||
|
||||
C.secp256k1_ec_pubkey_serialize( // always returns 1
|
||||
context,
|
||||
pubkey65_ptr,
|
||||
&output_len,
|
||||
pubkey64_ptr,
|
||||
0, // SECP256K1_EC_COMPRESSED
|
||||
)
|
||||
|
||||
return pubkey65, seckey
|
||||
}
|
||||
|
||||
func GeneratePubKey(seckey []byte) ([]byte, error) {
|
||||
if err := VerifySeckeyValidity(seckey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pubkey []byte = make([]byte, 64)
|
||||
var pubkey_ptr *C.secp256k1_pubkey = (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
|
||||
|
||||
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
||||
|
||||
ret := C.secp256k1_ec_pubkey_create(
|
||||
context,
|
||||
pubkey_ptr,
|
||||
seckey_ptr,
|
||||
)
|
||||
|
||||
if ret != C.int(1) {
|
||||
return nil, errors.New("Unable to generate pubkey from seckey")
|
||||
}
|
||||
|
||||
return pubkey, nil
|
||||
}
|
||||
|
||||
// Sign creates a recoverable ECDSA signature.
|
||||
// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1.
|
||||
//
|
||||
// The caller is responsible for ensuring that msg cannot be chosen
|
||||
// directly by an attacker. It is usually preferable to use a cryptographic
|
||||
// hash function on any input before handing it to this function.
|
||||
func Sign(msg []byte, seckey []byte) ([]byte, error) {
|
||||
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
|
||||
seckey_ptr := (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
||||
|
||||
sig := make([]byte, 65)
|
||||
sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&sig[0]))
|
||||
|
||||
nonce := randentropy.GetEntropyCSPRNG(32)
|
||||
ndata_ptr := unsafe.Pointer(&nonce[0])
|
||||
|
||||
noncefp_ptr := &(*C.secp256k1_nonce_function_default)
|
||||
|
||||
if C.secp256k1_ec_seckey_verify(context, seckey_ptr) != C.int(1) {
|
||||
return nil, errors.New("Invalid secret key")
|
||||
if len(msg) != 32 {
|
||||
return nil, ErrInvalidMsgLen
|
||||
}
|
||||
|
||||
ret := C.secp256k1_ecdsa_sign_recoverable(
|
||||
context,
|
||||
sig_ptr,
|
||||
msg_ptr,
|
||||
seckey_ptr,
|
||||
noncefp_ptr,
|
||||
ndata_ptr,
|
||||
)
|
||||
|
||||
if ret == C.int(0) {
|
||||
return Sign(msg, seckey) //invalid secret, try again
|
||||
}
|
||||
|
||||
sig_serialized := make([]byte, 65)
|
||||
sig_serialized_ptr := (*C.uchar)(unsafe.Pointer(&sig_serialized[0]))
|
||||
var recid C.int
|
||||
|
||||
C.secp256k1_ecdsa_recoverable_signature_serialize_compact(
|
||||
context,
|
||||
sig_serialized_ptr, // 64 byte compact signature
|
||||
&recid,
|
||||
sig_ptr, // 65 byte "recoverable" signature
|
||||
)
|
||||
|
||||
sig_serialized[64] = byte(int(recid)) // add back recid to get 65 bytes sig
|
||||
|
||||
return sig_serialized, nil
|
||||
|
||||
}
|
||||
|
||||
func VerifySeckeyValidity(seckey []byte) error {
|
||||
if len(seckey) != 32 {
|
||||
return errors.New("priv key is not 32 bytes")
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
||||
ret := C.secp256k1_ec_seckey_verify(context, seckey_ptr)
|
||||
if int(ret) != 1 {
|
||||
return errors.New("invalid seckey")
|
||||
seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0]))
|
||||
if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 {
|
||||
return nil, ErrInvalidKey
|
||||
}
|
||||
return nil
|
||||
|
||||
var (
|
||||
msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
|
||||
nonce = randentropy.GetEntropyCSPRNG(32)
|
||||
noncefunc = &(*C.secp256k1_nonce_function_default)
|
||||
noncefuncData = unsafe.Pointer(&nonce[0])
|
||||
sigstruct C.secp256k1_ecdsa_recoverable_signature
|
||||
)
|
||||
if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, noncefuncData) == 0 {
|
||||
return nil, ErrSignFailed
|
||||
}
|
||||
|
||||
var (
|
||||
sig = make([]byte, 65)
|
||||
sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
|
||||
recid C.int
|
||||
)
|
||||
C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct)
|
||||
sig[64] = byte(recid) // add back recid to get 65 bytes sig
|
||||
return sig, nil
|
||||
}
|
||||
|
||||
// RecoverPubkey returns the the public key of the signer.
|
||||
|
@ -202,49 +121,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
|
||||
sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
|
||||
pubkey := make([]byte, 64)
|
||||
/*
|
||||
this slice is used for both the recoverable signature and the
|
||||
resulting serialized pubkey (both types in libsecp256k1 are 65
|
||||
bytes). this saves one allocation of 65 bytes, which is nice as
|
||||
pubkey recovery is one bottleneck during load in Ethereum
|
||||
*/
|
||||
bytes65 := make([]byte, 65)
|
||||
pubkey_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
|
||||
recoverable_sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&bytes65[0]))
|
||||
recid := C.int(sig[64])
|
||||
|
||||
ret := C.secp256k1_ecdsa_recoverable_signature_parse_compact(
|
||||
context,
|
||||
recoverable_sig_ptr,
|
||||
sig_ptr,
|
||||
recid)
|
||||
if ret == C.int(0) {
|
||||
return nil, errors.New("Failed to parse signature")
|
||||
}
|
||||
|
||||
ret = C.secp256k1_ecdsa_recover(
|
||||
context,
|
||||
pubkey_ptr,
|
||||
recoverable_sig_ptr,
|
||||
msg_ptr,
|
||||
var (
|
||||
pubkey = make([]byte, 65)
|
||||
sigdata = (*C.uchar)(unsafe.Pointer(&sig[0]))
|
||||
msgdata = (*C.uchar)(unsafe.Pointer(&msg[0]))
|
||||
)
|
||||
if ret == C.int(0) {
|
||||
return nil, errors.New("Failed to recover public key")
|
||||
if C.secp256k1_ecdsa_recover_pubkey(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 {
|
||||
return nil, ErrRecoverFailed
|
||||
}
|
||||
|
||||
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
|
||||
var output_len C.size_t
|
||||
C.secp256k1_ec_pubkey_serialize( // always returns 1
|
||||
context,
|
||||
serialized_pubkey_ptr,
|
||||
&output_len,
|
||||
pubkey_ptr,
|
||||
0, // SECP256K1_EC_COMPRESSED
|
||||
)
|
||||
return bytes65, nil
|
||||
return pubkey, nil
|
||||
}
|
||||
|
||||
func checkSignature(sig []byte) error {
|
||||
|
|
|
@ -18,6 +18,9 @@ package secp256k1
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
|
@ -26,15 +29,41 @@ import (
|
|||
|
||||
const TestCount = 1000
|
||||
|
||||
func TestPrivkeyGenerate(t *testing.T) {
|
||||
_, seckey := GenerateKeyPair()
|
||||
if err := VerifySeckeyValidity(seckey); err != nil {
|
||||
t.Errorf("seckey not valid: %s", err)
|
||||
func generateKeyPair() (pubkey, privkey []byte) {
|
||||
key, err := ecdsa.GenerateKey(S256(), rand.Reader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
pubkey = elliptic.Marshal(S256(), key.X, key.Y)
|
||||
privkey = make([]byte, 32)
|
||||
readBits(privkey, key.D)
|
||||
return pubkey, privkey
|
||||
}
|
||||
|
||||
func randSig() []byte {
|
||||
sig := randentropy.GetEntropyCSPRNG(65)
|
||||
sig[32] &= 0x70
|
||||
sig[64] %= 4
|
||||
return sig
|
||||
}
|
||||
|
||||
// tests for malleability
|
||||
// highest bit of signature ECDSA s value must be 0, in the 33th byte
|
||||
func compactSigCheck(t *testing.T, sig []byte) {
|
||||
var b int = int(sig[32])
|
||||
if b < 0 {
|
||||
t.Errorf("highest bit is negative: %d", b)
|
||||
}
|
||||
if ((b >> 7) == 1) != ((b & 0x80) == 0x80) {
|
||||
t.Errorf("highest bit: %d bit >> 7: %d", b, b>>7)
|
||||
}
|
||||
if (b & 0x80) == 0x80 {
|
||||
t.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignatureValidity(t *testing.T) {
|
||||
pubkey, seckey := GenerateKeyPair()
|
||||
pubkey, seckey := generateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
sig, err := Sign(msg, seckey)
|
||||
if err != nil {
|
||||
|
@ -57,7 +86,7 @@ func TestSignatureValidity(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInvalidRecoveryID(t *testing.T) {
|
||||
_, seckey := GenerateKeyPair()
|
||||
_, seckey := generateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
sig, _ := Sign(msg, seckey)
|
||||
sig[64] = 99
|
||||
|
@ -68,7 +97,7 @@ func TestInvalidRecoveryID(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSignAndRecover(t *testing.T) {
|
||||
pubkey1, seckey := GenerateKeyPair()
|
||||
pubkey1, seckey := generateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
sig, err := Sign(msg, seckey)
|
||||
if err != nil {
|
||||
|
@ -84,7 +113,7 @@ func TestSignAndRecover(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRandomMessagesWithSameKey(t *testing.T) {
|
||||
pubkey, seckey := GenerateKeyPair()
|
||||
pubkey, seckey := generateKeyPair()
|
||||
keys := func() ([]byte, []byte) {
|
||||
return pubkey, seckey
|
||||
}
|
||||
|
@ -93,7 +122,7 @@ func TestRandomMessagesWithSameKey(t *testing.T) {
|
|||
|
||||
func TestRandomMessagesWithRandomKeys(t *testing.T) {
|
||||
keys := func() ([]byte, []byte) {
|
||||
pubkey, seckey := GenerateKeyPair()
|
||||
pubkey, seckey := generateKeyPair()
|
||||
return pubkey, seckey
|
||||
}
|
||||
signAndRecoverWithRandomMessages(t, keys)
|
||||
|
@ -129,7 +158,7 @@ func signAndRecoverWithRandomMessages(t *testing.T, keys func() ([]byte, []byte)
|
|||
}
|
||||
|
||||
func TestRecoveryOfRandomSignature(t *testing.T) {
|
||||
pubkey1, _ := GenerateKeyPair()
|
||||
pubkey1, _ := generateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
|
||||
for i := 0; i < TestCount; i++ {
|
||||
|
@ -141,15 +170,8 @@ func TestRecoveryOfRandomSignature(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func randSig() []byte {
|
||||
sig := randentropy.GetEntropyCSPRNG(65)
|
||||
sig[32] &= 0x70
|
||||
sig[64] %= 4
|
||||
return sig
|
||||
}
|
||||
|
||||
func TestRandomMessagesAgainstValidSig(t *testing.T) {
|
||||
pubkey1, seckey := GenerateKeyPair()
|
||||
pubkey1, seckey := generateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
sig, _ := Sign(msg, seckey)
|
||||
|
||||
|
@ -163,14 +185,6 @@ func TestRandomMessagesAgainstValidSig(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestZeroPrivkey(t *testing.T) {
|
||||
zeroedBytes := make([]byte, 32)
|
||||
err := VerifySeckeyValidity(zeroedBytes)
|
||||
if err == nil {
|
||||
t.Errorf("zeroed bytes should have returned error")
|
||||
}
|
||||
}
|
||||
|
||||
// Useful when the underlying libsecp256k1 API changes to quickly
|
||||
// check only recover function without use of signature function
|
||||
func TestRecoverSanity(t *testing.T) {
|
||||
|
@ -186,47 +200,23 @@ func TestRecoverSanity(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// tests for malleability
|
||||
// highest bit of signature ECDSA s value must be 0, in the 33th byte
|
||||
func compactSigCheck(t *testing.T, sig []byte) {
|
||||
var b int = int(sig[32])
|
||||
if b < 0 {
|
||||
t.Errorf("highest bit is negative: %d", b)
|
||||
}
|
||||
if ((b >> 7) == 1) != ((b & 0x80) == 0x80) {
|
||||
t.Errorf("highest bit: %d bit >> 7: %d", b, b>>7)
|
||||
}
|
||||
if (b & 0x80) == 0x80 {
|
||||
t.Errorf("highest bit: %d bit & 0x80: %d", b, b&0x80)
|
||||
}
|
||||
}
|
||||
|
||||
// godep go test -v -run=XXX -bench=BenchmarkSign
|
||||
// add -benchtime=10s to benchmark longer for more accurate average
|
||||
|
||||
// to avoid compiler optimizing the benchmarked function call
|
||||
var err error
|
||||
|
||||
func BenchmarkSign(b *testing.B) {
|
||||
_, seckey := generateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, seckey := GenerateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
b.StartTimer()
|
||||
_, e := Sign(msg, seckey)
|
||||
err = e
|
||||
b.StopTimer()
|
||||
Sign(msg, seckey)
|
||||
}
|
||||
}
|
||||
|
||||
//godep go test -v -run=XXX -bench=BenchmarkECRec
|
||||
func BenchmarkRecover(b *testing.B) {
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
_, seckey := generateKeyPair()
|
||||
sig, _ := Sign(msg, seckey)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, seckey := GenerateKeyPair()
|
||||
msg := randentropy.GetEntropyCSPRNG(32)
|
||||
sig, _ := Sign(msg, seckey)
|
||||
b.StartTimer()
|
||||
_, e := RecoverPubkey(msg, sig)
|
||||
err = e
|
||||
b.StopTimer()
|
||||
RecoverPubkey(msg, sig)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue