mirror of https://github.com/ethereum/go-ethereum
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 codepull/3561/head
parent
93077c98e4
commit
e0ceeab0d1
@ -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; |
||||
} |
@ -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 |
||||
]) |
@ -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 |
@ -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 |
@ -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 |
||||
|
@ -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(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 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 |
||||
*/ |
||||
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; |
||||
} |
||||
|
||||
/** |
||||
* @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 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 |
||||
*/ |
||||
private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff); |
||||
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) |
||||
{ |
||||
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; |
||||
|
||||
} |
||||
|
||||
SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize |
||||
(JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) |
||||
{ |
||||
unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); |
||||
int sigLen = *((int*)(data + 32)); |
||||
int pubLen = *((int*)(data + 32 + 4)); |
||||
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); |
||||
|
||||
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 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); |
||||
|
||||
static void __javasecp256k1_attach(void) { |
||||
secp256k1_start(SECP256K1_START_VERIFY); |
||||
(void)classObject;(void)env; |
||||
} |
||||
|
||||
static void __javasecp256k1_detach(void) { |
||||
secp256k1_stop(); |
||||
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; |
||||
} |
||||
|
@ -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 |
@ -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 |
@ -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 |
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; |
||||
} |
||||
|
@ -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; |
||||
} |
||||
|
Loading…
Reference in new issue