diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-11-09 05:12:59 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-11-09 05:12:59 +0000 |
commit | 26c046fbc57d53136b4fb3b5e0d18298318125d4 (patch) | |
tree | 0397d2184e7fba8a51f7fb9a6fc01a82d0748411 /nss/lib/freebl/ecl | |
parent | c416b91e36567df4ec765a495c5a6ca6a1853f58 (diff) | |
download | nss-26c046fbc57d53136b4fb3b5e0d18298318125d4.tar.gz |
nss-3.21nss-3.21
Diffstat (limited to 'nss/lib/freebl/ecl')
40 files changed, 13758 insertions, 0 deletions
diff --git a/nss/lib/freebl/ecl/Makefile b/nss/lib/freebl/ecl/Makefile new file mode 100644 index 0000000..8237d06 --- /dev/null +++ b/nss/lib/freebl/ecl/Makefile @@ -0,0 +1,195 @@ +# +# Makefile for elliptic curve library + +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +## Define CC to be the C compiler you wish to use. The GNU cc +## compiler (gcc) should work, at the very least +#CC=cc +#CC=gcc + +## +## Define PERL to point to your local Perl interpreter. It +## should be Perl 5.x, although it's conceivable that Perl 4 +## might work ... I haven't tested it. +## +#PERL=/usr/bin/perl +#PERL=perl + +include ../mpi/target.mk + +## +## Define platform-dependent variables for use of floating-point code. +## +ifeq ($(TARGET),v9SOLARIS) +ECL_USE_FP=1 +else +ifeq ($(TARGET),v8plusSOLARIS) +ECL_USE_FP=1 +else +ifeq ($(TARGET),v8SOLARIS) +ECL_USE_FP=1 +else +ifeq ($(TARGET),x86LINUX) +ECL_USE_FP=1 +endif +endif +endif +endif + +## +## Add to definition of CFLAGS depending on use of floating-point code. +## +ifeq ($(ECL_USE_FP),1) +CFLAGS+= -DECL_USE_FP +endif + +## +## Define LIBS to include any libraries you need to link against. +## If NO_TABLE is define, LIBS should include '-lm' or whatever is +## necessary to bring in the math library. Otherwise, it can be +## left alone, unless your system has other peculiar requirements. +## +LIBS=-L../mpi -lmpi -lm#-lmalloc#-lefence + +## +## Define INCLUDES to include any include directories you need to +## compile with. +## +INCLUDES=-I../mpi +CFLAGS+= $(INCLUDES) $(XCFLAGS) + +## +## Define RANLIB to be the library header randomizer; you might not +## need this on some systems (just set it to 'echo' on these systems, +## such as IRIX) +## +RANLIB=echo + +## +## Define LIBOBJS to be the object files that will be created during +## the build process. +## +LIBOBJS = ecl.o ecl_curve.o ecl_mult.o ecl_gf.o \ + ec2_aff.o ec2_mont.o ec2_proj.o \ + ec2_163.o ec2_193.o ec2_233.o \ + ecp_aff.o ecp_jac.o ecp_mont.o \ + ec_naf.o ecp_jm.o \ + ecp_192.o ecp_224.o ecp_256.o ecp_384.o ecp_521.o \ + ecp_256_32.o +ifeq ($(ECL_USE_FP),1) +LIBOBJS+= ecp_fp160.o ecp_fp192.o ecp_fp224.o ecp_fp.o +endif + +## The headers contained in this library. +LIBHDRS = ecl-exp.h ecl.h ec2.h ecp.h ecl-priv.h ecl-curve.h +APPHDRS = ecl-exp.h ecl.h ec2.h ecp.h ecl-priv.h ecl-curve.h +ifeq ($(ECL_GFP_ASSEMBLY_FP),1) +LIBHDRS += ecp_fp.h +APPHDRS += ecp_fp.h +endif + + +help: + @ echo "" + @ echo "The following targets can be built with this Makefile:" + @ echo "" + @ echo "libecl.a - elliptic curve library" + @ echo "tests - build command line tests" + @ echo "test - run command line tests" + @ echo "clean - clean up objects and such" + @ echo "" + +.SUFFIXES: .c .o .i + +.c.i: + $(CC) $(CFLAGS) -E $< > $@ + +#--------------------------------------- + +$(LIBOBJS): $(LIBHDRS) + +ecl.o: ecl.c $(LIBHDRS) +ecl_curve.o: ecl_curve.c $(LIBHDRS) +ecl_mult.o: ecl_mult.c $(LIBHDRS) +ecl_gf.o: ecl_gf.c $(LIBHDRS) +ec2_aff.o: ec2_aff.c $(LIBHDRS) +ec2_mont.o: ec2_mont.c $(LIBHDRS) +ec2_proj.o: ec2_proj.c $(LIBHDRS) +ec2_163.o: ec2_163.c $(LIBHDRS) +ec2_193.o: ec2_193.c $(LIBHDRS) +ec2_233.o: ec2_233.c $(LIBHDRS) +ecp_aff.o: ecp_aff.c $(LIBHDRS) +ecp_jac.o: ecp_jac.c $(LIBHDRS) +ecp_jm.o: ecp_jm.c $(LIBHDRS) +ecp_mont.o: ecp_mont.c $(LIBHDRS) +ecp_192.o: ecp_192.c $(LIBHDRS) +ecp_224.o: ecp_224.c $(LIBHDRS) +ecp_256.o: ecp_256.c $(LIBHDRS) +ecp_384.o: ecp_384.c $(LIBHDRS) +ecp_521.o: ecp_521.c $(LIBHDRS) +ecp_fp.o: ecp_fp.c $(LIBHDRS) +ifeq ($(ECL_USE_FP),1) +ecp_fp160.o: ecp_fp160.c ecp_fpinc.c $(LIBHDRS) +ecp_fp192.o: ecp_fp192.c ecp_fpinc.c $(LIBHDRS) +ecp_fp224.o: ecp_fp224.c ecp_fpinc.c $(LIBHDRS) +endif + +libecl.a: $(LIBOBJS) + ar -cvr libecl.a $(LIBOBJS) + $(RANLIB) libecl.a + +lib libs: libecl.a + +ecl.i: ecl.h + +#--------------------------------------- + +ECLTESTOBJS = ec2_test.o ecp_test.o ec_naft.o +ifeq ($(ECL_USE_FP),1) +ECLTESTOBJS+= ecp_fpt.o +endif +ECLTESTS = $(ECLTESTOBJS:.o=) + +$(ECLTESTOBJS): %.o: tests/%.c $(LIBHDRS) + $(CC) $(CFLAGS) -o $@ -c $< $(INCLUDES) + +$(ECLTESTS): %: %.o libecl.a + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + +ifeq ($(ECL_USE_FP),1) +tests: ec2_test ecp_test ec_naft ecp_fpt +else +tests: ec2_test ecp_test ec_naft +endif + +#--------------------------------------- + +ifeq ($(ECL_USE_FP),1) +test: tests + ./ecp_test + ./ec2_test + ./ec_naft + ./ecp_fpt +else +test: tests + ./ecp_test + ./ec_naft + ./ec2_test +endif + +#--------------------------------------- + +alltests: tests + +clean: + rm -f *.o *.a *.i + rm -f core + rm -f *~ .*~ + rm -f $(ECLTESTS) + +clobber: clean + +# END diff --git a/nss/lib/freebl/ecl/README b/nss/lib/freebl/ecl/README new file mode 100644 index 0000000..f086cde --- /dev/null +++ b/nss/lib/freebl/ecl/README @@ -0,0 +1,297 @@ +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +The ECL exposes routines for constructing and converting curve +parameters for internal use. + + +HEADER FILES +============ + +ecl-exp.h - Exports data structures and curve names. For use by code +that does not have access to mp_ints. + +ecl-curve.h - Provides hex encodings (in the form of ECCurveParams +structs) of standardizes elliptic curve domain parameters and mappings +from ECCurveName to ECCurveParams. For use by code that does not have +access to mp_ints. + +ecl.h - Interface to constructors for curve parameters and group object, +and point multiplication operations. Used by higher level algorithms +(like ECDH and ECDSA) to actually perform elliptic curve cryptography. + +ecl-priv.h - Data structures and functions for internal use within the +library. + +ec2.h - Internal header file that contains all functions for point +arithmetic over binary polynomial fields. + +ecp.h - Internal header file that contains all functions for point +arithmetic over prime fields. + +DATA STRUCTURES AND TYPES +========================= + +ECCurveName (from ecl-exp.h) - Opaque name for standardized elliptic +curve domain parameters. + +ECCurveParams (from ecl-exp.h) - Provides hexadecimal encoding +of elliptic curve domain parameters. Can be generated by a user +and passed to ECGroup_fromHex or can be generated from a name by +EC_GetNamedCurveParams. ecl-curve.h contains ECCurveParams structs for +the standardized curves defined by ECCurveName. + +ECGroup (from ecl.h and ecl-priv.h) - Opaque data structure that +represents a group of elliptic curve points for a particular set of +elliptic curve domain parameters. Contains all domain parameters (curve +a and b, field, base point) as well as pointers to the functions that +should be used for point arithmetic and the underlying field GFMethod. +Generated by either ECGroup_fromHex or ECGroup_fromName. + +GFMethod (from ecl-priv.h) - Represents a field underlying a set of +elliptic curve domain parameters. Contains the irreducible that defines +the field (either the prime or the binary polynomial) as well as +pointers to the functions that should be used for field arithmetic. + +ARITHMETIC FUNCTIONS +==================== + +Higher-level algorithms (like ECDH and ECDSA) should call ECPoint_mul +or ECPoints_mul (from ecl.h) to do point arithmetic. These functions +will choose which underlying algorithms to use, based on the ECGroup +structure. + +Point Multiplication +-------------------- + +ecl_mult.c provides the ECPoints_mul and ECPoint_mul wrappers. +It also provides two implementations for the pts_mul operation - +ec_pts_mul_basic (which computes kP, lQ, and then adds kP + lQ) and +ec_pts_mul_simul_w2 (which does a simultaneous point multiplication +using a table with window size 2*2). + +ec_naf.c provides an implementation of an algorithm to calculate a +non-adjacent form of a scalar, minimizing the number of point +additions that need to be done in a point multiplication. + +Point Arithmetic over Prime Fields +---------------------------------- + +ecp_aff.c provides point arithmetic using affine coordinates. + +ecp_jac.c provides point arithmetic using Jacobian projective +coordinates and mixed Jacobian-affine coordinates. (Jacobian projective +coordinates represent a point (x, y) as (X, Y, Z), where x=X/Z^2, +y=Y/Z^3). + +ecp_jm.c provides point arithmetic using Modified Jacobian +coordinates and mixed Modified_Jacobian-affine coordinates. +(Modified Jacobian coordinates represent a point (x, y) +as (X, Y, Z, a*Z^4), where x=X/Z^2, y=Y/Z^3, and a is +the linear coefficient in the curve defining equation). + +ecp_192.c and ecp_224.c provide optimized field arithmetic. + +Point Arithmetic over Binary Polynomial Fields +---------------------------------------------- + +ec2_aff.c provides point arithmetic using affine coordinates. + +ec2_proj.c provides point arithmetic using projective coordinates. +(Projective coordinates represent a point (x, y) as (X, Y, Z), where +x=X/Z, y=Y/Z^2). + +ec2_mont.c provides point multiplication using Montgomery projective +coordinates. + +ec2_163.c, ec2_193.c, and ec2_233.c provide optimized field arithmetic. + +Field Arithmetic +---------------- + +ecl_gf.c provides constructors for field objects (GFMethod) with the +functions GFMethod_cons*. It also provides wrappers around the basic +field operations. + +Prime Field Arithmetic +---------------------- + +The mpi library provides the basic prime field arithmetic. + +ecp_mont.c provides wrappers around the Montgomery multiplication +functions from the mpi library and adds encoding and decoding functions. +It also provides the function to construct a GFMethod object using +Montgomery multiplication. + +ecp_192.c and ecp_224.c provide optimized modular reduction for the +fields defined by nistp192 and nistp224 primes. + +ecl_gf.c provides wrappers around the basic field operations. + +Binary Polynomial Field Arithmetic +---------------------------------- + +../mpi/mp_gf2m.c provides basic binary polynomial field arithmetic, +including addition, multiplication, squaring, mod, and division, as well +as conversion ob polynomial representations between bitstring and int[]. + +ec2_163.c, ec2_193.c, and ec2_233.c provide optimized field mod, mul, +and sqr operations. + +ecl_gf.c provides wrappers around the basic field operations. + +Field Encoding +-------------- + +By default, field elements are encoded in their basic form. It is +possible to use an alternative encoding, however. For example, it is +possible to Montgomery representation of prime field elements and +take advantage of the fast modular multiplication that Montgomery +representation provides. The process of converting from basic form to +Montgomery representation is called field encoding, and the opposite +process would be field decoding. All internal point operations assume +that the operands are field encoded as appropriate. By rewiring the +underlying field arithmetic to perform operations on these encoded +values, the same overlying point arithmetic operations can be used +regardless of field representation. + +ALGORITHM WIRING +================ + +The EC library allows point and field arithmetic algorithms to be +substituted ("wired-in") on a fine-grained basis. This allows for +generic algorithms and algorithms that are optimized for a particular +curve, field, or architecture, to coexist and to be automatically +selected at runtime. + +Wiring Mechanism +---------------- + +The ECGroup and GFMethod structure contain pointers to the point and +field arithmetic functions, respectively, that are to be used in +operations. + +The selection of algorithms to use is handled in the function +ecgroup_fromNameAndHex in ecl.c. + +Default Wiring +-------------- + +Curves over prime fields by default use montgomery field arithmetic, +point multiplication using 5-bit window non-adjacent-form with +Modified Jacobian coordinates, and 2*2-bit simultaneous point +multiplication using Jacobian coordinates. +(Wiring in function ECGroup_consGFp_mont in ecl.c.) + +Curves over prime fields that have optimized modular reduction (i.e., +secp160r1, nistp192, and nistp224) do not use Montgomery field +arithmetic. Instead, they use basic field arithmetic with their +optimized reduction (as in ecp_192.c and ecp_224.c). They +use the same point multiplication and simultaneous point multiplication +algorithms as other curves over prime fields. + +Curves over binary polynomial fields by default use generic field +arithmetic with montgomery point multiplication and basic kP + lQ +computation (multiply, multiply, and add). (Wiring in function +ECGroup_cons_GF2m in ecl.c.) + +Curves over binary polynomial fields that have optimized field +arithmetic (i.e., any 163-, 193, or 233-bit field) use their optimized +field arithmetic. They use the same point multiplication and +simultaneous point multiplication algorithms as other curves over binary +fields. + +Example +------- + +We provide an example for plugging in an optimized implementation for +the Koblitz curve nistk163. + +Suppose the file ec2_k163.c contains the optimized implementation. In +particular it contains a point multiplication function: + + mp_err ec_GF2m_nistk163_pt_mul(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, const ECGroup *group); + +Since only a pt_mul function is provided, the generic pt_add function +will be used. + +There are two options for handling the optimized field arithmetic used +by the ..._pt_mul function. Say the optimized field arithmetic includes +the following functions: + + mp_err ec_GF2m_nistk163_add(const mp_int *a, const mp_int *b, + mp_int *r, const GFMethod *meth); + mp_err ec_GF2m_nistk163_mul(const mp_int *a, const mp_int *b, + mp_int *r, const GFMethod *meth); + mp_err ec_GF2m_nistk163_sqr(const mp_int *a, const mp_int *b, + mp_int *r, const GFMethod *meth); + mp_err ec_GF2m_nistk163_div(const mp_int *a, const mp_int *b, + mp_int *r, const GFMethod *meth); + +First, the optimized field arithmetic could simply be called directly +by the ..._pt_mul function. This would be accomplished by changing +the ecgroup_fromNameAndHex function in ecl.c to include the following +statements: + + if (name == ECCurve_NIST_K163) { + group = ECGroup_consGF2m(&irr, NULL, &curvea, &curveb, &genx, + &geny, &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK( ec_group_set_nistk163(group) ); + } + +and including in ec2_k163.c the following function: + + mp_err ec_group_set_nistk163(ECGroup *group) { + group->point_mul = &ec_GF2m_nistk163_pt_mul; + return MP_OKAY; + } + +As a result, ec_GF2m_pt_add and similar functions would use the +basic binary polynomial field arithmetic ec_GF2m_add, ec_GF2m_mul, +ec_GF2m_sqr, and ec_GF2m_div. + +Alternatively, the optimized field arithmetic could be wired into the +group's GFMethod. This would be accomplished by putting the following +function in ec2_k163.c: + + mp_err ec_group_set_nistk163(ECGroup *group) { + group->meth->field_add = &ec_GF2m_nistk163_add; + group->meth->field_mul = &ec_GF2m_nistk163_mul; + group->meth->field_sqr = &ec_GF2m_nistk163_sqr; + group->meth->field_div = &ec_GF2m_nistk163_div; + group->point_mul = &ec_GF2m_nistk163_pt_mul; + return MP_OKAY; + } + +For an example of functions that use special field encodings, take a +look at ecp_mont.c. + +TESTING +======= + +The ecl/tests directory contains a collection of standalone tests that +verify the correctness of the elliptic curve library. + +Both ecp_test and ec2_test take the following arguments: + + --print Print out results of each point arithmetic test. + --time Benchmark point operations and print results. + +The set of curves over which ecp_test and ec2_test run is coded into the +program, but can be changed by editing the source files. + +BUILDING +======== + +The ecl can be built as a standalone library, separate from NSS, +dependent only on the mpi library. To build the library: + + > cd ../mpi + > make libs + > cd ../ecl + > make libs + > make tests # to build test files + > make test # to run automated tests diff --git a/nss/lib/freebl/ecl/README.FP b/nss/lib/freebl/ecl/README.FP new file mode 100644 index 0000000..833f42e --- /dev/null +++ b/nss/lib/freebl/ecl/README.FP @@ -0,0 +1,284 @@ +This Source Code Form is subject to the terms of the Mozilla Public +License, v. 2.0. If a copy of the MPL was not distributed with this +file, You can obtain one at http://mozilla.org/MPL/2.0/. + +The ECL exposes routines for constructing and converting curve +parameters for internal use. + +The floating point code of the ECL provides algorithms for performing +elliptic-curve point multiplications in floating point. + +The point multiplication algorithms perform calculations almost +exclusively in floating point for efficiency, but have the same +(integer) interface as the ECL for compatibility and to be easily +wired-in to the ECL. Please see README file (not this README.FP file) +for information on wiring-in. + +This has been implemented for 3 curves as specified in [1]: + secp160r1 + secp192r1 + secp224r1 + +RATIONALE +========= +Calculations are done in the floating-point unit (FPU) since it +gives better performance on the UltraSPARC III chips. This is +because the FPU allows for faster multiplication than the integer unit. +The integer unit has a longer multiplication instruction latency, and +does not allow full pipelining, as described in [2]. +Since performance is an important selling feature of Elliptic Curve +Cryptography (ECC), this implementation was created. + +DATA REPRESENTATION +=================== +Data is primarily represented in an array of double-precision floating +point numbers. Generally, each array element has 24 bits of precision +(i.e. be x * 2^y, where x is an integer of at most 24 bits, y some positive +integer), although the actual implementation details are more complicated. + +e.g. a way to store an 80 bit number might be: +double p[4] = { 632613 * 2^0, 329841 * 2^24, 9961 * 2^48, 51 * 2^64 }; +See section ARITHMETIC OPERATIONS for more details. + +This implementation assumes that the floating-point unit rounding mode +is round-to-even as specified in IEEE 754 +(as opposed to chopping, rounding up, or rounding down). +When subtracting integers represented as arrays of floating point +numbers, some coefficients (array elements) may become negative. +This effectively gives an extra bit of precision that is important +for correctness in some cases. + +The described number presentation limits the size of integers to 1023 bits. +This is due to an upper bound of 1024 for the exponent of a double precision +floating point number as specified in IEEE-754. +However, this is acceptable for ECC key sizes of the foreseeable future. + +DATA STRUCTURES +=============== +For more information on coordinate representations, see [3]. + +ecfp_aff_pt +----------- +Affine EC Point Representation. This is the basic +representation (x, y) of an elliptic curve point. + +ecfp_jac_pt +----------- +Jacobian EC Point. This stores a point as (X, Y, Z), where +the affine point corresponds to (X/Z^2, Y/Z^3). This allows +for fewer inversions in calculations. + +ecfp_chud_pt +------------ +Chudnovsky Jacobian Point. This representation stores a point +as (X, Y, Z, Z^2, Z^3), the same as a Jacobian representation +but also storing Z^2 and Z^3 for faster point additions. + +ecfp_jm_pt +---------- +Modified Jacobian Point. This representation stores a point +as (X, Y, Z, a*Z^4), the same as Jacobian representation but +also storing a*Z^4 for faster point doublings. Here "a" represents +the linear coefficient of x defining the curve. + +EC_group_fp +----------- +Stores information on the elliptic curve group for floating +point calculations. Contains curve specific information, as +well as function pointers to routines, allowing different +optimizations to be easily wired in. +This should be made accessible from an ECGroup for the floating +point implementations of point multiplication. + +POINT MULTIPLICATION ALGORITHMS +=============================== +Elliptic Curve Point multiplication can be done at a higher level orthogonal +to the implementation of point additions and point doublings. There +are a variety of algorithms that can be used. + +The following algorithms have been implemented: + +4-bit Window (Jacobian Coordinates) +Double & Add (Jacobian & Affine Coordinates) +5-bit Non-Adjacent Form (Modified Jacobian & Chudnovsky Jacobian) + +Currently, the fastest algorithm for multiplying a generic point +is the 5-bit Non-Adjacent Form. + +See comments in ecp_fp.c for more details and references. + +SOURCE / HEADER FILES +===================== + +ecp_fp.c +-------- +Main source file for floating point calculations. Contains routines +to convert from floating-point to integer (mp_int format), point +multiplication algorithms, and several other routines. + +ecp_fp.h +-------- +Main header file. Contains most constants used and function prototypes. + +ecp_fp[160, 192, 224].c +----------------------- +Source files for specific curves. Contains curve specific code such +as specialized reduction based on the field defining prime. Contains +code wiring-in different algorithms and optimizations. + +ecp_fpinc.c +----------- +Source file that is included by ecp_fp[160, 192, 224].c. This generates +functions with different preprocessor-defined names and loop iterations, +allowing for static linking and strong compiler optimizations without +code duplication. + +TESTING +======= +The test suite can be found in ecl/tests/ecp_fpt. This tests and gets +timings of the different algorithms for the curves implemented. + +ARITHMETIC OPERATIONS +--------------------- +The primary operations in ECC over the prime fields are modular arithmetic: +i.e. n * m (mod p) and n + m (mod p). In this implementation, multiplication, +addition, and reduction are implemented as separate functions. This +enables computation of formulae with fewer reductions, e.g. +(a * b) + (c * d) (mod p) rather than: +((a * b) (mod p)) + ((c * d) (mod p)) (mod p) +This takes advantage of the fact that the double precision mantissa in +floating point can hold numbers up to 2^53, i.e. it has some leeway to +store larger intermediate numbers. See further detail in the section on +FLOATING POINT PRECISION. + +Multiplication +-------------- +Multiplication is implemented in a standard polynomial multiplication +fashion. The terms in opposite factors are pairwise multiplied and +added together appropriately. Note that the result requires twice +as many doubles for storage, as the bit size of the product is twice +that of the multiplicands. +e.g. suppose we have double n[3], m[3], r[6], and want to calculate r = n * m +r[0] = n[0] * m[0] +r[1] = n[0] * m[1] + n[1] * m[0] +r[2] = n[0] * m[2] + n[1] * m[1] + n[2] * m[0] +r[3] = n[1] * m[2] + n[2] * m[1] +r[4] = n[2] * m[2] +r[5] = 0 (This is used later to hold spillover from r[4], see tidying in +the reduction section.) + +Addition +-------- +Addition is done term by term. The only caveat is to be careful with +the number of terms that need to be added. When adding results of +multiplication (before reduction), twice as many terms need to be added +together. This is done in the addLong function. +e.g. for double n[4], m[4], r[4]: r = n + m +r[0] = n[0] + m[0] +r[1] = n[1] + m[1] +r[2] = n[2] + m[2] +r[3] = n[3] + m[3] + +Modular Reduction +----------------- +For the curves implemented, reduction is possible by fast reduction +for Generalized Mersenne Primes, as described in [4]. For the +floating point implementation, a significant step of the reduction +process is tidying: that is, the propagation of carry bits from +low-order to high-order coefficients to reduce the precision of each +coefficient to 24 bits. +This is done by adding and then subtracting +ecfp_alpha, a large floating point number that induces precision roundoff. +See [5] for more details on tidying using floating point arithmetic. +e.g. suppose we have r = 961838 * 2^24 + 519308 +then if we set alpha = 3 * 2^51 * 2^24, +FP(FP(r + alpha) - alpha) = 961838 * 2^24, because the precision for +the intermediate results is limited. Our values of alpha are chosen +to truncate to a desired number of bits. + +The reduction is then performed as in [4], adding multiples of prime p. +e.g. suppose we are working over a polynomial of 10^2. Take the number +2 * 10^8 + 11 * 10^6 + 53 * 10^4 + 23 * 10^2 + 95, stored in 5 elements +for coefficients of 10^0, 10^2, ..., 10^8. +We wish to reduce modulo p = 10^6 - 2 * 10^4 + 1 +We can subtract off from the higher terms +(2 * 10^8 + 11 * 10^6 + 53 * 10^4 + 23 * 10^2 + 95) - (2 * 10^2) * (10^6 - 2 * 10^4 + 1) += 15 * 10^6 + 53 * 10^4 + 21 * 10^2 + 95 += 15 * 10^6 + 53 * 10^4 + 21 * 10^2 + 95 - (15) * (10^6 - 2 * 10^4 + 1) += 83 * 10^4 + 21 * 10^2 + 80 + +Integrated Example +------------------ +This example shows how multiplication, addition, tidying, and reduction +work together in our modular arithmetic. This is simplified from the +actual implementation, but should convey the main concepts. +Working over polynomials of 10^2 and with p as in the prior example, +Let a = 16 * 10^4 + 53 * 10^2 + 33 +let b = 81 * 10^4 + 31 * 10^2 + 49 +let c = 22 * 10^4 + 0 * 10^2 + 95 +And suppose we want to compute a * b + c mod p. +We first do a multiplication: then a * b = +0 * 10^10 + 1296 * 10^8 + 4789 * 10^6 + 5100 * 10^4 + 3620 * 10^2 + 1617 +Then we add in c before doing reduction, allowing us to get a * b + c = +0 * 10^10 + 1296 * 10^8 + 4789 * 10^6 + 5122 * 10^4 + 3620 * 10^2 + 1712 +We then perform a tidying on the upper half of the terms: +0 * 10^10 + 1296 * 10^8 + 4789 * 10^6 +0 * 10^10 + (1296 + 47) * 10^8 + 89 * 10^6 +0 * 10^10 + 1343 * 10^8 + 89 * 10^6 +13 * 10^10 + 43 * 10^8 + 89 * 10^6 +which then gives us +13 * 10^10 + 43 * 10^8 + 89 * 10^6 + 5122 * 10^4 + 3620 * 10^2 + 1712 +we then reduce modulo p similar to the reduction example above: +13 * 10^10 + 43 * 10^8 + 89 * 10^6 + 5122 * 10^4 + 3620 * 10^2 + 1712 + - (13 * 10^4 * p) +69 * 10^8 + 89 * 10^6 + 5109 * 10^4 + 3620 * 10^2 + 1712 + - (69 * 10^2 * p) +227 * 10^6 + 5109 * 10^4 + 3551 * 10^2 + 1712 + - (227 * p) +5563 * 10^4 + 3551 * 10^2 + 1485 +finally, we do tidying to get the precision of each term down to 2 digits +5563 * 10^4 + 3565 * 10^2 + 85 +5598 * 10^4 + 65 * 10^2 + 85 +55 * 10^6 + 98 * 10^4 + 65 * 10^2 + 85 +and perform another reduction step + - (55 * p) +208 * 10^4 + 65 * 10^2 + 30 +There may be a small number of further reductions that could be done at +this point, but this is typically done only at the end when converting +from floating point to an integer unit representation. + +FLOATING POINT PRECISION +======================== +This section discusses the precision of floating point numbers, which +one writing new formulae or a larger bit size should be aware of. The +danger is that an intermediate result may be required to store a +mantissa larger than 53 bits, which would cause error by rounding off. + +Note that the tidying with IEEE rounding mode set to round-to-even +allows negative numbers, which actually reduces the size of the double +mantissa to 23 bits - since it rounds the mantissa to the nearest number +modulo 2^24, i.e. roughly between -2^23 and 2^23. +A multiplication increases the bit size to 2^46 * n, where n is the number +of doubles to store a number. For the 224 bit curve, n = 10. This gives +doubles of size 5 * 2^47. Adding two of these doubles gives a result +of size 5 * 2^48, which is less than 2^53, so this is safe. +Similar analysis can be done for other formulae to ensure numbers remain +below 2^53. + +Extended-Precision Floating Point +--------------------------------- +Some platforms, notably x86 Linux, may use an extended-precision floating +point representation that has a 64-bit mantissa. [6] Although this +implementation is optimized for the IEEE standard 53-bit mantissa, +it should work with the 64-bit mantissa. A check is done at run-time +in the function ec_set_fp_precision that detects if the precision is +greater than 53 bits, and runs code for the 64-bit mantissa accordingly. + +REFERENCES +========== +[1] Certicom Corp., "SEC 2: Recommended Elliptic Curve Domain Parameters", Sept. 20, 2000. www.secg.org +[2] Sun Microsystems Inc. UltraSPARC III Cu User's Manual, Version 1.0, May 2002, Table 4.4 +[3] H. Cohen, A. Miyaji, and T. Ono, "Efficient Elliptic Curve Exponentiation Using Mixed Coordinates". +[4] Henk C.A. van Tilborg, Generalized Mersenne Prime. http://www.win.tue.nl/~henkvt/GenMersenne.pdf +[5] Daniel J. Bernstein, Floating-Point Arithmetic and Message Authentication, Journal of Cryptology, March 2000, Section 2. +[6] Daniel J. Bernstein, Floating-Point Arithmetic and Message Authentication, Journal of Cryptology, March 2000, Section 2 Notes. diff --git a/nss/lib/freebl/ecl/ec2.h b/nss/lib/freebl/ecl/ec2.h new file mode 100644 index 0000000..5d75d48 --- /dev/null +++ b/nss/lib/freebl/ecl/ec2.h @@ -0,0 +1,92 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __ec2_h_ +#define __ec2_h_ + +#include "ecl-priv.h" + +/* Checks if point P(px, py) is at infinity. Uses affine coordinates. */ +mp_err ec_GF2m_pt_is_inf_aff(const mp_int *px, const mp_int *py); + +/* Sets P(px, py) to be the point at infinity. Uses affine coordinates. */ +mp_err ec_GF2m_pt_set_inf_aff(mp_int *px, mp_int *py); + +/* Computes R = P + Q where R is (rx, ry), P is (px, py) and Q is (qx, + * qy). Uses affine coordinates. */ +mp_err ec_GF2m_pt_add_aff(const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes R = P - Q. Uses affine coordinates. */ +mp_err ec_GF2m_pt_sub_aff(const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes R = 2P. Uses affine coordinates. */ +mp_err ec_GF2m_pt_dbl_aff(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Validates a point on a GF2m curve. */ +mp_err ec_GF2m_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group); + +/* by default, this routine is unused and thus doesn't need to be compiled */ +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the irreducible that + * determines the field GF2m. Uses affine coordinates. */ +mp_err ec_GF2m_pt_mul_aff(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); +#endif + +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the irreducible that + * determines the field GF2m. Uses Montgomery projective coordinates. */ +mp_err ec_GF2m_pt_mul_mont(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); + +#ifdef ECL_ENABLE_GF2M_PROJ +/* Converts a point P(px, py) from affine coordinates to projective + * coordinates R(rx, ry, rz). */ +mp_err ec_GF2m_pt_aff2proj(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group); + +/* Converts a point P(px, py, pz) from projective coordinates to affine + * coordinates R(rx, ry). */ +mp_err ec_GF2m_pt_proj2aff(const mp_int *px, const mp_int *py, + const mp_int *pz, mp_int *rx, mp_int *ry, + const ECGroup *group); + +/* Checks if point P(px, py, pz) is at infinity. Uses projective + * coordinates. */ +mp_err ec_GF2m_pt_is_inf_proj(const mp_int *px, const mp_int *py, + const mp_int *pz); + +/* Sets P(px, py, pz) to be the point at infinity. Uses projective + * coordinates. */ +mp_err ec_GF2m_pt_set_inf_proj(mp_int *px, mp_int *py, mp_int *pz); + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, qz). Uses projective coordinates. */ +mp_err ec_GF2m_pt_add_proj(const mp_int *px, const mp_int *py, + const mp_int *pz, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + mp_int *rz, const ECGroup *group); + +/* Computes R = 2P. Uses projective coordinates. */ +mp_err ec_GF2m_pt_dbl_proj(const mp_int *px, const mp_int *py, + const mp_int *pz, mp_int *rx, mp_int *ry, + mp_int *rz, const ECGroup *group); + +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GF2m. Uses projective coordinates. */ +mp_err ec_GF2m_pt_mul_proj(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); +#endif + +#endif /* __ec2_h_ */ diff --git a/nss/lib/freebl/ecl/ec2_163.c b/nss/lib/freebl/ecl/ec2_163.c new file mode 100644 index 0000000..8ed40a4 --- /dev/null +++ b/nss/lib/freebl/ecl/ec2_163.c @@ -0,0 +1,223 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ec2.h" +#include "mp_gf2m.h" +#include "mp_gf2m-priv.h" +#include "mpi.h" +#include "mpi-priv.h" +#include <stdlib.h> + +/* Fast reduction for polynomials over a 163-bit curve. Assumes reduction + * polynomial with terms {163, 7, 6, 3, 0}. */ +mp_err +ec_GF2m_163_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, z; + + if (a != r) { + MP_CHECKOK(mp_copy(a, r)); + } +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(r) < 6) { + MP_CHECKOK(s_mp_pad(r, 6)); + } + u = MP_DIGITS(r); + MP_USED(r) = 6; + + /* u[5] only has 6 significant bits */ + z = u[5]; + u[2] ^= (z << 36) ^ (z << 35) ^ (z << 32) ^ (z << 29); + z = u[4]; + u[2] ^= (z >> 28) ^ (z >> 29) ^ (z >> 32) ^ (z >> 35); + u[1] ^= (z << 36) ^ (z << 35) ^ (z << 32) ^ (z << 29); + z = u[3]; + u[1] ^= (z >> 28) ^ (z >> 29) ^ (z >> 32) ^ (z >> 35); + u[0] ^= (z << 36) ^ (z << 35) ^ (z << 32) ^ (z << 29); + z = u[2] >> 35; /* z only has 29 significant bits */ + u[0] ^= (z << 7) ^ (z << 6) ^ (z << 3) ^ z; + /* clear bits above 163 */ + u[5] = u[4] = u[3] = 0; + u[2] ^= z << 35; +#else + if (MP_USED(r) < 11) { + MP_CHECKOK(s_mp_pad(r, 11)); + } + u = MP_DIGITS(r); + MP_USED(r) = 11; + + /* u[11] only has 6 significant bits */ + z = u[10]; + u[5] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[4] ^= (z << 29); + z = u[9]; + u[5] ^= (z >> 28) ^ (z >> 29); + u[4] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[3] ^= (z << 29); + z = u[8]; + u[4] ^= (z >> 28) ^ (z >> 29); + u[3] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[2] ^= (z << 29); + z = u[7]; + u[3] ^= (z >> 28) ^ (z >> 29); + u[2] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[1] ^= (z << 29); + z = u[6]; + u[2] ^= (z >> 28) ^ (z >> 29); + u[1] ^= (z << 4) ^ (z << 3) ^ z ^ (z >> 3); + u[0] ^= (z << 29); + z = u[5] >> 3; /* z only has 29 significant bits */ + u[1] ^= (z >> 25) ^ (z >> 26); + u[0] ^= (z << 7) ^ (z << 6) ^ (z << 3) ^ z; + /* clear bits above 163 */ + u[11] = u[10] = u[9] = u[8] = u[7] = u[6] = 0; + u[5] ^= z << 3; +#endif + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* Fast squaring for polynomials over a 163-bit curve. Assumes reduction + * polynomial with terms {163, 7, 6, 3, 0}. */ +mp_err +ec_GF2m_163_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, *v; + + v = MP_DIGITS(a); + +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(a) < 3) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 6) { + MP_CHECKOK(s_mp_pad(r, 6)); + } + MP_USED(r) = 6; +#else + if (MP_USED(a) < 6) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 12) { + MP_CHECKOK(s_mp_pad(r, 12)); + } + MP_USED(r) = 12; +#endif + u = MP_DIGITS(r); + +#ifdef ECL_THIRTY_TWO_BIT + u[11] = gf2m_SQR1(v[5]); + u[10] = gf2m_SQR0(v[5]); + u[9] = gf2m_SQR1(v[4]); + u[8] = gf2m_SQR0(v[4]); + u[7] = gf2m_SQR1(v[3]); + u[6] = gf2m_SQR0(v[3]); +#endif + u[5] = gf2m_SQR1(v[2]); + u[4] = gf2m_SQR0(v[2]); + u[3] = gf2m_SQR1(v[1]); + u[2] = gf2m_SQR0(v[1]); + u[1] = gf2m_SQR1(v[0]); + u[0] = gf2m_SQR0(v[0]); + return ec_GF2m_163_mod(r, r, meth); + + CLEANUP: + return res; +} + +/* Fast multiplication for polynomials over a 163-bit curve. Assumes + * reduction polynomial with terms {163, 7, 6, 3, 0}. */ +mp_err +ec_GF2m_163_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a2 = 0, a1 = 0, a0, b2 = 0, b1 = 0, b0; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a5 = 0, a4 = 0, a3 = 0, b5 = 0, b4 = 0, b3 = 0; + mp_digit rm[6]; +#endif + + if (a == b) { + return ec_GF2m_163_sqr(a, r, meth); + } else { + switch (MP_USED(a)) { +#ifdef ECL_THIRTY_TWO_BIT + case 6: + a5 = MP_DIGIT(a, 5); + case 5: + a4 = MP_DIGIT(a, 4); + case 4: + a3 = MP_DIGIT(a, 3); +#endif + case 3: + a2 = MP_DIGIT(a, 2); + case 2: + a1 = MP_DIGIT(a, 1); + default: + a0 = MP_DIGIT(a, 0); + } + switch (MP_USED(b)) { +#ifdef ECL_THIRTY_TWO_BIT + case 6: + b5 = MP_DIGIT(b, 5); + case 5: + b4 = MP_DIGIT(b, 4); + case 4: + b3 = MP_DIGIT(b, 3); +#endif + case 3: + b2 = MP_DIGIT(b, 2); + case 2: + b1 = MP_DIGIT(b, 1); + default: + b0 = MP_DIGIT(b, 0); + } +#ifdef ECL_SIXTY_FOUR_BIT + MP_CHECKOK(s_mp_pad(r, 6)); + s_bmul_3x3(MP_DIGITS(r), a2, a1, a0, b2, b1, b0); + MP_USED(r) = 6; + s_mp_clamp(r); +#else + MP_CHECKOK(s_mp_pad(r, 12)); + s_bmul_3x3(MP_DIGITS(r) + 6, a5, a4, a3, b5, b4, b3); + s_bmul_3x3(MP_DIGITS(r), a2, a1, a0, b2, b1, b0); + s_bmul_3x3(rm, a5 ^ a2, a4 ^ a1, a3 ^ a0, b5 ^ b2, b4 ^ b1, + b3 ^ b0); + rm[5] ^= MP_DIGIT(r, 5) ^ MP_DIGIT(r, 11); + rm[4] ^= MP_DIGIT(r, 4) ^ MP_DIGIT(r, 10); + rm[3] ^= MP_DIGIT(r, 3) ^ MP_DIGIT(r, 9); + rm[2] ^= MP_DIGIT(r, 2) ^ MP_DIGIT(r, 8); + rm[1] ^= MP_DIGIT(r, 1) ^ MP_DIGIT(r, 7); + rm[0] ^= MP_DIGIT(r, 0) ^ MP_DIGIT(r, 6); + MP_DIGIT(r, 8) ^= rm[5]; + MP_DIGIT(r, 7) ^= rm[4]; + MP_DIGIT(r, 6) ^= rm[3]; + MP_DIGIT(r, 5) ^= rm[2]; + MP_DIGIT(r, 4) ^= rm[1]; + MP_DIGIT(r, 3) ^= rm[0]; + MP_USED(r) = 12; + s_mp_clamp(r); +#endif + return ec_GF2m_163_mod(r, r, meth); + } + + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic for 163-bit curves. */ +mp_err +ec_group_set_gf2m163(ECGroup *group, ECCurveName name) +{ + group->meth->field_mod = &ec_GF2m_163_mod; + group->meth->field_mul = &ec_GF2m_163_mul; + group->meth->field_sqr = &ec_GF2m_163_sqr; + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ec2_193.c b/nss/lib/freebl/ecl/ec2_193.c new file mode 100644 index 0000000..edb38a6 --- /dev/null +++ b/nss/lib/freebl/ecl/ec2_193.c @@ -0,0 +1,240 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ec2.h" +#include "mp_gf2m.h" +#include "mp_gf2m-priv.h" +#include "mpi.h" +#include "mpi-priv.h" +#include <stdlib.h> + +/* Fast reduction for polynomials over a 193-bit curve. Assumes reduction + * polynomial with terms {193, 15, 0}. */ +mp_err +ec_GF2m_193_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, z; + + if (a != r) { + MP_CHECKOK(mp_copy(a, r)); + } +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(r) < 7) { + MP_CHECKOK(s_mp_pad(r, 7)); + } + u = MP_DIGITS(r); + MP_USED(r) = 7; + + /* u[6] only has 2 significant bits */ + z = u[6]; + u[3] ^= (z << 14) ^ (z >> 1); + u[2] ^= (z << 63); + z = u[5]; + u[3] ^= (z >> 50); + u[2] ^= (z << 14) ^ (z >> 1); + u[1] ^= (z << 63); + z = u[4]; + u[2] ^= (z >> 50); + u[1] ^= (z << 14) ^ (z >> 1); + u[0] ^= (z << 63); + z = u[3] >> 1; /* z only has 63 significant bits */ + u[1] ^= (z >> 49); + u[0] ^= (z << 15) ^ z; + /* clear bits above 193 */ + u[6] = u[5] = u[4] = 0; + u[3] ^= z << 1; +#else + if (MP_USED(r) < 13) { + MP_CHECKOK(s_mp_pad(r, 13)); + } + u = MP_DIGITS(r); + MP_USED(r) = 13; + + /* u[12] only has 2 significant bits */ + z = u[12]; + u[6] ^= (z << 14) ^ (z >> 1); + u[5] ^= (z << 31); + z = u[11]; + u[6] ^= (z >> 18); + u[5] ^= (z << 14) ^ (z >> 1); + u[4] ^= (z << 31); + z = u[10]; + u[5] ^= (z >> 18); + u[4] ^= (z << 14) ^ (z >> 1); + u[3] ^= (z << 31); + z = u[9]; + u[4] ^= (z >> 18); + u[3] ^= (z << 14) ^ (z >> 1); + u[2] ^= (z << 31); + z = u[8]; + u[3] ^= (z >> 18); + u[2] ^= (z << 14) ^ (z >> 1); + u[1] ^= (z << 31); + z = u[7]; + u[2] ^= (z >> 18); + u[1] ^= (z << 14) ^ (z >> 1); + u[0] ^= (z << 31); + z = u[6] >> 1; /* z only has 31 significant bits */ + u[1] ^= (z >> 17); + u[0] ^= (z << 15) ^ z; + /* clear bits above 193 */ + u[12] = u[11] = u[10] = u[9] = u[8] = u[7] = 0; + u[6] ^= z << 1; +#endif + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* Fast squaring for polynomials over a 193-bit curve. Assumes reduction + * polynomial with terms {193, 15, 0}. */ +mp_err +ec_GF2m_193_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, *v; + + v = MP_DIGITS(a); + +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(a) < 4) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 7) { + MP_CHECKOK(s_mp_pad(r, 7)); + } + MP_USED(r) = 7; +#else + if (MP_USED(a) < 7) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 13) { + MP_CHECKOK(s_mp_pad(r, 13)); + } + MP_USED(r) = 13; +#endif + u = MP_DIGITS(r); + +#ifdef ECL_THIRTY_TWO_BIT + u[12] = gf2m_SQR0(v[6]); + u[11] = gf2m_SQR1(v[5]); + u[10] = gf2m_SQR0(v[5]); + u[9] = gf2m_SQR1(v[4]); + u[8] = gf2m_SQR0(v[4]); + u[7] = gf2m_SQR1(v[3]); +#endif + u[6] = gf2m_SQR0(v[3]); + u[5] = gf2m_SQR1(v[2]); + u[4] = gf2m_SQR0(v[2]); + u[3] = gf2m_SQR1(v[1]); + u[2] = gf2m_SQR0(v[1]); + u[1] = gf2m_SQR1(v[0]); + u[0] = gf2m_SQR0(v[0]); + return ec_GF2m_193_mod(r, r, meth); + + CLEANUP: + return res; +} + +/* Fast multiplication for polynomials over a 193-bit curve. Assumes + * reduction polynomial with terms {193, 15, 0}. */ +mp_err +ec_GF2m_193_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a3 = 0, a2 = 0, a1 = 0, a0, b3 = 0, b2 = 0, b1 = 0, b0; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a6 = 0, a5 = 0, a4 = 0, b6 = 0, b5 = 0, b4 = 0; + mp_digit rm[8]; +#endif + + if (a == b) { + return ec_GF2m_193_sqr(a, r, meth); + } else { + switch (MP_USED(a)) { +#ifdef ECL_THIRTY_TWO_BIT + case 7: + a6 = MP_DIGIT(a, 6); + case 6: + a5 = MP_DIGIT(a, 5); + case 5: + a4 = MP_DIGIT(a, 4); +#endif + case 4: + a3 = MP_DIGIT(a, 3); + case 3: + a2 = MP_DIGIT(a, 2); + case 2: + a1 = MP_DIGIT(a, 1); + default: + a0 = MP_DIGIT(a, 0); + } + switch (MP_USED(b)) { +#ifdef ECL_THIRTY_TWO_BIT + case 7: + b6 = MP_DIGIT(b, 6); + case 6: + b5 = MP_DIGIT(b, 5); + case 5: + b4 = MP_DIGIT(b, 4); +#endif + case 4: + b3 = MP_DIGIT(b, 3); + case 3: + b2 = MP_DIGIT(b, 2); + case 2: + b1 = MP_DIGIT(b, 1); + default: + b0 = MP_DIGIT(b, 0); + } +#ifdef ECL_SIXTY_FOUR_BIT + MP_CHECKOK(s_mp_pad(r, 8)); + s_bmul_4x4(MP_DIGITS(r), a3, a2, a1, a0, b3, b2, b1, b0); + MP_USED(r) = 8; + s_mp_clamp(r); +#else + MP_CHECKOK(s_mp_pad(r, 14)); + s_bmul_3x3(MP_DIGITS(r) + 8, a6, a5, a4, b6, b5, b4); + s_bmul_4x4(MP_DIGITS(r), a3, a2, a1, a0, b3, b2, b1, b0); + s_bmul_4x4(rm, a3, a6 ^ a2, a5 ^ a1, a4 ^ a0, b3, b6 ^ b2, b5 ^ b1, + b4 ^ b0); + rm[7] ^= MP_DIGIT(r, 7); + rm[6] ^= MP_DIGIT(r, 6); + rm[5] ^= MP_DIGIT(r, 5) ^ MP_DIGIT(r, 13); + rm[4] ^= MP_DIGIT(r, 4) ^ MP_DIGIT(r, 12); + rm[3] ^= MP_DIGIT(r, 3) ^ MP_DIGIT(r, 11); + rm[2] ^= MP_DIGIT(r, 2) ^ MP_DIGIT(r, 10); + rm[1] ^= MP_DIGIT(r, 1) ^ MP_DIGIT(r, 9); + rm[0] ^= MP_DIGIT(r, 0) ^ MP_DIGIT(r, 8); + MP_DIGIT(r, 11) ^= rm[7]; + MP_DIGIT(r, 10) ^= rm[6]; + MP_DIGIT(r, 9) ^= rm[5]; + MP_DIGIT(r, 8) ^= rm[4]; + MP_DIGIT(r, 7) ^= rm[3]; + MP_DIGIT(r, 6) ^= rm[2]; + MP_DIGIT(r, 5) ^= rm[1]; + MP_DIGIT(r, 4) ^= rm[0]; + MP_USED(r) = 14; + s_mp_clamp(r); +#endif + return ec_GF2m_193_mod(r, r, meth); + } + + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic for 193-bit curves. */ +mp_err +ec_group_set_gf2m193(ECGroup *group, ECCurveName name) +{ + group->meth->field_mod = &ec_GF2m_193_mod; + group->meth->field_mul = &ec_GF2m_193_mul; + group->meth->field_sqr = &ec_GF2m_193_sqr; + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ec2_233.c b/nss/lib/freebl/ecl/ec2_233.c new file mode 100644 index 0000000..f73673c --- /dev/null +++ b/nss/lib/freebl/ecl/ec2_233.c @@ -0,0 +1,263 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ec2.h" +#include "mp_gf2m.h" +#include "mp_gf2m-priv.h" +#include "mpi.h" +#include "mpi-priv.h" +#include <stdlib.h> + +/* Fast reduction for polynomials over a 233-bit curve. Assumes reduction + * polynomial with terms {233, 74, 0}. */ +mp_err +ec_GF2m_233_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, z; + + if (a != r) { + MP_CHECKOK(mp_copy(a, r)); + } +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(r) < 8) { + MP_CHECKOK(s_mp_pad(r, 8)); + } + u = MP_DIGITS(r); + MP_USED(r) = 8; + + /* u[7] only has 18 significant bits */ + z = u[7]; + u[4] ^= (z << 33) ^ (z >> 41); + u[3] ^= (z << 23); + z = u[6]; + u[4] ^= (z >> 31); + u[3] ^= (z << 33) ^ (z >> 41); + u[2] ^= (z << 23); + z = u[5]; + u[3] ^= (z >> 31); + u[2] ^= (z << 33) ^ (z >> 41); + u[1] ^= (z << 23); + z = u[4]; + u[2] ^= (z >> 31); + u[1] ^= (z << 33) ^ (z >> 41); + u[0] ^= (z << 23); + z = u[3] >> 41; /* z only has 23 significant bits */ + u[1] ^= (z << 10); + u[0] ^= z; + /* clear bits above 233 */ + u[7] = u[6] = u[5] = u[4] = 0; + u[3] ^= z << 41; +#else + if (MP_USED(r) < 15) { + MP_CHECKOK(s_mp_pad(r, 15)); + } + u = MP_DIGITS(r); + MP_USED(r) = 15; + + /* u[14] only has 18 significant bits */ + z = u[14]; + u[9] ^= (z << 1); + u[7] ^= (z >> 9); + u[6] ^= (z << 23); + z = u[13]; + u[9] ^= (z >> 31); + u[8] ^= (z << 1); + u[6] ^= (z >> 9); + u[5] ^= (z << 23); + z = u[12]; + u[8] ^= (z >> 31); + u[7] ^= (z << 1); + u[5] ^= (z >> 9); + u[4] ^= (z << 23); + z = u[11]; + u[7] ^= (z >> 31); + u[6] ^= (z << 1); + u[4] ^= (z >> 9); + u[3] ^= (z << 23); + z = u[10]; + u[6] ^= (z >> 31); + u[5] ^= (z << 1); + u[3] ^= (z >> 9); + u[2] ^= (z << 23); + z = u[9]; + u[5] ^= (z >> 31); + u[4] ^= (z << 1); + u[2] ^= (z >> 9); + u[1] ^= (z << 23); + z = u[8]; + u[4] ^= (z >> 31); + u[3] ^= (z << 1); + u[1] ^= (z >> 9); + u[0] ^= (z << 23); + z = u[7] >> 9; /* z only has 23 significant bits */ + u[3] ^= (z >> 22); + u[2] ^= (z << 10); + u[0] ^= z; + /* clear bits above 233 */ + u[14] = u[13] = u[12] = u[11] = u[10] = u[9] = u[8] = 0; + u[7] ^= z << 9; +#endif + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* Fast squaring for polynomials over a 233-bit curve. Assumes reduction + * polynomial with terms {233, 74, 0}. */ +mp_err +ec_GF2m_233_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit *u, *v; + + v = MP_DIGITS(a); + +#ifdef ECL_SIXTY_FOUR_BIT + if (MP_USED(a) < 4) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 8) { + MP_CHECKOK(s_mp_pad(r, 8)); + } + MP_USED(r) = 8; +#else + if (MP_USED(a) < 8) { + return mp_bsqrmod(a, meth->irr_arr, r); + } + if (MP_USED(r) < 15) { + MP_CHECKOK(s_mp_pad(r, 15)); + } + MP_USED(r) = 15; +#endif + u = MP_DIGITS(r); + +#ifdef ECL_THIRTY_TWO_BIT + u[14] = gf2m_SQR0(v[7]); + u[13] = gf2m_SQR1(v[6]); + u[12] = gf2m_SQR0(v[6]); + u[11] = gf2m_SQR1(v[5]); + u[10] = gf2m_SQR0(v[5]); + u[9] = gf2m_SQR1(v[4]); + u[8] = gf2m_SQR0(v[4]); +#endif + u[7] = gf2m_SQR1(v[3]); + u[6] = gf2m_SQR0(v[3]); + u[5] = gf2m_SQR1(v[2]); + u[4] = gf2m_SQR0(v[2]); + u[3] = gf2m_SQR1(v[1]); + u[2] = gf2m_SQR0(v[1]); + u[1] = gf2m_SQR1(v[0]); + u[0] = gf2m_SQR0(v[0]); + return ec_GF2m_233_mod(r, r, meth); + + CLEANUP: + return res; +} + +/* Fast multiplication for polynomials over a 233-bit curve. Assumes + * reduction polynomial with terms {233, 74, 0}. */ +mp_err +ec_GF2m_233_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a3 = 0, a2 = 0, a1 = 0, a0, b3 = 0, b2 = 0, b1 = 0, b0; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a7 = 0, a6 = 0, a5 = 0, a4 = 0, b7 = 0, b6 = 0, b5 = 0, b4 = + 0; + mp_digit rm[8]; +#endif + + if (a == b) { + return ec_GF2m_233_sqr(a, r, meth); + } else { + switch (MP_USED(a)) { +#ifdef ECL_THIRTY_TWO_BIT + case 8: + a7 = MP_DIGIT(a, 7); + case 7: + a6 = MP_DIGIT(a, 6); + case 6: + a5 = MP_DIGIT(a, 5); + case 5: + a4 = MP_DIGIT(a, 4); +#endif + case 4: + a3 = MP_DIGIT(a, 3); + case 3: + a2 = MP_DIGIT(a, 2); + case 2: + a1 = MP_DIGIT(a, 1); + default: + a0 = MP_DIGIT(a, 0); + } + switch (MP_USED(b)) { +#ifdef ECL_THIRTY_TWO_BIT + case 8: + b7 = MP_DIGIT(b, 7); + case 7: + b6 = MP_DIGIT(b, 6); + case 6: + b5 = MP_DIGIT(b, 5); + case 5: + b4 = MP_DIGIT(b, 4); +#endif + case 4: + b3 = MP_DIGIT(b, 3); + case 3: + b2 = MP_DIGIT(b, 2); + case 2: + b1 = MP_DIGIT(b, 1); + default: + b0 = MP_DIGIT(b, 0); + } +#ifdef ECL_SIXTY_FOUR_BIT + MP_CHECKOK(s_mp_pad(r, 8)); + s_bmul_4x4(MP_DIGITS(r), a3, a2, a1, a0, b3, b2, b1, b0); + MP_USED(r) = 8; + s_mp_clamp(r); +#else + MP_CHECKOK(s_mp_pad(r, 16)); + s_bmul_4x4(MP_DIGITS(r) + 8, a7, a6, a5, a4, b7, b6, b5, b4); + s_bmul_4x4(MP_DIGITS(r), a3, a2, a1, a0, b3, b2, b1, b0); + s_bmul_4x4(rm, a7 ^ a3, a6 ^ a2, a5 ^ a1, a4 ^ a0, b7 ^ b3, + b6 ^ b2, b5 ^ b1, b4 ^ b0); + rm[7] ^= MP_DIGIT(r, 7) ^ MP_DIGIT(r, 15); + rm[6] ^= MP_DIGIT(r, 6) ^ MP_DIGIT(r, 14); + rm[5] ^= MP_DIGIT(r, 5) ^ MP_DIGIT(r, 13); + rm[4] ^= MP_DIGIT(r, 4) ^ MP_DIGIT(r, 12); + rm[3] ^= MP_DIGIT(r, 3) ^ MP_DIGIT(r, 11); + rm[2] ^= MP_DIGIT(r, 2) ^ MP_DIGIT(r, 10); + rm[1] ^= MP_DIGIT(r, 1) ^ MP_DIGIT(r, 9); + rm[0] ^= MP_DIGIT(r, 0) ^ MP_DIGIT(r, 8); + MP_DIGIT(r, 11) ^= rm[7]; + MP_DIGIT(r, 10) ^= rm[6]; + MP_DIGIT(r, 9) ^= rm[5]; + MP_DIGIT(r, 8) ^= rm[4]; + MP_DIGIT(r, 7) ^= rm[3]; + MP_DIGIT(r, 6) ^= rm[2]; + MP_DIGIT(r, 5) ^= rm[1]; + MP_DIGIT(r, 4) ^= rm[0]; + MP_USED(r) = 16; + s_mp_clamp(r); +#endif + return ec_GF2m_233_mod(r, r, meth); + } + + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic for 233-bit curves. */ +mp_err +ec_group_set_gf2m233(ECGroup *group, ECCurveName name) +{ + group->meth->field_mod = &ec_GF2m_233_mod; + group->meth->field_mul = &ec_GF2m_233_mul; + group->meth->field_sqr = &ec_GF2m_233_sqr; + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ec2_aff.c b/nss/lib/freebl/ecl/ec2_aff.c new file mode 100644 index 0000000..50edc54 --- /dev/null +++ b/nss/lib/freebl/ecl/ec2_aff.c @@ -0,0 +1,312 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ec2.h" +#include "mplogic.h" +#include "mp_gf2m.h" +#include <stdlib.h> + +/* Checks if point P(px, py) is at infinity. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_is_inf_aff(const mp_int *px, const mp_int *py) +{ + + if ((mp_cmp_z(px) == 0) && (mp_cmp_z(py) == 0)) { + return MP_YES; + } else { + return MP_NO; + } + +} + +/* Sets P(px, py) to be the point at infinity. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_set_inf_aff(mp_int *px, mp_int *py) +{ + mp_zero(px); + mp_zero(py); + return MP_OKAY; +} + +/* Computes R = P + Q based on IEEE P1363 A.10.2. Elliptic curve points P, + * Q, and R can all be identical. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_add_aff(const mp_int *px, const mp_int *py, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int lambda, tempx, tempy; + + MP_DIGITS(&lambda) = 0; + MP_DIGITS(&tempx) = 0; + MP_DIGITS(&tempy) = 0; + MP_CHECKOK(mp_init(&lambda)); + MP_CHECKOK(mp_init(&tempx)); + MP_CHECKOK(mp_init(&tempy)); + /* if P = inf, then R = Q */ + if (ec_GF2m_pt_is_inf_aff(px, py) == 0) { + MP_CHECKOK(mp_copy(qx, rx)); + MP_CHECKOK(mp_copy(qy, ry)); + res = MP_OKAY; + goto CLEANUP; + } + /* if Q = inf, then R = P */ + if (ec_GF2m_pt_is_inf_aff(qx, qy) == 0) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + res = MP_OKAY; + goto CLEANUP; + } + /* if px != qx, then lambda = (py+qy) / (px+qx), tempx = a + lambda^2 + * + lambda + px + qx */ + if (mp_cmp(px, qx) != 0) { + MP_CHECKOK(group->meth->field_add(py, qy, &tempy, group->meth)); + MP_CHECKOK(group->meth->field_add(px, qx, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_div(&tempy, &tempx, &lambda, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &group->curvea, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, px, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, qx, &tempx, group->meth)); + } else { + /* if py != qy or qx = 0, then R = inf */ + if (((mp_cmp(py, qy) != 0)) || (mp_cmp_z(qx) == 0)) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + } + /* lambda = qx + qy / qx */ + MP_CHECKOK(group->meth->field_div(qy, qx, &lambda, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&lambda, qx, &lambda, group->meth)); + /* tempx = a + lambda^2 + lambda */ + MP_CHECKOK(group->meth->field_sqr(&lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &group->curvea, &tempx, group->meth)); + } + /* ry = (qx + tempx) * lambda + tempx + qy */ + MP_CHECKOK(group->meth->field_add(qx, &tempx, &tempy, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&tempy, &lambda, &tempy, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempy, &tempx, &tempy, group->meth)); + MP_CHECKOK(group->meth->field_add(&tempy, qy, ry, group->meth)); + /* rx = tempx */ + MP_CHECKOK(mp_copy(&tempx, rx)); + + CLEANUP: + mp_clear(&lambda); + mp_clear(&tempx); + mp_clear(&tempy); + return res; +} + +/* Computes R = P - Q. Elliptic curve points P, Q, and R can all be + * identical. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_sub_aff(const mp_int *px, const mp_int *py, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int nqy; + + MP_DIGITS(&nqy) = 0; + MP_CHECKOK(mp_init(&nqy)); + /* nqy = qx+qy */ + MP_CHECKOK(group->meth->field_add(qx, qy, &nqy, group->meth)); + MP_CHECKOK(group->point_add(px, py, qx, &nqy, rx, ry, group)); + CLEANUP: + mp_clear(&nqy); + return res; +} + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * affine coordinates. */ +mp_err +ec_GF2m_pt_dbl_aff(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group) +{ + return group->point_add(px, py, px, py, rx, ry, group); +} + +/* by default, this routine is unused and thus doesn't need to be compiled */ +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF +/* Computes R = nP based on IEEE P1363 A.10.3. Elliptic curve points P and + * R can be identical. Uses affine coordinates. */ +mp_err +ec_GF2m_pt_mul_aff(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int k, k3, qx, qy, sx, sy; + int b1, b3, i, l; + + MP_DIGITS(&k) = 0; + MP_DIGITS(&k3) = 0; + MP_DIGITS(&qx) = 0; + MP_DIGITS(&qy) = 0; + MP_DIGITS(&sx) = 0; + MP_DIGITS(&sy) = 0; + MP_CHECKOK(mp_init(&k)); + MP_CHECKOK(mp_init(&k3)); + MP_CHECKOK(mp_init(&qx)); + MP_CHECKOK(mp_init(&qy)); + MP_CHECKOK(mp_init(&sx)); + MP_CHECKOK(mp_init(&sy)); + + /* if n = 0 then r = inf */ + if (mp_cmp_z(n) == 0) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + } + /* Q = P, k = n */ + MP_CHECKOK(mp_copy(px, &qx)); + MP_CHECKOK(mp_copy(py, &qy)); + MP_CHECKOK(mp_copy(n, &k)); + /* if n < 0 then Q = -Q, k = -k */ + if (mp_cmp_z(n) < 0) { + MP_CHECKOK(group->meth->field_add(&qx, &qy, &qy, group->meth)); + MP_CHECKOK(mp_neg(&k, &k)); + } +#ifdef ECL_DEBUG /* basic double and add method */ + l = mpl_significant_bits(&k) - 1; + MP_CHECKOK(mp_copy(&qx, &sx)); + MP_CHECKOK(mp_copy(&qy, &sy)); + for (i = l - 1; i >= 0; i--) { + /* S = 2S */ + MP_CHECKOK(group->point_dbl(&sx, &sy, &sx, &sy, group)); + /* if k_i = 1, then S = S + Q */ + if (mpl_get_bit(&k, i) != 0) { + MP_CHECKOK(group-> + point_add(&sx, &sy, &qx, &qy, &sx, &sy, group)); + } + } +#else /* double and add/subtract method from + * standard */ + /* k3 = 3 * k */ + MP_CHECKOK(mp_set_int(&k3, 3)); + MP_CHECKOK(mp_mul(&k, &k3, &k3)); + /* S = Q */ + MP_CHECKOK(mp_copy(&qx, &sx)); + MP_CHECKOK(mp_copy(&qy, &sy)); + /* l = index of high order bit in binary representation of 3*k */ + l = mpl_significant_bits(&k3) - 1; + /* for i = l-1 downto 1 */ + for (i = l - 1; i >= 1; i--) { + /* S = 2S */ + MP_CHECKOK(group->point_dbl(&sx, &sy, &sx, &sy, group)); + b3 = MP_GET_BIT(&k3, i); + b1 = MP_GET_BIT(&k, i); + /* if k3_i = 1 and k_i = 0, then S = S + Q */ + if ((b3 == 1) && (b1 == 0)) { + MP_CHECKOK(group-> + point_add(&sx, &sy, &qx, &qy, &sx, &sy, group)); + /* if k3_i = 0 and k_i = 1, then S = S - Q */ + } else if ((b3 == 0) && (b1 == 1)) { + MP_CHECKOK(group-> + point_sub(&sx, &sy, &qx, &qy, &sx, &sy, group)); + } + } +#endif + /* output S */ + MP_CHECKOK(mp_copy(&sx, rx)); + MP_CHECKOK(mp_copy(&sy, ry)); + + CLEANUP: + mp_clear(&k); + mp_clear(&k3); + mp_clear(&qx); + mp_clear(&qy); + mp_clear(&sx); + mp_clear(&sy); + return res; +} +#endif + +/* Validates a point on a GF2m curve. */ +mp_err +ec_GF2m_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group) +{ + mp_err res = MP_NO; + mp_int accl, accr, tmp, pxt, pyt; + + MP_DIGITS(&accl) = 0; + MP_DIGITS(&accr) = 0; + MP_DIGITS(&tmp) = 0; + MP_DIGITS(&pxt) = 0; + MP_DIGITS(&pyt) = 0; + MP_CHECKOK(mp_init(&accl)); + MP_CHECKOK(mp_init(&accr)); + MP_CHECKOK(mp_init(&tmp)); + MP_CHECKOK(mp_init(&pxt)); + MP_CHECKOK(mp_init(&pyt)); + + /* 1: Verify that publicValue is not the point at infinity */ + if (ec_GF2m_pt_is_inf_aff(px, py) == MP_YES) { + res = MP_NO; + goto CLEANUP; + } + /* 2: Verify that the coordinates of publicValue are elements + * of the field. + */ + if ((MP_SIGN(px) == MP_NEG) || (mp_cmp(px, &group->meth->irr) >= 0) || + (MP_SIGN(py) == MP_NEG) || (mp_cmp(py, &group->meth->irr) >= 0)) { + res = MP_NO; + goto CLEANUP; + } + /* 3: Verify that publicValue is on the curve. */ + if (group->meth->field_enc) { + group->meth->field_enc(px, &pxt, group->meth); + group->meth->field_enc(py, &pyt, group->meth); + } else { + mp_copy(px, &pxt); + mp_copy(py, &pyt); + } + /* left-hand side: y^2 + x*y */ + MP_CHECKOK( group->meth->field_sqr(&pyt, &accl, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&pxt, &pyt, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_add(&accl, &tmp, &accl, group->meth) ); + /* right-hand side: x^3 + a*x^2 + b */ + MP_CHECKOK( group->meth->field_sqr(&pxt, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&pxt, &tmp, &accr, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&group->curvea, &tmp, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_add(&tmp, &accr, &accr, group->meth) ); + MP_CHECKOK( group->meth->field_add(&accr, &group->curveb, &accr, group->meth) ); + /* check LHS - RHS == 0 */ + MP_CHECKOK( group->meth->field_add(&accl, &accr, &accr, group->meth) ); + if (mp_cmp_z(&accr) != 0) { + res = MP_NO; + goto CLEANUP; + } + /* 4: Verify that the order of the curve times the publicValue + * is the point at infinity. + */ + MP_CHECKOK( ECPoint_mul(group, &group->order, px, py, &pxt, &pyt) ); + if (ec_GF2m_pt_is_inf_aff(&pxt, &pyt) != MP_YES) { + res = MP_NO; + goto CLEANUP; + } + + res = MP_YES; + +CLEANUP: + mp_clear(&accl); + mp_clear(&accr); + mp_clear(&tmp); + mp_clear(&pxt); + mp_clear(&pyt); + return res; +} diff --git a/nss/lib/freebl/ecl/ec2_mont.c b/nss/lib/freebl/ecl/ec2_mont.c new file mode 100644 index 0000000..8d35f25 --- /dev/null +++ b/nss/lib/freebl/ecl/ec2_mont.c @@ -0,0 +1,238 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ec2.h" +#include "mplogic.h" +#include "mp_gf2m.h" +#include <stdlib.h> + +/* Compute the x-coordinate x/z for the point 2*(x/z) in Montgomery + * projective coordinates. Uses algorithm Mdouble in appendix of Lopez, J. + * and Dahab, R. "Fast multiplication on elliptic curves over GF(2^m) + * without precomputation". modified to not require precomputation of + * c=b^{2^{m-1}}. */ +static mp_err +gf2m_Mdouble(mp_int *x, mp_int *z, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int t1; + + MP_DIGITS(&t1) = 0; + MP_CHECKOK(mp_init(&t1)); + + MP_CHECKOK(group->meth->field_sqr(x, x, group->meth)); + MP_CHECKOK(group->meth->field_sqr(z, &t1, group->meth)); + MP_CHECKOK(group->meth->field_mul(x, &t1, z, group->meth)); + MP_CHECKOK(group->meth->field_sqr(x, x, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&t1, &t1, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&group->curveb, &t1, &t1, group->meth)); + MP_CHECKOK(group->meth->field_add(x, &t1, x, group->meth)); + + CLEANUP: + mp_clear(&t1); + return res; +} + +/* Compute the x-coordinate x1/z1 for the point (x1/z1)+(x2/x2) in + * Montgomery projective coordinates. Uses algorithm Madd in appendix of + * Lopex, J. and Dahab, R. "Fast multiplication on elliptic curves over + * GF(2^m) without precomputation". */ +static mp_err +gf2m_Madd(const mp_int *x, mp_int *x1, mp_int *z1, mp_int *x2, mp_int *z2, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int t1, t2; + + MP_DIGITS(&t1) = 0; + MP_DIGITS(&t2) = 0; + MP_CHECKOK(mp_init(&t1)); + MP_CHECKOK(mp_init(&t2)); + + MP_CHECKOK(mp_copy(x, &t1)); + MP_CHECKOK(group->meth->field_mul(x1, z2, x1, group->meth)); + MP_CHECKOK(group->meth->field_mul(z1, x2, z1, group->meth)); + MP_CHECKOK(group->meth->field_mul(x1, z1, &t2, group->meth)); + MP_CHECKOK(group->meth->field_add(z1, x1, z1, group->meth)); + MP_CHECKOK(group->meth->field_sqr(z1, z1, group->meth)); + MP_CHECKOK(group->meth->field_mul(z1, &t1, x1, group->meth)); + MP_CHECKOK(group->meth->field_add(x1, &t2, x1, group->meth)); + + CLEANUP: + mp_clear(&t1); + mp_clear(&t2); + return res; +} + +/* Compute the x, y affine coordinates from the point (x1, z1) (x2, z2) + * using Montgomery point multiplication algorithm Mxy() in appendix of + * Lopex, J. and Dahab, R. "Fast multiplication on elliptic curves over + * GF(2^m) without precomputation". Returns: 0 on error 1 if return value + * should be the point at infinity 2 otherwise */ +static int +gf2m_Mxy(const mp_int *x, const mp_int *y, mp_int *x1, mp_int *z1, + mp_int *x2, mp_int *z2, const ECGroup *group) +{ + mp_err res = MP_OKAY; + int ret = 0; + mp_int t3, t4, t5; + + MP_DIGITS(&t3) = 0; + MP_DIGITS(&t4) = 0; + MP_DIGITS(&t5) = 0; + MP_CHECKOK(mp_init(&t3)); + MP_CHECKOK(mp_init(&t4)); + MP_CHECKOK(mp_init(&t5)); + + if (mp_cmp_z(z1) == 0) { + mp_zero(x2); + mp_zero(z2); + ret = 1; + goto CLEANUP; + } + + if (mp_cmp_z(z2) == 0) { + MP_CHECKOK(mp_copy(x, x2)); + MP_CHECKOK(group->meth->field_add(x, y, z2, group->meth)); + ret = 2; + goto CLEANUP; + } + + MP_CHECKOK(mp_set_int(&t5, 1)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(&t5, &t5, group->meth)); + } + + MP_CHECKOK(group->meth->field_mul(z1, z2, &t3, group->meth)); + + MP_CHECKOK(group->meth->field_mul(z1, x, z1, group->meth)); + MP_CHECKOK(group->meth->field_add(z1, x1, z1, group->meth)); + MP_CHECKOK(group->meth->field_mul(z2, x, z2, group->meth)); + MP_CHECKOK(group->meth->field_mul(z2, x1, x1, group->meth)); + MP_CHECKOK(group->meth->field_add(z2, x2, z2, group->meth)); + + MP_CHECKOK(group->meth->field_mul(z2, z1, z2, group->meth)); + MP_CHECKOK(group->meth->field_sqr(x, &t4, group->meth)); + MP_CHECKOK(group->meth->field_add(&t4, y, &t4, group->meth)); + MP_CHECKOK(group->meth->field_mul(&t4, &t3, &t4, group->meth)); + MP_CHECKOK(group->meth->field_add(&t4, z2, &t4, group->meth)); + + MP_CHECKOK(group->meth->field_mul(&t3, x, &t3, group->meth)); + MP_CHECKOK(group->meth->field_div(&t5, &t3, &t3, group->meth)); + MP_CHECKOK(group->meth->field_mul(&t3, &t4, &t4, group->meth)); + MP_CHECKOK(group->meth->field_mul(x1, &t3, x2, group->meth)); + MP_CHECKOK(group->meth->field_add(x2, x, z2, group->meth)); + + MP_CHECKOK(group->meth->field_mul(z2, &t4, z2, group->meth)); + MP_CHECKOK(group->meth->field_add(z2, y, z2, group->meth)); + + ret = 2; + + CLEANUP: + mp_clear(&t3); + mp_clear(&t4); + mp_clear(&t5); + if (res == MP_OKAY) { + return ret; + } else { + return 0; + } +} + +/* Computes R = nP based on algorithm 2P of Lopex, J. and Dahab, R. "Fast + * multiplication on elliptic curves over GF(2^m) without + * precomputation". Elliptic curve points P and R can be identical. Uses + * Montgomery projective coordinates. */ +mp_err +ec_GF2m_pt_mul_mont(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int x1, x2, z1, z2; + int i, j; + mp_digit top_bit, mask; + + MP_DIGITS(&x1) = 0; + MP_DIGITS(&x2) = 0; + MP_DIGITS(&z1) = 0; + MP_DIGITS(&z2) = 0; + MP_CHECKOK(mp_init(&x1)); + MP_CHECKOK(mp_init(&x2)); + MP_CHECKOK(mp_init(&z1)); + MP_CHECKOK(mp_init(&z2)); + + /* if result should be point at infinity */ + if ((mp_cmp_z(n) == 0) || (ec_GF2m_pt_is_inf_aff(px, py) == MP_YES)) { + MP_CHECKOK(ec_GF2m_pt_set_inf_aff(rx, ry)); + goto CLEANUP; + } + + MP_CHECKOK(mp_copy(px, &x1)); /* x1 = px */ + MP_CHECKOK(mp_set_int(&z1, 1)); /* z1 = 1 */ + MP_CHECKOK(group->meth->field_sqr(&x1, &z2, group->meth)); /* z2 = + * x1^2 = + * px^2 */ + MP_CHECKOK(group->meth->field_sqr(&z2, &x2, group->meth)); + MP_CHECKOK(group->meth->field_add(&x2, &group->curveb, &x2, group->meth)); /* x2 + * = + * px^4 + * + + * b + */ + + /* find top-most bit and go one past it */ + i = MP_USED(n) - 1; + j = MP_DIGIT_BIT - 1; + top_bit = 1; + top_bit <<= MP_DIGIT_BIT - 1; + mask = top_bit; + while (!(MP_DIGITS(n)[i] & mask)) { + mask >>= 1; + j--; + } + mask >>= 1; + j--; + + /* if top most bit was at word break, go to next word */ + if (!mask) { + i--; + j = MP_DIGIT_BIT - 1; + mask = top_bit; + } + + for (; i >= 0; i--) { + for (; j >= 0; j--) { + if (MP_DIGITS(n)[i] & mask) { + MP_CHECKOK(gf2m_Madd(px, &x1, &z1, &x2, &z2, group)); + MP_CHECKOK(gf2m_Mdouble(&x2, &z2, group)); + } else { + MP_CHECKOK(gf2m_Madd(px, &x2, &z2, &x1, &z1, group)); + MP_CHECKOK(gf2m_Mdouble(&x1, &z1, group)); + } + mask >>= 1; + } + j = MP_DIGIT_BIT - 1; + mask = top_bit; + } + + /* convert out of "projective" coordinates */ + i = gf2m_Mxy(px, py, &x1, &z1, &x2, &z2, group); + if (i == 0) { + res = MP_BADARG; + goto CLEANUP; + } else if (i == 1) { + MP_CHECKOK(ec_GF2m_pt_set_inf_aff(rx, ry)); + } else { + MP_CHECKOK(mp_copy(&x2, rx)); + MP_CHECKOK(mp_copy(&z2, ry)); + } + + CLEANUP: + mp_clear(&x1); + mp_clear(&x2); + mp_clear(&z1); + mp_clear(&z2); + return res; +} diff --git a/nss/lib/freebl/ecl/ec2_proj.c b/nss/lib/freebl/ecl/ec2_proj.c new file mode 100644 index 0000000..9378982 --- /dev/null +++ b/nss/lib/freebl/ecl/ec2_proj.c @@ -0,0 +1,333 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ec2.h" +#include "mplogic.h" +#include "mp_gf2m.h" +#include <stdlib.h> +#ifdef ECL_DEBUG +#include <assert.h> +#endif + +/* by default, these routines are unused and thus don't need to be compiled */ +#ifdef ECL_ENABLE_GF2M_PROJ +/* Converts a point P(px, py) from affine coordinates to projective + * coordinates R(rx, ry, rz). Assumes input is already field-encoded using + * field_enc, and returns output that is still field-encoded. */ +mp_err +ec_GF2m_pt_aff2proj(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + MP_CHECKOK(mp_set_int(rz, 1)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(rz, rz, group->meth)); + } + CLEANUP: + return res; +} + +/* Converts a point P(px, py, pz) from projective coordinates to affine + * coordinates R(rx, ry). P and R can share x and y coordinates. Assumes + * input is already field-encoded using field_enc, and returns output that + * is still field-encoded. */ +mp_err +ec_GF2m_pt_proj2aff(const mp_int *px, const mp_int *py, const mp_int *pz, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int z1, z2; + + MP_DIGITS(&z1) = 0; + MP_DIGITS(&z2) = 0; + MP_CHECKOK(mp_init(&z1)); + MP_CHECKOK(mp_init(&z2)); + + /* if point at infinity, then set point at infinity and exit */ + if (ec_GF2m_pt_is_inf_proj(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GF2m_pt_set_inf_aff(rx, ry)); + goto CLEANUP; + } + + /* transform (px, py, pz) into (px / pz, py / pz^2) */ + if (mp_cmp_d(pz, 1) == 0) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + } else { + MP_CHECKOK(group->meth->field_div(NULL, pz, &z1, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&z1, &z2, group->meth)); + MP_CHECKOK(group->meth->field_mul(px, &z1, rx, group->meth)); + MP_CHECKOK(group->meth->field_mul(py, &z2, ry, group->meth)); + } + + CLEANUP: + mp_clear(&z1); + mp_clear(&z2); + return res; +} + +/* Checks if point P(px, py, pz) is at infinity. Uses projective + * coordinates. */ +mp_err +ec_GF2m_pt_is_inf_proj(const mp_int *px, const mp_int *py, + const mp_int *pz) +{ + return mp_cmp_z(pz); +} + +/* Sets P(px, py, pz) to be the point at infinity. Uses projective + * coordinates. */ +mp_err +ec_GF2m_pt_set_inf_proj(mp_int *px, mp_int *py, mp_int *pz) +{ + mp_zero(pz); + return MP_OKAY; +} + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, 1). Elliptic curve points P, Q, and R can all be identical. + * Uses mixed projective-affine coordinates. Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. Uses equation (3) from Hankerson, Hernandez, Menezes. + * Software Implementation of Elliptic Curve Cryptography Over Binary + * Fields. */ +mp_err +ec_GF2m_pt_add_proj(const mp_int *px, const mp_int *py, const mp_int *pz, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int A, B, C, D, E, F, G; + + /* If either P or Q is the point at infinity, then return the other + * point */ + if (ec_GF2m_pt_is_inf_proj(px, py, pz) == MP_YES) { + return ec_GF2m_pt_aff2proj(qx, qy, rx, ry, rz, group); + } + if (ec_GF2m_pt_is_inf_aff(qx, qy) == MP_YES) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + return mp_copy(pz, rz); + } + + MP_DIGITS(&A) = 0; + MP_DIGITS(&B) = 0; + MP_DIGITS(&C) = 0; + MP_DIGITS(&D) = 0; + MP_DIGITS(&E) = 0; + MP_DIGITS(&F) = 0; + MP_DIGITS(&G) = 0; + MP_CHECKOK(mp_init(&A)); + MP_CHECKOK(mp_init(&B)); + MP_CHECKOK(mp_init(&C)); + MP_CHECKOK(mp_init(&D)); + MP_CHECKOK(mp_init(&E)); + MP_CHECKOK(mp_init(&F)); + MP_CHECKOK(mp_init(&G)); + + /* D = pz^2 */ + MP_CHECKOK(group->meth->field_sqr(pz, &D, group->meth)); + + /* A = qy * pz^2 + py */ + MP_CHECKOK(group->meth->field_mul(qy, &D, &A, group->meth)); + MP_CHECKOK(group->meth->field_add(&A, py, &A, group->meth)); + + /* B = qx * pz + px */ + MP_CHECKOK(group->meth->field_mul(qx, pz, &B, group->meth)); + MP_CHECKOK(group->meth->field_add(&B, px, &B, group->meth)); + + /* C = pz * B */ + MP_CHECKOK(group->meth->field_mul(pz, &B, &C, group->meth)); + + /* D = B^2 * (C + a * pz^2) (using E as a temporary variable) */ + MP_CHECKOK(group->meth-> + field_mul(&group->curvea, &D, &D, group->meth)); + MP_CHECKOK(group->meth->field_add(&C, &D, &D, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&B, &E, group->meth)); + MP_CHECKOK(group->meth->field_mul(&E, &D, &D, group->meth)); + + /* rz = C^2 */ + MP_CHECKOK(group->meth->field_sqr(&C, rz, group->meth)); + + /* E = A * C */ + MP_CHECKOK(group->meth->field_mul(&A, &C, &E, group->meth)); + + /* rx = A^2 + D + E */ + MP_CHECKOK(group->meth->field_sqr(&A, rx, group->meth)); + MP_CHECKOK(group->meth->field_add(rx, &D, rx, group->meth)); + MP_CHECKOK(group->meth->field_add(rx, &E, rx, group->meth)); + + /* F = rx + qx * rz */ + MP_CHECKOK(group->meth->field_mul(qx, rz, &F, group->meth)); + MP_CHECKOK(group->meth->field_add(rx, &F, &F, group->meth)); + + /* G = rx + qy * rz */ + MP_CHECKOK(group->meth->field_mul(qy, rz, &G, group->meth)); + MP_CHECKOK(group->meth->field_add(rx, &G, &G, group->meth)); + + /* ry = E * F + rz * G (using G as a temporary variable) */ + MP_CHECKOK(group->meth->field_mul(rz, &G, &G, group->meth)); + MP_CHECKOK(group->meth->field_mul(&E, &F, ry, group->meth)); + MP_CHECKOK(group->meth->field_add(ry, &G, ry, group->meth)); + + CLEANUP: + mp_clear(&A); + mp_clear(&B); + mp_clear(&C); + mp_clear(&D); + mp_clear(&E); + mp_clear(&F); + mp_clear(&G); + return res; +} + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * projective coordinates. + * + * Assumes input is already field-encoded using field_enc, and returns + * output that is still field-encoded. + * + * Uses equation (3) from Hankerson, Hernandez, Menezes. Software + * Implementation of Elliptic Curve Cryptography Over Binary Fields. + */ +mp_err +ec_GF2m_pt_dbl_proj(const mp_int *px, const mp_int *py, const mp_int *pz, + mp_int *rx, mp_int *ry, mp_int *rz, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int t0, t1; + + if (ec_GF2m_pt_is_inf_proj(px, py, pz) == MP_YES) { + return ec_GF2m_pt_set_inf_proj(rx, ry, rz); + } + + MP_DIGITS(&t0) = 0; + MP_DIGITS(&t1) = 0; + MP_CHECKOK(mp_init(&t0)); + MP_CHECKOK(mp_init(&t1)); + + /* t0 = px^2 */ + /* t1 = pz^2 */ + MP_CHECKOK(group->meth->field_sqr(px, &t0, group->meth)); + MP_CHECKOK(group->meth->field_sqr(pz, &t1, group->meth)); + + /* rz = px^2 * pz^2 */ + MP_CHECKOK(group->meth->field_mul(&t0, &t1, rz, group->meth)); + + /* t0 = px^4 */ + /* t1 = b * pz^4 */ + MP_CHECKOK(group->meth->field_sqr(&t0, &t0, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&t1, &t1, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&group->curveb, &t1, &t1, group->meth)); + + /* rx = px^4 + b * pz^4 */ + MP_CHECKOK(group->meth->field_add(&t0, &t1, rx, group->meth)); + + /* ry = b * pz^4 * rz + rx * (a * rz + py^2 + b * pz^4) */ + MP_CHECKOK(group->meth->field_sqr(py, ry, group->meth)); + MP_CHECKOK(group->meth->field_add(ry, &t1, ry, group->meth)); + /* t0 = a * rz */ + MP_CHECKOK(group->meth-> + field_mul(&group->curvea, rz, &t0, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, ry, ry, group->meth)); + MP_CHECKOK(group->meth->field_mul(rx, ry, ry, group->meth)); + /* t1 = b * pz^4 * rz */ + MP_CHECKOK(group->meth->field_mul(&t1, rz, &t1, group->meth)); + MP_CHECKOK(group->meth->field_add(&t1, ry, ry, group->meth)); + + CLEANUP: + mp_clear(&t0); + mp_clear(&t1); + return res; +} + +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GF2m. Elliptic curve points P and R can be + * identical. Uses mixed projective-affine coordinates. Assumes input is + * already field-encoded using field_enc, and returns output that is still + * field-encoded. Uses 4-bit window method. */ +mp_err +ec_GF2m_pt_mul_proj(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[16][2], rz; + mp_digit precomp_arr[ECL_MAX_FIELD_SIZE_DIGITS * 16 * 2], *t; + int i, ni, d; + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); + + /* initialize precomputation table */ + t = precomp_arr; + for (i = 0; i < 16; i++) { + /* x co-ord */ + MP_SIGN(&precomp[i][0]) = MP_ZPOS; + MP_ALLOC(&precomp[i][0]) = ECL_MAX_FIELD_SIZE_DIGITS; + MP_USED(&precomp[i][0]) = 1; + *t = 0; + MP_DIGITS(&precomp[i][0]) = t; + t += ECL_MAX_FIELD_SIZE_DIGITS; + /* y co-ord */ + MP_SIGN(&precomp[i][1]) = MP_ZPOS; + MP_ALLOC(&precomp[i][1]) = ECL_MAX_FIELD_SIZE_DIGITS; + MP_USED(&precomp[i][1]) = 1; + *t = 0; + MP_DIGITS(&precomp[i][1]) = t; + t += ECL_MAX_FIELD_SIZE_DIGITS; + } + + /* fill precomputation table */ + mp_zero(&precomp[0][0]); + mp_zero(&precomp[0][1]); + MP_CHECKOK(mp_copy(px, &precomp[1][0])); + MP_CHECKOK(mp_copy(py, &precomp[1][1])); + for (i = 2; i < 16; i++) { + MP_CHECKOK(group-> + point_add(&precomp[1][0], &precomp[1][1], + &precomp[i - 1][0], &precomp[i - 1][1], + &precomp[i][0], &precomp[i][1], group)); + } + + d = (mpl_significant_bits(n) + 3) / 4; + + /* R = inf */ + MP_DIGITS(&rz) = 0; + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(ec_GF2m_pt_set_inf_proj(rx, ry, &rz)); + + for (i = d - 1; i >= 0; i--) { + /* compute window ni */ + ni = MP_GET_BIT(n, 4 * i + 3); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 2); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 1); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i); + /* R = 2^4 * R */ + MP_CHECKOK(ec_GF2m_pt_dbl_proj(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GF2m_pt_dbl_proj(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GF2m_pt_dbl_proj(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GF2m_pt_dbl_proj(rx, ry, &rz, rx, ry, &rz, group)); + /* R = R + (ni * P) */ + MP_CHECKOK(ec_GF2m_pt_add_proj + (rx, ry, &rz, &precomp[ni][0], &precomp[ni][1], rx, ry, + &rz, group)); + } + + /* convert result S to affine coordinates */ + MP_CHECKOK(ec_GF2m_pt_proj2aff(rx, ry, &rz, rx, ry, group)); + + CLEANUP: + mp_clear(&rz); + return res; +} +#endif diff --git a/nss/lib/freebl/ecl/ec_naf.c b/nss/lib/freebl/ecl/ec_naf.c new file mode 100644 index 0000000..3db6f30 --- /dev/null +++ b/nss/lib/freebl/ecl/ec_naf.c @@ -0,0 +1,69 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecl-priv.h" + +/* Returns 2^e as an integer. This is meant to be used for small powers of + * two. */ +int +ec_twoTo(int e) +{ + int a = 1; + int i; + + for (i = 0; i < e; i++) { + a *= 2; + } + return a; +} + +/* Computes the windowed non-adjacent-form (NAF) of a scalar. Out should + * be an array of signed char's to output to, bitsize should be the number + * of bits of out, in is the original scalar, and w is the window size. + * NAF is discussed in the paper: D. Hankerson, J. Hernandez and A. + * Menezes, "Software implementation of elliptic curve cryptography over + * binary fields", Proc. CHES 2000. */ +mp_err +ec_compute_wNAF(signed char *out, int bitsize, const mp_int *in, int w) +{ + mp_int k; + mp_err res = MP_OKAY; + int i, twowm1, mask; + + twowm1 = ec_twoTo(w - 1); + mask = 2 * twowm1 - 1; + + MP_DIGITS(&k) = 0; + MP_CHECKOK(mp_init_copy(&k, in)); + + i = 0; + /* Compute wNAF form */ + while (mp_cmp_z(&k) > 0) { + if (mp_isodd(&k)) { + out[i] = MP_DIGIT(&k, 0) & mask; + if (out[i] >= twowm1) + out[i] -= 2 * twowm1; + + /* Subtract off out[i]. Note mp_sub_d only works with + * unsigned digits */ + if (out[i] >= 0) { + mp_sub_d(&k, out[i], &k); + } else { + mp_add_d(&k, -(out[i]), &k); + } + } else { + out[i] = 0; + } + mp_div_2(&k, &k); + i++; + } + /* Zero out the remaining elements of the out array. */ + for (; i < bitsize + 1; i++) { + out[i] = 0; + } + CLEANUP: + mp_clear(&k); + return res; + +} diff --git a/nss/lib/freebl/ecl/ecl-curve.h b/nss/lib/freebl/ecl/ecl-curve.h new file mode 100644 index 0000000..d81d6df --- /dev/null +++ b/nss/lib/freebl/ecl/ecl-curve.h @@ -0,0 +1,110 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecl-exp.h" +#include <stdlib.h> + +#ifndef __ecl_curve_h_ +#define __ecl_curve_h_ + +#ifdef NSS_ECC_MORE_THAN_SUITE_B +#error This source file is for Basic ECC only . +#endif + +static const ECCurveParams ecCurve_NIST_P256 = { + "NIST-P256", ECField_GFp, 256, + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 1 +}; + +static const ECCurveParams ecCurve_NIST_P384 = { + "NIST-P384", ECField_GFp, 384, + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", + "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", + "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", + "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", + 1 +}; + +static const ECCurveParams ecCurve_NIST_P521 = { + "NIST-P521", ECField_GFp, 521, + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", + "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", + "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", + "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650", + "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", + 1 +}; + +/* mapping between ECCurveName enum and pointers to ECCurveParams */ +static const ECCurveParams *ecCurve_map[] = { + NULL, /* ECCurve_noName */ + NULL, /* ECCurve_NIST_P192 */ + NULL, /* ECCurve_NIST_P224 */ + &ecCurve_NIST_P256, /* ECCurve_NIST_P256 */ + &ecCurve_NIST_P384, /* ECCurve_NIST_P384 */ + &ecCurve_NIST_P521, /* ECCurve_NIST_P521 */ + NULL, /* ECCurve_NIST_K163 */ + NULL, /* ECCurve_NIST_B163 */ + NULL, /* ECCurve_NIST_K233 */ + NULL, /* ECCurve_NIST_B233 */ + NULL, /* ECCurve_NIST_K283 */ + NULL, /* ECCurve_NIST_B283 */ + NULL, /* ECCurve_NIST_K409 */ + NULL, /* ECCurve_NIST_B409 */ + NULL, /* ECCurve_NIST_K571 */ + NULL, /* ECCurve_NIST_B571 */ + NULL, /* ECCurve_X9_62_PRIME_192V2 */ + NULL, /* ECCurve_X9_62_PRIME_192V3 */ + NULL, /* ECCurve_X9_62_PRIME_239V1 */ + NULL, /* ECCurve_X9_62_PRIME_239V2 */ + NULL, /* ECCurve_X9_62_PRIME_239V3 */ + NULL, /* ECCurve_X9_62_CHAR2_PNB163V1 */ + NULL, /* ECCurve_X9_62_CHAR2_PNB163V2 */ + NULL, /* ECCurve_X9_62_CHAR2_PNB163V3 */ + NULL, /* ECCurve_X9_62_CHAR2_PNB176V1 */ + NULL, /* ECCurve_X9_62_CHAR2_TNB191V1 */ + NULL, /* ECCurve_X9_62_CHAR2_TNB191V2 */ + NULL, /* ECCurve_X9_62_CHAR2_TNB191V3 */ + NULL, /* ECCurve_X9_62_CHAR2_PNB208W1 */ + NULL, /* ECCurve_X9_62_CHAR2_TNB239V1 */ + NULL, /* ECCurve_X9_62_CHAR2_TNB239V2 */ + NULL, /* ECCurve_X9_62_CHAR2_TNB239V3 */ + NULL, /* ECCurve_X9_62_CHAR2_PNB272W1 */ + NULL, /* ECCurve_X9_62_CHAR2_PNB304W1 */ + NULL, /* ECCurve_X9_62_CHAR2_TNB359V1 */ + NULL, /* ECCurve_X9_62_CHAR2_PNB368W1 */ + NULL, /* ECCurve_X9_62_CHAR2_TNB431R1 */ + NULL, /* ECCurve_SECG_PRIME_112R1 */ + NULL, /* ECCurve_SECG_PRIME_112R2 */ + NULL, /* ECCurve_SECG_PRIME_128R1 */ + NULL, /* ECCurve_SECG_PRIME_128R2 */ + NULL, /* ECCurve_SECG_PRIME_160K1 */ + NULL, /* ECCurve_SECG_PRIME_160R1 */ + NULL, /* ECCurve_SECG_PRIME_160R2 */ + NULL, /* ECCurve_SECG_PRIME_192K1 */ + NULL, /* ECCurve_SECG_PRIME_224K1 */ + NULL, /* ECCurve_SECG_PRIME_256K1 */ + NULL, /* ECCurve_SECG_CHAR2_113R1 */ + NULL, /* ECCurve_SECG_CHAR2_113R2 */ + NULL, /* ECCurve_SECG_CHAR2_131R1 */ + NULL, /* ECCurve_SECG_CHAR2_131R2 */ + NULL, /* ECCurve_SECG_CHAR2_163R1 */ + NULL, /* ECCurve_SECG_CHAR2_193R1 */ + NULL, /* ECCurve_SECG_CHAR2_193R2 */ + NULL, /* ECCurve_SECG_CHAR2_239K1 */ + NULL, /* ECCurve_WTLS_1 */ + NULL, /* ECCurve_WTLS_8 */ + NULL, /* ECCurve_WTLS_9 */ + NULL /* ECCurve_pastLastCurve */ +}; + +#endif diff --git a/nss/lib/freebl/ecl/ecl-exp.h b/nss/lib/freebl/ecl/ecl-exp.h new file mode 100644 index 0000000..b79eb30 --- /dev/null +++ b/nss/lib/freebl/ecl/ecl-exp.h @@ -0,0 +1,162 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __ecl_exp_h_ +#define __ecl_exp_h_ + +/* Curve field type */ +typedef enum { + ECField_GFp, + ECField_GF2m +} ECField; + +/* Hexadecimal encoding of curve parameters */ +struct ECCurveParamsStr { + char *text; + ECField field; + unsigned int size; + char *irr; + char *curvea; + char *curveb; + char *genx; + char *geny; + char *order; + int cofactor; +}; +typedef struct ECCurveParamsStr ECCurveParams; + +/* Named curve parameters */ +typedef enum { + + ECCurve_noName = 0, + + /* NIST prime curves */ + ECCurve_NIST_P192, + ECCurve_NIST_P224, + ECCurve_NIST_P256, + ECCurve_NIST_P384, + ECCurve_NIST_P521, + + /* NIST binary curves */ + ECCurve_NIST_K163, + ECCurve_NIST_B163, + ECCurve_NIST_K233, + ECCurve_NIST_B233, + ECCurve_NIST_K283, + ECCurve_NIST_B283, + ECCurve_NIST_K409, + ECCurve_NIST_B409, + ECCurve_NIST_K571, + ECCurve_NIST_B571, + + /* ANSI X9.62 prime curves */ + /* ECCurve_X9_62_PRIME_192V1 == ECCurve_NIST_P192 */ + ECCurve_X9_62_PRIME_192V2, + ECCurve_X9_62_PRIME_192V3, + ECCurve_X9_62_PRIME_239V1, + ECCurve_X9_62_PRIME_239V2, + ECCurve_X9_62_PRIME_239V3, + /* ECCurve_X9_62_PRIME_256V1 == ECCurve_NIST_P256 */ + + /* ANSI X9.62 binary curves */ + ECCurve_X9_62_CHAR2_PNB163V1, + ECCurve_X9_62_CHAR2_PNB163V2, + ECCurve_X9_62_CHAR2_PNB163V3, + ECCurve_X9_62_CHAR2_PNB176V1, + ECCurve_X9_62_CHAR2_TNB191V1, + ECCurve_X9_62_CHAR2_TNB191V2, + ECCurve_X9_62_CHAR2_TNB191V3, + ECCurve_X9_62_CHAR2_PNB208W1, + ECCurve_X9_62_CHAR2_TNB239V1, + ECCurve_X9_62_CHAR2_TNB239V2, + ECCurve_X9_62_CHAR2_TNB239V3, + ECCurve_X9_62_CHAR2_PNB272W1, + ECCurve_X9_62_CHAR2_PNB304W1, + ECCurve_X9_62_CHAR2_TNB359V1, + ECCurve_X9_62_CHAR2_PNB368W1, + ECCurve_X9_62_CHAR2_TNB431R1, + + /* SEC2 prime curves */ + ECCurve_SECG_PRIME_112R1, + ECCurve_SECG_PRIME_112R2, + ECCurve_SECG_PRIME_128R1, + ECCurve_SECG_PRIME_128R2, + ECCurve_SECG_PRIME_160K1, + ECCurve_SECG_PRIME_160R1, + ECCurve_SECG_PRIME_160R2, + ECCurve_SECG_PRIME_192K1, + /* ECCurve_SECG_PRIME_192R1 == ECCurve_NIST_P192 */ + ECCurve_SECG_PRIME_224K1, + /* ECCurve_SECG_PRIME_224R1 == ECCurve_NIST_P224 */ + ECCurve_SECG_PRIME_256K1, + /* ECCurve_SECG_PRIME_256R1 == ECCurve_NIST_P256 */ + /* ECCurve_SECG_PRIME_384R1 == ECCurve_NIST_P384 */ + /* ECCurve_SECG_PRIME_521R1 == ECCurve_NIST_P521 */ + + /* SEC2 binary curves */ + ECCurve_SECG_CHAR2_113R1, + ECCurve_SECG_CHAR2_113R2, + ECCurve_SECG_CHAR2_131R1, + ECCurve_SECG_CHAR2_131R2, + /* ECCurve_SECG_CHAR2_163K1 == ECCurve_NIST_K163 */ + ECCurve_SECG_CHAR2_163R1, + /* ECCurve_SECG_CHAR2_163R2 == ECCurve_NIST_B163 */ + ECCurve_SECG_CHAR2_193R1, + ECCurve_SECG_CHAR2_193R2, + /* ECCurve_SECG_CHAR2_233K1 == ECCurve_NIST_K233 */ + /* ECCurve_SECG_CHAR2_233R1 == ECCurve_NIST_B233 */ + ECCurve_SECG_CHAR2_239K1, + /* ECCurve_SECG_CHAR2_283K1 == ECCurve_NIST_K283 */ + /* ECCurve_SECG_CHAR2_283R1 == ECCurve_NIST_B283 */ + /* ECCurve_SECG_CHAR2_409K1 == ECCurve_NIST_K409 */ + /* ECCurve_SECG_CHAR2_409R1 == ECCurve_NIST_B409 */ + /* ECCurve_SECG_CHAR2_571K1 == ECCurve_NIST_K571 */ + /* ECCurve_SECG_CHAR2_571R1 == ECCurve_NIST_B571 */ + + /* WTLS curves */ + ECCurve_WTLS_1, + /* there is no WTLS 2 curve */ + /* ECCurve_WTLS_3 == ECCurve_NIST_K163 */ + /* ECCurve_WTLS_4 == ECCurve_SECG_CHAR2_113R1 */ + /* ECCurve_WTLS_5 == ECCurve_X9_62_CHAR2_PNB163V1 */ + /* ECCurve_WTLS_6 == ECCurve_SECG_PRIME_112R1 */ + /* ECCurve_WTLS_7 == ECCurve_SECG_PRIME_160R1 */ + ECCurve_WTLS_8, + ECCurve_WTLS_9, + /* ECCurve_WTLS_10 == ECCurve_NIST_K233 */ + /* ECCurve_WTLS_11 == ECCurve_NIST_B233 */ + /* ECCurve_WTLS_12 == ECCurve_NIST_P224 */ + + ECCurve_pastLastCurve +} ECCurveName; + +/* Aliased named curves */ + +#define ECCurve_X9_62_PRIME_192V1 ECCurve_NIST_P192 +#define ECCurve_X9_62_PRIME_256V1 ECCurve_NIST_P256 +#define ECCurve_SECG_PRIME_192R1 ECCurve_NIST_P192 +#define ECCurve_SECG_PRIME_224R1 ECCurve_NIST_P224 +#define ECCurve_SECG_PRIME_256R1 ECCurve_NIST_P256 +#define ECCurve_SECG_PRIME_384R1 ECCurve_NIST_P384 +#define ECCurve_SECG_PRIME_521R1 ECCurve_NIST_P521 +#define ECCurve_SECG_CHAR2_163K1 ECCurve_NIST_K163 +#define ECCurve_SECG_CHAR2_163R2 ECCurve_NIST_B163 +#define ECCurve_SECG_CHAR2_233K1 ECCurve_NIST_K233 +#define ECCurve_SECG_CHAR2_233R1 ECCurve_NIST_B233 +#define ECCurve_SECG_CHAR2_283K1 ECCurve_NIST_K283 +#define ECCurve_SECG_CHAR2_283R1 ECCurve_NIST_B283 +#define ECCurve_SECG_CHAR2_409K1 ECCurve_NIST_K409 +#define ECCurve_SECG_CHAR2_409R1 ECCurve_NIST_B409 +#define ECCurve_SECG_CHAR2_571K1 ECCurve_NIST_K571 +#define ECCurve_SECG_CHAR2_571R1 ECCurve_NIST_B571 +#define ECCurve_WTLS_3 ECCurve_NIST_K163 +#define ECCurve_WTLS_4 ECCurve_SECG_CHAR2_113R1 +#define ECCurve_WTLS_5 ECCurve_X9_62_CHAR2_PNB163V1 +#define ECCurve_WTLS_6 ECCurve_SECG_PRIME_112R1 +#define ECCurve_WTLS_7 ECCurve_SECG_PRIME_160R1 +#define ECCurve_WTLS_10 ECCurve_NIST_K233 +#define ECCurve_WTLS_11 ECCurve_NIST_B233 +#define ECCurve_WTLS_12 ECCurve_NIST_P224 + +#endif /* __ecl_exp_h_ */ diff --git a/nss/lib/freebl/ecl/ecl-priv.h b/nss/lib/freebl/ecl/ecl-priv.h new file mode 100644 index 0000000..16f80a4 --- /dev/null +++ b/nss/lib/freebl/ecl/ecl-priv.h @@ -0,0 +1,248 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __ecl_priv_h_ +#define __ecl_priv_h_ + +#include "ecl.h" +#include "mpi.h" +#include "mplogic.h" + +/* MAX_FIELD_SIZE_DIGITS is the maximum size of field element supported */ +/* the following needs to go away... */ +#if defined(MP_USE_LONG_LONG_DIGIT) || defined(MP_USE_LONG_DIGIT) +#define ECL_SIXTY_FOUR_BIT +#else +#define ECL_THIRTY_TWO_BIT +#endif + +#define ECL_CURVE_DIGITS(curve_size_in_bits) \ + (((curve_size_in_bits)+(sizeof(mp_digit)*8-1))/(sizeof(mp_digit)*8)) +#define ECL_BITS (sizeof(mp_digit)*8) +#define ECL_MAX_FIELD_SIZE_DIGITS (80/sizeof(mp_digit)) + +/* Gets the i'th bit in the binary representation of a. If i >= length(a), + * then return 0. (The above behaviour differs from mpl_get_bit, which + * causes an error if i >= length(a).) */ +#define MP_GET_BIT(a, i) \ + ((i) >= mpl_significant_bits((a))) ? 0 : mpl_get_bit((a), (i)) + +#if !defined(MP_NO_MP_WORD) && !defined(MP_NO_ADD_WORD) +#define MP_ADD_CARRY(a1, a2, s, carry) \ + { mp_word w; \ + w = ((mp_word)carry) + (a1) + (a2); \ + s = ACCUM(w); \ + carry = CARRYOUT(w); } + +#define MP_SUB_BORROW(a1, a2, s, borrow) \ + { mp_word w; \ + w = ((mp_word)(a1)) - (a2) - borrow; \ + s = ACCUM(w); \ + borrow = (w >> MP_DIGIT_BIT) & 1; } + +#else +/* NOTE, + * carry and borrow are both read and written. + * a1 or a2 and s could be the same variable. + * don't trash those outputs until their respective inputs have + * been read. */ +#define MP_ADD_CARRY(a1, a2, s, carry) \ + { mp_digit tmp,sum; \ + tmp = (a1); \ + sum = tmp + (a2); \ + tmp = (sum < tmp); /* detect overflow */ \ + s = sum += carry; \ + carry = tmp + (sum < carry); } + +#define MP_SUB_BORROW(a1, a2, s, borrow) \ + { mp_digit tmp; \ + tmp = (a1); \ + s = tmp - (a2); \ + tmp = (s > tmp); /* detect borrow */ \ + if (borrow && !s--) tmp++; \ + borrow = tmp; } +#endif + + +struct GFMethodStr; +typedef struct GFMethodStr GFMethod; +struct GFMethodStr { + /* Indicates whether the structure was constructed from dynamic memory + * or statically created. */ + int constructed; + /* Irreducible that defines the field. For prime fields, this is the + * prime p. For binary polynomial fields, this is the bitstring + * representation of the irreducible polynomial. */ + mp_int irr; + /* For prime fields, the value irr_arr[0] is the number of bits in the + * field. For binary polynomial fields, the irreducible polynomial + * f(t) is represented as an array of unsigned int[], where f(t) is + * of the form: f(t) = t^p[0] + t^p[1] + ... + t^p[4] where m = p[0] + * > p[1] > ... > p[4] = 0. */ + unsigned int irr_arr[5]; + /* Field arithmetic methods. All methods (except field_enc and + * field_dec) are assumed to take field-encoded parameters and return + * field-encoded values. All methods (except field_enc and field_dec) + * are required to be implemented. */ + mp_err (*field_add) (const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + mp_err (*field_neg) (const mp_int *a, mp_int *r, const GFMethod *meth); + mp_err (*field_sub) (const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + mp_err (*field_mod) (const mp_int *a, mp_int *r, const GFMethod *meth); + mp_err (*field_mul) (const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + mp_err (*field_sqr) (const mp_int *a, mp_int *r, const GFMethod *meth); + mp_err (*field_div) (const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + mp_err (*field_enc) (const mp_int *a, mp_int *r, const GFMethod *meth); + mp_err (*field_dec) (const mp_int *a, mp_int *r, const GFMethod *meth); + /* Extra storage for implementation-specific data. Any memory + * allocated to these extra fields will be cleared by extra_free. */ + void *extra1; + void *extra2; + void (*extra_free) (GFMethod *meth); +}; + +/* Construct generic GFMethods. */ +GFMethod *GFMethod_consGFp(const mp_int *irr); +GFMethod *GFMethod_consGFp_mont(const mp_int *irr); +GFMethod *GFMethod_consGF2m(const mp_int *irr, + const unsigned int irr_arr[5]); +/* Free the memory allocated (if any) to a GFMethod object. */ +void GFMethod_free(GFMethod *meth); + +struct ECGroupStr { + /* Indicates whether the structure was constructed from dynamic memory + * or statically created. */ + int constructed; + /* Field definition and arithmetic. */ + GFMethod *meth; + /* Textual representation of curve name, if any. */ + char *text; + /* Curve parameters, field-encoded. */ + mp_int curvea, curveb; + /* x and y coordinates of the base point, field-encoded. */ + mp_int genx, geny; + /* Order and cofactor of the base point. */ + mp_int order; + int cofactor; + /* Point arithmetic methods. All methods are assumed to take + * field-encoded parameters and return field-encoded values. All + * methods (except base_point_mul and points_mul) are required to be + * implemented. */ + mp_err (*point_add) (const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + mp_err (*point_sub) (const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + mp_err (*point_dbl) (const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + mp_err (*point_mul) (const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); + mp_err (*base_point_mul) (const mp_int *n, mp_int *rx, mp_int *ry, + const ECGroup *group); + mp_err (*points_mul) (const mp_int *k1, const mp_int *k2, + const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + mp_err (*validate_point) (const mp_int *px, const mp_int *py, const ECGroup *group); + /* Extra storage for implementation-specific data. Any memory + * allocated to these extra fields will be cleared by extra_free. */ + void *extra1; + void *extra2; + void (*extra_free) (ECGroup *group); +}; + +/* Wrapper functions for generic prime field arithmetic. */ +mp_err ec_GFp_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_neg(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_sub(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + +/* fixed length in-line adds. Count is in words */ +mp_err ec_GFp_add_3(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_add_4(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_add_5(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_add_6(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sub_3(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sub_4(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sub_5(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sub_6(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + +mp_err ec_GFp_mod(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sqr(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +/* Wrapper functions for generic binary polynomial field arithmetic. */ +mp_err ec_GF2m_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GF2m_neg(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GF2m_mod(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GF2m_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GF2m_sqr(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GF2m_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); + +/* Montgomery prime field arithmetic. */ +mp_err ec_GFp_mul_mont(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_sqr_mont(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_div_mont(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth); +mp_err ec_GFp_enc_mont(const mp_int *a, mp_int *r, const GFMethod *meth); +mp_err ec_GFp_dec_mont(const mp_int *a, mp_int *r, const GFMethod *meth); +void ec_GFp_extra_free_mont(GFMethod *meth); + +/* point multiplication */ +mp_err ec_pts_mul_basic(const mp_int *k1, const mp_int *k2, + const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); +mp_err ec_pts_mul_simul_w2(const mp_int *k1, const mp_int *k2, + const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes the windowed non-adjacent-form (NAF) of a scalar. Out should + * be an array of signed char's to output to, bitsize should be the number + * of bits of out, in is the original scalar, and w is the window size. + * NAF is discussed in the paper: D. Hankerson, J. Hernandez and A. + * Menezes, "Software implementation of elliptic curve cryptography over + * binary fields", Proc. CHES 2000. */ +mp_err ec_compute_wNAF(signed char *out, int bitsize, const mp_int *in, + int w); + +/* Optimized field arithmetic */ +mp_err ec_group_set_gfp192(ECGroup *group, ECCurveName); +mp_err ec_group_set_gfp224(ECGroup *group, ECCurveName); +mp_err ec_group_set_gfp256(ECGroup *group, ECCurveName); +mp_err ec_group_set_gfp384(ECGroup *group, ECCurveName); +mp_err ec_group_set_gfp521(ECGroup *group, ECCurveName); +mp_err ec_group_set_gf2m163(ECGroup *group, ECCurveName name); +mp_err ec_group_set_gf2m193(ECGroup *group, ECCurveName name); +mp_err ec_group_set_gf2m233(ECGroup *group, ECCurveName name); + +/* Optimized point multiplication */ +mp_err ec_group_set_gfp256_32(ECGroup *group, ECCurveName name); + +/* Optimized floating-point arithmetic */ +#ifdef ECL_USE_FP +mp_err ec_group_set_secp160r1_fp(ECGroup *group); +mp_err ec_group_set_nistp192_fp(ECGroup *group); +mp_err ec_group_set_nistp224_fp(ECGroup *group); +#endif + +#endif /* __ecl_priv_h_ */ diff --git a/nss/lib/freebl/ecl/ecl.c b/nss/lib/freebl/ecl/ecl.c new file mode 100644 index 0000000..d55e593 --- /dev/null +++ b/nss/lib/freebl/ecl/ecl.c @@ -0,0 +1,396 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mpi.h" +#include "mplogic.h" +#include "ecl.h" +#include "ecl-priv.h" +#include "ec2.h" +#include "ecp.h" +#include <stdlib.h> +#include <string.h> + +/* Allocate memory for a new ECGroup object. */ +ECGroup * +ECGroup_new() +{ + mp_err res = MP_OKAY; + ECGroup *group; + group = (ECGroup *) malloc(sizeof(ECGroup)); + if (group == NULL) + return NULL; + group->constructed = MP_YES; + group->meth = NULL; + group->text = NULL; + MP_DIGITS(&group->curvea) = 0; + MP_DIGITS(&group->curveb) = 0; + MP_DIGITS(&group->genx) = 0; + MP_DIGITS(&group->geny) = 0; + MP_DIGITS(&group->order) = 0; + group->base_point_mul = NULL; + group->points_mul = NULL; + group->validate_point = NULL; + group->extra1 = NULL; + group->extra2 = NULL; + group->extra_free = NULL; + MP_CHECKOK(mp_init(&group->curvea)); + MP_CHECKOK(mp_init(&group->curveb)); + MP_CHECKOK(mp_init(&group->genx)); + MP_CHECKOK(mp_init(&group->geny)); + MP_CHECKOK(mp_init(&group->order)); + + CLEANUP: + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +/* Construct a generic ECGroup for elliptic curves over prime fields. */ +ECGroup * +ECGroup_consGFp(const mp_int *irr, const mp_int *curvea, + const mp_int *curveb, const mp_int *genx, + const mp_int *geny, const mp_int *order, int cofactor) +{ + mp_err res = MP_OKAY; + ECGroup *group = NULL; + + group = ECGroup_new(); + if (group == NULL) + return NULL; + + group->meth = GFMethod_consGFp(irr); + if (group->meth == NULL) { + res = MP_MEM; + goto CLEANUP; + } + MP_CHECKOK(mp_copy(curvea, &group->curvea)); + MP_CHECKOK(mp_copy(curveb, &group->curveb)); + MP_CHECKOK(mp_copy(genx, &group->genx)); + MP_CHECKOK(mp_copy(geny, &group->geny)); + MP_CHECKOK(mp_copy(order, &group->order)); + group->cofactor = cofactor; + group->point_add = &ec_GFp_pt_add_aff; + group->point_sub = &ec_GFp_pt_sub_aff; + group->point_dbl = &ec_GFp_pt_dbl_aff; + group->point_mul = &ec_GFp_pt_mul_jm_wNAF; + group->base_point_mul = NULL; + group->points_mul = &ec_GFp_pts_mul_jac; + group->validate_point = &ec_GFp_validate_point; + + CLEANUP: + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +/* Construct a generic ECGroup for elliptic curves over prime fields with + * field arithmetic implemented in Montgomery coordinates. */ +ECGroup * +ECGroup_consGFp_mont(const mp_int *irr, const mp_int *curvea, + const mp_int *curveb, const mp_int *genx, + const mp_int *geny, const mp_int *order, int cofactor) +{ + mp_err res = MP_OKAY; + ECGroup *group = NULL; + + group = ECGroup_new(); + if (group == NULL) + return NULL; + + group->meth = GFMethod_consGFp_mont(irr); + if (group->meth == NULL) { + res = MP_MEM; + goto CLEANUP; + } + MP_CHECKOK(group->meth-> + field_enc(curvea, &group->curvea, group->meth)); + MP_CHECKOK(group->meth-> + field_enc(curveb, &group->curveb, group->meth)); + MP_CHECKOK(group->meth->field_enc(genx, &group->genx, group->meth)); + MP_CHECKOK(group->meth->field_enc(geny, &group->geny, group->meth)); + MP_CHECKOK(mp_copy(order, &group->order)); + group->cofactor = cofactor; + group->point_add = &ec_GFp_pt_add_aff; + group->point_sub = &ec_GFp_pt_sub_aff; + group->point_dbl = &ec_GFp_pt_dbl_aff; + group->point_mul = &ec_GFp_pt_mul_jm_wNAF; + group->base_point_mul = NULL; + group->points_mul = &ec_GFp_pts_mul_jac; + group->validate_point = &ec_GFp_validate_point; + + CLEANUP: + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +#ifdef NSS_ECC_MORE_THAN_SUITE_B +/* Construct a generic ECGroup for elliptic curves over binary polynomial + * fields. */ +ECGroup * +ECGroup_consGF2m(const mp_int *irr, const unsigned int irr_arr[5], + const mp_int *curvea, const mp_int *curveb, + const mp_int *genx, const mp_int *geny, + const mp_int *order, int cofactor) +{ + mp_err res = MP_OKAY; + ECGroup *group = NULL; + + group = ECGroup_new(); + if (group == NULL) + return NULL; + + group->meth = GFMethod_consGF2m(irr, irr_arr); + if (group->meth == NULL) { + res = MP_MEM; + goto CLEANUP; + } + MP_CHECKOK(mp_copy(curvea, &group->curvea)); + MP_CHECKOK(mp_copy(curveb, &group->curveb)); + MP_CHECKOK(mp_copy(genx, &group->genx)); + MP_CHECKOK(mp_copy(geny, &group->geny)); + MP_CHECKOK(mp_copy(order, &group->order)); + group->cofactor = cofactor; + group->point_add = &ec_GF2m_pt_add_aff; + group->point_sub = &ec_GF2m_pt_sub_aff; + group->point_dbl = &ec_GF2m_pt_dbl_aff; + group->point_mul = &ec_GF2m_pt_mul_mont; + group->base_point_mul = NULL; + group->points_mul = &ec_pts_mul_basic; + group->validate_point = &ec_GF2m_validate_point; + + CLEANUP: + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} +#endif + +/* Construct ECGroup from hex parameters and name, if any. Called by + * ECGroup_fromHex and ECGroup_fromName. */ +ECGroup * +ecgroup_fromNameAndHex(const ECCurveName name, + const ECCurveParams * params) +{ + mp_int irr, curvea, curveb, genx, geny, order; + int bits; + ECGroup *group = NULL; + mp_err res = MP_OKAY; + + /* initialize values */ + MP_DIGITS(&irr) = 0; + MP_DIGITS(&curvea) = 0; + MP_DIGITS(&curveb) = 0; + MP_DIGITS(&genx) = 0; + MP_DIGITS(&geny) = 0; + MP_DIGITS(&order) = 0; + MP_CHECKOK(mp_init(&irr)); + MP_CHECKOK(mp_init(&curvea)); + MP_CHECKOK(mp_init(&curveb)); + MP_CHECKOK(mp_init(&genx)); + MP_CHECKOK(mp_init(&geny)); + MP_CHECKOK(mp_init(&order)); + MP_CHECKOK(mp_read_radix(&irr, params->irr, 16)); + MP_CHECKOK(mp_read_radix(&curvea, params->curvea, 16)); + MP_CHECKOK(mp_read_radix(&curveb, params->curveb, 16)); + MP_CHECKOK(mp_read_radix(&genx, params->genx, 16)); + MP_CHECKOK(mp_read_radix(&geny, params->geny, 16)); + MP_CHECKOK(mp_read_radix(&order, params->order, 16)); + + /* determine number of bits */ + bits = mpl_significant_bits(&irr) - 1; + if (bits < MP_OKAY) { + res = bits; + goto CLEANUP; + } + + /* determine which optimizations (if any) to use */ + if (params->field == ECField_GFp) { + switch (name) { +#ifdef NSS_ECC_MORE_THAN_SUITE_B +#ifdef ECL_USE_FP + case ECCurve_SECG_PRIME_160R1: + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_secp160r1_fp(group)); + break; +#endif + case ECCurve_SECG_PRIME_192R1: +#ifdef ECL_USE_FP + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_nistp192_fp(group)); +#else + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_gfp192(group, name)); +#endif + break; + case ECCurve_SECG_PRIME_224R1: +#ifdef ECL_USE_FP + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_nistp224_fp(group)); +#else + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_gfp224(group, name)); +#endif + break; +#endif /* NSS_ECC_MORE_THAN_SUITE_B */ + case ECCurve_SECG_PRIME_256R1: + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_gfp256(group, name)); + MP_CHECKOK(ec_group_set_gfp256_32(group, name)); + break; + case ECCurve_SECG_PRIME_521R1: + group = + ECGroup_consGFp(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + MP_CHECKOK(ec_group_set_gfp521(group, name)); + break; + default: + /* use generic arithmetic */ + group = + ECGroup_consGFp_mont(&irr, &curvea, &curveb, &genx, &geny, + &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + } +#ifdef NSS_ECC_MORE_THAN_SUITE_B + } else if (params->field == ECField_GF2m) { + group = ECGroup_consGF2m(&irr, NULL, &curvea, &curveb, &genx, &geny, &order, params->cofactor); + if (group == NULL) { res = MP_UNDEF; goto CLEANUP; } + if ((name == ECCurve_NIST_K163) || + (name == ECCurve_NIST_B163) || + (name == ECCurve_SECG_CHAR2_163R1)) { + MP_CHECKOK(ec_group_set_gf2m163(group, name)); + } else if ((name == ECCurve_SECG_CHAR2_193R1) || + (name == ECCurve_SECG_CHAR2_193R2)) { + MP_CHECKOK(ec_group_set_gf2m193(group, name)); + } else if ((name == ECCurve_NIST_K233) || + (name == ECCurve_NIST_B233)) { + MP_CHECKOK(ec_group_set_gf2m233(group, name)); + } +#endif + } else { + res = MP_UNDEF; + goto CLEANUP; + } + + /* set name, if any */ + if ((group != NULL) && (params->text != NULL)) { + group->text = strdup(params->text); + if (group->text == NULL) { + res = MP_MEM; + } + } + + CLEANUP: + mp_clear(&irr); + mp_clear(&curvea); + mp_clear(&curveb); + mp_clear(&genx); + mp_clear(&geny); + mp_clear(&order); + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +/* Construct ECGroup from hexadecimal representations of parameters. */ +ECGroup * +ECGroup_fromHex(const ECCurveParams * params) +{ + return ecgroup_fromNameAndHex(ECCurve_noName, params); +} + +/* Construct ECGroup from named parameters. */ +ECGroup * +ECGroup_fromName(const ECCurveName name) +{ + ECGroup *group = NULL; + ECCurveParams *params = NULL; + mp_err res = MP_OKAY; + + params = EC_GetNamedCurveParams(name); + if (params == NULL) { + res = MP_UNDEF; + goto CLEANUP; + } + + /* construct actual group */ + group = ecgroup_fromNameAndHex(name, params); + if (group == NULL) { + res = MP_UNDEF; + goto CLEANUP; + } + + CLEANUP: + EC_FreeCurveParams(params); + if (res != MP_OKAY) { + ECGroup_free(group); + return NULL; + } + return group; +} + +/* Validates an EC public key as described in Section 5.2.2 of X9.62. */ +mp_err ECPoint_validate(const ECGroup *group, const mp_int *px, const + mp_int *py) +{ + /* 1: Verify that publicValue is not the point at infinity */ + /* 2: Verify that the coordinates of publicValue are elements + * of the field. + */ + /* 3: Verify that publicValue is on the curve. */ + /* 4: Verify that the order of the curve times the publicValue + * is the point at infinity. + */ + return group->validate_point(px, py, group); +} + +/* Free the memory allocated (if any) to an ECGroup object. */ +void +ECGroup_free(ECGroup *group) +{ + if (group == NULL) + return; + GFMethod_free(group->meth); + if (group->constructed == MP_NO) + return; + mp_clear(&group->curvea); + mp_clear(&group->curveb); + mp_clear(&group->genx); + mp_clear(&group->geny); + mp_clear(&group->order); + if (group->text != NULL) + free(group->text); + if (group->extra_free != NULL) + group->extra_free(group); + free(group); +} diff --git a/nss/lib/freebl/ecl/ecl.h b/nss/lib/freebl/ecl/ecl.h new file mode 100644 index 0000000..3e44803 --- /dev/null +++ b/nss/lib/freebl/ecl/ecl.h @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Although this is not an exported header file, code which uses elliptic + * curve point operations will need to include it. */ + +#ifndef __ecl_h_ +#define __ecl_h_ + +#include "ecl-exp.h" +#include "mpi.h" + +struct ECGroupStr; +typedef struct ECGroupStr ECGroup; + +/* Construct ECGroup from hexadecimal representations of parameters. */ +ECGroup *ECGroup_fromHex(const ECCurveParams * params); + +/* Construct ECGroup from named parameters. */ +ECGroup *ECGroup_fromName(const ECCurveName name); + +/* Free an allocated ECGroup. */ +void ECGroup_free(ECGroup *group); + +/* Construct ECCurveParams from an ECCurveName */ +ECCurveParams *EC_GetNamedCurveParams(const ECCurveName name); + +/* Duplicates an ECCurveParams */ +ECCurveParams *ECCurveParams_dup(const ECCurveParams * params); + +/* Free an allocated ECCurveParams */ +void EC_FreeCurveParams(ECCurveParams * params); + +/* Elliptic curve scalar-point multiplication. Computes Q(x, y) = k * P(x, + * y). If x, y = NULL, then P is assumed to be the generator (base point) + * of the group of points on the elliptic curve. Input and output values + * are assumed to be NOT field-encoded. */ +mp_err ECPoint_mul(const ECGroup *group, const mp_int *k, const mp_int *px, + const mp_int *py, mp_int *qx, mp_int *qy); + +/* Elliptic curve scalar-point multiplication. Computes Q(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Input and output values are assumed to + * be NOT field-encoded. */ +mp_err ECPoints_mul(const ECGroup *group, const mp_int *k1, + const mp_int *k2, const mp_int *px, const mp_int *py, + mp_int *qx, mp_int *qy); + +/* Validates an EC public key as described in Section 5.2.2 of X9.62. + * Returns MP_YES if the public key is valid, MP_NO if the public key + * is invalid, or an error code if the validation could not be + * performed. */ +mp_err ECPoint_validate(const ECGroup *group, const mp_int *px, const + mp_int *py); + +#endif /* __ecl_h_ */ diff --git a/nss/lib/freebl/ecl/ecl_curve.c b/nss/lib/freebl/ecl/ecl_curve.c new file mode 100644 index 0000000..192dab1 --- /dev/null +++ b/nss/lib/freebl/ecl/ecl_curve.c @@ -0,0 +1,89 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecl.h" +#include "ecl-curve.h" +#include "ecl-priv.h" +#include <stdlib.h> +#include <string.h> + +#define CHECK(func) if ((func) == NULL) { res = 0; goto CLEANUP; } + +/* Duplicates an ECCurveParams */ +ECCurveParams * +ECCurveParams_dup(const ECCurveParams * params) +{ + int res = 1; + ECCurveParams *ret = NULL; + + CHECK(ret = (ECCurveParams *) calloc(1, sizeof(ECCurveParams))); + if (params->text != NULL) { + CHECK(ret->text = strdup(params->text)); + } + ret->field = params->field; + ret->size = params->size; + if (params->irr != NULL) { + CHECK(ret->irr = strdup(params->irr)); + } + if (params->curvea != NULL) { + CHECK(ret->curvea = strdup(params->curvea)); + } + if (params->curveb != NULL) { + CHECK(ret->curveb = strdup(params->curveb)); + } + if (params->genx != NULL) { + CHECK(ret->genx = strdup(params->genx)); + } + if (params->geny != NULL) { + CHECK(ret->geny = strdup(params->geny)); + } + if (params->order != NULL) { + CHECK(ret->order = strdup(params->order)); + } + ret->cofactor = params->cofactor; + + CLEANUP: + if (res != 1) { + EC_FreeCurveParams(ret); + return NULL; + } + return ret; +} + +#undef CHECK + +/* Construct ECCurveParams from an ECCurveName */ +ECCurveParams * +EC_GetNamedCurveParams(const ECCurveName name) +{ + if ((name <= ECCurve_noName) || (ECCurve_pastLastCurve <= name) || + (ecCurve_map[name] == NULL)) { + return NULL; + } else { + return ECCurveParams_dup(ecCurve_map[name]); + } +} + +/* Free the memory allocated (if any) to an ECCurveParams object. */ +void +EC_FreeCurveParams(ECCurveParams * params) +{ + if (params == NULL) + return; + if (params->text != NULL) + free(params->text); + if (params->irr != NULL) + free(params->irr); + if (params->curvea != NULL) + free(params->curvea); + if (params->curveb != NULL) + free(params->curveb); + if (params->genx != NULL) + free(params->genx); + if (params->geny != NULL) + free(params->geny); + if (params->order != NULL) + free(params->order); + free(params); +} diff --git a/nss/lib/freebl/ecl/ecl_gf.c b/nss/lib/freebl/ecl/ecl_gf.c new file mode 100644 index 0000000..d250d78 --- /dev/null +++ b/nss/lib/freebl/ecl/ecl_gf.c @@ -0,0 +1,1013 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mpi.h" +#include "mp_gf2m.h" +#include "ecl-priv.h" +#include "mpi-priv.h" +#include <stdlib.h> + +/* Allocate memory for a new GFMethod object. */ +GFMethod * +GFMethod_new() +{ + mp_err res = MP_OKAY; + GFMethod *meth; + meth = (GFMethod *) malloc(sizeof(GFMethod)); + if (meth == NULL) + return NULL; + meth->constructed = MP_YES; + MP_DIGITS(&meth->irr) = 0; + meth->extra_free = NULL; + MP_CHECKOK(mp_init(&meth->irr)); + + CLEANUP: + if (res != MP_OKAY) { + GFMethod_free(meth); + return NULL; + } + return meth; +} + +/* Construct a generic GFMethod for arithmetic over prime fields with + * irreducible irr. */ +GFMethod * +GFMethod_consGFp(const mp_int *irr) +{ + mp_err res = MP_OKAY; + GFMethod *meth = NULL; + + meth = GFMethod_new(); + if (meth == NULL) + return NULL; + + MP_CHECKOK(mp_copy(irr, &meth->irr)); + meth->irr_arr[0] = mpl_significant_bits(irr); + meth->irr_arr[1] = meth->irr_arr[2] = meth->irr_arr[3] = + meth->irr_arr[4] = 0; + switch(MP_USED(&meth->irr)) { + /* maybe we need 1 and 2 words here as well?*/ + case 3: + meth->field_add = &ec_GFp_add_3; + meth->field_sub = &ec_GFp_sub_3; + break; + case 4: + meth->field_add = &ec_GFp_add_4; + meth->field_sub = &ec_GFp_sub_4; + break; + case 5: + meth->field_add = &ec_GFp_add_5; + meth->field_sub = &ec_GFp_sub_5; + break; + case 6: + meth->field_add = &ec_GFp_add_6; + meth->field_sub = &ec_GFp_sub_6; + break; + default: + meth->field_add = &ec_GFp_add; + meth->field_sub = &ec_GFp_sub; + } + meth->field_neg = &ec_GFp_neg; + meth->field_mod = &ec_GFp_mod; + meth->field_mul = &ec_GFp_mul; + meth->field_sqr = &ec_GFp_sqr; + meth->field_div = &ec_GFp_div; + meth->field_enc = NULL; + meth->field_dec = NULL; + meth->extra1 = NULL; + meth->extra2 = NULL; + meth->extra_free = NULL; + + CLEANUP: + if (res != MP_OKAY) { + GFMethod_free(meth); + return NULL; + } + return meth; +} + +/* Construct a generic GFMethod for arithmetic over binary polynomial + * fields with irreducible irr that has array representation irr_arr (see + * ecl-priv.h for description of the representation). If irr_arr is NULL, + * then it is constructed from the bitstring representation. */ +GFMethod * +GFMethod_consGF2m(const mp_int *irr, const unsigned int irr_arr[5]) +{ + mp_err res = MP_OKAY; + int ret; + GFMethod *meth = NULL; + + meth = GFMethod_new(); + if (meth == NULL) + return NULL; + + MP_CHECKOK(mp_copy(irr, &meth->irr)); + if (irr_arr != NULL) { + /* Irreducible polynomials are either trinomials or pentanomials. */ + meth->irr_arr[0] = irr_arr[0]; + meth->irr_arr[1] = irr_arr[1]; + meth->irr_arr[2] = irr_arr[2]; + if (irr_arr[2] > 0) { + meth->irr_arr[3] = irr_arr[3]; + meth->irr_arr[4] = irr_arr[4]; + } else { + meth->irr_arr[3] = meth->irr_arr[4] = 0; + } + } else { + ret = mp_bpoly2arr(irr, meth->irr_arr, 5); + /* Irreducible polynomials are either trinomials or pentanomials. */ + if ((ret != 5) && (ret != 3)) { + res = MP_UNDEF; + goto CLEANUP; + } + } + meth->field_add = &ec_GF2m_add; + meth->field_neg = &ec_GF2m_neg; + meth->field_sub = &ec_GF2m_add; + meth->field_mod = &ec_GF2m_mod; + meth->field_mul = &ec_GF2m_mul; + meth->field_sqr = &ec_GF2m_sqr; + meth->field_div = &ec_GF2m_div; + meth->field_enc = NULL; + meth->field_dec = NULL; + meth->extra1 = NULL; + meth->extra2 = NULL; + meth->extra_free = NULL; + + CLEANUP: + if (res != MP_OKAY) { + GFMethod_free(meth); + return NULL; + } + return meth; +} + +/* Free the memory allocated (if any) to a GFMethod object. */ +void +GFMethod_free(GFMethod *meth) +{ + if (meth == NULL) + return; + if (meth->constructed == MP_NO) + return; + mp_clear(&meth->irr); + if (meth->extra_free != NULL) + meth->extra_free(meth); + free(meth); +} + +/* Wrapper functions for generic prime field arithmetic. */ + +/* Add two field elements. Assumes that 0 <= a, b < meth->irr */ +mp_err +ec_GFp_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + /* PRE: 0 <= a, b < p = meth->irr POST: 0 <= r < p, r = a + b (mod p) */ + mp_err res; + + if ((res = mp_add(a, b, r)) != MP_OKAY) { + return res; + } + if (mp_cmp(r, &meth->irr) >= 0) { + return mp_sub(r, &meth->irr, r); + } + return res; +} + +/* Negates a field element. Assumes that 0 <= a < meth->irr */ +mp_err +ec_GFp_neg(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + /* PRE: 0 <= a < p = meth->irr POST: 0 <= r < p, r = -a (mod p) */ + + if (mp_cmp_z(a) == 0) { + mp_zero(r); + return MP_OKAY; + } + return mp_sub(&meth->irr, a, r); +} + +/* Subtracts two field elements. Assumes that 0 <= a, b < meth->irr */ +mp_err +ec_GFp_sub(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + /* PRE: 0 <= a, b < p = meth->irr POST: 0 <= r < p, r = a - b (mod p) */ + res = mp_sub(a, b, r); + if (res == MP_RANGE) { + MP_CHECKOK(mp_sub(b, a, r)); + if (mp_cmp_z(r) < 0) { + MP_CHECKOK(mp_add(r, &meth->irr, r)); + } + MP_CHECKOK(ec_GFp_neg(r, r, meth)); + } + if (mp_cmp_z(r) < 0) { + MP_CHECKOK(mp_add(r, &meth->irr, r)); + } + CLEANUP: + return res; +} +/* + * Inline adds for small curve lengths. + */ +/* 3 words */ +mp_err +ec_GFp_add_3(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + carry = 0; + MP_ADD_CARRY(a0, r0, r0, carry); + MP_ADD_CARRY(a1, r1, r1, carry); + MP_ADD_CARRY(a2, r2, r2, carry); +#else + __asm__ ( + "xorq %3,%3 \n\t" + "addq %4,%0 \n\t" + "adcq %5,%1 \n\t" + "adcq %6,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(carry) + : "r" (a0), "r" (a1), "r" (a2), + "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + + MP_CHECKOK(s_mp_pad(r, 3)); + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 3; + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + a2 = MP_DIGIT(&meth->irr,2); + if (carry || r2 > a2 || + ((r2 == a2) && mp_cmp(r,&meth->irr) != MP_LT)) { + a1 = MP_DIGIT(&meth->irr,1); + a0 = MP_DIGIT(&meth->irr,0); +#ifndef MPI_AMD64_ADD + carry = 0; + MP_SUB_BORROW(r0, a0, r0, carry); + MP_SUB_BORROW(r1, a1, r1, carry); + MP_SUB_BORROW(r2, a2, r2, carry); +#else + __asm__ ( + "subq %3,%0 \n\t" + "sbbq %4,%1 \n\t" + "sbbq %5,%2 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2) + : "r" (a0), "r" (a1), "r" (a2), + "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + } + + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 4 words */ +mp_err +ec_GFp_add_4(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0, a3 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 4: + a3 = MP_DIGIT(a,3); + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 4: + r3 = MP_DIGIT(b,3); + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + carry = 0; + MP_ADD_CARRY(a0, r0, r0, carry); + MP_ADD_CARRY(a1, r1, r1, carry); + MP_ADD_CARRY(a2, r2, r2, carry); + MP_ADD_CARRY(a3, r3, r3, carry); +#else + __asm__ ( + "xorq %4,%4 \n\t" + "addq %5,%0 \n\t" + "adcq %6,%1 \n\t" + "adcq %7,%2 \n\t" + "adcq %8,%3 \n\t" + "adcq $0,%4 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r"(carry) + : "r" (a0), "r" (a1), "r" (a2), "r" (a3), + "0" (r0), "1" (r1), "2" (r2), "3" (r3) + : "%cc" ); +#endif + + MP_CHECKOK(s_mp_pad(r, 4)); + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 4; + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + a3 = MP_DIGIT(&meth->irr,3); + if (carry || r3 > a3 || + ((r3 == a3) && mp_cmp(r,&meth->irr) != MP_LT)) { + a2 = MP_DIGIT(&meth->irr,2); + a1 = MP_DIGIT(&meth->irr,1); + a0 = MP_DIGIT(&meth->irr,0); +#ifndef MPI_AMD64_ADD + carry = 0; + MP_SUB_BORROW(r0, a0, r0, carry); + MP_SUB_BORROW(r1, a1, r1, carry); + MP_SUB_BORROW(r2, a2, r2, carry); + MP_SUB_BORROW(r3, a3, r3, carry); +#else + __asm__ ( + "subq %4,%0 \n\t" + "sbbq %5,%1 \n\t" + "sbbq %6,%2 \n\t" + "sbbq %7,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3) + : "r" (a0), "r" (a1), "r" (a2), "r" (a3), + "0" (r0), "1" (r1), "2" (r2), "3" (r3) + : "%cc" ); +#endif + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + } + + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 5 words */ +mp_err +ec_GFp_add_5(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 5: + a4 = MP_DIGIT(a,4); + case 4: + a3 = MP_DIGIT(a,3); + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 5: + r4 = MP_DIGIT(b,4); + case 4: + r3 = MP_DIGIT(b,3); + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + + carry = 0; + MP_ADD_CARRY(a0, r0, r0, carry); + MP_ADD_CARRY(a1, r1, r1, carry); + MP_ADD_CARRY(a2, r2, r2, carry); + MP_ADD_CARRY(a3, r3, r3, carry); + MP_ADD_CARRY(a4, r4, r4, carry); + + MP_CHECKOK(s_mp_pad(r, 5)); + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 5; + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + a4 = MP_DIGIT(&meth->irr,4); + if (carry || r4 > a4 || + ((r4 == a4) && mp_cmp(r,&meth->irr) != MP_LT)) { + a3 = MP_DIGIT(&meth->irr,3); + a2 = MP_DIGIT(&meth->irr,2); + a1 = MP_DIGIT(&meth->irr,1); + a0 = MP_DIGIT(&meth->irr,0); + carry = 0; + MP_SUB_BORROW(r0, a0, r0, carry); + MP_SUB_BORROW(r1, a1, r1, carry); + MP_SUB_BORROW(r2, a2, r2, carry); + MP_SUB_BORROW(r3, a3, r3, carry); + MP_SUB_BORROW(r4, a4, r4, carry); + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + } + + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 6 words */ +mp_err +ec_GFp_add_6(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0, a3 = 0, a4 = 0, a5 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 6: + a5 = MP_DIGIT(a,5); + case 5: + a4 = MP_DIGIT(a,4); + case 4: + a3 = MP_DIGIT(a,3); + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 6: + r5 = MP_DIGIT(b,5); + case 5: + r4 = MP_DIGIT(b,4); + case 4: + r3 = MP_DIGIT(b,3); + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + + carry = 0; + MP_ADD_CARRY(a0, r0, r0, carry); + MP_ADD_CARRY(a1, r1, r1, carry); + MP_ADD_CARRY(a2, r2, r2, carry); + MP_ADD_CARRY(a3, r3, r3, carry); + MP_ADD_CARRY(a4, r4, r4, carry); + MP_ADD_CARRY(a5, r5, r5, carry); + + MP_CHECKOK(s_mp_pad(r, 6)); + MP_DIGIT(r, 5) = r5; + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 6; + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + a5 = MP_DIGIT(&meth->irr,5); + if (carry || r5 > a5 || + ((r5 == a5) && mp_cmp(r,&meth->irr) != MP_LT)) { + a4 = MP_DIGIT(&meth->irr,4); + a3 = MP_DIGIT(&meth->irr,3); + a2 = MP_DIGIT(&meth->irr,2); + a1 = MP_DIGIT(&meth->irr,1); + a0 = MP_DIGIT(&meth->irr,0); + carry = 0; + MP_SUB_BORROW(r0, a0, r0, carry); + MP_SUB_BORROW(r1, a1, r1, carry); + MP_SUB_BORROW(r2, a2, r2, carry); + MP_SUB_BORROW(r3, a3, r3, carry); + MP_SUB_BORROW(r4, a4, r4, carry); + MP_SUB_BORROW(r5, a5, r5, carry); + MP_DIGIT(r, 5) = r5; + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + } + + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* + * The following subraction functions do in-line subractions based + * on our curve size. + * + * ... 3 words + */ +mp_err +ec_GFp_sub_3(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + borrow = 0; + MP_SUB_BORROW(r0, b0, r0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow); +#else + __asm__ ( + "xorq %3,%3 \n\t" + "subq %4,%0 \n\t" + "sbbq %5,%1 \n\t" + "sbbq %6,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r" (borrow) + : "r" (b0), "r" (b1), "r" (b2), + "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { + b2 = MP_DIGIT(&meth->irr,2); + b1 = MP_DIGIT(&meth->irr,1); + b0 = MP_DIGIT(&meth->irr,0); +#ifndef MPI_AMD64_ADD + borrow = 0; + MP_ADD_CARRY(b0, r0, r0, borrow); + MP_ADD_CARRY(b1, r1, r1, borrow); + MP_ADD_CARRY(b2, r2, r2, borrow); +#else + __asm__ ( + "addq %3,%0 \n\t" + "adcq %4,%1 \n\t" + "adcq %5,%2 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2) + : "r" (b0), "r" (b1), "r" (b2), + "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + } + +#ifdef MPI_AMD64_ADD + /* compiler fakeout? */ + if ((r2 == b0) && (r1 == b0) && (r0 == b0)) { + MP_CHECKOK(s_mp_pad(r, 4)); + } +#endif + MP_CHECKOK(s_mp_pad(r, 3)); + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 3; + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 4 words */ +mp_err +ec_GFp_sub_4(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0, b3 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 4: + r3 = MP_DIGIT(a,3); + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 4: + b3 = MP_DIGIT(b,3); + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + borrow = 0; + MP_SUB_BORROW(r0, b0, r0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow); + MP_SUB_BORROW(r3, b3, r3, borrow); +#else + __asm__ ( + "xorq %4,%4 \n\t" + "subq %5,%0 \n\t" + "sbbq %6,%1 \n\t" + "sbbq %7,%2 \n\t" + "sbbq %8,%3 \n\t" + "adcq $0,%4 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r" (borrow) + : "r" (b0), "r" (b1), "r" (b2), "r" (b3), + "0" (r0), "1" (r1), "2" (r2), "3" (r3) + : "%cc" ); +#endif + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { + b3 = MP_DIGIT(&meth->irr,3); + b2 = MP_DIGIT(&meth->irr,2); + b1 = MP_DIGIT(&meth->irr,1); + b0 = MP_DIGIT(&meth->irr,0); +#ifndef MPI_AMD64_ADD + borrow = 0; + MP_ADD_CARRY(b0, r0, r0, borrow); + MP_ADD_CARRY(b1, r1, r1, borrow); + MP_ADD_CARRY(b2, r2, r2, borrow); + MP_ADD_CARRY(b3, r3, r3, borrow); +#else + __asm__ ( + "addq %4,%0 \n\t" + "adcq %5,%1 \n\t" + "adcq %6,%2 \n\t" + "adcq %7,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3) + : "r" (b0), "r" (b1), "r" (b2), "r" (b3), + "0" (r0), "1" (r1), "2" (r2), "3" (r3) + : "%cc" ); +#endif + } +#ifdef MPI_AMD64_ADD + /* compiler fakeout? */ + if ((r3 == b0) && (r1 == b0) && (r0 == b0)) { + MP_CHECKOK(s_mp_pad(r, 4)); + } +#endif + MP_CHECKOK(s_mp_pad(r, 4)); + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 4; + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 5 words */ +mp_err +ec_GFp_sub_5(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 5: + r4 = MP_DIGIT(a,4); + case 4: + r3 = MP_DIGIT(a,3); + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 5: + b4 = MP_DIGIT(b,4); + case 4: + b3 = MP_DIGIT(b,3); + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + + borrow = 0; + MP_SUB_BORROW(r0, b0, r0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow); + MP_SUB_BORROW(r3, b3, r3, borrow); + MP_SUB_BORROW(r4, b4, r4, borrow); + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { + b4 = MP_DIGIT(&meth->irr,4); + b3 = MP_DIGIT(&meth->irr,3); + b2 = MP_DIGIT(&meth->irr,2); + b1 = MP_DIGIT(&meth->irr,1); + b0 = MP_DIGIT(&meth->irr,0); + borrow = 0; + MP_ADD_CARRY(b0, r0, r0, borrow); + MP_ADD_CARRY(b1, r1, r1, borrow); + MP_ADD_CARRY(b2, r2, r2, borrow); + MP_ADD_CARRY(b3, r3, r3, borrow); + } + MP_CHECKOK(s_mp_pad(r, 5)); + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 5; + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* 6 words */ +mp_err +ec_GFp_sub_6(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, b5 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 6: + r5 = MP_DIGIT(a,5); + case 5: + r4 = MP_DIGIT(a,4); + case 4: + r3 = MP_DIGIT(a,3); + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 6: + b5 = MP_DIGIT(b,5); + case 5: + b4 = MP_DIGIT(b,4); + case 4: + b3 = MP_DIGIT(b,3); + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + + borrow = 0; + MP_SUB_BORROW(r0, b0, r0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow); + MP_SUB_BORROW(r3, b3, r3, borrow); + MP_SUB_BORROW(r4, b4, r4, borrow); + MP_SUB_BORROW(r5, b5, r5, borrow); + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { + b5 = MP_DIGIT(&meth->irr,5); + b4 = MP_DIGIT(&meth->irr,4); + b3 = MP_DIGIT(&meth->irr,3); + b2 = MP_DIGIT(&meth->irr,2); + b1 = MP_DIGIT(&meth->irr,1); + b0 = MP_DIGIT(&meth->irr,0); + borrow = 0; + MP_ADD_CARRY(b0, r0, r0, borrow); + MP_ADD_CARRY(b1, r1, r1, borrow); + MP_ADD_CARRY(b2, r2, r2, borrow); + MP_ADD_CARRY(b3, r3, r3, borrow); + MP_ADD_CARRY(b4, r4, r4, borrow); + } + + MP_CHECKOK(s_mp_pad(r, 6)); + MP_DIGIT(r, 5) = r5; + MP_DIGIT(r, 4) = r4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 6; + s_mp_clamp(r); + + CLEANUP: + return res; +} + + +/* Reduces an integer to a field element. */ +mp_err +ec_GFp_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return mp_mod(a, &meth->irr, r); +} + +/* Multiplies two field elements. */ +mp_err +ec_GFp_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + return mp_mulmod(a, b, &meth->irr, r); +} + +/* Squares a field element. */ +mp_err +ec_GFp_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return mp_sqrmod(a, &meth->irr, r); +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +mp_err +ec_GFp_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + return mp_invmod(b, &meth->irr, r); + } else { + /* MPI doesn't support divmod, so we implement it using invmod and + * mulmod. */ + MP_CHECKOK(mp_init(&t)); + MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); + MP_CHECKOK(mp_mulmod(a, &t, &meth->irr, r)); + CLEANUP: + mp_clear(&t); + return res; + } +} + +/* Wrapper functions for generic binary polynomial field arithmetic. */ + +/* Adds two field elements. */ +mp_err +ec_GF2m_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + return mp_badd(a, b, r); +} + +/* Negates a field element. Note that for binary polynomial fields, the + * negation of a field element is the field element itself. */ +mp_err +ec_GF2m_neg(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + if (a == r) { + return MP_OKAY; + } else { + return mp_copy(a, r); + } +} + +/* Reduces a binary polynomial to a field element. */ +mp_err +ec_GF2m_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return mp_bmod(a, meth->irr_arr, r); +} + +/* Multiplies two field elements. */ +mp_err +ec_GF2m_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + return mp_bmulmod(a, b, meth->irr_arr, r); +} + +/* Squares a field element. */ +mp_err +ec_GF2m_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return mp_bsqrmod(a, meth->irr_arr, r); +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +mp_err +ec_GF2m_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + /* The GF(2^m) portion of MPI doesn't support invmod, so we + * compute 1/b. */ + MP_CHECKOK(mp_init(&t)); + MP_CHECKOK(mp_set_int(&t, 1)); + MP_CHECKOK(mp_bdivmod(&t, b, &meth->irr, meth->irr_arr, r)); + CLEANUP: + mp_clear(&t); + return res; + } else { + return mp_bdivmod(a, b, &meth->irr, meth->irr_arr, r); + } +} diff --git a/nss/lib/freebl/ecl/ecl_mult.c b/nss/lib/freebl/ecl/ecl_mult.c new file mode 100644 index 0000000..5932828 --- /dev/null +++ b/nss/lib/freebl/ecl/ecl_mult.c @@ -0,0 +1,322 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mpi.h" +#include "mplogic.h" +#include "ecl.h" +#include "ecl-priv.h" +#include <stdlib.h> + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k * P(x, + * y). If x, y = NULL, then P is assumed to be the generator (base point) + * of the group of points on the elliptic curve. Input and output values + * are assumed to be NOT field-encoded. */ +mp_err +ECPoint_mul(const ECGroup *group, const mp_int *k, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry) +{ + mp_err res = MP_OKAY; + mp_int kt; + + ARGCHK((k != NULL) && (group != NULL), MP_BADARG); + MP_DIGITS(&kt) = 0; + + /* want scalar to be less than or equal to group order */ + if (mp_cmp(k, &group->order) > 0) { + MP_CHECKOK(mp_init(&kt)); + MP_CHECKOK(mp_mod(k, &group->order, &kt)); + } else { + MP_SIGN(&kt) = MP_ZPOS; + MP_USED(&kt) = MP_USED(k); + MP_ALLOC(&kt) = MP_ALLOC(k); + MP_DIGITS(&kt) = MP_DIGITS(k); + } + + if ((px == NULL) || (py == NULL)) { + if (group->base_point_mul) { + MP_CHECKOK(group->base_point_mul(&kt, rx, ry, group)); + } else { + MP_CHECKOK(group-> + point_mul(&kt, &group->genx, &group->geny, rx, ry, + group)); + } + } else { + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(px, rx, group->meth)); + MP_CHECKOK(group->meth->field_enc(py, ry, group->meth)); + MP_CHECKOK(group->point_mul(&kt, rx, ry, rx, ry, group)); + } else { + MP_CHECKOK(group->point_mul(&kt, px, py, rx, ry, group)); + } + } + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); + } + + CLEANUP: + if (MP_DIGITS(&kt) != MP_DIGITS(k)) { + mp_clear(&kt); + } + return res; +} + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. + * Input and output values are assumed to be NOT field-encoded. */ +mp_err +ec_pts_mul_basic(const mp_int *k1, const mp_int *k2, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int sx, sy; + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK(!((k1 == NULL) + && ((k2 == NULL) || (px == NULL) + || (py == NULL))), MP_BADARG); + + /* if some arguments are not defined used ECPoint_mul */ + if (k1 == NULL) { + return ECPoint_mul(group, k2, px, py, rx, ry); + } else if ((k2 == NULL) || (px == NULL) || (py == NULL)) { + return ECPoint_mul(group, k1, NULL, NULL, rx, ry); + } + + MP_DIGITS(&sx) = 0; + MP_DIGITS(&sy) = 0; + MP_CHECKOK(mp_init(&sx)); + MP_CHECKOK(mp_init(&sy)); + + MP_CHECKOK(ECPoint_mul(group, k1, NULL, NULL, &sx, &sy)); + MP_CHECKOK(ECPoint_mul(group, k2, px, py, rx, ry)); + + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(&sx, &sx, group->meth)); + MP_CHECKOK(group->meth->field_enc(&sy, &sy, group->meth)); + MP_CHECKOK(group->meth->field_enc(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_enc(ry, ry, group->meth)); + } + + MP_CHECKOK(group->point_add(&sx, &sy, rx, ry, rx, ry, group)); + + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); + } + + CLEANUP: + mp_clear(&sx); + mp_clear(&sy); + return res; +} + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. + * Input and output values are assumed to be NOT field-encoded. Uses + * algorithm 15 (simultaneous multiple point multiplication) from Brown, + * Hankerson, Lopez, Menezes. Software Implementation of the NIST + * Elliptic Curves over Prime Fields. */ +mp_err +ec_pts_mul_simul_w2(const mp_int *k1, const mp_int *k2, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[4][4][2]; + const mp_int *a, *b; + unsigned int i, j; + int ai, bi, d; + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK(!((k1 == NULL) + && ((k2 == NULL) || (px == NULL) + || (py == NULL))), MP_BADARG); + + /* if some arguments are not defined used ECPoint_mul */ + if (k1 == NULL) { + return ECPoint_mul(group, k2, px, py, rx, ry); + } else if ((k2 == NULL) || (px == NULL) || (py == NULL)) { + return ECPoint_mul(group, k1, NULL, NULL, rx, ry); + } + + /* initialize precomputation table */ + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + MP_DIGITS(&precomp[i][j][0]) = 0; + MP_DIGITS(&precomp[i][j][1]) = 0; + } + } + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + MP_CHECKOK( mp_init_size(&precomp[i][j][0], + ECL_MAX_FIELD_SIZE_DIGITS) ); + MP_CHECKOK( mp_init_size(&precomp[i][j][1], + ECL_MAX_FIELD_SIZE_DIGITS) ); + } + } + + /* fill precomputation table */ + /* assign {k1, k2} = {a, b} such that len(a) >= len(b) */ + if (mpl_significant_bits(k1) < mpl_significant_bits(k2)) { + a = k2; + b = k1; + if (group->meth->field_enc) { + MP_CHECKOK(group->meth-> + field_enc(px, &precomp[1][0][0], group->meth)); + MP_CHECKOK(group->meth-> + field_enc(py, &precomp[1][0][1], group->meth)); + } else { + MP_CHECKOK(mp_copy(px, &precomp[1][0][0])); + MP_CHECKOK(mp_copy(py, &precomp[1][0][1])); + } + MP_CHECKOK(mp_copy(&group->genx, &precomp[0][1][0])); + MP_CHECKOK(mp_copy(&group->geny, &precomp[0][1][1])); + } else { + a = k1; + b = k2; + MP_CHECKOK(mp_copy(&group->genx, &precomp[1][0][0])); + MP_CHECKOK(mp_copy(&group->geny, &precomp[1][0][1])); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth-> + field_enc(px, &precomp[0][1][0], group->meth)); + MP_CHECKOK(group->meth-> + field_enc(py, &precomp[0][1][1], group->meth)); + } else { + MP_CHECKOK(mp_copy(px, &precomp[0][1][0])); + MP_CHECKOK(mp_copy(py, &precomp[0][1][1])); + } + } + /* precompute [*][0][*] */ + mp_zero(&precomp[0][0][0]); + mp_zero(&precomp[0][0][1]); + MP_CHECKOK(group-> + point_dbl(&precomp[1][0][0], &precomp[1][0][1], + &precomp[2][0][0], &precomp[2][0][1], group)); + MP_CHECKOK(group-> + point_add(&precomp[1][0][0], &precomp[1][0][1], + &precomp[2][0][0], &precomp[2][0][1], + &precomp[3][0][0], &precomp[3][0][1], group)); + /* precompute [*][1][*] */ + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][1][0], &precomp[0][1][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][1][0], &precomp[i][1][1], group)); + } + /* precompute [*][2][*] */ + MP_CHECKOK(group-> + point_dbl(&precomp[0][1][0], &precomp[0][1][1], + &precomp[0][2][0], &precomp[0][2][1], group)); + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][2][0], &precomp[0][2][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][2][0], &precomp[i][2][1], group)); + } + /* precompute [*][3][*] */ + MP_CHECKOK(group-> + point_add(&precomp[0][1][0], &precomp[0][1][1], + &precomp[0][2][0], &precomp[0][2][1], + &precomp[0][3][0], &precomp[0][3][1], group)); + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][3][0], &precomp[0][3][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][3][0], &precomp[i][3][1], group)); + } + + d = (mpl_significant_bits(a) + 1) / 2; + + /* R = inf */ + mp_zero(rx); + mp_zero(ry); + + for (i = d; i-- > 0;) { + ai = MP_GET_BIT(a, 2 * i + 1); + ai <<= 1; + ai |= MP_GET_BIT(a, 2 * i); + bi = MP_GET_BIT(b, 2 * i + 1); + bi <<= 1; + bi |= MP_GET_BIT(b, 2 * i); + /* R = 2^2 * R */ + MP_CHECKOK(group->point_dbl(rx, ry, rx, ry, group)); + MP_CHECKOK(group->point_dbl(rx, ry, rx, ry, group)); + /* R = R + (ai * A + bi * B) */ + MP_CHECKOK(group-> + point_add(rx, ry, &precomp[ai][bi][0], + &precomp[ai][bi][1], rx, ry, group)); + } + + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); + } + + CLEANUP: + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + mp_clear(&precomp[i][j][0]); + mp_clear(&precomp[i][j][1]); + } + } + return res; +} + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. + * Input and output values are assumed to be NOT field-encoded. */ +mp_err +ECPoints_mul(const ECGroup *group, const mp_int *k1, const mp_int *k2, + const mp_int *px, const mp_int *py, mp_int *rx, mp_int *ry) +{ + mp_err res = MP_OKAY; + mp_int k1t, k2t; + const mp_int *k1p, *k2p; + + MP_DIGITS(&k1t) = 0; + MP_DIGITS(&k2t) = 0; + + ARGCHK(group != NULL, MP_BADARG); + + /* want scalar to be less than or equal to group order */ + if (k1 != NULL) { + if (mp_cmp(k1, &group->order) >= 0) { + MP_CHECKOK(mp_init(&k1t)); + MP_CHECKOK(mp_mod(k1, &group->order, &k1t)); + k1p = &k1t; + } else { + k1p = k1; + } + } else { + k1p = k1; + } + if (k2 != NULL) { + if (mp_cmp(k2, &group->order) >= 0) { + MP_CHECKOK(mp_init(&k2t)); + MP_CHECKOK(mp_mod(k2, &group->order, &k2t)); + k2p = &k2t; + } else { + k2p = k2; + } + } else { + k2p = k2; + } + + /* if points_mul is defined, then use it */ + if (group->points_mul) { + res = group->points_mul(k1p, k2p, px, py, rx, ry, group); + } else { + res = ec_pts_mul_simul_w2(k1p, k2p, px, py, rx, ry, group); + } + + CLEANUP: + mp_clear(&k1t); + mp_clear(&k2t); + return res; +} diff --git a/nss/lib/freebl/ecl/ecp.h b/nss/lib/freebl/ecl/ecp.h new file mode 100644 index 0000000..4784b02 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp.h @@ -0,0 +1,106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __ecp_h_ +#define __ecp_h_ + +#include "ecl-priv.h" + +/* Checks if point P(px, py) is at infinity. Uses affine coordinates. */ +mp_err ec_GFp_pt_is_inf_aff(const mp_int *px, const mp_int *py); + +/* Sets P(px, py) to be the point at infinity. Uses affine coordinates. */ +mp_err ec_GFp_pt_set_inf_aff(mp_int *px, mp_int *py); + +/* Computes R = P + Q where R is (rx, ry), P is (px, py) and Q is (qx, + * qy). Uses affine coordinates. */ +mp_err ec_GFp_pt_add_aff(const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes R = P - Q. Uses affine coordinates. */ +mp_err ec_GFp_pt_sub_aff(const mp_int *px, const mp_int *py, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Computes R = 2P. Uses affine coordinates. */ +mp_err ec_GFp_pt_dbl_aff(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group); + +/* Validates a point on a GFp curve. */ +mp_err ec_GFp_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group); + +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GFp. Uses affine coordinates. */ +mp_err ec_GFp_pt_mul_aff(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); +#endif + +/* Converts a point P(px, py) from affine coordinates to Jacobian + * projective coordinates R(rx, ry, rz). */ +mp_err ec_GFp_pt_aff2jac(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group); + +/* Converts a point P(px, py, pz) from Jacobian projective coordinates to + * affine coordinates R(rx, ry). */ +mp_err ec_GFp_pt_jac2aff(const mp_int *px, const mp_int *py, + const mp_int *pz, mp_int *rx, mp_int *ry, + const ECGroup *group); + +/* Checks if point P(px, py, pz) is at infinity. Uses Jacobian + * coordinates. */ +mp_err ec_GFp_pt_is_inf_jac(const mp_int *px, const mp_int *py, + const mp_int *pz); + +/* Sets P(px, py, pz) to be the point at infinity. Uses Jacobian + * coordinates. */ +mp_err ec_GFp_pt_set_inf_jac(mp_int *px, mp_int *py, mp_int *pz); + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, qz). Uses Jacobian coordinates. */ +mp_err ec_GFp_pt_add_jac_aff(const mp_int *px, const mp_int *py, + const mp_int *pz, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + mp_int *rz, const ECGroup *group); + +/* Computes R = 2P. Uses Jacobian coordinates. */ +mp_err ec_GFp_pt_dbl_jac(const mp_int *px, const mp_int *py, + const mp_int *pz, mp_int *rx, mp_int *ry, + mp_int *rz, const ECGroup *group); + +#ifdef ECL_ENABLE_GFP_PT_MUL_JAC +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GFp. Uses Jacobian coordinates. */ +mp_err ec_GFp_pt_mul_jac(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); +#endif + +/* Computes R(x, y) = k1 * G + k2 * P(x, y), where G is the generator + * (base point) of the group of points on the elliptic curve. Allows k1 = + * NULL or { k2, P } = NULL. Implemented using mixed Jacobian-affine + * coordinates. Input and output values are assumed to be NOT + * field-encoded and are in affine form. */ +mp_err + ec_GFp_pts_mul_jac(const mp_int *k1, const mp_int *k2, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group); + +/* Computes R = nP where R is (rx, ry) and P is the base point. Elliptic + * curve points P and R can be identical. Uses mixed Modified-Jacobian + * co-ordinates for doubling and Chudnovsky Jacobian coordinates for + * additions. Assumes input is already field-encoded using field_enc, and + * returns output that is still field-encoded. Uses 5-bit window NAF + * method (algorithm 11) for scalar-point multiplication from Brown, + * Hankerson, Lopez, Menezes. Software Implementation of the NIST Elliptic + * Curves Over Prime Fields. */ +mp_err + ec_GFp_pt_mul_jm_wNAF(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group); + +#endif /* __ecp_h_ */ diff --git a/nss/lib/freebl/ecl/ecp_192.c b/nss/lib/freebl/ecl/ecp_192.c new file mode 100644 index 0000000..0bfd95e --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_192.c @@ -0,0 +1,493 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" + +#define ECP192_DIGITS ECL_CURVE_DIGITS(192) + +/* Fast modular reduction for p192 = 2^192 - 2^64 - 1. a can be r. Uses + * algorithm 7 from Brown, Hankerson, Lopez, Menezes. Software + * Implementation of the NIST Elliptic Curves over Prime Fields. */ +static mp_err +ec_GFp_nistp192_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_size a_used = MP_USED(a); + mp_digit r3; +#ifndef MPI_AMD64_ADD + mp_digit carry; +#endif +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a5a = 0, a5b = 0, a4a = 0, a4b = 0, a3a = 0, a3b = 0; + mp_digit r0a, r0b, r1a, r1b, r2a, r2b; +#else + mp_digit a5 = 0, a4 = 0, a3 = 0; + mp_digit r0, r1, r2; +#endif + + /* reduction not needed if a is not larger than field size */ + if (a_used < ECP192_DIGITS) { + if (a == r) { + return MP_OKAY; + } + return mp_copy(a, r); + } + + /* for polynomials larger than twice the field size, use regular + * reduction */ + if (a_used > ECP192_DIGITS*2) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { + /* copy out upper words of a */ + +#ifdef ECL_THIRTY_TWO_BIT + + /* in all the math below, + * nXb is most signifiant, nXa is least significant */ + switch (a_used) { + case 12: + a5b = MP_DIGIT(a, 11); + case 11: + a5a = MP_DIGIT(a, 10); + case 10: + a4b = MP_DIGIT(a, 9); + case 9: + a4a = MP_DIGIT(a, 8); + case 8: + a3b = MP_DIGIT(a, 7); + case 7: + a3a = MP_DIGIT(a, 6); + } + + + r2b= MP_DIGIT(a, 5); + r2a= MP_DIGIT(a, 4); + r1b = MP_DIGIT(a, 3); + r1a = MP_DIGIT(a, 2); + r0b = MP_DIGIT(a, 1); + r0a = MP_DIGIT(a, 0); + + /* implement r = (a2,a1,a0)+(a5,a5,a5)+(a4,a4,0)+(0,a3,a3) */ + carry = 0; + MP_ADD_CARRY(r0a, a3a, r0a, carry); + MP_ADD_CARRY(r0b, a3b, r0b, carry); + MP_ADD_CARRY(r1a, a3a, r1a, carry); + MP_ADD_CARRY(r1b, a3b, r1b, carry); + MP_ADD_CARRY(r2a, a4a, r2a, carry); + MP_ADD_CARRY(r2b, a4b, r2b, carry); + r3 = carry; carry = 0; + MP_ADD_CARRY(r0a, a5a, r0a, carry); + MP_ADD_CARRY(r0b, a5b, r0b, carry); + MP_ADD_CARRY(r1a, a5a, r1a, carry); + MP_ADD_CARRY(r1b, a5b, r1b, carry); + MP_ADD_CARRY(r2a, a5a, r2a, carry); + MP_ADD_CARRY(r2b, a5b, r2b, carry); + r3 += carry; carry = 0; + MP_ADD_CARRY(r1a, a4a, r1a, carry); + MP_ADD_CARRY(r1b, a4b, r1b, carry); + MP_ADD_CARRY(r2a, 0, r2a, carry); + MP_ADD_CARRY(r2b, 0, r2b, carry); + r3 += carry; + + /* reduce out the carry */ + while (r3) { + carry = 0; + MP_ADD_CARRY(r0a, r3, r0a, carry); + MP_ADD_CARRY(r0b, 0, r0b, carry); + MP_ADD_CARRY(r1a, r3, r1a, carry); + MP_ADD_CARRY(r1b, 0, r1b, carry); + MP_ADD_CARRY(r2a, 0, r2a, carry); + MP_ADD_CARRY(r2b, 0, r2b, carry); + r3 = carry; + } + + /* check for final reduction */ + /* + * our field is 0xffffffffffffffff, 0xfffffffffffffffe, + * 0xffffffffffffffff. That means we can only be over and need + * one more reduction + * if r2 == 0xffffffffffffffffff (same as r2+1 == 0) + * and + * r1 == 0xffffffffffffffffff or + * r1 == 0xfffffffffffffffffe and r0 = 0xfffffffffffffffff + * In all cases, we subtract the field (or add the 2's + * complement value (1,1,0)). (r0, r1, r2) + */ + if (((r2b == 0xffffffff) && (r2a == 0xffffffff) + && (r1b == 0xffffffff) ) && + ((r1a == 0xffffffff) || + ((r1a == 0xfffffffe) && (r0a == 0xffffffff) && + (r0b == 0xffffffff))) ) { + /* do a quick subtract */ + carry = 0; + MP_ADD_CARRY(r0a, 1, r0a, carry); + MP_ADD_CARRY(r0b, carry, r0a, carry); + r1a += 1+carry; + r1b = r2a = r2b = 0; + } + + /* set the lower words of r */ + if (a != r) { + MP_CHECKOK(s_mp_pad(r, 6)); + } + MP_DIGIT(r, 5) = r2b; + MP_DIGIT(r, 4) = r2a; + MP_DIGIT(r, 3) = r1b; + MP_DIGIT(r, 2) = r1a; + MP_DIGIT(r, 1) = r0b; + MP_DIGIT(r, 0) = r0a; + MP_USED(r) = 6; +#else + switch (a_used) { + case 6: + a5 = MP_DIGIT(a, 5); + case 5: + a4 = MP_DIGIT(a, 4); + case 4: + a3 = MP_DIGIT(a, 3); + } + + r2 = MP_DIGIT(a, 2); + r1 = MP_DIGIT(a, 1); + r0 = MP_DIGIT(a, 0); + + /* implement r = (a2,a1,a0)+(a5,a5,a5)+(a4,a4,0)+(0,a3,a3) */ +#ifndef MPI_AMD64_ADD + carry = 0; + MP_ADD_CARRY(r0, a3, r0, carry); + MP_ADD_CARRY(r1, a3, r1, carry); + MP_ADD_CARRY(r2, a4, r2, carry); + r3 = carry; carry = 0; + MP_ADD_CARRY(r0, a5, r0, carry); + MP_ADD_CARRY(r1, a5, r1, carry); + MP_ADD_CARRY(r2, a5, r2, carry); + r3 += carry; carry = 0; + MP_ADD_CARRY(r1, a4, r1, carry); + MP_ADD_CARRY(r2, 0, r2, carry); + r3 += carry; + +#else + r2 = MP_DIGIT(a, 2); + r1 = MP_DIGIT(a, 1); + r0 = MP_DIGIT(a, 0); + + /* set the lower words of r */ + __asm__ ( + "xorq %3,%3 \n\t" + "addq %4,%0 \n\t" + "adcq %4,%1 \n\t" + "adcq %5,%2 \n\t" + "adcq $0,%3 \n\t" + "addq %6,%0 \n\t" + "adcq %6,%1 \n\t" + "adcq %6,%2 \n\t" + "adcq $0,%3 \n\t" + "addq %5,%1 \n\t" + "adcq $0,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r"(a3), + "=r"(a4), "=r"(a5) + : "0" (r0), "1" (r1), "2" (r2), "3" (r3), + "4" (a3), "5" (a4), "6"(a5) + : "%cc" ); +#endif + + /* reduce out the carry */ + while (r3) { +#ifndef MPI_AMD64_ADD + carry = 0; + MP_ADD_CARRY(r0, r3, r0, carry); + MP_ADD_CARRY(r1, r3, r1, carry); + MP_ADD_CARRY(r2, 0, r2, carry); + r3 = carry; +#else + a3=r3; + __asm__ ( + "xorq %3,%3 \n\t" + "addq %4,%0 \n\t" + "adcq %4,%1 \n\t" + "adcq $0,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(r3), "=r"(a3) + : "0" (r0), "1" (r1), "2" (r2), "3" (r3), "4"(a3) + : "%cc" ); +#endif + } + + /* check for final reduction */ + /* + * our field is 0xffffffffffffffff, 0xfffffffffffffffe, + * 0xffffffffffffffff. That means we can only be over and need + * one more reduction + * if r2 == 0xffffffffffffffffff (same as r2+1 == 0) + * and + * r1 == 0xffffffffffffffffff or + * r1 == 0xfffffffffffffffffe and r0 = 0xfffffffffffffffff + * In all cases, we subtract the field (or add the 2's + * complement value (1,1,0)). (r0, r1, r2) + */ + if (r3 || ((r2 == MP_DIGIT_MAX) && + ((r1 == MP_DIGIT_MAX) || + ((r1 == (MP_DIGIT_MAX-1)) && (r0 == MP_DIGIT_MAX))))) { + /* do a quick subtract */ + carry = 0; + MP_ADD_CARRY(r0, 1, r0, carry); + r1 += 1+carry; + r2 = 0; + } + /* set the lower words of r */ + if (a != r) { + MP_CHECKOK(s_mp_pad(r, 3)); + } + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_USED(r) = 3; +#endif + } + s_mp_clamp(r); + CLEANUP: + return res; +} + +#ifndef ECL_THIRTY_TWO_BIT +/* Compute the sum of 192 bit curves. Do the work in-line since the + * number of words are so small, we don't want to overhead of mp function + * calls. Uses optimized modular reduction for p192. + */ +static mp_err +ec_GFp_nistp192_add(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit a0 = 0, a1 = 0, a2 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0; + mp_digit carry; + + switch(MP_USED(a)) { + case 3: + a2 = MP_DIGIT(a,2); + case 2: + a1 = MP_DIGIT(a,1); + case 1: + a0 = MP_DIGIT(a,0); + } + switch(MP_USED(b)) { + case 3: + r2 = MP_DIGIT(b,2); + case 2: + r1 = MP_DIGIT(b,1); + case 1: + r0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + carry = 0; + MP_ADD_CARRY(a0, r0, r0, carry); + MP_ADD_CARRY(a1, r1, r1, carry); + MP_ADD_CARRY(a2, r2, r2, carry); +#else + __asm__ ( + "xorq %3,%3 \n\t" + "addq %4,%0 \n\t" + "adcq %5,%1 \n\t" + "adcq %6,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(carry) + : "r" (a0), "r" (a1), "r" (a2), "0" (r0), + "1" (r1), "2" (r2) + : "%cc" ); +#endif + + /* Do quick 'subract' if we've gone over + * (add the 2's complement of the curve field) */ + if (carry || ((r2 == MP_DIGIT_MAX) && + ((r1 == MP_DIGIT_MAX) || + ((r1 == (MP_DIGIT_MAX-1)) && (r0 == MP_DIGIT_MAX))))) { +#ifndef MPI_AMD64_ADD + carry = 0; + MP_ADD_CARRY(r0, 1, r0, carry); + MP_ADD_CARRY(r1, 1, r1, carry); + MP_ADD_CARRY(r2, 0, r2, carry); +#else + __asm__ ( + "addq $1,%0 \n\t" + "adcq $1,%1 \n\t" + "adcq $0,%2 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2) + : "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + } + + + MP_CHECKOK(s_mp_pad(r, 3)); + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 3; + s_mp_clamp(r); + + + CLEANUP: + return res; +} + +/* Compute the diff of 192 bit curves. Do the work in-line since the + * number of words are so small, we don't want to overhead of mp function + * calls. Uses optimized modular reduction for p192. + */ +static mp_err +ec_GFp_nistp192_sub(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_digit b0 = 0, b1 = 0, b2 = 0; + mp_digit r0 = 0, r1 = 0, r2 = 0; + mp_digit borrow; + + switch(MP_USED(a)) { + case 3: + r2 = MP_DIGIT(a,2); + case 2: + r1 = MP_DIGIT(a,1); + case 1: + r0 = MP_DIGIT(a,0); + } + + switch(MP_USED(b)) { + case 3: + b2 = MP_DIGIT(b,2); + case 2: + b1 = MP_DIGIT(b,1); + case 1: + b0 = MP_DIGIT(b,0); + } + +#ifndef MPI_AMD64_ADD + borrow = 0; + MP_SUB_BORROW(r0, b0, r0, borrow); + MP_SUB_BORROW(r1, b1, r1, borrow); + MP_SUB_BORROW(r2, b2, r2, borrow); +#else + __asm__ ( + "xorq %3,%3 \n\t" + "subq %4,%0 \n\t" + "sbbq %5,%1 \n\t" + "sbbq %6,%2 \n\t" + "adcq $0,%3 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2), "=r"(borrow) + : "r" (b0), "r" (b1), "r" (b2), "0" (r0), + "1" (r1), "2" (r2) + : "%cc" ); +#endif + + /* Do quick 'add' if we've gone under 0 + * (subtract the 2's complement of the curve field) */ + if (borrow) { +#ifndef MPI_AMD64_ADD + borrow = 0; + MP_SUB_BORROW(r0, 1, r0, borrow); + MP_SUB_BORROW(r1, 1, r1, borrow); + MP_SUB_BORROW(r2, 0, r2, borrow); +#else + __asm__ ( + "subq $1,%0 \n\t" + "sbbq $1,%1 \n\t" + "sbbq $0,%2 \n\t" + : "=r"(r0), "=r"(r1), "=r"(r2) + : "0" (r0), "1" (r1), "2" (r2) + : "%cc" ); +#endif + } + + MP_CHECKOK(s_mp_pad(r, 3)); + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 3; + s_mp_clamp(r); + + CLEANUP: + return res; +} + +#endif + +/* Compute the square of polynomial a, reduce modulo p192. Store the + * result in r. r could be a. Uses optimized modular reduction for p192. + */ +static mp_err +ec_GFp_nistp192_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp192_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p192. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p192. */ +static mp_err +ec_GFp_nistp192_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp192_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +static mp_err +ec_GFp_nistp192_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + return mp_invmod(b, &meth->irr, r); + } else { + /* MPI doesn't support divmod, so we implement it using invmod and + * mulmod. */ + MP_CHECKOK(mp_init(&t)); + MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); + MP_CHECKOK(mp_mul(a, &t, r)); + MP_CHECKOK(ec_GFp_nistp192_mod(r, r, meth)); + CLEANUP: + mp_clear(&t); + return res; + } +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp192(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P192) { + group->meth->field_mod = &ec_GFp_nistp192_mod; + group->meth->field_mul = &ec_GFp_nistp192_mul; + group->meth->field_sqr = &ec_GFp_nistp192_sqr; + group->meth->field_div = &ec_GFp_nistp192_div; +#ifndef ECL_THIRTY_TWO_BIT + group->meth->field_add = &ec_GFp_nistp192_add; + group->meth->field_sub = &ec_GFp_nistp192_sub; +#endif + } + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_224.c b/nss/lib/freebl/ecl/ecp_224.c new file mode 100644 index 0000000..142f255 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_224.c @@ -0,0 +1,352 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" + +#define ECP224_DIGITS ECL_CURVE_DIGITS(224) + +/* Fast modular reduction for p224 = 2^224 - 2^96 + 1. a can be r. Uses + * algorithm 7 from Brown, Hankerson, Lopez, Menezes. Software + * Implementation of the NIST Elliptic Curves over Prime Fields. */ +static mp_err +ec_GFp_nistp224_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_size a_used = MP_USED(a); + + int r3b; + mp_digit carry; +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a6a = 0, a6b = 0, + a5a = 0, a5b = 0, a4a = 0, a4b = 0, a3b = 0; + mp_digit r0a, r0b, r1a, r1b, r2a, r2b, r3a; +#else + mp_digit a6 = 0, a5 = 0, a4 = 0, a3b = 0, a5a = 0; + mp_digit a6b = 0, a6a_a5b = 0, a5b = 0, a5a_a4b = 0, a4a_a3b = 0; + mp_digit r0, r1, r2, r3; +#endif + + /* reduction not needed if a is not larger than field size */ + if (a_used < ECP224_DIGITS) { + if (a == r) return MP_OKAY; + return mp_copy(a, r); + } + /* for polynomials larger than twice the field size, use regular + * reduction */ + if (a_used > ECL_CURVE_DIGITS(224*2)) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { +#ifdef ECL_THIRTY_TWO_BIT + /* copy out upper words of a */ + switch (a_used) { + case 14: + a6b = MP_DIGIT(a, 13); + case 13: + a6a = MP_DIGIT(a, 12); + case 12: + a5b = MP_DIGIT(a, 11); + case 11: + a5a = MP_DIGIT(a, 10); + case 10: + a4b = MP_DIGIT(a, 9); + case 9: + a4a = MP_DIGIT(a, 8); + case 8: + a3b = MP_DIGIT(a, 7); + } + r3a = MP_DIGIT(a, 6); + r2b= MP_DIGIT(a, 5); + r2a= MP_DIGIT(a, 4); + r1b = MP_DIGIT(a, 3); + r1a = MP_DIGIT(a, 2); + r0b = MP_DIGIT(a, 1); + r0a = MP_DIGIT(a, 0); + + + /* implement r = (a3a,a2,a1,a0) + +(a5a, a4,a3b, 0) + +( 0, a6,a5b, 0) + -( 0 0, 0|a6b, a6a|a5b ) + -( a6b, a6a|a5b, a5a|a4b, a4a|a3b ) */ + carry = 0; + MP_ADD_CARRY (r1b, a3b, r1b, carry); + MP_ADD_CARRY (r2a, a4a, r2a, carry); + MP_ADD_CARRY (r2b, a4b, r2b, carry); + MP_ADD_CARRY (r3a, a5a, r3a, carry); + r3b = carry; carry = 0; + MP_ADD_CARRY (r1b, a5b, r1b, carry); + MP_ADD_CARRY (r2a, a6a, r2a, carry); + MP_ADD_CARRY (r2b, a6b, r2b, carry); + MP_ADD_CARRY (r3a, 0, r3a, carry); + r3b += carry; carry = 0; + MP_SUB_BORROW(r0a, a3b, r0a, carry); + MP_SUB_BORROW(r0b, a4a, r0b, carry); + MP_SUB_BORROW(r1a, a4b, r1a, carry); + MP_SUB_BORROW(r1b, a5a, r1b, carry); + MP_SUB_BORROW(r2a, a5b, r2a, carry); + MP_SUB_BORROW(r2b, a6a, r2b, carry); + MP_SUB_BORROW(r3a, a6b, r3a, carry); + r3b -= carry; carry = 0; + MP_SUB_BORROW(r0a, a5b, r0a, carry); + MP_SUB_BORROW(r0b, a6a, r0b, carry); + MP_SUB_BORROW(r1a, a6b, r1a, carry); + if (carry) { + MP_SUB_BORROW(r1b, 0, r1b, carry); + MP_SUB_BORROW(r2a, 0, r2a, carry); + MP_SUB_BORROW(r2b, 0, r2b, carry); + MP_SUB_BORROW(r3a, 0, r3a, carry); + r3b -= carry; + } + + while (r3b > 0) { + int tmp; + carry = 0; + MP_ADD_CARRY(r1b, r3b, r1b, carry); + if (carry) { + MP_ADD_CARRY(r2a, 0, r2a, carry); + MP_ADD_CARRY(r2b, 0, r2b, carry); + MP_ADD_CARRY(r3a, 0, r3a, carry); + } + tmp = carry; carry = 0; + MP_SUB_BORROW(r0a, r3b, r0a, carry); + if (carry) { + MP_SUB_BORROW(r0b, 0, r0b, carry); + MP_SUB_BORROW(r1a, 0, r1a, carry); + MP_SUB_BORROW(r1b, 0, r1b, carry); + MP_SUB_BORROW(r2a, 0, r2a, carry); + MP_SUB_BORROW(r2b, 0, r2b, carry); + MP_SUB_BORROW(r3a, 0, r3a, carry); + tmp -= carry; + } + r3b = tmp; + } + + while (r3b < 0) { + mp_digit maxInt = MP_DIGIT_MAX; + carry = 0; + MP_ADD_CARRY (r0a, 1, r0a, carry); + MP_ADD_CARRY (r0b, 0, r0b, carry); + MP_ADD_CARRY (r1a, 0, r1a, carry); + MP_ADD_CARRY (r1b, maxInt, r1b, carry); + MP_ADD_CARRY (r2a, maxInt, r2a, carry); + MP_ADD_CARRY (r2b, maxInt, r2b, carry); + MP_ADD_CARRY (r3a, maxInt, r3a, carry); + r3b += carry; + } + /* check for final reduction */ + /* now the only way we are over is if the top 4 words are all ones */ + if ((r3a == MP_DIGIT_MAX) && (r2b == MP_DIGIT_MAX) + && (r2a == MP_DIGIT_MAX) && (r1b == MP_DIGIT_MAX) && + ((r1a != 0) || (r0b != 0) || (r0a != 0)) ) { + /* one last subraction */ + carry = 0; + MP_SUB_BORROW(r0a, 1, r0a, carry); + MP_SUB_BORROW(r0b, 0, r0b, carry); + MP_SUB_BORROW(r1a, 0, r1a, carry); + r1b = r2a = r2b = r3a = 0; + } + + + if (a != r) { + MP_CHECKOK(s_mp_pad(r, 7)); + } + /* set the lower words of r */ + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 7; + MP_DIGIT(r, 6) = r3a; + MP_DIGIT(r, 5) = r2b; + MP_DIGIT(r, 4) = r2a; + MP_DIGIT(r, 3) = r1b; + MP_DIGIT(r, 2) = r1a; + MP_DIGIT(r, 1) = r0b; + MP_DIGIT(r, 0) = r0a; +#else + /* copy out upper words of a */ + switch (a_used) { + case 7: + a6 = MP_DIGIT(a, 6); + a6b = a6 >> 32; + a6a_a5b = a6 << 32; + case 6: + a5 = MP_DIGIT(a, 5); + a5b = a5 >> 32; + a6a_a5b |= a5b; + a5b = a5b << 32; + a5a_a4b = a5 << 32; + a5a = a5 & 0xffffffff; + case 5: + a4 = MP_DIGIT(a, 4); + a5a_a4b |= a4 >> 32; + a4a_a3b = a4 << 32; + case 4: + a3b = MP_DIGIT(a, 3) >> 32; + a4a_a3b |= a3b; + a3b = a3b << 32; + } + + r3 = MP_DIGIT(a, 3) & 0xffffffff; + r2 = MP_DIGIT(a, 2); + r1 = MP_DIGIT(a, 1); + r0 = MP_DIGIT(a, 0); + + /* implement r = (a3a,a2,a1,a0) + +(a5a, a4,a3b, 0) + +( 0, a6,a5b, 0) + -( 0 0, 0|a6b, a6a|a5b ) + -( a6b, a6a|a5b, a5a|a4b, a4a|a3b ) */ + carry = 0; + MP_ADD_CARRY (r1, a3b, r1, carry); + MP_ADD_CARRY (r2, a4 , r2, carry); + MP_ADD_CARRY (r3, a5a, r3, carry); + carry = 0; + MP_ADD_CARRY (r1, a5b, r1, carry); + MP_ADD_CARRY (r2, a6 , r2, carry); + MP_ADD_CARRY (r3, 0, r3, carry); + + carry = 0; + MP_SUB_BORROW(r0, a4a_a3b, r0, carry); + MP_SUB_BORROW(r1, a5a_a4b, r1, carry); + MP_SUB_BORROW(r2, a6a_a5b, r2, carry); + MP_SUB_BORROW(r3, a6b , r3, carry); + carry = 0; + MP_SUB_BORROW(r0, a6a_a5b, r0, carry); + MP_SUB_BORROW(r1, a6b , r1, carry); + if (carry) { + MP_SUB_BORROW(r2, 0, r2, carry); + MP_SUB_BORROW(r3, 0, r3, carry); + } + + + /* if the value is negative, r3 has a 2's complement + * high value */ + r3b = (int)(r3 >>32); + while (r3b > 0) { + r3 &= 0xffffffff; + carry = 0; + MP_ADD_CARRY(r1,((mp_digit)r3b) << 32, r1, carry); + if (carry) { + MP_ADD_CARRY(r2, 0, r2, carry); + MP_ADD_CARRY(r3, 0, r3, carry); + } + carry = 0; + MP_SUB_BORROW(r0, r3b, r0, carry); + if (carry) { + MP_SUB_BORROW(r1, 0, r1, carry); + MP_SUB_BORROW(r2, 0, r2, carry); + MP_SUB_BORROW(r3, 0, r3, carry); + } + r3b = (int)(r3 >>32); + } + + while (r3b < 0) { + carry = 0; + MP_ADD_CARRY (r0, 1, r0, carry); + MP_ADD_CARRY (r1, MP_DIGIT_MAX <<32, r1, carry); + MP_ADD_CARRY (r2, MP_DIGIT_MAX, r2, carry); + MP_ADD_CARRY (r3, MP_DIGIT_MAX >> 32, r3, carry); + r3b = (int)(r3 >>32); + } + /* check for final reduction */ + /* now the only way we are over is if the top 4 words are + * all ones. Subtract the curve. (curve is 2^224 - 2^96 +1) + */ + if ((r3 == (MP_DIGIT_MAX >> 32)) && (r2 == MP_DIGIT_MAX) + && ((r1 & MP_DIGIT_MAX << 32)== MP_DIGIT_MAX << 32) && + ((r1 != MP_DIGIT_MAX << 32 ) || (r0 != 0)) ) { + /* one last subraction */ + carry = 0; + MP_SUB_BORROW(r0, 1, r0, carry); + MP_SUB_BORROW(r1, MP_DIGIT_MAX << 32, r1, carry); + r2 = r3 = 0; + } + + + if (a != r) { + MP_CHECKOK(s_mp_pad(r, 4)); + } + /* set the lower words of r */ + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 4; + MP_DIGIT(r, 3) = r3; + MP_DIGIT(r, 2) = r2; + MP_DIGIT(r, 1) = r1; + MP_DIGIT(r, 0) = r0; +#endif + } + s_mp_clamp(r); + + CLEANUP: + return res; +} + +/* Compute the square of polynomial a, reduce modulo p224. Store the + * result in r. r could be a. Uses optimized modular reduction for p224. + */ +static mp_err +ec_GFp_nistp224_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp224_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p224. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p224. */ +static mp_err +ec_GFp_nistp224_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp224_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +static mp_err +ec_GFp_nistp224_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + return mp_invmod(b, &meth->irr, r); + } else { + /* MPI doesn't support divmod, so we implement it using invmod and + * mulmod. */ + MP_CHECKOK(mp_init(&t)); + MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); + MP_CHECKOK(mp_mul(a, &t, r)); + MP_CHECKOK(ec_GFp_nistp224_mod(r, r, meth)); + CLEANUP: + mp_clear(&t); + return res; + } +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp224(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P224) { + group->meth->field_mod = &ec_GFp_nistp224_mod; + group->meth->field_mul = &ec_GFp_nistp224_mul; + group->meth->field_sqr = &ec_GFp_nistp224_sqr; + group->meth->field_div = &ec_GFp_nistp224_div; + } + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_256.c b/nss/lib/freebl/ecl/ecp_256.c new file mode 100644 index 0000000..936ee6d --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_256.c @@ -0,0 +1,394 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" + +/* Fast modular reduction for p256 = 2^256 - 2^224 + 2^192+ 2^96 - 1. a can be r. + * Uses algorithm 2.29 from Hankerson, Menezes, Vanstone. Guide to + * Elliptic Curve Cryptography. */ +static mp_err +ec_GFp_nistp256_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_size a_used = MP_USED(a); + int a_bits = mpl_significant_bits(a); + mp_digit carry; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit a8=0, a9=0, a10=0, a11=0, a12=0, a13=0, a14=0, a15=0; + mp_digit r0, r1, r2, r3, r4, r5, r6, r7; + int r8; /* must be a signed value ! */ +#else + mp_digit a4=0, a5=0, a6=0, a7=0; + mp_digit a4h, a4l, a5h, a5l, a6h, a6l, a7h, a7l; + mp_digit r0, r1, r2, r3; + int r4; /* must be a signed value ! */ +#endif + /* for polynomials larger than twice the field size + * use regular reduction */ + if (a_bits < 256) { + if (a == r) return MP_OKAY; + return mp_copy(a,r); + } + if (a_bits > 512) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { + +#ifdef ECL_THIRTY_TWO_BIT + switch (a_used) { + case 16: + a15 = MP_DIGIT(a,15); + case 15: + a14 = MP_DIGIT(a,14); + case 14: + a13 = MP_DIGIT(a,13); + case 13: + a12 = MP_DIGIT(a,12); + case 12: + a11 = MP_DIGIT(a,11); + case 11: + a10 = MP_DIGIT(a,10); + case 10: + a9 = MP_DIGIT(a,9); + case 9: + a8 = MP_DIGIT(a,8); + } + + r0 = MP_DIGIT(a,0); + r1 = MP_DIGIT(a,1); + r2 = MP_DIGIT(a,2); + r3 = MP_DIGIT(a,3); + r4 = MP_DIGIT(a,4); + r5 = MP_DIGIT(a,5); + r6 = MP_DIGIT(a,6); + r7 = MP_DIGIT(a,7); + + /* sum 1 */ + carry = 0; + MP_ADD_CARRY(r3, a11, r3, carry); + MP_ADD_CARRY(r4, a12, r4, carry); + MP_ADD_CARRY(r5, a13, r5, carry); + MP_ADD_CARRY(r6, a14, r6, carry); + MP_ADD_CARRY(r7, a15, r7, carry); + r8 = carry; carry = 0; + MP_ADD_CARRY(r3, a11, r3, carry); + MP_ADD_CARRY(r4, a12, r4, carry); + MP_ADD_CARRY(r5, a13, r5, carry); + MP_ADD_CARRY(r6, a14, r6, carry); + MP_ADD_CARRY(r7, a15, r7, carry); + r8 += carry; carry = 0; + /* sum 2 */ + MP_ADD_CARRY(r3, a12, r3, carry); + MP_ADD_CARRY(r4, a13, r4, carry); + MP_ADD_CARRY(r5, a14, r5, carry); + MP_ADD_CARRY(r6, a15, r6, carry); + MP_ADD_CARRY(r7, 0, r7, carry); + r8 += carry; carry = 0; + /* combine last bottom of sum 3 with second sum 2 */ + MP_ADD_CARRY(r0, a8, r0, carry); + MP_ADD_CARRY(r1, a9, r1, carry); + MP_ADD_CARRY(r2, a10, r2, carry); + MP_ADD_CARRY(r3, a12, r3, carry); + MP_ADD_CARRY(r4, a13, r4, carry); + MP_ADD_CARRY(r5, a14, r5, carry); + MP_ADD_CARRY(r6, a15, r6, carry); + MP_ADD_CARRY(r7, a15, r7, carry); /* from sum 3 */ + r8 += carry; carry = 0; + /* sum 3 (rest of it)*/ + MP_ADD_CARRY(r6, a14, r6, carry); + MP_ADD_CARRY(r7, 0, r7, carry); + r8 += carry; carry = 0; + /* sum 4 (rest of it)*/ + MP_ADD_CARRY(r0, a9, r0, carry); + MP_ADD_CARRY(r1, a10, r1, carry); + MP_ADD_CARRY(r2, a11, r2, carry); + MP_ADD_CARRY(r3, a13, r3, carry); + MP_ADD_CARRY(r4, a14, r4, carry); + MP_ADD_CARRY(r5, a15, r5, carry); + MP_ADD_CARRY(r6, a13, r6, carry); + MP_ADD_CARRY(r7, a8, r7, carry); + r8 += carry; carry = 0; + /* diff 5 */ + MP_SUB_BORROW(r0, a11, r0, carry); + MP_SUB_BORROW(r1, a12, r1, carry); + MP_SUB_BORROW(r2, a13, r2, carry); + MP_SUB_BORROW(r3, 0, r3, carry); + MP_SUB_BORROW(r4, 0, r4, carry); + MP_SUB_BORROW(r5, 0, r5, carry); + MP_SUB_BORROW(r6, a8, r6, carry); + MP_SUB_BORROW(r7, a10, r7, carry); + r8 -= carry; carry = 0; + /* diff 6 */ + MP_SUB_BORROW(r0, a12, r0, carry); + MP_SUB_BORROW(r1, a13, r1, carry); + MP_SUB_BORROW(r2, a14, r2, carry); + MP_SUB_BORROW(r3, a15, r3, carry); + MP_SUB_BORROW(r4, 0, r4, carry); + MP_SUB_BORROW(r5, 0, r5, carry); + MP_SUB_BORROW(r6, a9, r6, carry); + MP_SUB_BORROW(r7, a11, r7, carry); + r8 -= carry; carry = 0; + /* diff 7 */ + MP_SUB_BORROW(r0, a13, r0, carry); + MP_SUB_BORROW(r1, a14, r1, carry); + MP_SUB_BORROW(r2, a15, r2, carry); + MP_SUB_BORROW(r3, a8, r3, carry); + MP_SUB_BORROW(r4, a9, r4, carry); + MP_SUB_BORROW(r5, a10, r5, carry); + MP_SUB_BORROW(r6, 0, r6, carry); + MP_SUB_BORROW(r7, a12, r7, carry); + r8 -= carry; carry = 0; + /* diff 8 */ + MP_SUB_BORROW(r0, a14, r0, carry); + MP_SUB_BORROW(r1, a15, r1, carry); + MP_SUB_BORROW(r2, 0, r2, carry); + MP_SUB_BORROW(r3, a9, r3, carry); + MP_SUB_BORROW(r4, a10, r4, carry); + MP_SUB_BORROW(r5, a11, r5, carry); + MP_SUB_BORROW(r6, 0, r6, carry); + MP_SUB_BORROW(r7, a13, r7, carry); + r8 -= carry; + + /* reduce the overflows */ + while (r8 > 0) { + mp_digit r8_d = r8; carry = 0; + carry = 0; + MP_ADD_CARRY(r0, r8_d, r0, carry); + MP_ADD_CARRY(r1, 0, r1, carry); + MP_ADD_CARRY(r2, 0, r2, carry); + MP_ADD_CARRY(r3, 0-r8_d, r3, carry); + MP_ADD_CARRY(r4, MP_DIGIT_MAX, r4, carry); + MP_ADD_CARRY(r5, MP_DIGIT_MAX, r5, carry); + MP_ADD_CARRY(r6, 0-(r8_d+1), r6, carry); + MP_ADD_CARRY(r7, (r8_d-1), r7, carry); + r8 = carry; + } + + /* reduce the underflows */ + while (r8 < 0) { + mp_digit r8_d = -r8; + carry = 0; + MP_SUB_BORROW(r0, r8_d, r0, carry); + MP_SUB_BORROW(r1, 0, r1, carry); + MP_SUB_BORROW(r2, 0, r2, carry); + MP_SUB_BORROW(r3, 0-r8_d, r3, carry); + MP_SUB_BORROW(r4, MP_DIGIT_MAX, r4, carry); + MP_SUB_BORROW(r5, MP_DIGIT_MAX, r5, carry); + MP_SUB_BORROW(r6, 0-(r8_d+1), r6, carry); + MP_SUB_BORROW(r7, (r8_d-1), r7, carry); + r8 = 0-carry; + } + if (a != r) { + MP_CHECKOK(s_mp_pad(r,8)); + } + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 8; + + MP_DIGIT(r,7) = r7; + MP_DIGIT(r,6) = r6; + MP_DIGIT(r,5) = r5; + MP_DIGIT(r,4) = r4; + MP_DIGIT(r,3) = r3; + MP_DIGIT(r,2) = r2; + MP_DIGIT(r,1) = r1; + MP_DIGIT(r,0) = r0; + + /* final reduction if necessary */ + if ((r7 == MP_DIGIT_MAX) && + ((r6 > 1) || ((r6 == 1) && + (r5 || r4 || r3 || + ((r2 == MP_DIGIT_MAX) && (r1 == MP_DIGIT_MAX) + && (r0 == MP_DIGIT_MAX)))))) { + MP_CHECKOK(mp_sub(r, &meth->irr, r)); + } + + s_mp_clamp(r); +#else + switch (a_used) { + case 8: + a7 = MP_DIGIT(a,7); + case 7: + a6 = MP_DIGIT(a,6); + case 6: + a5 = MP_DIGIT(a,5); + case 5: + a4 = MP_DIGIT(a,4); + } + a7l = a7 << 32; + a7h = a7 >> 32; + a6l = a6 << 32; + a6h = a6 >> 32; + a5l = a5 << 32; + a5h = a5 >> 32; + a4l = a4 << 32; + a4h = a4 >> 32; + r3 = MP_DIGIT(a,3); + r2 = MP_DIGIT(a,2); + r1 = MP_DIGIT(a,1); + r0 = MP_DIGIT(a,0); + + /* sum 1 */ + carry = 0; + carry = 0; + MP_ADD_CARRY(r1, a5h << 32, r1, carry); + MP_ADD_CARRY(r2, a6, r2, carry); + MP_ADD_CARRY(r3, a7, r3, carry); + r4 = carry; carry = 0; + carry = 0; + MP_ADD_CARRY(r1, a5h << 32, r1, carry); + MP_ADD_CARRY(r2, a6, r2, carry); + MP_ADD_CARRY(r3, a7, r3, carry); + r4 += carry; carry = 0; + /* sum 2 */ + carry = 0; + MP_ADD_CARRY(r1, a6l, r1, carry); + MP_ADD_CARRY(r2, a6h | a7l, r2, carry); + MP_ADD_CARRY(r3, a7h, r3, carry); + r4 += carry; carry = 0; + carry = 0; + MP_ADD_CARRY(r1, a6l, r1, carry); + MP_ADD_CARRY(r2, a6h | a7l, r2, carry); + MP_ADD_CARRY(r3, a7h, r3, carry); + r4 += carry; carry = 0; + + /* sum 3 */ + carry = 0; + MP_ADD_CARRY(r0, a4, r0, carry); + MP_ADD_CARRY(r1, a5l >> 32, r1, carry); + MP_ADD_CARRY(r2, 0, r2, carry); + MP_ADD_CARRY(r3, a7, r3, carry); + r4 += carry; carry = 0; + /* sum 4 */ + carry = 0; + MP_ADD_CARRY(r0, a4h | a5l, r0, carry); + MP_ADD_CARRY(r1, a5h|(a6h<<32), r1, carry); + MP_ADD_CARRY(r2, a7, r2, carry); + MP_ADD_CARRY(r3, a6h | a4l, r3, carry); + r4 += carry; + /* diff 5 */ + carry = 0; + MP_SUB_BORROW(r0, a5h | a6l, r0, carry); + MP_SUB_BORROW(r1, a6h, r1, carry); + MP_SUB_BORROW(r2, 0, r2, carry); + MP_SUB_BORROW(r3, (a4l>>32)|a5l,r3, carry); + r4 -= carry; + /* diff 6 */ + carry = 0; + MP_SUB_BORROW(r0, a6, r0, carry); + MP_SUB_BORROW(r1, a7, r1, carry); + MP_SUB_BORROW(r2, 0, r2, carry); + MP_SUB_BORROW(r3, a4h|(a5h<<32),r3, carry); + r4 -= carry; + /* diff 7 */ + carry = 0; + MP_SUB_BORROW(r0, a6h|a7l, r0, carry); + MP_SUB_BORROW(r1, a7h|a4l, r1, carry); + MP_SUB_BORROW(r2, a4h|a5l, r2, carry); + MP_SUB_BORROW(r3, a6l, r3, carry); + r4 -= carry; + /* diff 8 */ + carry = 0; + MP_SUB_BORROW(r0, a7, r0, carry); + MP_SUB_BORROW(r1, a4h<<32, r1, carry); + MP_SUB_BORROW(r2, a5, r2, carry); + MP_SUB_BORROW(r3, a6h<<32, r3, carry); + r4 -= carry; + + /* reduce the overflows */ + while (r4 > 0) { + mp_digit r4_long = r4; + mp_digit r4l = (r4_long << 32); + carry = 0; + carry = 0; + MP_ADD_CARRY(r0, r4_long, r0, carry); + MP_ADD_CARRY(r1, 0-r4l, r1, carry); + MP_ADD_CARRY(r2, MP_DIGIT_MAX, r2, carry); + MP_ADD_CARRY(r3, r4l-r4_long-1,r3, carry); + r4 = carry; + } + + /* reduce the underflows */ + while (r4 < 0) { + mp_digit r4_long = -r4; + mp_digit r4l = (r4_long << 32); + carry = 0; + MP_SUB_BORROW(r0, r4_long, r0, carry); + MP_SUB_BORROW(r1, 0-r4l, r1, carry); + MP_SUB_BORROW(r2, MP_DIGIT_MAX, r2, carry); + MP_SUB_BORROW(r3, r4l-r4_long-1,r3, carry); + r4 = 0-carry; + } + + if (a != r) { + MP_CHECKOK(s_mp_pad(r,4)); + } + MP_SIGN(r) = MP_ZPOS; + MP_USED(r) = 4; + + MP_DIGIT(r,3) = r3; + MP_DIGIT(r,2) = r2; + MP_DIGIT(r,1) = r1; + MP_DIGIT(r,0) = r0; + + /* final reduction if necessary */ + if ((r3 > 0xFFFFFFFF00000001ULL) || + ((r3 == 0xFFFFFFFF00000001ULL) && + (r2 || (r1 >> 32)|| + (r1 == 0xFFFFFFFFULL && r0 == MP_DIGIT_MAX)))) { + /* very rare, just use mp_sub */ + MP_CHECKOK(mp_sub(r, &meth->irr, r)); + } + + s_mp_clamp(r); +#endif + } + + CLEANUP: + return res; +} + +/* Compute the square of polynomial a, reduce modulo p256. Store the + * result in r. r could be a. Uses optimized modular reduction for p256. + */ +static mp_err +ec_GFp_nistp256_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp256_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p256. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p256. */ +static mp_err +ec_GFp_nistp256_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp256_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp256(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P256) { + group->meth->field_mod = &ec_GFp_nistp256_mod; + group->meth->field_mul = &ec_GFp_nistp256_mul; + group->meth->field_sqr = &ec_GFp_nistp256_sqr; + } + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_256_32.c b/nss/lib/freebl/ecl/ecp_256_32.c new file mode 100644 index 0000000..cd8cd23 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_256_32.c @@ -0,0 +1,1504 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* A 32-bit implementation of the NIST P-256 elliptic curve. */ + +#include <string.h> + +#include "prtypes.h" +#include "mpi.h" +#include "mpi-priv.h" +#include "ecp.h" + +typedef PRUint8 u8; +typedef PRUint32 u32; +typedef PRUint64 u64; + +/* Our field elements are represented as nine, unsigned 32-bit words. Freebl's + * MPI library calls them digits, but here they are called limbs, which is + * GMP's terminology. + * + * The value of an felem (field element) is: + * x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228) + * + * That is, each limb is alternately 29 or 28-bits wide in little-endian + * order. + * + * This means that an felem hits 2**257, rather than 2**256 as we would like. A + * 28, 29, ... pattern would cause us to hit 2**256, but that causes problems + * when multiplying as terms end up one bit short of a limb which would require + * much bit-shifting to correct. + * + * Finally, the values stored in an felem are in Montgomery form. So the value + * |y| is stored as (y*R) mod p, where p is the P-256 prime and R is 2**257. + */ +typedef u32 limb; +#define NLIMBS 9 +typedef limb felem[NLIMBS]; + +static const limb kBottom28Bits = 0xfffffff; +static const limb kBottom29Bits = 0x1fffffff; + +/* kOne is the number 1 as an felem. It's 2**257 mod p split up into 29 and + * 28-bit words. + */ +static const felem kOne = { + 2, 0, 0, 0xffff800, + 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff, + 0 +}; +static const felem kZero = {0}; +static const felem kP = { + 0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff, + 0, 0, 0x200000, 0xf000000, + 0xfffffff +}; +static const felem k2P = { + 0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff, + 0, 0, 0x400000, 0xe000000, + 0x1fffffff +}; + +/* kPrecomputed contains precomputed values to aid the calculation of scalar + * multiples of the base point, G. It's actually two, equal length, tables + * concatenated. + * + * The first table contains (x,y) felem pairs for 16 multiples of the base + * point, G. + * + * Index | Index (binary) | Value + * 0 | 0000 | 0G (all zeros, omitted) + * 1 | 0001 | G + * 2 | 0010 | 2**64G + * 3 | 0011 | 2**64G + G + * 4 | 0100 | 2**128G + * 5 | 0101 | 2**128G + G + * 6 | 0110 | 2**128G + 2**64G + * 7 | 0111 | 2**128G + 2**64G + G + * 8 | 1000 | 2**192G + * 9 | 1001 | 2**192G + G + * 10 | 1010 | 2**192G + 2**64G + * 11 | 1011 | 2**192G + 2**64G + G + * 12 | 1100 | 2**192G + 2**128G + * 13 | 1101 | 2**192G + 2**128G + G + * 14 | 1110 | 2**192G + 2**128G + 2**64G + * 15 | 1111 | 2**192G + 2**128G + 2**64G + G + * + * The second table follows the same style, but the terms are 2**32G, + * 2**96G, 2**160G, 2**224G. + * + * This is ~2KB of data. + */ +static const limb kPrecomputed[NLIMBS * 2 * 15 * 2] = { + 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee, + 0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3, + 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c, + 0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22, + 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050, + 0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b, + 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa, + 0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2, + 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609, + 0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581, + 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca, + 0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33, + 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6, + 0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd, + 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0, + 0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881, + 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a, + 0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26, + 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b, + 0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023, + 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133, + 0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa, + 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29, + 0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc, + 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8, + 0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59, + 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39, + 0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689, + 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa, + 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3, + 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1, + 0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f, + 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72, + 0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d, + 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b, + 0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a, + 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a, + 0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f, + 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb, + 0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc, + 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9, + 0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce, + 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2, + 0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca, + 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229, + 0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57, + 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c, + 0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa, + 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651, + 0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec, + 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7, + 0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c, + 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927, + 0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298, + 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8, + 0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2, + 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d, + 0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4, + 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8, + 0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78, +}; + +/* Field element operations: + */ + +/* NON_ZERO_TO_ALL_ONES returns: + * 0xffffffff for 0 < x <= 2**31 + * 0 for x == 0 or x > 2**31. + * + * x must be a u32 or an equivalent type such as limb. + */ +#define NON_ZERO_TO_ALL_ONES(x) ((((u32)(x) - 1) >> 31) - 1) + +/* felem_reduce_carry adds a multiple of p in order to cancel |carry|, + * which is a term at 2**257. + * + * On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28. + * On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. + */ +static void felem_reduce_carry(felem inout, limb carry) +{ + const u32 carry_mask = NON_ZERO_TO_ALL_ONES(carry); + + inout[0] += carry << 1; + inout[3] += 0x10000000 & carry_mask; + /* carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the + * previous line therefore this doesn't underflow. + */ + inout[3] -= carry << 11; + inout[4] += (0x20000000 - 1) & carry_mask; + inout[5] += (0x10000000 - 1) & carry_mask; + inout[6] += (0x20000000 - 1) & carry_mask; + inout[6] -= carry << 22; + /* This may underflow if carry is non-zero but, if so, we'll fix it in the + * next line. + */ + inout[7] -= 1 & carry_mask; + inout[7] += carry << 25; +} + +/* felem_sum sets out = in+in2. + * + * On entry, in[i]+in2[i] must not overflow a 32-bit word. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 + */ +static void felem_sum(felem out, const felem in, const felem in2) +{ + limb carry = 0; + unsigned int i; + for (i = 0;; i++) { + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] + in2[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +#define two31m3 (((limb)1) << 31) - (((limb)1) << 3) +#define two30m2 (((limb)1) << 30) - (((limb)1) << 2) +#define two30p13m2 (((limb)1) << 30) + (((limb)1) << 13) - (((limb)1) << 2) +#define two31m2 (((limb)1) << 31) - (((limb)1) << 2) +#define two31p24m2 (((limb)1) << 31) + (((limb)1) << 24) - (((limb)1) << 2) +#define two30m27m2 (((limb)1) << 30) - (((limb)1) << 27) - (((limb)1) << 2) + +/* zero31 is 0 mod p. + */ +static const felem zero31 = { + two31m3, two30m2, two31m2, two30p13m2, + two31m2, two30m2, two31p24m2, two30m27m2, + two31m2 +}; + +/* felem_diff sets out = in-in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + */ +static void felem_diff(felem out, const felem in, const felem in2) +{ + limb carry = 0; + unsigned int i; + + for (i = 0;; i++) { + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] = in[i] - in2[i]; + out[i] += zero31[i]; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_reduce_degree sets out = tmp/R mod p where tmp contains 64-bit words + * with the same 29,28,... bit positions as an felem. + * + * The values in felems are in Montgomery form: x*R mod p where R = 2**257. + * Since we just multiplied two Montgomery values together, the result is + * x*y*R*R mod p. We wish to divide by R in order for the result also to be + * in Montgomery form. + * + * On entry: tmp[i] < 2**64 + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 + */ +static void felem_reduce_degree(felem out, u64 tmp[17]) +{ + /* The following table may be helpful when reading this code: + * + * Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10... + * Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29 + * Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285 + * (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 + */ + limb tmp2[18], carry, x, xMask; + unsigned int i; + + /* tmp contains 64-bit words with the same 29,28,29-bit positions as an + * felem. So the top of an element of tmp might overlap with another + * element two positions down. The following loop eliminates this + * overlap. + */ + tmp2[0] = tmp[0] & kBottom29Bits; + + /* In the following we use "(limb) tmp[x]" and "(limb) (tmp[x]>>32)" to try + * and hint to the compiler that it can do a single-word shift by selecting + * the right register rather than doing a double-word shift and truncating + * afterwards. + */ + tmp2[1] = ((limb) tmp[0]) >> 29; + tmp2[1] |= (((limb) (tmp[0] >> 32)) << 3) & kBottom28Bits; + tmp2[1] += ((limb) tmp[1]) & kBottom28Bits; + carry = tmp2[1] >> 28; + tmp2[1] &= kBottom28Bits; + + for (i = 2; i < 17; i++) { + tmp2[i] = ((limb) (tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb) (tmp[i - 1])) >> 28; + tmp2[i] += (((limb) (tmp[i - 1] >> 32)) << 4) & kBottom29Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom29Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 29; + tmp2[i] &= kBottom29Bits; + + i++; + if (i == 17) + break; + tmp2[i] = ((limb) (tmp[i - 2] >> 32)) >> 25; + tmp2[i] += ((limb) (tmp[i - 1])) >> 29; + tmp2[i] += (((limb) (tmp[i - 1] >> 32)) << 3) & kBottom28Bits; + tmp2[i] += ((limb) tmp[i]) & kBottom28Bits; + tmp2[i] += carry; + carry = tmp2[i] >> 28; + tmp2[i] &= kBottom28Bits; + } + + tmp2[17] = ((limb) (tmp[15] >> 32)) >> 25; + tmp2[17] += ((limb) (tmp[16])) >> 29; + tmp2[17] += (((limb) (tmp[16] >> 32)) << 3); + tmp2[17] += carry; + + /* Montgomery elimination of terms: + * + * Since R is 2**257, we can divide by R with a bitwise shift if we can + * ensure that the right-most 257 bits are all zero. We can make that true + * by adding multiplies of p without affecting the value. + * + * So we eliminate limbs from right to left. Since the bottom 29 bits of p + * are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0. + * We can do that for 8 further limbs and then right shift to eliminate the + * extra factor of R. + */ + for (i = 0;; i += 2) { + tmp2[i + 1] += tmp2[i] >> 29; + x = tmp2[i] & kBottom29Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i] = 0; + + /* The bounds calculations for this loop are tricky. Each iteration of + * the loop eliminates two words by adding values to words to their + * right. + * + * The following table contains the amounts added to each word (as an + * offset from the value of i at the top of the loop). The amounts are + * accounted for from the first and second half of the loop separately + * and are written as, for example, 28 to mean a value <2**28. + * + * Word: 3 4 5 6 7 8 9 10 + * Added in top half: 28 11 29 21 29 28 + * 28 29 + * 29 + * Added in bottom half: 29 10 28 21 28 28 + * 29 + * + * The value that is currently offset 7 will be offset 5 for the next + * iteration and then offset 3 for the iteration after that. Therefore + * the total value added will be the values added at 7, 5 and 3. + * + * The following table accumulates these values. The sums at the bottom + * are written as, for example, 29+28, to mean a value < 2**29+2**28. + * + * Word: 3 4 5 6 7 8 9 10 11 12 13 + * 28 11 10 29 21 29 28 28 28 28 28 + * 29 28 11 28 29 28 29 28 29 28 + * 29 28 21 21 29 21 29 21 + * 10 29 28 21 28 21 28 + * 28 29 28 29 28 29 28 + * 11 10 29 10 29 10 + * 29 28 11 28 11 + * 29 29 + * -------------------------------------------- + * 30+ 31+ 30+ 31+ 30+ + * 28+ 29+ 28+ 29+ 21+ + * 21+ 28+ 21+ 28+ 10 + * 10 21+ 10 21+ + * 11 11 + * + * So the greatest amount is added to tmp2[10] and tmp2[12]. If + * tmp2[10/12] has an initial value of <2**29, then the maximum value + * will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32, + * as required. + */ + tmp2[i + 3] += (x << 10) & kBottom28Bits; + tmp2[i + 4] += (x >> 18); + + tmp2[i + 6] += (x << 21) & kBottom29Bits; + tmp2[i + 7] += x >> 8; + + /* At position 200, which is the starting bit position for word 7, we + * have a factor of 0xf000000 = 2**28 - 2**24. + */ + tmp2[i + 7] += 0x10000000 & xMask; + /* Word 7 is 28 bits wide, so the 2**28 term exactly hits word 8. */ + tmp2[i + 8] += (x - 1) & xMask; + tmp2[i + 7] -= (x << 24) & kBottom28Bits; + tmp2[i + 8] -= x >> 4; + + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 8] -= x; + tmp2[i + 8] += (x << 28) & kBottom29Bits; + tmp2[i + 9] += ((x >> 1) - 1) & xMask; + + if (i+1 == NLIMBS) + break; + tmp2[i + 2] += tmp2[i + 1] >> 28; + x = tmp2[i + 1] & kBottom28Bits; + xMask = NON_ZERO_TO_ALL_ONES(x); + tmp2[i + 1] = 0; + + tmp2[i + 4] += (x << 11) & kBottom29Bits; + tmp2[i + 5] += (x >> 18); + + tmp2[i + 7] += (x << 21) & kBottom28Bits; + tmp2[i + 8] += x >> 7; + + /* At position 199, which is the starting bit of the 8th word when + * dealing with a context starting on an odd word, we have a factor of + * 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th + * word from i+1 is i+8. + */ + tmp2[i + 8] += 0x20000000 & xMask; + tmp2[i + 9] += (x - 1) & xMask; + tmp2[i + 8] -= (x << 25) & kBottom29Bits; + tmp2[i + 9] -= x >> 4; + + tmp2[i + 9] += 0x10000000 & xMask; + tmp2[i + 9] -= x; + tmp2[i + 10] += (x - 1) & xMask; + } + + /* We merge the right shift with a carry chain. The words above 2**257 have + * widths of 28,29,... which we need to correct when copying them down. + */ + carry = 0; + for (i = 0; i < 8; i++) { + /* The maximum value of tmp2[i + 9] occurs on the first iteration and + * is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is + * therefore safe. + */ + out[i] = tmp2[i + 9]; + out[i] += carry; + out[i] += (tmp2[i + 10] << 28) & kBottom29Bits; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + out[i] = tmp2[i + 9] >> 1; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + out[8] = tmp2[17]; + out[8] += carry; + carry = out[8] >> 29; + out[8] &= kBottom29Bits; + + felem_reduce_carry(out, carry); +} + +/* felem_square sets out=in*in. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + */ +static void felem_square(felem out, const felem in) +{ + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in[0]; + tmp[1] = ((u64) in[0]) * (in[1] << 1); + tmp[2] = ((u64) in[0]) * (in[2] << 1) + + ((u64) in[1]) * (in[1] << 1); + tmp[3] = ((u64) in[0]) * (in[3] << 1) + + ((u64) in[1]) * (in[2] << 1); + tmp[4] = ((u64) in[0]) * (in[4] << 1) + + ((u64) in[1]) * (in[3] << 2) + + ((u64) in[2]) * in[2]; + tmp[5] = ((u64) in[0]) * (in[5] << 1) + + ((u64) in[1]) * (in[4] << 1) + + ((u64) in[2]) * (in[3] << 1); + tmp[6] = ((u64) in[0]) * (in[6] << 1) + + ((u64) in[1]) * (in[5] << 2) + + ((u64) in[2]) * (in[4] << 1) + + ((u64) in[3]) * (in[3] << 1); + tmp[7] = ((u64) in[0]) * (in[7] << 1) + + ((u64) in[1]) * (in[6] << 1) + + ((u64) in[2]) * (in[5] << 1) + + ((u64) in[3]) * (in[4] << 1); + /* tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60, + * which is < 2**64 as required. + */ + tmp[8] = ((u64) in[0]) * (in[8] << 1) + + ((u64) in[1]) * (in[7] << 2) + + ((u64) in[2]) * (in[6] << 1) + + ((u64) in[3]) * (in[5] << 2) + + ((u64) in[4]) * in[4]; + tmp[9] = ((u64) in[1]) * (in[8] << 1) + + ((u64) in[2]) * (in[7] << 1) + + ((u64) in[3]) * (in[6] << 1) + + ((u64) in[4]) * (in[5] << 1); + tmp[10] = ((u64) in[2]) * (in[8] << 1) + + ((u64) in[3]) * (in[7] << 2) + + ((u64) in[4]) * (in[6] << 1) + + ((u64) in[5]) * (in[5] << 1); + tmp[11] = ((u64) in[3]) * (in[8] << 1) + + ((u64) in[4]) * (in[7] << 1) + + ((u64) in[5]) * (in[6] << 1); + tmp[12] = ((u64) in[4]) * (in[8] << 1) + + ((u64) in[5]) * (in[7] << 2) + + ((u64) in[6]) * in[6]; + tmp[13] = ((u64) in[5]) * (in[8] << 1) + + ((u64) in[6]) * (in[7] << 1); + tmp[14] = ((u64) in[6]) * (in[8] << 1) + + ((u64) in[7]) * (in[7] << 1); + tmp[15] = ((u64) in[7]) * (in[8] << 1); + tmp[16] = ((u64) in[8]) * in[8]; + + felem_reduce_degree(out, tmp); +} + +/* felem_mul sets out=in*in2. + * + * On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and + * in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + */ +static void felem_mul(felem out, const felem in, const felem in2) +{ + u64 tmp[17]; + + tmp[0] = ((u64) in[0]) * in2[0]; + tmp[1] = ((u64) in[0]) * (in2[1] << 0) + + ((u64) in[1]) * (in2[0] << 0); + tmp[2] = ((u64) in[0]) * (in2[2] << 0) + + ((u64) in[1]) * (in2[1] << 1) + + ((u64) in[2]) * (in2[0] << 0); + tmp[3] = ((u64) in[0]) * (in2[3] << 0) + + ((u64) in[1]) * (in2[2] << 0) + + ((u64) in[2]) * (in2[1] << 0) + + ((u64) in[3]) * (in2[0] << 0); + tmp[4] = ((u64) in[0]) * (in2[4] << 0) + + ((u64) in[1]) * (in2[3] << 1) + + ((u64) in[2]) * (in2[2] << 0) + + ((u64) in[3]) * (in2[1] << 1) + + ((u64) in[4]) * (in2[0] << 0); + tmp[5] = ((u64) in[0]) * (in2[5] << 0) + + ((u64) in[1]) * (in2[4] << 0) + + ((u64) in[2]) * (in2[3] << 0) + + ((u64) in[3]) * (in2[2] << 0) + + ((u64) in[4]) * (in2[1] << 0) + + ((u64) in[5]) * (in2[0] << 0); + tmp[6] = ((u64) in[0]) * (in2[6] << 0) + + ((u64) in[1]) * (in2[5] << 1) + + ((u64) in[2]) * (in2[4] << 0) + + ((u64) in[3]) * (in2[3] << 1) + + ((u64) in[4]) * (in2[2] << 0) + + ((u64) in[5]) * (in2[1] << 1) + + ((u64) in[6]) * (in2[0] << 0); + tmp[7] = ((u64) in[0]) * (in2[7] << 0) + + ((u64) in[1]) * (in2[6] << 0) + + ((u64) in[2]) * (in2[5] << 0) + + ((u64) in[3]) * (in2[4] << 0) + + ((u64) in[4]) * (in2[3] << 0) + + ((u64) in[5]) * (in2[2] << 0) + + ((u64) in[6]) * (in2[1] << 0) + + ((u64) in[7]) * (in2[0] << 0); + /* tmp[8] has the greatest value but doesn't overflow. See logic in + * felem_square. + */ + tmp[8] = ((u64) in[0]) * (in2[8] << 0) + + ((u64) in[1]) * (in2[7] << 1) + + ((u64) in[2]) * (in2[6] << 0) + + ((u64) in[3]) * (in2[5] << 1) + + ((u64) in[4]) * (in2[4] << 0) + + ((u64) in[5]) * (in2[3] << 1) + + ((u64) in[6]) * (in2[2] << 0) + + ((u64) in[7]) * (in2[1] << 1) + + ((u64) in[8]) * (in2[0] << 0); + tmp[9] = ((u64) in[1]) * (in2[8] << 0) + + ((u64) in[2]) * (in2[7] << 0) + + ((u64) in[3]) * (in2[6] << 0) + + ((u64) in[4]) * (in2[5] << 0) + + ((u64) in[5]) * (in2[4] << 0) + + ((u64) in[6]) * (in2[3] << 0) + + ((u64) in[7]) * (in2[2] << 0) + + ((u64) in[8]) * (in2[1] << 0); + tmp[10] = ((u64) in[2]) * (in2[8] << 0) + + ((u64) in[3]) * (in2[7] << 1) + + ((u64) in[4]) * (in2[6] << 0) + + ((u64) in[5]) * (in2[5] << 1) + + ((u64) in[6]) * (in2[4] << 0) + + ((u64) in[7]) * (in2[3] << 1) + + ((u64) in[8]) * (in2[2] << 0); + tmp[11] = ((u64) in[3]) * (in2[8] << 0) + + ((u64) in[4]) * (in2[7] << 0) + + ((u64) in[5]) * (in2[6] << 0) + + ((u64) in[6]) * (in2[5] << 0) + + ((u64) in[7]) * (in2[4] << 0) + + ((u64) in[8]) * (in2[3] << 0); + tmp[12] = ((u64) in[4]) * (in2[8] << 0) + + ((u64) in[5]) * (in2[7] << 1) + + ((u64) in[6]) * (in2[6] << 0) + + ((u64) in[7]) * (in2[5] << 1) + + ((u64) in[8]) * (in2[4] << 0); + tmp[13] = ((u64) in[5]) * (in2[8] << 0) + + ((u64) in[6]) * (in2[7] << 0) + + ((u64) in[7]) * (in2[6] << 0) + + ((u64) in[8]) * (in2[5] << 0); + tmp[14] = ((u64) in[6]) * (in2[8] << 0) + + ((u64) in[7]) * (in2[7] << 1) + + ((u64) in[8]) * (in2[6] << 0); + tmp[15] = ((u64) in[7]) * (in2[8] << 0) + + ((u64) in[8]) * (in2[7] << 0); + tmp[16] = ((u64) in[8]) * (in2[8] << 0); + + felem_reduce_degree(out, tmp); +} + +static void felem_assign(felem out, const felem in) +{ + memcpy(out, in, sizeof(felem)); +} + +/* felem_inv calculates |out| = |in|^{-1} + * + * Based on Fermat's Little Theorem: + * a^p = a (mod p) + * a^{p-1} = 1 (mod p) + * a^{p-2} = a^{-1} (mod p) + */ +static void felem_inv(felem out, const felem in) +{ + felem ftmp, ftmp2; + /* each e_I will hold |in|^{2^I - 1} */ + felem e2, e4, e8, e16, e32, e64; + unsigned int i; + + felem_square(ftmp, in); /* 2^1 */ + felem_mul(ftmp, in, ftmp); /* 2^2 - 2^0 */ + felem_assign(e2, ftmp); + felem_square(ftmp, ftmp); /* 2^3 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^4 - 2^2 */ + felem_mul(ftmp, ftmp, e2); /* 2^4 - 2^0 */ + felem_assign(e4, ftmp); + felem_square(ftmp, ftmp); /* 2^5 - 2^1 */ + felem_square(ftmp, ftmp); /* 2^6 - 2^2 */ + felem_square(ftmp, ftmp); /* 2^7 - 2^3 */ + felem_square(ftmp, ftmp); /* 2^8 - 2^4 */ + felem_mul(ftmp, ftmp, e4); /* 2^8 - 2^0 */ + felem_assign(e8, ftmp); + for (i = 0; i < 8; i++) { + felem_square(ftmp, ftmp); + } /* 2^16 - 2^8 */ + felem_mul(ftmp, ftmp, e8); /* 2^16 - 2^0 */ + felem_assign(e16, ftmp); + for (i = 0; i < 16; i++) { + felem_square(ftmp, ftmp); + } /* 2^32 - 2^16 */ + felem_mul(ftmp, ftmp, e16); /* 2^32 - 2^0 */ + felem_assign(e32, ftmp); + for (i = 0; i < 32; i++) { + felem_square(ftmp, ftmp); + } /* 2^64 - 2^32 */ + felem_assign(e64, ftmp); + felem_mul(ftmp, ftmp, in); /* 2^64 - 2^32 + 2^0 */ + for (i = 0; i < 192; i++) { + felem_square(ftmp, ftmp); + } /* 2^256 - 2^224 + 2^192 */ + + felem_mul(ftmp2, e64, e32); /* 2^64 - 2^0 */ + for (i = 0; i < 16; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^80 - 2^16 */ + felem_mul(ftmp2, ftmp2, e16); /* 2^80 - 2^0 */ + for (i = 0; i < 8; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^88 - 2^8 */ + felem_mul(ftmp2, ftmp2, e8); /* 2^88 - 2^0 */ + for (i = 0; i < 4; i++) { + felem_square(ftmp2, ftmp2); + } /* 2^92 - 2^4 */ + felem_mul(ftmp2, ftmp2, e4); /* 2^92 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^93 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^94 - 2^2 */ + felem_mul(ftmp2, ftmp2, e2); /* 2^94 - 2^0 */ + felem_square(ftmp2, ftmp2); /* 2^95 - 2^1 */ + felem_square(ftmp2, ftmp2); /* 2^96 - 2^2 */ + felem_mul(ftmp2, ftmp2, in); /* 2^96 - 3 */ + + felem_mul(out, ftmp2, ftmp); /* 2^256 - 2^224 + 2^192 + 2^96 - 3 */ +} + +/* felem_scalar_3 sets out=3*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + */ +static void felem_scalar_3(felem out) +{ + limb carry = 0; + unsigned int i; + + for (i = 0;; i++) { + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 29; + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + out[i] *= 3; + out[i] += carry; + carry = out[i] >> 28; + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_4 sets out=4*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + */ +static void felem_scalar_4(felem out) +{ + limb carry = 0, next_carry; + unsigned int i; + + for (i = 0;; i++) { + next_carry = out[i] >> 27; + out[i] <<= 2; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + next_carry = out[i] >> 26; + out[i] <<= 2; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_scalar_8 sets out=8*out. + * + * On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + * On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. + */ +static void felem_scalar_8(felem out) +{ + limb carry = 0, next_carry; + unsigned int i; + + for (i = 0;; i++) { + next_carry = out[i] >> 26; + out[i] <<= 3; + out[i] &= kBottom29Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 29); + out[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + next_carry = out[i] >> 25; + out[i] <<= 3; + out[i] &= kBottom28Bits; + out[i] += carry; + carry = next_carry + (out[i] >> 28); + out[i] &= kBottom28Bits; + } + + felem_reduce_carry(out, carry); +} + +/* felem_is_zero_vartime returns 1 iff |in| == 0. It takes a variable amount of + * time depending on the value of |in|. + */ +static char felem_is_zero_vartime(const felem in) +{ + limb carry; + int i; + limb tmp[NLIMBS]; + felem_assign(tmp, in); + + /* First, reduce tmp to a minimal form. + */ + do { + carry = 0; + for (i = 0;; i++) { + tmp[i] += carry; + carry = tmp[i] >> 29; + tmp[i] &= kBottom29Bits; + + i++; + if (i == NLIMBS) + break; + + tmp[i] += carry; + carry = tmp[i] >> 28; + tmp[i] &= kBottom28Bits; + } + + felem_reduce_carry(tmp, carry); + } while (carry); + + /* tmp < 2**257, so the only possible zero values are 0, p and 2p. + */ + return memcmp(tmp, kZero, sizeof(tmp)) == 0 || + memcmp(tmp, kP, sizeof(tmp)) == 0 || + memcmp(tmp, k2P, sizeof(tmp)) == 0; +} + +/* Group operations: + * + * Elements of the elliptic curve group are represented in Jacobian + * coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in + * Jacobian form. + */ + +/* point_double sets {x_out,y_out,z_out} = 2*{x,y,z}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + */ +static void point_double(felem x_out, felem y_out, felem z_out, + const felem x, const felem y, const felem z) +{ + felem delta, gamma, alpha, beta, tmp, tmp2; + + felem_square(delta, z); + felem_square(gamma, y); + felem_mul(beta, x, gamma); + + felem_sum(tmp, x, delta); + felem_diff(tmp2, x, delta); + felem_mul(alpha, tmp, tmp2); + felem_scalar_3(alpha); + + felem_sum(tmp, y, z); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, gamma); + felem_diff(z_out, tmp, delta); + + felem_scalar_4(beta); + felem_square(x_out, alpha); + felem_diff(x_out, x_out, beta); + felem_diff(x_out, x_out, beta); + + felem_diff(tmp, beta, x_out); + felem_mul(tmp, alpha, tmp); + felem_square(tmp2, gamma); + felem_scalar_8(tmp2); + felem_diff(y_out, tmp, tmp2); +} + +/* point_add_mixed sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,1}. + * (i.e. the second point is affine.) + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. + */ +static void point_add_mixed(felem x_out, felem y_out, felem z_out, + const felem x1, const felem y1, const felem z1, + const felem x2, const felem y2) +{ + felem z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_sum(tmp, z1, z1); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, x1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, y1); + felem_sum(r, r, r); + felem_mul(v, x1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, y1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add sets {x_out,y_out,z_out} = {x1,y1,z1} + {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * Note that this function does not handle P+P, infinity+P nor P+infinity + * correctly. + */ +static void point_add(felem x_out, felem y_out, felem z_out, + const felem x1, const felem y1, const felem z1, + const felem x2, const felem y2, const felem z2) +{ + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* point_add_or_double_vartime sets {x_out,y_out,z_out} = {x1,y1,z1} + + * {x2,y2,z2}. + * + * See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + * + * This function handles the case where {x1,y1,z1}={x2,y2,z2}. + */ +static void point_add_or_double_vartime( + felem x_out, felem y_out, felem z_out, + const felem x1, const felem y1, const felem z1, + const felem x2, const felem y2, const felem z2) +{ + felem z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp; + char x_equal, y_equal; + + felem_square(z1z1, z1); + felem_square(z2z2, z2); + felem_mul(u1, x1, z2z2); + + felem_sum(tmp, z1, z2); + felem_square(tmp, tmp); + felem_diff(tmp, tmp, z1z1); + felem_diff(tmp, tmp, z2z2); + + felem_mul(z2z2z2, z2, z2z2); + felem_mul(s1, y1, z2z2z2); + + felem_mul(u2, x2, z1z1); + felem_mul(z1z1z1, z1, z1z1); + felem_mul(s2, y2, z1z1z1); + felem_diff(h, u2, u1); + x_equal = felem_is_zero_vartime(h); + felem_sum(i, h, h); + felem_square(i, i); + felem_mul(j, h, i); + felem_diff(r, s2, s1); + y_equal = felem_is_zero_vartime(r); + if (x_equal && y_equal) { + point_double(x_out, y_out, z_out, x1, y1, z1); + return; + } + felem_sum(r, r, r); + felem_mul(v, u1, i); + + felem_mul(z_out, tmp, h); + felem_square(rr, r); + felem_diff(x_out, rr, j); + felem_diff(x_out, x_out, v); + felem_diff(x_out, x_out, v); + + felem_diff(tmp, v, x_out); + felem_mul(y_out, tmp, r); + felem_mul(tmp, s1, j); + felem_diff(y_out, y_out, tmp); + felem_diff(y_out, y_out, tmp); +} + +/* copy_conditional sets out=in if mask = 0xffffffff in constant time. + * + * On entry: mask is either 0 or 0xffffffff. + */ +static void copy_conditional(felem out, const felem in, limb mask) +{ + int i; + + for (i = 0; i < NLIMBS; i++) { + const limb tmp = mask & (in[i] ^ out[i]); + out[i] ^= tmp; + } +} + +/* select_affine_point sets {out_x,out_y} to the index'th entry of table. + * On entry: index < 16, table[0] must be zero. + */ +static void select_affine_point(felem out_x, felem out_y, + const limb *table, limb index) +{ + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) { + out_x[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_y[j] |= *table & mask; + } + } +} + +/* select_jacobian_point sets {out_x,out_y,out_z} to the index'th entry of + * table. On entry: index < 16, table[0] must be zero. + */ +static void select_jacobian_point(felem out_x, felem out_y, felem out_z, + const limb *table, limb index) +{ + limb i, j; + + memset(out_x, 0, sizeof(felem)); + memset(out_y, 0, sizeof(felem)); + memset(out_z, 0, sizeof(felem)); + + /* The implicit value at index 0 is all zero. We don't need to perform that + * iteration of the loop because we already set out_* to zero. + */ + table += 3*NLIMBS; + + for (i = 1; i < 16; i++) { + limb mask = i ^ index; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 1; + mask--; + for (j = 0; j < NLIMBS; j++, table++) { + out_x[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_y[j] |= *table & mask; + } + for (j = 0; j < NLIMBS; j++, table++) { + out_z[j] |= *table & mask; + } + } +} + +/* get_bit returns the bit'th bit of scalar. */ +static char get_bit(const u8 scalar[32], int bit) +{ + return ((scalar[bit >> 3]) >> (bit & 7)) & 1; +} + +/* scalar_base_mult sets {nx,ny,nz} = scalar*G where scalar is a little-endian + * number. Note that the value of scalar must be less than the order of the + * group. + */ +static void scalar_base_mult(felem nx, felem ny, felem nz, const u8 scalar[32]) +{ + int i, j; + limb n_is_infinity_mask = -1, p_is_noninfinite_mask, mask; + u32 table_offset; + + felem px, py; + felem tx, ty, tz; + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + + /* The loop adds bits at positions 0, 64, 128 and 192, followed by + * positions 32,96,160 and 224 and does this 32 times. + */ + for (i = 0; i < 32; i++) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + } + table_offset = 0; + for (j = 0; j <= 32; j += 32) { + char bit0 = get_bit(scalar, 31 - i + j); + char bit1 = get_bit(scalar, 95 - i + j); + char bit2 = get_bit(scalar, 159 - i + j); + char bit3 = get_bit(scalar, 223 - i + j); + limb index = bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3); + + select_affine_point(px, py, kPrecomputed + table_offset, index); + table_offset += 30 * NLIMBS; + + /* Since scalar is less than the order of the group, we know that + * {nx,ny,nz} != {px,py,1}, unless both are zero, which we handle + * below. + */ + point_add_mixed(tx, ty, tz, nx, ny, nz, px, py); + /* The result of point_add_mixed is incorrect if {nx,ny,nz} is zero + * (a.k.a. the point at infinity). We handle that situation by + * copying the point from the table. + */ + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, kOne, n_is_infinity_mask); + + /* Equally, the result is also wrong if the point from the table is + * zero, which happens when the index is zero. We handle that by + * only copying from {tx,ty,tz} to {nx,ny,nz} if index != 0. + */ + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + /* If p was not zero, then n is now non-zero. */ + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } + } +} + +/* point_to_affine converts a Jacobian point to an affine point. If the input + * is the point at infinity then it returns (0, 0) in constant time. + */ +static void point_to_affine(felem x_out, felem y_out, + const felem nx, const felem ny, const felem nz) { + felem z_inv, z_inv_sq; + felem_inv(z_inv, nz); + felem_square(z_inv_sq, z_inv); + felem_mul(x_out, nx, z_inv_sq); + felem_mul(z_inv, z_inv, z_inv_sq); + felem_mul(y_out, ny, z_inv); +} + +/* scalar_mult sets {nx,ny,nz} = scalar*{x,y}. */ +static void scalar_mult(felem nx, felem ny, felem nz, + const felem x, const felem y, const u8 scalar[32]) +{ + int i; + felem px, py, pz, tx, ty, tz; + felem precomp[16][3]; + limb n_is_infinity_mask, index, p_is_noninfinite_mask, mask; + + /* We precompute 0,1,2,... times {x,y}. */ + memset(precomp, 0, sizeof(felem) * 3); + memcpy(&precomp[1][0], x, sizeof(felem)); + memcpy(&precomp[1][1], y, sizeof(felem)); + memcpy(&precomp[1][2], kOne, sizeof(felem)); + + for (i = 2; i < 16; i += 2) { + point_double(precomp[i][0], precomp[i][1], precomp[i][2], + precomp[i / 2][0], precomp[i / 2][1], precomp[i / 2][2]); + + point_add_mixed(precomp[i + 1][0], precomp[i + 1][1], precomp[i + 1][2], + precomp[i][0], precomp[i][1], precomp[i][2], x, y); + } + + memset(nx, 0, sizeof(felem)); + memset(ny, 0, sizeof(felem)); + memset(nz, 0, sizeof(felem)); + n_is_infinity_mask = -1; + + /* We add in a window of four bits each iteration and do this 64 times. */ + for (i = 0; i < 64; i++) { + if (i) { + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + point_double(nx, ny, nz, nx, ny, nz); + } + + index = scalar[31 - i / 2]; + if ((i & 1) == 1) { + index &= 15; + } else { + index >>= 4; + } + + /* See the comments in scalar_base_mult about handling infinities. */ + select_jacobian_point(px, py, pz, precomp[0][0], index); + point_add(tx, ty, tz, nx, ny, nz, px, py, pz); + copy_conditional(nx, px, n_is_infinity_mask); + copy_conditional(ny, py, n_is_infinity_mask); + copy_conditional(nz, pz, n_is_infinity_mask); + + p_is_noninfinite_mask = NON_ZERO_TO_ALL_ONES(index); + mask = p_is_noninfinite_mask & ~n_is_infinity_mask; + copy_conditional(nx, tx, mask); + copy_conditional(ny, ty, mask); + copy_conditional(nz, tz, mask); + n_is_infinity_mask &= ~p_is_noninfinite_mask; + } +} + +/* Interface with Freebl: */ + +/* BYTESWAP_MP_DIGIT_TO_LE swaps the bytes of a mp_digit to + * little-endian order. + */ +#ifdef IS_BIG_ENDIAN +#ifdef __APPLE__ +#include <libkern/OSByteOrder.h> +#define BYTESWAP32(x) OSSwapInt32(x) +#define BYTESWAP64(x) OSSwapInt64(x) +#else +#define BYTESWAP32(x) \ + (((x) >> 24) | (((x) >> 8) & 0xff00) | (((x) & 0xff00) << 8) | ((x) << 24)) +#define BYTESWAP64(x) \ + (((x) >> 56) | (((x) >> 40) & 0xff00) | \ + (((x) >> 24) & 0xff0000) | (((x) >> 8) & 0xff000000) | \ + (((x) & 0xff000000) << 8) | (((x) & 0xff0000) << 24) | \ + (((x) & 0xff00) << 40) | ((x) << 56)) +#endif + +#ifdef MP_USE_UINT_DIGIT +#define BYTESWAP_MP_DIGIT_TO_LE(x) BYTESWAP32(x) +#else +#define BYTESWAP_MP_DIGIT_TO_LE(x) BYTESWAP64(x) +#endif +#endif /* IS_BIG_ENDIAN */ + +#ifdef MP_USE_UINT_DIGIT +static const mp_digit kRInvDigits[8] = { + 0x80000000, 1, 0xffffffff, 0, + 0x80000001, 0xfffffffe, 1, 0x7fffffff +}; +#else +static const mp_digit kRInvDigits[4] = { + PR_UINT64(0x180000000), 0xffffffff, + PR_UINT64(0xfffffffe80000001), PR_UINT64(0x7fffffff00000001) +}; +#endif +#define MP_DIGITS_IN_256_BITS (32/sizeof(mp_digit)) +static const mp_int kRInv = { + MP_ZPOS, + MP_DIGITS_IN_256_BITS, + MP_DIGITS_IN_256_BITS, + (mp_digit*) kRInvDigits +}; + +static const limb kTwo28 = 0x10000000; +static const limb kTwo29 = 0x20000000; + +/* to_montgomery sets out = R*in. */ +static mp_err to_montgomery(felem out, const mp_int *in, const ECGroup *group) +{ + /* There are no MPI functions for bitshift operations and we wish to shift + * in 257 bits left so we move the digits 256-bits left and then multiply + * by two. + */ + mp_int in_shifted; + int i; + mp_err res; + + mp_init(&in_shifted); + s_mp_pad(&in_shifted, MP_USED(in) + MP_DIGITS_IN_256_BITS); + memcpy(&MP_DIGIT(&in_shifted, MP_DIGITS_IN_256_BITS), + MP_DIGITS(in), + MP_USED(in)*sizeof(mp_digit)); + mp_mul_2(&in_shifted, &in_shifted); + MP_CHECKOK(group->meth->field_mod(&in_shifted, &in_shifted, group->meth)); + + for (i = 0;; i++) { + out[i] = MP_DIGIT(&in_shifted, 0) & kBottom29Bits; + mp_div_d(&in_shifted, kTwo29, &in_shifted, NULL); + + i++; + if (i == NLIMBS) + break; + out[i] = MP_DIGIT(&in_shifted, 0) & kBottom28Bits; + mp_div_d(&in_shifted, kTwo28, &in_shifted, NULL); + } + +CLEANUP: + mp_clear(&in_shifted); + return res; +} + +/* from_montgomery sets out=in/R. */ +static mp_err from_montgomery(mp_int *out, const felem in, + const ECGroup *group) +{ + mp_int result, tmp; + mp_err res; + int i; + + mp_init(&result); + mp_init(&tmp); + + MP_CHECKOK(mp_add_d(&tmp, in[NLIMBS-1], &result)); + for (i = NLIMBS-2; i >= 0; i--) { + if ((i & 1) == 0) { + MP_CHECKOK(mp_mul_d(&result, kTwo29, &tmp)); + } else { + MP_CHECKOK(mp_mul_d(&result, kTwo28, &tmp)); + } + MP_CHECKOK(mp_add_d(&tmp, in[i], &result)); + } + + MP_CHECKOK(mp_mul(&result, &kRInv, out)); + MP_CHECKOK(group->meth->field_mod(out, out, group->meth)); + +CLEANUP: + mp_clear(&result); + mp_clear(&tmp); + return res; +} + +/* scalar_from_mp_int sets out_scalar=n, where n < the group order. */ +static void scalar_from_mp_int(u8 out_scalar[32], const mp_int *n) +{ + /* We require that |n| is less than the order of the group and therefore it + * will fit into |out_scalar|. However, these is a timing side-channel here + * that we cannot avoid: if |n| is sufficiently small it may be one or more + * words too short and we'll copy less data. + */ + memset(out_scalar, 0, 32); +#ifdef IS_LITTLE_ENDIAN + memcpy(out_scalar, MP_DIGITS(n), MP_USED(n) * sizeof(mp_digit)); +#else + { + mp_size i; + mp_digit swapped[MP_DIGITS_IN_256_BITS]; + for (i = 0; i < MP_USED(n); i++) { + swapped[i] = BYTESWAP_MP_DIGIT_TO_LE(MP_DIGIT(n, i)); + } + memcpy(out_scalar, swapped, MP_USED(n) * sizeof(mp_digit)); + } +#endif +} + +/* ec_GFp_nistp256_base_point_mul sets {out_x,out_y} = nG, where n is < the + * order of the group. + */ +static mp_err ec_GFp_nistp256_base_point_mul(const mp_int *n, + mp_int *out_x, mp_int *out_y, + const ECGroup *group) +{ + u8 scalar[32]; + felem x, y, z, x_affine, y_affine; + mp_err res; + + /* FIXME(agl): test that n < order. */ + + scalar_from_mp_int(scalar, n); + scalar_base_mult(x, y, z, scalar); + point_to_affine(x_affine, y_affine, x, y, z); + MP_CHECKOK(from_montgomery(out_x, x_affine, group)); + MP_CHECKOK(from_montgomery(out_y, y_affine, group)); + +CLEANUP: + return res; +} + +/* ec_GFp_nistp256_point_mul sets {out_x,out_y} = n*{in_x,in_y}, where n is < + * the order of the group. + */ +static mp_err ec_GFp_nistp256_point_mul(const mp_int *n, + const mp_int *in_x, const mp_int *in_y, + mp_int *out_x, mp_int *out_y, + const ECGroup *group) +{ + u8 scalar[32]; + felem x, y, z, x_affine, y_affine, px, py; + mp_err res; + + scalar_from_mp_int(scalar, n); + + MP_CHECKOK(to_montgomery(px, in_x, group)); + MP_CHECKOK(to_montgomery(py, in_y, group)); + + scalar_mult(x, y, z, px, py, scalar); + point_to_affine(x_affine, y_affine, x, y, z); + MP_CHECKOK(from_montgomery(out_x, x_affine, group)); + MP_CHECKOK(from_montgomery(out_y, y_affine, group)); + +CLEANUP: + return res; +} + +/* ec_GFp_nistp256_point_mul_vartime sets {out_x,out_y} = n1*G + + * n2*{in_x,in_y}, where n1 and n2 are < the order of the group. + * + * As indicated by the name, this function operates in variable time. This + * is safe because it's used for signature validation which doesn't deal + * with secrets. + */ +static mp_err ec_GFp_nistp256_points_mul_vartime( + const mp_int *n1, const mp_int *n2, + const mp_int *in_x, const mp_int *in_y, + mp_int *out_x, mp_int *out_y, + const ECGroup *group) +{ + u8 scalar1[32], scalar2[32]; + felem x1, y1, z1, x2, y2, z2, x_affine, y_affine, px, py; + mp_err res = MP_OKAY; + + /* If n2 == NULL, this is just a base-point multiplication. */ + if (n2 == NULL) { + return ec_GFp_nistp256_base_point_mul(n1, out_x, out_y, group); + } + + /* If n1 == nULL, this is just an arbitary-point multiplication. */ + if (n1 == NULL) { + return ec_GFp_nistp256_point_mul(n2, in_x, in_y, out_x, out_y, group); + } + + /* If both scalars are zero, then the result is the point at infinity. */ + if (mp_cmp_z(n1) == 0 && mp_cmp_z(n2) == 0) { + mp_zero(out_x); + mp_zero(out_y); + return res; + } + + scalar_from_mp_int(scalar1, n1); + scalar_from_mp_int(scalar2, n2); + + MP_CHECKOK(to_montgomery(px, in_x, group)); + MP_CHECKOK(to_montgomery(py, in_y, group)); + scalar_base_mult(x1, y1, z1, scalar1); + scalar_mult(x2, y2, z2, px, py, scalar2); + + if (mp_cmp_z(n2) == 0) { + /* If n2 == 0, then {x2,y2,z2} is zero and the result is just + * {x1,y1,z1}. */ + } else if (mp_cmp_z(n1) == 0) { + /* If n1 == 0, then {x1,y1,z1} is zero and the result is just + * {x2,y2,z2}. */ + memcpy(x1, x2, sizeof(x2)); + memcpy(y1, y2, sizeof(y2)); + memcpy(z1, z2, sizeof(z2)); + } else { + /* This function handles the case where {x1,y1,z1} == {x2,y2,z2}. */ + point_add_or_double_vartime(x1, y1, z1, x1, y1, z1, x2, y2, z2); + } + + point_to_affine(x_affine, y_affine, x1, y1, z1); + MP_CHECKOK(from_montgomery(out_x, x_affine, group)); + MP_CHECKOK(from_montgomery(out_y, y_affine, group)); + +CLEANUP: + return res; +} + +/* Wire in fast point multiplication for named curves. */ +mp_err ec_group_set_gfp256_32(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P256) { + group->base_point_mul = &ec_GFp_nistp256_base_point_mul; + group->point_mul = &ec_GFp_nistp256_point_mul; + group->points_mul = &ec_GFp_nistp256_points_mul_vartime; + } + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_384.c b/nss/lib/freebl/ecl/ecp_384.c new file mode 100644 index 0000000..4c1e85e --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_384.c @@ -0,0 +1,258 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" + +/* Fast modular reduction for p384 = 2^384 - 2^128 - 2^96 + 2^32 - 1. a can be r. + * Uses algorithm 2.30 from Hankerson, Menezes, Vanstone. Guide to + * Elliptic Curve Cryptography. */ +static mp_err +ec_GFp_nistp384_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + int a_bits = mpl_significant_bits(a); + int i; + + /* m1, m2 are statically-allocated mp_int of exactly the size we need */ + mp_int m[10]; + +#ifdef ECL_THIRTY_TWO_BIT + mp_digit s[10][12]; + for (i = 0; i < 10; i++) { + MP_SIGN(&m[i]) = MP_ZPOS; + MP_ALLOC(&m[i]) = 12; + MP_USED(&m[i]) = 12; + MP_DIGITS(&m[i]) = s[i]; + } +#else + mp_digit s[10][6]; + for (i = 0; i < 10; i++) { + MP_SIGN(&m[i]) = MP_ZPOS; + MP_ALLOC(&m[i]) = 6; + MP_USED(&m[i]) = 6; + MP_DIGITS(&m[i]) = s[i]; + } +#endif + +#ifdef ECL_THIRTY_TWO_BIT + /* for polynomials larger than twice the field size or polynomials + * not using all words, use regular reduction */ + if ((a_bits > 768) || (a_bits <= 736)) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { + for (i = 0; i < 12; i++) { + s[0][i] = MP_DIGIT(a, i); + } + s[1][0] = 0; + s[1][1] = 0; + s[1][2] = 0; + s[1][3] = 0; + s[1][4] = MP_DIGIT(a, 21); + s[1][5] = MP_DIGIT(a, 22); + s[1][6] = MP_DIGIT(a, 23); + s[1][7] = 0; + s[1][8] = 0; + s[1][9] = 0; + s[1][10] = 0; + s[1][11] = 0; + for (i = 0; i < 12; i++) { + s[2][i] = MP_DIGIT(a, i+12); + } + s[3][0] = MP_DIGIT(a, 21); + s[3][1] = MP_DIGIT(a, 22); + s[3][2] = MP_DIGIT(a, 23); + for (i = 3; i < 12; i++) { + s[3][i] = MP_DIGIT(a, i+9); + } + s[4][0] = 0; + s[4][1] = MP_DIGIT(a, 23); + s[4][2] = 0; + s[4][3] = MP_DIGIT(a, 20); + for (i = 4; i < 12; i++) { + s[4][i] = MP_DIGIT(a, i+8); + } + s[5][0] = 0; + s[5][1] = 0; + s[5][2] = 0; + s[5][3] = 0; + s[5][4] = MP_DIGIT(a, 20); + s[5][5] = MP_DIGIT(a, 21); + s[5][6] = MP_DIGIT(a, 22); + s[5][7] = MP_DIGIT(a, 23); + s[5][8] = 0; + s[5][9] = 0; + s[5][10] = 0; + s[5][11] = 0; + s[6][0] = MP_DIGIT(a, 20); + s[6][1] = 0; + s[6][2] = 0; + s[6][3] = MP_DIGIT(a, 21); + s[6][4] = MP_DIGIT(a, 22); + s[6][5] = MP_DIGIT(a, 23); + s[6][6] = 0; + s[6][7] = 0; + s[6][8] = 0; + s[6][9] = 0; + s[6][10] = 0; + s[6][11] = 0; + s[7][0] = MP_DIGIT(a, 23); + for (i = 1; i < 12; i++) { + s[7][i] = MP_DIGIT(a, i+11); + } + s[8][0] = 0; + s[8][1] = MP_DIGIT(a, 20); + s[8][2] = MP_DIGIT(a, 21); + s[8][3] = MP_DIGIT(a, 22); + s[8][4] = MP_DIGIT(a, 23); + s[8][5] = 0; + s[8][6] = 0; + s[8][7] = 0; + s[8][8] = 0; + s[8][9] = 0; + s[8][10] = 0; + s[8][11] = 0; + s[9][0] = 0; + s[9][1] = 0; + s[9][2] = 0; + s[9][3] = MP_DIGIT(a, 23); + s[9][4] = MP_DIGIT(a, 23); + s[9][5] = 0; + s[9][6] = 0; + s[9][7] = 0; + s[9][8] = 0; + s[9][9] = 0; + s[9][10] = 0; + s[9][11] = 0; + + MP_CHECKOK(mp_add(&m[0], &m[1], r)); + MP_CHECKOK(mp_add(r, &m[1], r)); + MP_CHECKOK(mp_add(r, &m[2], r)); + MP_CHECKOK(mp_add(r, &m[3], r)); + MP_CHECKOK(mp_add(r, &m[4], r)); + MP_CHECKOK(mp_add(r, &m[5], r)); + MP_CHECKOK(mp_add(r, &m[6], r)); + MP_CHECKOK(mp_sub(r, &m[7], r)); + MP_CHECKOK(mp_sub(r, &m[8], r)); + MP_CHECKOK(mp_submod(r, &m[9], &meth->irr, r)); + s_mp_clamp(r); + } +#else + /* for polynomials larger than twice the field size or polynomials + * not using all words, use regular reduction */ + if ((a_bits > 768) || (a_bits <= 736)) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { + for (i = 0; i < 6; i++) { + s[0][i] = MP_DIGIT(a, i); + } + s[1][0] = 0; + s[1][1] = 0; + s[1][2] = (MP_DIGIT(a, 10) >> 32) | (MP_DIGIT(a, 11) << 32); + s[1][3] = MP_DIGIT(a, 11) >> 32; + s[1][4] = 0; + s[1][5] = 0; + for (i = 0; i < 6; i++) { + s[2][i] = MP_DIGIT(a, i+6); + } + s[3][0] = (MP_DIGIT(a, 10) >> 32) | (MP_DIGIT(a, 11) << 32); + s[3][1] = (MP_DIGIT(a, 11) >> 32) | (MP_DIGIT(a, 6) << 32); + for (i = 2; i < 6; i++) { + s[3][i] = (MP_DIGIT(a, i+4) >> 32) | (MP_DIGIT(a, i+5) << 32); + } + s[4][0] = (MP_DIGIT(a, 11) >> 32) << 32; + s[4][1] = MP_DIGIT(a, 10) << 32; + for (i = 2; i < 6; i++) { + s[4][i] = MP_DIGIT(a, i+4); + } + s[5][0] = 0; + s[5][1] = 0; + s[5][2] = MP_DIGIT(a, 10); + s[5][3] = MP_DIGIT(a, 11); + s[5][4] = 0; + s[5][5] = 0; + s[6][0] = (MP_DIGIT(a, 10) << 32) >> 32; + s[6][1] = (MP_DIGIT(a, 10) >> 32) << 32; + s[6][2] = MP_DIGIT(a, 11); + s[6][3] = 0; + s[6][4] = 0; + s[6][5] = 0; + s[7][0] = (MP_DIGIT(a, 11) >> 32) | (MP_DIGIT(a, 6) << 32); + for (i = 1; i < 6; i++) { + s[7][i] = (MP_DIGIT(a, i+5) >> 32) | (MP_DIGIT(a, i+6) << 32); + } + s[8][0] = MP_DIGIT(a, 10) << 32; + s[8][1] = (MP_DIGIT(a, 10) >> 32) | (MP_DIGIT(a, 11) << 32); + s[8][2] = MP_DIGIT(a, 11) >> 32; + s[8][3] = 0; + s[8][4] = 0; + s[8][5] = 0; + s[9][0] = 0; + s[9][1] = (MP_DIGIT(a, 11) >> 32) << 32; + s[9][2] = MP_DIGIT(a, 11) >> 32; + s[9][3] = 0; + s[9][4] = 0; + s[9][5] = 0; + + MP_CHECKOK(mp_add(&m[0], &m[1], r)); + MP_CHECKOK(mp_add(r, &m[1], r)); + MP_CHECKOK(mp_add(r, &m[2], r)); + MP_CHECKOK(mp_add(r, &m[3], r)); + MP_CHECKOK(mp_add(r, &m[4], r)); + MP_CHECKOK(mp_add(r, &m[5], r)); + MP_CHECKOK(mp_add(r, &m[6], r)); + MP_CHECKOK(mp_sub(r, &m[7], r)); + MP_CHECKOK(mp_sub(r, &m[8], r)); + MP_CHECKOK(mp_submod(r, &m[9], &meth->irr, r)); + s_mp_clamp(r); + } +#endif + + CLEANUP: + return res; +} + +/* Compute the square of polynomial a, reduce modulo p384. Store the + * result in r. r could be a. Uses optimized modular reduction for p384. + */ +static mp_err +ec_GFp_nistp384_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp384_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p384. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p384. */ +static mp_err +ec_GFp_nistp384_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp384_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp384(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P384) { + group->meth->field_mod = &ec_GFp_nistp384_mod; + group->meth->field_mul = &ec_GFp_nistp384_mul; + group->meth->field_sqr = &ec_GFp_nistp384_sqr; + } + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_521.c b/nss/lib/freebl/ecl/ecp_521.c new file mode 100644 index 0000000..f70c2f4 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_521.c @@ -0,0 +1,137 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp.h" +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" + +#define ECP521_DIGITS ECL_CURVE_DIGITS(521) + +/* Fast modular reduction for p521 = 2^521 - 1. a can be r. Uses + * algorithm 2.31 from Hankerson, Menezes, Vanstone. Guide to + * Elliptic Curve Cryptography. */ +static mp_err +ec_GFp_nistp521_mod(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + int a_bits = mpl_significant_bits(a); + unsigned int i; + + /* m1, m2 are statically-allocated mp_int of exactly the size we need */ + mp_int m1; + + mp_digit s1[ECP521_DIGITS] = { 0 }; + + MP_SIGN(&m1) = MP_ZPOS; + MP_ALLOC(&m1) = ECP521_DIGITS; + MP_USED(&m1) = ECP521_DIGITS; + MP_DIGITS(&m1) = s1; + + if (a_bits < 521) { + if (a==r) return MP_OKAY; + return mp_copy(a, r); + } + /* for polynomials larger than twice the field size or polynomials + * not using all words, use regular reduction */ + if (a_bits > (521*2)) { + MP_CHECKOK(mp_mod(a, &meth->irr, r)); + } else { +#define FIRST_DIGIT (ECP521_DIGITS-1) + for (i = FIRST_DIGIT; i < MP_USED(a)-1; i++) { + s1[i-FIRST_DIGIT] = (MP_DIGIT(a, i) >> 9) + | (MP_DIGIT(a, 1+i) << (MP_DIGIT_BIT-9)); + } + s1[i-FIRST_DIGIT] = MP_DIGIT(a, i) >> 9; + + if ( a != r ) { + MP_CHECKOK(s_mp_pad(r,ECP521_DIGITS)); + for (i = 0; i < ECP521_DIGITS; i++) { + MP_DIGIT(r,i) = MP_DIGIT(a, i); + } + } + MP_USED(r) = ECP521_DIGITS; + MP_DIGIT(r,FIRST_DIGIT) &= 0x1FF; + + MP_CHECKOK(s_mp_add(r, &m1)); + if (MP_DIGIT(r, FIRST_DIGIT) & 0x200) { + MP_CHECKOK(s_mp_add_d(r,1)); + MP_DIGIT(r,FIRST_DIGIT) &= 0x1FF; + } else if (s_mp_cmp(r, &meth->irr) == 0) { + mp_zero(r); + } + s_mp_clamp(r); + } + + CLEANUP: + return res; +} + +/* Compute the square of polynomial a, reduce modulo p521. Store the + * result in r. r could be a. Uses optimized modular reduction for p521. + */ +static mp_err +ec_GFp_nistp521_sqr(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_sqr(a, r)); + MP_CHECKOK(ec_GFp_nistp521_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Compute the product of two polynomials a and b, reduce modulo p521. + * Store the result in r. r could be a or b; a could be b. Uses + * optimized modular reduction for p521. */ +static mp_err +ec_GFp_nistp521_mul(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(ec_GFp_nistp521_mod(r, r, meth)); + CLEANUP: + return res; +} + +/* Divides two field elements. If a is NULL, then returns the inverse of + * b. */ +static mp_err +ec_GFp_nistp521_div(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + mp_int t; + + /* If a is NULL, then return the inverse of b, otherwise return a/b. */ + if (a == NULL) { + return mp_invmod(b, &meth->irr, r); + } else { + /* MPI doesn't support divmod, so we implement it using invmod and + * mulmod. */ + MP_CHECKOK(mp_init(&t)); + MP_CHECKOK(mp_invmod(b, &meth->irr, &t)); + MP_CHECKOK(mp_mul(a, &t, r)); + MP_CHECKOK(ec_GFp_nistp521_mod(r, r, meth)); + CLEANUP: + mp_clear(&t); + return res; + } +} + +/* Wire in fast field arithmetic and precomputation of base point for + * named curves. */ +mp_err +ec_group_set_gfp521(ECGroup *group, ECCurveName name) +{ + if (name == ECCurve_NIST_P521) { + group->meth->field_mod = &ec_GFp_nistp521_mod; + group->meth->field_mul = &ec_GFp_nistp521_mul; + group->meth->field_sqr = &ec_GFp_nistp521_sqr; + group->meth->field_div = &ec_GFp_nistp521_div; + } + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_aff.c b/nss/lib/freebl/ecl/ecp_aff.c new file mode 100644 index 0000000..92e8604 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_aff.c @@ -0,0 +1,317 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp.h" +#include "mplogic.h" +#include <stdlib.h> + +/* Checks if point P(px, py) is at infinity. Uses affine coordinates. */ +mp_err +ec_GFp_pt_is_inf_aff(const mp_int *px, const mp_int *py) +{ + + if ((mp_cmp_z(px) == 0) && (mp_cmp_z(py) == 0)) { + return MP_YES; + } else { + return MP_NO; + } + +} + +/* Sets P(px, py) to be the point at infinity. Uses affine coordinates. */ +mp_err +ec_GFp_pt_set_inf_aff(mp_int *px, mp_int *py) +{ + mp_zero(px); + mp_zero(py); + return MP_OKAY; +} + +/* Computes R = P + Q based on IEEE P1363 A.10.1. Elliptic curve points P, + * Q, and R can all be identical. Uses affine coordinates. Assumes input + * is already field-encoded using field_enc, and returns output that is + * still field-encoded. */ +mp_err +ec_GFp_pt_add_aff(const mp_int *px, const mp_int *py, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int lambda, temp, tempx, tempy; + + MP_DIGITS(&lambda) = 0; + MP_DIGITS(&temp) = 0; + MP_DIGITS(&tempx) = 0; + MP_DIGITS(&tempy) = 0; + MP_CHECKOK(mp_init(&lambda)); + MP_CHECKOK(mp_init(&temp)); + MP_CHECKOK(mp_init(&tempx)); + MP_CHECKOK(mp_init(&tempy)); + /* if P = inf, then R = Q */ + if (ec_GFp_pt_is_inf_aff(px, py) == 0) { + MP_CHECKOK(mp_copy(qx, rx)); + MP_CHECKOK(mp_copy(qy, ry)); + res = MP_OKAY; + goto CLEANUP; + } + /* if Q = inf, then R = P */ + if (ec_GFp_pt_is_inf_aff(qx, qy) == 0) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + res = MP_OKAY; + goto CLEANUP; + } + /* if px != qx, then lambda = (py-qy) / (px-qx) */ + if (mp_cmp(px, qx) != 0) { + MP_CHECKOK(group->meth->field_sub(py, qy, &tempy, group->meth)); + MP_CHECKOK(group->meth->field_sub(px, qx, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_div(&tempy, &tempx, &lambda, group->meth)); + } else { + /* if py != qy or qy = 0, then R = inf */ + if (((mp_cmp(py, qy) != 0)) || (mp_cmp_z(qy) == 0)) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + } + /* lambda = (3qx^2+a) / (2qy) */ + MP_CHECKOK(group->meth->field_sqr(qx, &tempx, group->meth)); + MP_CHECKOK(mp_set_int(&temp, 3)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(&temp, &temp, group->meth)); + } + MP_CHECKOK(group->meth-> + field_mul(&tempx, &temp, &tempx, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&tempx, &group->curvea, &tempx, group->meth)); + MP_CHECKOK(mp_set_int(&temp, 2)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(&temp, &temp, group->meth)); + } + MP_CHECKOK(group->meth->field_mul(qy, &temp, &tempy, group->meth)); + MP_CHECKOK(group->meth-> + field_div(&tempx, &tempy, &lambda, group->meth)); + } + /* rx = lambda^2 - px - qx */ + MP_CHECKOK(group->meth->field_sqr(&lambda, &tempx, group->meth)); + MP_CHECKOK(group->meth->field_sub(&tempx, px, &tempx, group->meth)); + MP_CHECKOK(group->meth->field_sub(&tempx, qx, &tempx, group->meth)); + /* ry = (x1-x2) * lambda - y1 */ + MP_CHECKOK(group->meth->field_sub(qx, &tempx, &tempy, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&tempy, &lambda, &tempy, group->meth)); + MP_CHECKOK(group->meth->field_sub(&tempy, qy, &tempy, group->meth)); + MP_CHECKOK(mp_copy(&tempx, rx)); + MP_CHECKOK(mp_copy(&tempy, ry)); + + CLEANUP: + mp_clear(&lambda); + mp_clear(&temp); + mp_clear(&tempx); + mp_clear(&tempy); + return res; +} + +/* Computes R = P - Q. Elliptic curve points P, Q, and R can all be + * identical. Uses affine coordinates. Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. */ +mp_err +ec_GFp_pt_sub_aff(const mp_int *px, const mp_int *py, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int nqy; + + MP_DIGITS(&nqy) = 0; + MP_CHECKOK(mp_init(&nqy)); + /* nqy = -qy */ + MP_CHECKOK(group->meth->field_neg(qy, &nqy, group->meth)); + res = group->point_add(px, py, qx, &nqy, rx, ry, group); + CLEANUP: + mp_clear(&nqy); + return res; +} + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * affine coordinates. Assumes input is already field-encoded using + * field_enc, and returns output that is still field-encoded. */ +mp_err +ec_GFp_pt_dbl_aff(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, const ECGroup *group) +{ + return ec_GFp_pt_add_aff(px, py, px, py, rx, ry, group); +} + +/* by default, this routine is unused and thus doesn't need to be compiled */ +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF +/* Computes R = nP based on IEEE P1363 A.10.3. Elliptic curve points P and + * R can be identical. Uses affine coordinates. Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. */ +mp_err +ec_GFp_pt_mul_aff(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int k, k3, qx, qy, sx, sy; + int b1, b3, i, l; + + MP_DIGITS(&k) = 0; + MP_DIGITS(&k3) = 0; + MP_DIGITS(&qx) = 0; + MP_DIGITS(&qy) = 0; + MP_DIGITS(&sx) = 0; + MP_DIGITS(&sy) = 0; + MP_CHECKOK(mp_init(&k)); + MP_CHECKOK(mp_init(&k3)); + MP_CHECKOK(mp_init(&qx)); + MP_CHECKOK(mp_init(&qy)); + MP_CHECKOK(mp_init(&sx)); + MP_CHECKOK(mp_init(&sy)); + + /* if n = 0 then r = inf */ + if (mp_cmp_z(n) == 0) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + } + /* Q = P, k = n */ + MP_CHECKOK(mp_copy(px, &qx)); + MP_CHECKOK(mp_copy(py, &qy)); + MP_CHECKOK(mp_copy(n, &k)); + /* if n < 0 then Q = -Q, k = -k */ + if (mp_cmp_z(n) < 0) { + MP_CHECKOK(group->meth->field_neg(&qy, &qy, group->meth)); + MP_CHECKOK(mp_neg(&k, &k)); + } +#ifdef ECL_DEBUG /* basic double and add method */ + l = mpl_significant_bits(&k) - 1; + MP_CHECKOK(mp_copy(&qx, &sx)); + MP_CHECKOK(mp_copy(&qy, &sy)); + for (i = l - 1; i >= 0; i--) { + /* S = 2S */ + MP_CHECKOK(group->point_dbl(&sx, &sy, &sx, &sy, group)); + /* if k_i = 1, then S = S + Q */ + if (mpl_get_bit(&k, i) != 0) { + MP_CHECKOK(group-> + point_add(&sx, &sy, &qx, &qy, &sx, &sy, group)); + } + } +#else /* double and add/subtract method from + * standard */ + /* k3 = 3 * k */ + MP_CHECKOK(mp_set_int(&k3, 3)); + MP_CHECKOK(mp_mul(&k, &k3, &k3)); + /* S = Q */ + MP_CHECKOK(mp_copy(&qx, &sx)); + MP_CHECKOK(mp_copy(&qy, &sy)); + /* l = index of high order bit in binary representation of 3*k */ + l = mpl_significant_bits(&k3) - 1; + /* for i = l-1 downto 1 */ + for (i = l - 1; i >= 1; i--) { + /* S = 2S */ + MP_CHECKOK(group->point_dbl(&sx, &sy, &sx, &sy, group)); + b3 = MP_GET_BIT(&k3, i); + b1 = MP_GET_BIT(&k, i); + /* if k3_i = 1 and k_i = 0, then S = S + Q */ + if ((b3 == 1) && (b1 == 0)) { + MP_CHECKOK(group-> + point_add(&sx, &sy, &qx, &qy, &sx, &sy, group)); + /* if k3_i = 0 and k_i = 1, then S = S - Q */ + } else if ((b3 == 0) && (b1 == 1)) { + MP_CHECKOK(group-> + point_sub(&sx, &sy, &qx, &qy, &sx, &sy, group)); + } + } +#endif + /* output S */ + MP_CHECKOK(mp_copy(&sx, rx)); + MP_CHECKOK(mp_copy(&sy, ry)); + + CLEANUP: + mp_clear(&k); + mp_clear(&k3); + mp_clear(&qx); + mp_clear(&qy); + mp_clear(&sx); + mp_clear(&sy); + return res; +} +#endif + +/* Validates a point on a GFp curve. */ +mp_err +ec_GFp_validate_point(const mp_int *px, const mp_int *py, const ECGroup *group) +{ + mp_err res = MP_NO; + mp_int accl, accr, tmp, pxt, pyt; + + MP_DIGITS(&accl) = 0; + MP_DIGITS(&accr) = 0; + MP_DIGITS(&tmp) = 0; + MP_DIGITS(&pxt) = 0; + MP_DIGITS(&pyt) = 0; + MP_CHECKOK(mp_init(&accl)); + MP_CHECKOK(mp_init(&accr)); + MP_CHECKOK(mp_init(&tmp)); + MP_CHECKOK(mp_init(&pxt)); + MP_CHECKOK(mp_init(&pyt)); + + /* 1: Verify that publicValue is not the point at infinity */ + if (ec_GFp_pt_is_inf_aff(px, py) == MP_YES) { + res = MP_NO; + goto CLEANUP; + } + /* 2: Verify that the coordinates of publicValue are elements + * of the field. + */ + if ((MP_SIGN(px) == MP_NEG) || (mp_cmp(px, &group->meth->irr) >= 0) || + (MP_SIGN(py) == MP_NEG) || (mp_cmp(py, &group->meth->irr) >= 0)) { + res = MP_NO; + goto CLEANUP; + } + /* 3: Verify that publicValue is on the curve. */ + if (group->meth->field_enc) { + group->meth->field_enc(px, &pxt, group->meth); + group->meth->field_enc(py, &pyt, group->meth); + } else { + mp_copy(px, &pxt); + mp_copy(py, &pyt); + } + /* left-hand side: y^2 */ + MP_CHECKOK( group->meth->field_sqr(&pyt, &accl, group->meth) ); + /* right-hand side: x^3 + a*x + b = (x^2 + a)*x + b by Horner's rule */ + MP_CHECKOK( group->meth->field_sqr(&pxt, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_add(&tmp, &group->curvea, &tmp, group->meth) ); + MP_CHECKOK( group->meth->field_mul(&tmp, &pxt, &accr, group->meth) ); + MP_CHECKOK( group->meth->field_add(&accr, &group->curveb, &accr, group->meth) ); + /* check LHS - RHS == 0 */ + MP_CHECKOK( group->meth->field_sub(&accl, &accr, &accr, group->meth) ); + if (mp_cmp_z(&accr) != 0) { + res = MP_NO; + goto CLEANUP; + } + /* 4: Verify that the order of the curve times the publicValue + * is the point at infinity. + */ + MP_CHECKOK( ECPoint_mul(group, &group->order, px, py, &pxt, &pyt) ); + if (ec_GFp_pt_is_inf_aff(&pxt, &pyt) != MP_YES) { + res = MP_NO; + goto CLEANUP; + } + + res = MP_YES; + +CLEANUP: + mp_clear(&accl); + mp_clear(&accr); + mp_clear(&tmp); + mp_clear(&pxt); + mp_clear(&pyt); + return res; +} diff --git a/nss/lib/freebl/ecl/ecp_fp.c b/nss/lib/freebl/ecl/ecp_fp.c new file mode 100644 index 0000000..46dc123 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_fp.c @@ -0,0 +1,531 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp_fp.h" +#include "ecl-priv.h" +#include <stdlib.h> + +/* Performs tidying on a short multi-precision floating point integer (the + * lower group->numDoubles floats). */ +void +ecfp_tidyShort(double *t, const EC_group_fp * group) +{ + group->ecfp_tidy(t, group->alpha, group); +} + +/* Performs tidying on only the upper float digits of a multi-precision + * floating point integer, i.e. the digits beyond the regular length which + * are removed in the reduction step. */ +void +ecfp_tidyUpper(double *t, const EC_group_fp * group) +{ + group->ecfp_tidy(t + group->numDoubles, + group->alpha + group->numDoubles, group); +} + +/* Performs a "tidy" operation, which performs carrying, moving excess + * bits from one double to the next double, so that the precision of the + * doubles is reduced to the regular precision group->doubleBitSize. This + * might result in some float digits being negative. Alternative C version + * for portability. */ +void +ecfp_tidy(double *t, const double *alpha, const EC_group_fp * group) +{ + double q; + int i; + + /* Do carrying */ + for (i = 0; i < group->numDoubles - 1; i++) { + q = t[i] + alpha[i + 1]; + q -= alpha[i + 1]; + t[i] -= q; + t[i + 1] += q; + + /* If we don't assume that truncation rounding is used, then q + * might be 2^n bigger than expected (if it rounds up), then t[0] + * could be negative and t[1] 2^n larger than expected. */ + } +} + +/* Performs a more mathematically precise "tidying" so that each term is + * positive. This is slower than the regular tidying, and is used for + * conversion from floating point to integer. */ +void +ecfp_positiveTidy(double *t, const EC_group_fp * group) +{ + double q; + int i; + + /* Do carrying */ + for (i = 0; i < group->numDoubles - 1; i++) { + /* Subtract beta to force rounding down */ + q = t[i] - ecfp_beta[i + 1]; + q += group->alpha[i + 1]; + q -= group->alpha[i + 1]; + t[i] -= q; + t[i + 1] += q; + + /* Due to subtracting ecfp_beta, we should have each term a + * non-negative int */ + ECFP_ASSERT(t[i] / ecfp_exp[i] == + (unsigned long long) (t[i] / ecfp_exp[i])); + ECFP_ASSERT(t[i] >= 0); + } +} + +/* Converts from a floating point representation into an mp_int. Expects + * that d is already reduced. */ +void +ecfp_fp2i(mp_int *mpout, double *d, const ECGroup *ecgroup) +{ + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + unsigned short i16[(group->primeBitSize + 15) / 16]; + double q = 1; + +#ifdef ECL_THIRTY_TWO_BIT + /* TEST uint32_t z = 0; */ + unsigned int z = 0; +#else + uint64_t z = 0; +#endif + int zBits = 0; + int copiedBits = 0; + int i = 0; + int j = 0; + + mp_digit *out; + + /* Result should always be >= 0, so set sign accordingly */ + MP_SIGN(mpout) = MP_ZPOS; + + /* Tidy up so we're just dealing with positive numbers */ + ecfp_positiveTidy(d, group); + + /* We might need to do this reduction step more than once if the + * reduction adds smaller terms which carry-over to cause another + * reduction. However, this should happen very rarely, if ever, + * depending on the elliptic curve. */ + do { + /* Init loop data */ + z = 0; + zBits = 0; + q = 1; + i = 0; + j = 0; + copiedBits = 0; + + /* Might have to do a bit more reduction */ + group->ecfp_singleReduce(d, group); + + /* Grow the size of the mpint if it's too small */ + s_mp_grow(mpout, group->numInts); + MP_USED(mpout) = group->numInts; + out = MP_DIGITS(mpout); + + /* Convert double to 16 bit integers */ + while (copiedBits < group->primeBitSize) { + if (zBits < 16) { + z += d[i] * q; + i++; + ECFP_ASSERT(i < (group->primeBitSize + 15) / 16); + zBits += group->doubleBitSize; + } + i16[j] = z; + j++; + z >>= 16; + zBits -= 16; + q *= ecfp_twom16; + copiedBits += 16; + } + } while (z != 0); + + /* Convert 16 bit integers to mp_digit */ +#ifdef ECL_THIRTY_TWO_BIT + for (i = 0; i < (group->primeBitSize + 15) / 16; i += 2) { + *out = 0; + if (i + 1 < (group->primeBitSize + 15) / 16) { + *out = i16[i + 1]; + *out <<= 16; + } + *out++ += i16[i]; + } +#else /* 64 bit */ + for (i = 0; i < (group->primeBitSize + 15) / 16; i += 4) { + *out = 0; + if (i + 3 < (group->primeBitSize + 15) / 16) { + *out = i16[i + 3]; + *out <<= 16; + } + if (i + 2 < (group->primeBitSize + 15) / 16) { + *out += i16[i + 2]; + *out <<= 16; + } + if (i + 1 < (group->primeBitSize + 15) / 16) { + *out += i16[i + 1]; + *out <<= 16; + } + *out++ += i16[i]; + } +#endif + + /* Perform final reduction. mpout should already be the same number + * of bits as p, but might not be less than p. Make it so. Since + * mpout has the same number of bits as p, and 2p has a larger bit + * size, then mpout < 2p, so a single subtraction of p will suffice. */ + if (mp_cmp(mpout, &ecgroup->meth->irr) >= 0) { + mp_sub(mpout, &ecgroup->meth->irr, mpout); + } + + /* Shrink the size of the mp_int to the actual used size (required for + * mp_cmp_z == 0) */ + out = MP_DIGITS(mpout); + for (i = group->numInts - 1; i > 0; i--) { + if (out[i] != 0) + break; + } + MP_USED(mpout) = i + 1; + + /* Should be between 0 and p-1 */ + ECFP_ASSERT(mp_cmp(mpout, &ecgroup->meth->irr) < 0); + ECFP_ASSERT(mp_cmp_z(mpout) >= 0); +} + +/* Converts from an mpint into a floating point representation. */ +void +ecfp_i2fp(double *out, const mp_int *x, const ECGroup *ecgroup) +{ + int i; + int j = 0; + int size; + double shift = 1; + mp_digit *in; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + +#ifdef ECL_DEBUG + /* if debug mode, convert result back using ecfp_fp2i into cmp, then + * compare to x. */ + mp_int cmp; + + MP_DIGITS(&cmp) = NULL; + mp_init(&cmp); +#endif + + ECFP_ASSERT(group != NULL); + + /* init output to 0 (since we skip over some terms) */ + for (i = 0; i < group->numDoubles; i++) + out[i] = 0; + i = 0; + + size = MP_USED(x); + in = MP_DIGITS(x); + + /* Copy from int into doubles */ +#ifdef ECL_THIRTY_TWO_BIT + while (j < size) { + while (group->doubleBitSize * (i + 1) <= 32 * j) { + i++; + } + ECFP_ASSERT(group->doubleBitSize * i <= 32 * j); + out[i] = in[j]; + out[i] *= shift; + shift *= ecfp_two32; + j++; + } +#else + while (j < size) { + while (group->doubleBitSize * (i + 1) <= 64 * j) { + i++; + } + ECFP_ASSERT(group->doubleBitSize * i <= 64 * j); + out[i] = (in[j] & 0x00000000FFFFFFFF) * shift; + + while (group->doubleBitSize * (i + 1) <= 64 * j + 32) { + i++; + } + ECFP_ASSERT(24 * i <= 64 * j + 32); + out[i] = (in[j] & 0xFFFFFFFF00000000) * shift; + + shift *= ecfp_two64; + j++; + } +#endif + /* Realign bits to match double boundaries */ + ecfp_tidyShort(out, group); + +#ifdef ECL_DEBUG + /* Convert result back to mp_int, compare to original */ + ecfp_fp2i(&cmp, out, ecgroup); + ECFP_ASSERT(mp_cmp(&cmp, x) == 0); + mp_clear(&cmp); +#endif +} + +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GFp. Elliptic curve points P and R can be + * identical. Uses Jacobian coordinates. Uses 4-bit window method. */ +mp_err +ec_GFp_point_mul_jac_4w_fp(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *ecgroup) +{ + mp_err res = MP_OKAY; + ecfp_jac_pt precomp[16], r; + ecfp_aff_pt p; + EC_group_fp *group; + + mp_int rz; + int i, ni, d; + + ARGCHK(ecgroup != NULL, MP_BADARG); + ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); + + group = (EC_group_fp *) ecgroup->extra1; + MP_DIGITS(&rz) = 0; + MP_CHECKOK(mp_init(&rz)); + + /* init p, da */ + ecfp_i2fp(p.x, px, ecgroup); + ecfp_i2fp(p.y, py, ecgroup); + ecfp_i2fp(group->curvea, &ecgroup->curvea, ecgroup); + + /* Do precomputation */ + group->precompute_jac(precomp, &p, group); + + /* Do main body of calculations */ + d = (mpl_significant_bits(n) + 3) / 4; + + /* R = inf */ + for (i = 0; i < group->numDoubles; i++) { + r.z[i] = 0; + } + + for (i = d - 1; i >= 0; i--) { + /* compute window ni */ + ni = MP_GET_BIT(n, 4 * i + 3); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 2); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 1); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i); + + /* R = 2^4 * R */ + group->pt_dbl_jac(&r, &r, group); + group->pt_dbl_jac(&r, &r, group); + group->pt_dbl_jac(&r, &r, group); + group->pt_dbl_jac(&r, &r, group); + + /* R = R + (ni * P) */ + group->pt_add_jac(&r, &precomp[ni], &r, group); + } + + /* Convert back to integer */ + ecfp_fp2i(rx, r.x, ecgroup); + ecfp_fp2i(ry, r.y, ecgroup); + ecfp_fp2i(&rz, r.z, ecgroup); + + /* convert result S to affine coordinates */ + MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, ecgroup)); + + CLEANUP: + mp_clear(&rz); + return res; +} + +/* Uses mixed Jacobian-affine coordinates to perform a point + * multiplication: R = n * P, n scalar. Uses mixed Jacobian-affine + * coordinates (Jacobian coordinates for doubles and affine coordinates + * for additions; based on recommendation from Brown et al.). Not very + * time efficient but quite space efficient, no precomputation needed. + * group contains the elliptic curve coefficients and the prime that + * determines the field GFp. Elliptic curve points P and R can be + * identical. Performs calculations in floating point number format, since + * this is faster than the integer operations on the ULTRASPARC III. + * Uses left-to-right binary method (double & add) (algorithm 9) for + * scalar-point multiplication from Brown, Hankerson, Lopez, Menezes. + * Software Implementation of the NIST Elliptic Curves Over Prime Fields. */ +mp_err +ec_GFp_pt_mul_jac_fp(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *ecgroup) +{ + mp_err res; + mp_int sx, sy, sz; + + ecfp_aff_pt p; + ecfp_jac_pt r; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + int i, l; + + MP_DIGITS(&sx) = 0; + MP_DIGITS(&sy) = 0; + MP_DIGITS(&sz) = 0; + MP_CHECKOK(mp_init(&sx)); + MP_CHECKOK(mp_init(&sy)); + MP_CHECKOK(mp_init(&sz)); + + /* if n = 0 then r = inf */ + if (mp_cmp_z(n) == 0) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + /* if n < 0 then out of range error */ + } else if (mp_cmp_z(n) < 0) { + res = MP_RANGE; + goto CLEANUP; + } + + /* Convert from integer to floating point */ + ecfp_i2fp(p.x, px, ecgroup); + ecfp_i2fp(p.y, py, ecgroup); + ecfp_i2fp(group->curvea, &(ecgroup->curvea), ecgroup); + + /* Init r to point at infinity */ + for (i = 0; i < group->numDoubles; i++) { + r.z[i] = 0; + } + + /* double and add method */ + l = mpl_significant_bits(n) - 1; + + for (i = l; i >= 0; i--) { + /* R = 2R */ + group->pt_dbl_jac(&r, &r, group); + + /* if n_i = 1, then R = R + Q */ + if (MP_GET_BIT(n, i) != 0) { + group->pt_add_jac_aff(&r, &p, &r, group); + } + } + + /* Convert from floating point to integer */ + ecfp_fp2i(&sx, r.x, ecgroup); + ecfp_fp2i(&sy, r.y, ecgroup); + ecfp_fp2i(&sz, r.z, ecgroup); + + /* convert result R to affine coordinates */ + MP_CHECKOK(ec_GFp_pt_jac2aff(&sx, &sy, &sz, rx, ry, ecgroup)); + + CLEANUP: + mp_clear(&sx); + mp_clear(&sy); + mp_clear(&sz); + return res; +} + +/* Computes R = nP where R is (rx, ry) and P is the base point. Elliptic + * curve points P and R can be identical. Uses mixed Modified-Jacobian + * co-ordinates for doubling and Chudnovsky Jacobian coordinates for + * additions. Uses 5-bit window NAF method (algorithm 11) for scalar-point + * multiplication from Brown, Hankerson, Lopez, Menezes. Software + * Implementation of the NIST Elliptic Curves Over Prime Fields. */ +mp_err +ec_GFp_point_mul_wNAF_fp(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *ecgroup) +{ + mp_err res = MP_OKAY; + mp_int sx, sy, sz; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + ecfp_chud_pt precomp[16]; + + ecfp_aff_pt p; + ecfp_jm_pt r; + + signed char naf[group->orderBitSize + 1]; + int i; + + MP_DIGITS(&sx) = 0; + MP_DIGITS(&sy) = 0; + MP_DIGITS(&sz) = 0; + MP_CHECKOK(mp_init(&sx)); + MP_CHECKOK(mp_init(&sy)); + MP_CHECKOK(mp_init(&sz)); + + /* if n = 0 then r = inf */ + if (mp_cmp_z(n) == 0) { + mp_zero(rx); + mp_zero(ry); + res = MP_OKAY; + goto CLEANUP; + /* if n < 0 then out of range error */ + } else if (mp_cmp_z(n) < 0) { + res = MP_RANGE; + goto CLEANUP; + } + + /* Convert from integer to floating point */ + ecfp_i2fp(p.x, px, ecgroup); + ecfp_i2fp(p.y, py, ecgroup); + ecfp_i2fp(group->curvea, &(ecgroup->curvea), ecgroup); + + /* Perform precomputation */ + group->precompute_chud(precomp, &p, group); + + /* Compute 5NAF */ + ec_compute_wNAF(naf, group->orderBitSize, n, 5); + + /* Init R = pt at infinity */ + for (i = 0; i < group->numDoubles; i++) { + r.z[i] = 0; + } + + /* wNAF method */ + for (i = group->orderBitSize; i >= 0; i--) { + /* R = 2R */ + group->pt_dbl_jm(&r, &r, group); + + if (naf[i] != 0) { + group->pt_add_jm_chud(&r, &precomp[(naf[i] + 15) / 2], &r, + group); + } + } + + /* Convert from floating point to integer */ + ecfp_fp2i(&sx, r.x, ecgroup); + ecfp_fp2i(&sy, r.y, ecgroup); + ecfp_fp2i(&sz, r.z, ecgroup); + + /* convert result R to affine coordinates */ + MP_CHECKOK(ec_GFp_pt_jac2aff(&sx, &sy, &sz, rx, ry, ecgroup)); + + CLEANUP: + mp_clear(&sx); + mp_clear(&sy); + mp_clear(&sz); + return res; +} + +/* Cleans up extra memory allocated in ECGroup for this implementation. */ +void +ec_GFp_extra_free_fp(ECGroup *group) +{ + if (group->extra1 != NULL) { + free(group->extra1); + group->extra1 = NULL; + } +} + +/* Tests what precision floating point arithmetic is set to. This should + * be either a 53-bit mantissa (IEEE standard) or a 64-bit mantissa + * (extended precision on x86) and sets it into the EC_group_fp. Returns + * either 53 or 64 accordingly. */ +int +ec_set_fp_precision(EC_group_fp * group) +{ + double a = 9007199254740992.0; /* 2^53 */ + double b = a + 1; + + if (a == b) { + group->fpPrecision = 53; + group->alpha = ecfp_alpha_53; + return 53; + } + group->fpPrecision = 64; + group->alpha = ecfp_alpha_64; + return 64; +} diff --git a/nss/lib/freebl/ecl/ecp_fp.h b/nss/lib/freebl/ecl/ecp_fp.h new file mode 100644 index 0000000..a5a6769 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_fp.h @@ -0,0 +1,372 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __ecp_fp_h_ +#define __ecp_fp_h_ + +#include "mpi.h" +#include "ecl.h" +#include "ecp.h" + +#include <sys/types.h> +#include "mpi-priv.h" + +#ifdef ECL_DEBUG +#include <assert.h> +#endif + +/* Largest number of doubles to store one reduced number in floating + * point. Used for memory allocation on the stack. */ +#define ECFP_MAXDOUBLES 10 + +/* For debugging purposes */ +#ifndef ECL_DEBUG +#define ECFP_ASSERT(x) +#else +#define ECFP_ASSERT(x) assert(x) +#endif + +/* ECFP_Ti = 2^(i*24) Define as preprocessor constants so we can use in + * multiple static constants */ +#define ECFP_T0 1.0 +#define ECFP_T1 16777216.0 +#define ECFP_T2 281474976710656.0 +#define ECFP_T3 4722366482869645213696.0 +#define ECFP_T4 79228162514264337593543950336.0 +#define ECFP_T5 1329227995784915872903807060280344576.0 +#define ECFP_T6 22300745198530623141535718272648361505980416.0 +#define ECFP_T7 374144419156711147060143317175368453031918731001856.0 +#define ECFP_T8 6277101735386680763835789423207666416102355444464034512896.0 +#define ECFP_T9 105312291668557186697918027683670432318895095400549111254310977536.0 +#define ECFP_T10 1766847064778384329583297500742918515827483896875618958121606201292619776.0 +#define ECFP_T11 29642774844752946028434172162224104410437116074403984394101141506025761187823616.0 +#define ECFP_T12 497323236409786642155382248146820840100456150797347717440463976893159497012533375533056.0 +#define ECFP_T13 8343699359066055009355553539724812947666814540455674882605631280555545803830627148527195652096.0 +#define ECFP_T14 139984046386112763159840142535527767382602843577165595931249318810236991948760059086304843329475444736.0 +#define ECFP_T15 2348542582773833227889480596789337027375682548908319870707290971532209025114608443463698998384768703031934976.0 +#define ECFP_T16 39402006196394479212279040100143613805079739270465446667948293404245\ +721771497210611414266254884915640806627990306816.0 +#define ECFP_T17 66105596879024859895191530803277103982840468296428121928464879527440\ +5791236311345825189210439715284847591212025023358304256.0 +#define ECFP_T18 11090678776483259438313656736572334813745748301503266300681918322458\ +485231222502492159897624416558312389564843845614287315896631296.0 +#define ECFP_T19 18607071341967536398062689481932916079453218833595342343206149099024\ +36577570298683715049089827234727835552055312041415509848580169253519\ +36.0 + +#define ECFP_TWO160 1461501637330902918203684832716283019655932542976.0 +#define ECFP_TWO192 6277101735386680763835789423207666416102355444464034512896.0 +#define ECFP_TWO224 26959946667150639794667015087019630673637144422540572481103610249216.0 + +/* Multiplicative constants */ +static const double ecfp_two32 = 4294967296.0; +static const double ecfp_two64 = 18446744073709551616.0; +static const double ecfp_twom16 = .0000152587890625; +static const double ecfp_twom128 = + .00000000000000000000000000000000000000293873587705571876992184134305561419454666389193021880377187926569604314863681793212890625; +static const double ecfp_twom129 = + .000000000000000000000000000000000000001469367938527859384960920671527807097273331945965109401885939632848021574318408966064453125; +static const double ecfp_twom160 = + .0000000000000000000000000000000000000000000000006842277657836020854119773355907793609766904013068924666782559979930620520927053718196475529111921787261962890625; +static const double ecfp_twom192 = + .000000000000000000000000000000000000000000000000000000000159309191113245227702888039776771180559110455519261878607388585338616290151305816094308987472018268594098344692611135542392730712890625; +static const double ecfp_twom224 = + .00000000000000000000000000000000000000000000000000000000000000000003709206150687421385731735261547639513367564778757791002453039058917581340095629358997312082723208437536338919136001159027049567384892725385725498199462890625; + +/* ecfp_exp[i] = 2^(i*ECFP_DSIZE) */ +static const double ecfp_exp[2 * ECFP_MAXDOUBLES] = { + ECFP_T0, ECFP_T1, ECFP_T2, ECFP_T3, ECFP_T4, ECFP_T5, + ECFP_T6, ECFP_T7, ECFP_T8, ECFP_T9, ECFP_T10, ECFP_T11, + ECFP_T12, ECFP_T13, ECFP_T14, ECFP_T15, ECFP_T16, ECFP_T17, ECFP_T18, + ECFP_T19 +}; + +/* 1.1 * 2^52 Uses 2^52 to truncate, the .1 is an extra 2^51 to protect + * the 2^52 bit, so that adding alphas to a negative number won't borrow + * and empty the important 2^52 bit */ +#define ECFP_ALPHABASE_53 6755399441055744.0 +/* Special case: On some platforms, notably x86 Linux, there is an + * extended-precision floating point representation with 64-bits of + * precision in the mantissa. These extra bits of precision require a + * larger value of alpha to truncate, i.e. 1.1 * 2^63. */ +#define ECFP_ALPHABASE_64 13835058055282163712.0 + +/* + * ecfp_alpha[i] = 1.5 * 2^(52 + i*ECFP_DSIZE) we add and subtract alpha + * to truncate floating point numbers to a certain number of bits for + * tidying */ +static const double ecfp_alpha_53[2 * ECFP_MAXDOUBLES] = { + ECFP_ALPHABASE_53 * ECFP_T0, + ECFP_ALPHABASE_53 * ECFP_T1, + ECFP_ALPHABASE_53 * ECFP_T2, + ECFP_ALPHABASE_53 * ECFP_T3, + ECFP_ALPHABASE_53 * ECFP_T4, + ECFP_ALPHABASE_53 * ECFP_T5, + ECFP_ALPHABASE_53 * ECFP_T6, + ECFP_ALPHABASE_53 * ECFP_T7, + ECFP_ALPHABASE_53 * ECFP_T8, + ECFP_ALPHABASE_53 * ECFP_T9, + ECFP_ALPHABASE_53 * ECFP_T10, + ECFP_ALPHABASE_53 * ECFP_T11, + ECFP_ALPHABASE_53 * ECFP_T12, + ECFP_ALPHABASE_53 * ECFP_T13, + ECFP_ALPHABASE_53 * ECFP_T14, + ECFP_ALPHABASE_53 * ECFP_T15, + ECFP_ALPHABASE_53 * ECFP_T16, + ECFP_ALPHABASE_53 * ECFP_T17, + ECFP_ALPHABASE_53 * ECFP_T18, + ECFP_ALPHABASE_53 * ECFP_T19 +}; + +/* + * ecfp_alpha[i] = 1.5 * 2^(63 + i*ECFP_DSIZE) we add and subtract alpha + * to truncate floating point numbers to a certain number of bits for + * tidying */ +static const double ecfp_alpha_64[2 * ECFP_MAXDOUBLES] = { + ECFP_ALPHABASE_64 * ECFP_T0, + ECFP_ALPHABASE_64 * ECFP_T1, + ECFP_ALPHABASE_64 * ECFP_T2, + ECFP_ALPHABASE_64 * ECFP_T3, + ECFP_ALPHABASE_64 * ECFP_T4, + ECFP_ALPHABASE_64 * ECFP_T5, + ECFP_ALPHABASE_64 * ECFP_T6, + ECFP_ALPHABASE_64 * ECFP_T7, + ECFP_ALPHABASE_64 * ECFP_T8, + ECFP_ALPHABASE_64 * ECFP_T9, + ECFP_ALPHABASE_64 * ECFP_T10, + ECFP_ALPHABASE_64 * ECFP_T11, + ECFP_ALPHABASE_64 * ECFP_T12, + ECFP_ALPHABASE_64 * ECFP_T13, + ECFP_ALPHABASE_64 * ECFP_T14, + ECFP_ALPHABASE_64 * ECFP_T15, + ECFP_ALPHABASE_64 * ECFP_T16, + ECFP_ALPHABASE_64 * ECFP_T17, + ECFP_ALPHABASE_64 * ECFP_T18, + ECFP_ALPHABASE_64 * ECFP_T19 +}; + +/* 0.011111111111111111111111 (binary) = 0.5 - 2^25 (24 ones) */ +#define ECFP_BETABASE 0.4999999701976776123046875 + +/* + * We subtract beta prior to using alpha to simulate rounding down. We + * make this close to 0.5 to round almost everything down, but exactly 0.5 + * would cause some incorrect rounding. */ +static const double ecfp_beta[2 * ECFP_MAXDOUBLES] = { + ECFP_BETABASE * ECFP_T0, + ECFP_BETABASE * ECFP_T1, + ECFP_BETABASE * ECFP_T2, + ECFP_BETABASE * ECFP_T3, + ECFP_BETABASE * ECFP_T4, + ECFP_BETABASE * ECFP_T5, + ECFP_BETABASE * ECFP_T6, + ECFP_BETABASE * ECFP_T7, + ECFP_BETABASE * ECFP_T8, + ECFP_BETABASE * ECFP_T9, + ECFP_BETABASE * ECFP_T10, + ECFP_BETABASE * ECFP_T11, + ECFP_BETABASE * ECFP_T12, + ECFP_BETABASE * ECFP_T13, + ECFP_BETABASE * ECFP_T14, + ECFP_BETABASE * ECFP_T15, + ECFP_BETABASE * ECFP_T16, + ECFP_BETABASE * ECFP_T17, + ECFP_BETABASE * ECFP_T18, + ECFP_BETABASE * ECFP_T19 +}; + +static const double ecfp_beta_160 = ECFP_BETABASE * ECFP_TWO160; +static const double ecfp_beta_192 = ECFP_BETABASE * ECFP_TWO192; +static const double ecfp_beta_224 = ECFP_BETABASE * ECFP_TWO224; + +/* Affine EC Point. This is the basic representation (x, y) of an elliptic + * curve point. */ +typedef struct { + double x[ECFP_MAXDOUBLES]; + double y[ECFP_MAXDOUBLES]; +} ecfp_aff_pt; + +/* Jacobian EC Point. This coordinate system uses X = x/z^2, Y = y/z^3, + * which enables calculations with fewer inversions than affine + * coordinates. */ +typedef struct { + double x[ECFP_MAXDOUBLES]; + double y[ECFP_MAXDOUBLES]; + double z[ECFP_MAXDOUBLES]; +} ecfp_jac_pt; + +/* Chudnovsky Jacobian EC Point. This coordinate system is the same as + * Jacobian, except it keeps z^2, z^3 for faster additions. */ +typedef struct { + double x[ECFP_MAXDOUBLES]; + double y[ECFP_MAXDOUBLES]; + double z[ECFP_MAXDOUBLES]; + double z2[ECFP_MAXDOUBLES]; + double z3[ECFP_MAXDOUBLES]; +} ecfp_chud_pt; + +/* Modified Jacobian EC Point. This coordinate system is the same as + * Jacobian, except it keeps a*z^4 for faster doublings. */ +typedef struct { + double x[ECFP_MAXDOUBLES]; + double y[ECFP_MAXDOUBLES]; + double z[ECFP_MAXDOUBLES]; + double az4[ECFP_MAXDOUBLES]; +} ecfp_jm_pt; + +struct EC_group_fp_str; + +typedef struct EC_group_fp_str EC_group_fp; +struct EC_group_fp_str { + int fpPrecision; /* Set to number of bits in mantissa, 53 + * or 64 */ + int numDoubles; + int primeBitSize; + int orderBitSize; + int doubleBitSize; + int numInts; + int aIsM3; /* True if curvea == -3 (mod p), then we + * can optimize doubling */ + double curvea[ECFP_MAXDOUBLES]; + /* Used to truncate a double to the number of bits in the curve */ + double bitSize_alpha; + /* Pointer to either ecfp_alpha_53 or ecfp_alpha_64 */ + const double *alpha; + + void (*ecfp_singleReduce) (double *r, const EC_group_fp * group); + void (*ecfp_reduce) (double *r, double *x, const EC_group_fp * group); + /* Performs a "tidy" operation, which performs carrying, moving excess + * bits from one double to the next double, so that the precision of + * the doubles is reduced to the regular precision ECFP_DSIZE. This + * might result in some float digits being negative. */ + void (*ecfp_tidy) (double *t, const double *alpha, + const EC_group_fp * group); + /* Perform a point addition using coordinate system Jacobian + Affine + * -> Jacobian. Input and output should be multi-precision floating + * point integers. */ + void (*pt_add_jac_aff) (const ecfp_jac_pt * p, const ecfp_aff_pt * q, + ecfp_jac_pt * r, const EC_group_fp * group); + /* Perform a point doubling in Jacobian coordinates. Input and output + * should be multi-precision floating point integers. */ + void (*pt_dbl_jac) (const ecfp_jac_pt * dp, ecfp_jac_pt * dr, + const EC_group_fp * group); + /* Perform a point addition using Jacobian coordinate system. Input + * and output should be multi-precision floating point integers. */ + void (*pt_add_jac) (const ecfp_jac_pt * p, const ecfp_jac_pt * q, + ecfp_jac_pt * r, const EC_group_fp * group); + /* Perform a point doubling in Modified Jacobian coordinates. Input + * and output should be multi-precision floating point integers. */ + void (*pt_dbl_jm) (const ecfp_jm_pt * p, ecfp_jm_pt * r, + const EC_group_fp * group); + /* Perform a point doubling using coordinates Affine -> Chudnovsky + * Jacobian. Input and output should be multi-precision floating point + * integers. */ + void (*pt_dbl_aff2chud) (const ecfp_aff_pt * p, ecfp_chud_pt * r, + const EC_group_fp * group); + /* Perform a point addition using coordinates: Modified Jacobian + + * Chudnovsky Jacobian -> Modified Jacobian. Input and output should + * be multi-precision floating point integers. */ + void (*pt_add_jm_chud) (ecfp_jm_pt * p, ecfp_chud_pt * q, + ecfp_jm_pt * r, const EC_group_fp * group); + /* Perform a point addition using Chudnovsky Jacobian coordinates. + * Input and output should be multi-precision floating point integers. + */ + void (*pt_add_chud) (const ecfp_chud_pt * p, const ecfp_chud_pt * q, + ecfp_chud_pt * r, const EC_group_fp * group); + /* Expects out to be an array of size 16 of Chudnovsky Jacobian + * points. Fills in Chudnovsky Jacobian form (x, y, z, z^2, z^3), for + * -15P, -13P, -11P, -9P, -7P, -5P, -3P, -P, P, 3P, 5P, 7P, 9P, 11P, + * 13P, 15P */ + void (*precompute_chud) (ecfp_chud_pt * out, const ecfp_aff_pt * p, + const EC_group_fp * group); + /* Expects out to be an array of size 16 of Jacobian points. Fills in + * Chudnovsky Jacobian form (x, y, z), for O, P, 2P, ... 15P */ + void (*precompute_jac) (ecfp_jac_pt * out, const ecfp_aff_pt * p, + const EC_group_fp * group); + +}; + +/* Computes r = x*y. + * r must be different (point to different memory) than x and y. + * Does not tidy or reduce. */ +void ecfp_multiply(double *r, const double *x, const double *y); + +/* Performs a "tidy" operation, which performs carrying, moving excess + * bits from one double to the next double, so that the precision of the + * doubles is reduced to the regular precision group->doubleBitSize. This + * might result in some float digits being negative. */ +void ecfp_tidy(double *t, const double *alpha, const EC_group_fp * group); + +/* Performs tidying on only the upper float digits of a multi-precision + * floating point integer, i.e. the digits beyond the regular length which + * are removed in the reduction step. */ +void ecfp_tidyUpper(double *t, const EC_group_fp * group); + +/* Performs tidying on a short multi-precision floating point integer (the + * lower group->numDoubles floats). */ +void ecfp_tidyShort(double *t, const EC_group_fp * group); + +/* Performs a more mathematically precise "tidying" so that each term is + * positive. This is slower than the regular tidying, and is used for + * conversion from floating point to integer. */ +void ecfp_positiveTidy(double *t, const EC_group_fp * group); + +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GFp. Elliptic curve points P and R can be + * identical. Uses mixed Jacobian-affine coordinates. Uses 4-bit window + * method. */ +mp_err + ec_GFp_point_mul_jac_4w_fp(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *ecgroup); + +/* Computes R = nP where R is (rx, ry) and P is the base point. The + * parameters a, b and p are the elliptic curve coefficients and the prime + * that determines the field GFp. Elliptic curve points P and R can be + * identical. Uses mixed Jacobian-affine coordinates (Jacobian + * coordinates for doubles and affine coordinates for additions; based on + * recommendation from Brown et al.). Uses window NAF method (algorithm + * 11) for scalar-point multiplication from Brown, Hankerson, Lopez, + * Menezes. Software Implementation of the NIST Elliptic Curves Over Prime + * Fields. */ +mp_err ec_GFp_point_mul_wNAF_fp(const mp_int *n, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *ecgroup); + +/* Uses mixed Jacobian-affine coordinates to perform a point + * multiplication: R = n * P, n scalar. Uses mixed Jacobian-affine + * coordinates (Jacobian coordinates for doubles and affine coordinates + * for additions; based on recommendation from Brown et al.). Not very + * time efficient but quite space efficient, no precomputation needed. + * group contains the elliptic curve coefficients and the prime that + * determines the field GFp. Elliptic curve points P and R can be + * identical. Performs calculations in floating point number format, since + * this is faster than the integer operations on the ULTRASPARC III. + * Uses left-to-right binary method (double & add) (algorithm 9) for + * scalar-point multiplication from Brown, Hankerson, Lopez, Menezes. + * Software Implementation of the NIST Elliptic Curves Over Prime Fields. */ +mp_err + ec_GFp_pt_mul_jac_fp(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *ecgroup); + +/* Cleans up extra memory allocated in ECGroup for this implementation. */ +void ec_GFp_extra_free_fp(ECGroup *group); + +/* Converts from a floating point representation into an mp_int. Expects + * that d is already reduced. */ +void + ecfp_fp2i(mp_int *mpout, double *d, const ECGroup *ecgroup); + +/* Converts from an mpint into a floating point representation. */ +void + ecfp_i2fp(double *out, const mp_int *x, const ECGroup *ecgroup); + +/* Tests what precision floating point arithmetic is set to. This should + * be either a 53-bit mantissa (IEEE standard) or a 64-bit mantissa + * (extended precision on x86) and sets it into the EC_group_fp. Returns + * either 53 or 64 accordingly. */ +int ec_set_fp_precision(EC_group_fp * group); + +#endif diff --git a/nss/lib/freebl/ecl/ecp_fp160.c b/nss/lib/freebl/ecl/ecp_fp160.c new file mode 100644 index 0000000..f462f3b --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_fp160.c @@ -0,0 +1,145 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp_fp.h" +#include <stdlib.h> + +#define ECFP_BSIZE 160 +#define ECFP_NUMDOUBLES 7 + +#include "ecp_fpinc.c" + +/* Performs a single step of reduction, just on the uppermost float + * (assumes already tidied), and then retidies. Note, this does not + * guarantee that the result will be less than p, but truncates the number + * of bits. */ +void +ecfp160_singleReduce(double *d, const EC_group_fp * group) +{ + double q; + + ECFP_ASSERT(group->doubleBitSize == 24); + ECFP_ASSERT(group->primeBitSize == 160); + ECFP_ASSERT(ECFP_NUMDOUBLES == 7); + + q = d[ECFP_NUMDOUBLES - 1] - ecfp_beta_160; + q += group->bitSize_alpha; + q -= group->bitSize_alpha; + + d[ECFP_NUMDOUBLES - 1] -= q; + d[0] += q * ecfp_twom160; + d[1] += q * ecfp_twom129; + ecfp_positiveTidy(d, group); + + /* Assertions for the highest order term */ + ECFP_ASSERT(d[ECFP_NUMDOUBLES - 1] / ecfp_exp[ECFP_NUMDOUBLES - 1] == + (unsigned long long) (d[ECFP_NUMDOUBLES - 1] / + ecfp_exp[ECFP_NUMDOUBLES - 1])); + ECFP_ASSERT(d[ECFP_NUMDOUBLES - 1] >= 0); +} + +/* Performs imperfect reduction. This might leave some negative terms, + * and one more reduction might be required for the result to be between 0 + * and p-1. x should not already be reduced, i.e. should have + * 2*ECFP_NUMDOUBLES significant terms. x and r can be the same, but then + * the upper parts of r are not zeroed */ +void +ecfp160_reduce(double *r, double *x, const EC_group_fp * group) +{ + + double x7, x8, q; + + ECFP_ASSERT(group->doubleBitSize == 24); + ECFP_ASSERT(group->primeBitSize == 160); + ECFP_ASSERT(ECFP_NUMDOUBLES == 7); + + /* Tidy just the upper bits, the lower bits can wait. */ + ecfp_tidyUpper(x, group); + + /* Assume that this is already tidied so that we have enough extra + * bits */ + x7 = x[7] + x[13] * ecfp_twom129; /* adds bits 15-39 */ + + /* Tidy x7, or we won't have enough bits later to add it in */ + q = x7 + group->alpha[8]; + q -= group->alpha[8]; + x7 -= q; /* holds bits 0-24 */ + x8 = x[8] + q; /* holds bits 0-25 */ + + r[6] = x[6] + x[13] * ecfp_twom160 + x[12] * ecfp_twom129; /* adds + * bits + * 8-39 */ + r[5] = x[5] + x[12] * ecfp_twom160 + x[11] * ecfp_twom129; + r[4] = x[4] + x[11] * ecfp_twom160 + x[10] * ecfp_twom129; + r[3] = x[3] + x[10] * ecfp_twom160 + x[9] * ecfp_twom129; + r[2] = x[2] + x[9] * ecfp_twom160 + x8 * ecfp_twom129; /* adds bits + * 8-40 */ + r[1] = x[1] + x8 * ecfp_twom160 + x7 * ecfp_twom129; /* adds bits + * 8-39 */ + r[0] = x[0] + x7 * ecfp_twom160; + + /* Tidy up just r[ECFP_NUMDOUBLES-2] so that the number of reductions + * is accurate plus or minus one. (Rather than tidy all to make it + * totally accurate, which is more costly.) */ + q = r[ECFP_NUMDOUBLES - 2] + group->alpha[ECFP_NUMDOUBLES - 1]; + q -= group->alpha[ECFP_NUMDOUBLES - 1]; + r[ECFP_NUMDOUBLES - 2] -= q; + r[ECFP_NUMDOUBLES - 1] += q; + + /* Tidy up the excess bits on r[ECFP_NUMDOUBLES-1] using reduction */ + /* Use ecfp_beta so we get a positive result */ + q = r[ECFP_NUMDOUBLES - 1] - ecfp_beta_160; + q += group->bitSize_alpha; + q -= group->bitSize_alpha; + + r[ECFP_NUMDOUBLES - 1] -= q; + r[0] += q * ecfp_twom160; + r[1] += q * ecfp_twom129; + + /* Tidy the result */ + ecfp_tidyShort(r, group); +} + +/* Sets group to use optimized calculations in this file */ +mp_err +ec_group_set_secp160r1_fp(ECGroup *group) +{ + + EC_group_fp *fpg = NULL; + + /* Allocate memory for floating point group data */ + fpg = (EC_group_fp *) malloc(sizeof(EC_group_fp)); + if (fpg == NULL) { + return MP_MEM; + } + + fpg->numDoubles = ECFP_NUMDOUBLES; + fpg->primeBitSize = ECFP_BSIZE; + fpg->orderBitSize = 161; + fpg->doubleBitSize = 24; + fpg->numInts = (ECFP_BSIZE + ECL_BITS - 1) / ECL_BITS; + fpg->aIsM3 = 1; + fpg->ecfp_singleReduce = &ecfp160_singleReduce; + fpg->ecfp_reduce = &ecfp160_reduce; + fpg->ecfp_tidy = &ecfp_tidy; + + fpg->pt_add_jac_aff = &ecfp160_pt_add_jac_aff; + fpg->pt_add_jac = &ecfp160_pt_add_jac; + fpg->pt_add_jm_chud = &ecfp160_pt_add_jm_chud; + fpg->pt_add_chud = &ecfp160_pt_add_chud; + fpg->pt_dbl_jac = &ecfp160_pt_dbl_jac; + fpg->pt_dbl_jm = &ecfp160_pt_dbl_jm; + fpg->pt_dbl_aff2chud = &ecfp160_pt_dbl_aff2chud; + fpg->precompute_chud = &ecfp160_precompute_chud; + fpg->precompute_jac = &ecfp160_precompute_jac; + + group->point_mul = &ec_GFp_point_mul_wNAF_fp; + group->points_mul = &ec_pts_mul_basic; + group->extra1 = fpg; + group->extra_free = &ec_GFp_extra_free_fp; + + ec_set_fp_precision(fpg); + fpg->bitSize_alpha = ECFP_TWO160 * fpg->alpha[0]; + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_fp192.c b/nss/lib/freebl/ecl/ecp_fp192.c new file mode 100644 index 0000000..a415bcd --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_fp192.c @@ -0,0 +1,143 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp_fp.h" +#include <stdlib.h> + +#define ECFP_BSIZE 192 +#define ECFP_NUMDOUBLES 8 + +#include "ecp_fpinc.c" + +/* Performs a single step of reduction, just on the uppermost float + * (assumes already tidied), and then retidies. Note, this does not + * guarantee that the result will be less than p. */ +void +ecfp192_singleReduce(double *d, const EC_group_fp * group) +{ + double q; + + ECFP_ASSERT(group->doubleBitSize == 24); + ECFP_ASSERT(group->primeBitSize == 192); + ECFP_ASSERT(group->numDoubles == 8); + + q = d[ECFP_NUMDOUBLES - 1] - ecfp_beta_192; + q += group->bitSize_alpha; + q -= group->bitSize_alpha; + + d[ECFP_NUMDOUBLES - 1] -= q; + d[0] += q * ecfp_twom192; + d[2] += q * ecfp_twom128; + ecfp_positiveTidy(d, group); +} + +/* + * Performs imperfect reduction. This might leave some negative terms, + * and one more reduction might be required for the result to be between 0 + * and p-1. x should be be an array of at least 16, and r at least 8 x and + * r can be the same, but then the upper parts of r are not zeroed */ +void +ecfp_reduce_192(double *r, double *x, const EC_group_fp * group) +{ + double x8, x9, x10, q; + + ECFP_ASSERT(group->doubleBitSize == 24); + ECFP_ASSERT(group->primeBitSize == 192); + ECFP_ASSERT(group->numDoubles == 8); + + /* Tidy just the upper portion, the lower part can wait */ + ecfp_tidyUpper(x, group); + + x8 = x[8] + x[14] * ecfp_twom128; /* adds bits 16-40 */ + x9 = x[9] + x[15] * ecfp_twom128; /* adds bits 16-40 */ + + /* Tidy up, or we won't have enough bits later to add it in */ + + q = x8 + group->alpha[9]; + q -= group->alpha[9]; + x8 -= q; + x9 += q; + + q = x9 + group->alpha[10]; + q -= group->alpha[10]; + x9 -= q; + x10 = x[10] + q; + + r[7] = x[7] + x[15] * ecfp_twom192 + x[13] * ecfp_twom128; /* adds + * bits + * 0-40 */ + r[6] = x[6] + x[14] * ecfp_twom192 + x[12] * ecfp_twom128; + r[5] = x[5] + x[13] * ecfp_twom192 + x[11] * ecfp_twom128; + r[4] = x[4] + x[12] * ecfp_twom192 + x10 * ecfp_twom128; + r[3] = x[3] + x[11] * ecfp_twom192 + x9 * ecfp_twom128; /* adds bits + * 0-40 */ + r[2] = x[2] + x10 * ecfp_twom192 + x8 * ecfp_twom128; + r[1] = x[1] + x9 * ecfp_twom192; /* adds bits 16-40 */ + r[0] = x[0] + x8 * ecfp_twom192; + + /* + * Tidy up just r[group->numDoubles-2] so that the number of + * reductions is accurate plus or minus one. (Rather than tidy all to + * make it totally accurate) */ + q = r[ECFP_NUMDOUBLES - 2] + group->alpha[ECFP_NUMDOUBLES - 1]; + q -= group->alpha[ECFP_NUMDOUBLES - 1]; + r[ECFP_NUMDOUBLES - 2] -= q; + r[ECFP_NUMDOUBLES - 1] += q; + + /* Tidy up the excess bits on r[group->numDoubles-1] using reduction */ + /* Use ecfp_beta so we get a positive res */ + q = r[ECFP_NUMDOUBLES - 1] - ecfp_beta_192; + q += group->bitSize_alpha; + q -= group->bitSize_alpha; + + r[ECFP_NUMDOUBLES - 1] -= q; + r[0] += q * ecfp_twom192; + r[2] += q * ecfp_twom128; + + /* Tidy the result */ + ecfp_tidyShort(r, group); +} + +/* Sets group to use optimized calculations in this file */ +mp_err +ec_group_set_nistp192_fp(ECGroup *group) +{ + EC_group_fp *fpg; + + /* Allocate memory for floating point group data */ + fpg = (EC_group_fp *) malloc(sizeof(EC_group_fp)); + if (fpg == NULL) { + return MP_MEM; + } + + fpg->numDoubles = ECFP_NUMDOUBLES; + fpg->primeBitSize = ECFP_BSIZE; + fpg->orderBitSize = 192; + fpg->doubleBitSize = 24; + fpg->numInts = (ECFP_BSIZE + ECL_BITS - 1) / ECL_BITS; + fpg->aIsM3 = 1; + fpg->ecfp_singleReduce = &ecfp192_singleReduce; + fpg->ecfp_reduce = &ecfp_reduce_192; + fpg->ecfp_tidy = &ecfp_tidy; + + fpg->pt_add_jac_aff = &ecfp192_pt_add_jac_aff; + fpg->pt_add_jac = &ecfp192_pt_add_jac; + fpg->pt_add_jm_chud = &ecfp192_pt_add_jm_chud; + fpg->pt_add_chud = &ecfp192_pt_add_chud; + fpg->pt_dbl_jac = &ecfp192_pt_dbl_jac; + fpg->pt_dbl_jm = &ecfp192_pt_dbl_jm; + fpg->pt_dbl_aff2chud = &ecfp192_pt_dbl_aff2chud; + fpg->precompute_chud = &ecfp192_precompute_chud; + fpg->precompute_jac = &ecfp192_precompute_jac; + + group->point_mul = &ec_GFp_point_mul_wNAF_fp; + group->points_mul = &ec_pts_mul_basic; + group->extra1 = fpg; + group->extra_free = &ec_GFp_extra_free_fp; + + ec_set_fp_precision(fpg); + fpg->bitSize_alpha = ECFP_TWO192 * fpg->alpha[0]; + + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_fp224.c b/nss/lib/freebl/ecl/ecp_fp224.c new file mode 100644 index 0000000..71b6a6d --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_fp224.c @@ -0,0 +1,156 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp_fp.h" +#include <stdlib.h> + +#define ECFP_BSIZE 224 +#define ECFP_NUMDOUBLES 10 + +#include "ecp_fpinc.c" + +/* Performs a single step of reduction, just on the uppermost float + * (assumes already tidied), and then retidies. Note, this does not + * guarantee that the result will be less than p. */ +void +ecfp224_singleReduce(double *r, const EC_group_fp * group) +{ + double q; + + ECFP_ASSERT(group->doubleBitSize == 24); + ECFP_ASSERT(group->primeBitSize == 224); + ECFP_ASSERT(group->numDoubles == 10); + + q = r[ECFP_NUMDOUBLES - 1] - ecfp_beta_224; + q += group->bitSize_alpha; + q -= group->bitSize_alpha; + + r[ECFP_NUMDOUBLES - 1] -= q; + r[0] -= q * ecfp_twom224; + r[4] += q * ecfp_twom128; + + ecfp_positiveTidy(r, group); +} + +/* + * Performs imperfect reduction. This might leave some negative terms, + * and one more reduction might be required for the result to be between 0 + * and p-1. x should be be an array of at least 20, and r at least 10 x + * and r can be the same, but then the upper parts of r are not zeroed */ +void +ecfp224_reduce(double *r, double *x, const EC_group_fp * group) +{ + + double x10, x11, x12, x13, x14, q; + + ECFP_ASSERT(group->doubleBitSize == 24); + ECFP_ASSERT(group->primeBitSize == 224); + ECFP_ASSERT(group->numDoubles == 10); + + /* Tidy just the upper bits of x. Don't need to tidy the lower ones + * yet. */ + ecfp_tidyUpper(x, group); + + x10 = x[10] + x[16] * ecfp_twom128; + x11 = x[11] + x[17] * ecfp_twom128; + x12 = x[12] + x[18] * ecfp_twom128; + x13 = x[13] + x[19] * ecfp_twom128; + + /* Tidy up, or we won't have enough bits later to add it in */ + q = x10 + group->alpha[11]; + q -= group->alpha[11]; + x10 -= q; + x11 = x11 + q; + + q = x11 + group->alpha[12]; + q -= group->alpha[12]; + x11 -= q; + x12 = x12 + q; + + q = x12 + group->alpha[13]; + q -= group->alpha[13]; + x12 -= q; + x13 = x13 + q; + + q = x13 + group->alpha[14]; + q -= group->alpha[14]; + x13 -= q; + x14 = x[14] + q; + + r[9] = x[9] + x[15] * ecfp_twom128 - x[19] * ecfp_twom224; + r[8] = x[8] + x14 * ecfp_twom128 - x[18] * ecfp_twom224; + r[7] = x[7] + x13 * ecfp_twom128 - x[17] * ecfp_twom224; + r[6] = x[6] + x12 * ecfp_twom128 - x[16] * ecfp_twom224; + r[5] = x[5] + x11 * ecfp_twom128 - x[15] * ecfp_twom224; + r[4] = x[4] + x10 * ecfp_twom128 - x14 * ecfp_twom224; + r[3] = x[3] - x13 * ecfp_twom224; + r[2] = x[2] - x12 * ecfp_twom224; + r[1] = x[1] - x11 * ecfp_twom224; + r[0] = x[0] - x10 * ecfp_twom224; + + /* + * Tidy up just r[ECFP_NUMDOUBLES-2] so that the number of reductions + * is accurate plus or minus one. (Rather than tidy all to make it + * totally accurate) */ + q = r[ECFP_NUMDOUBLES - 2] + group->alpha[ECFP_NUMDOUBLES - 1]; + q -= group->alpha[ECFP_NUMDOUBLES - 1]; + r[ECFP_NUMDOUBLES - 2] -= q; + r[ECFP_NUMDOUBLES - 1] += q; + + /* Tidy up the excess bits on r[ECFP_NUMDOUBLES-1] using reduction */ + /* Use ecfp_beta so we get a positive res */ + q = r[ECFP_NUMDOUBLES - 1] - ecfp_beta_224; + q += group->bitSize_alpha; + q -= group->bitSize_alpha; + + r[ECFP_NUMDOUBLES - 1] -= q; + r[0] -= q * ecfp_twom224; + r[4] += q * ecfp_twom128; + + ecfp_tidyShort(r, group); +} + +/* Sets group to use optimized calculations in this file */ +mp_err +ec_group_set_nistp224_fp(ECGroup *group) +{ + + EC_group_fp *fpg; + + /* Allocate memory for floating point group data */ + fpg = (EC_group_fp *) malloc(sizeof(EC_group_fp)); + if (fpg == NULL) { + return MP_MEM; + } + + fpg->numDoubles = ECFP_NUMDOUBLES; + fpg->primeBitSize = ECFP_BSIZE; + fpg->orderBitSize = 224; + fpg->doubleBitSize = 24; + fpg->numInts = (ECFP_BSIZE + ECL_BITS - 1) / ECL_BITS; + fpg->aIsM3 = 1; + fpg->ecfp_singleReduce = &ecfp224_singleReduce; + fpg->ecfp_reduce = &ecfp224_reduce; + fpg->ecfp_tidy = &ecfp_tidy; + + fpg->pt_add_jac_aff = &ecfp224_pt_add_jac_aff; + fpg->pt_add_jac = &ecfp224_pt_add_jac; + fpg->pt_add_jm_chud = &ecfp224_pt_add_jm_chud; + fpg->pt_add_chud = &ecfp224_pt_add_chud; + fpg->pt_dbl_jac = &ecfp224_pt_dbl_jac; + fpg->pt_dbl_jm = &ecfp224_pt_dbl_jm; + fpg->pt_dbl_aff2chud = &ecfp224_pt_dbl_aff2chud; + fpg->precompute_chud = &ecfp224_precompute_chud; + fpg->precompute_jac = &ecfp224_precompute_jac; + + group->point_mul = &ec_GFp_point_mul_wNAF_fp; + group->points_mul = &ec_pts_mul_basic; + group->extra1 = fpg; + group->extra_free = &ec_GFp_extra_free_fp; + + ec_set_fp_precision(fpg); + fpg->bitSize_alpha = ECFP_TWO224 * fpg->alpha[0]; + + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/ecp_fpinc.c b/nss/lib/freebl/ecl/ecp_fpinc.c new file mode 100644 index 0000000..be0f966 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_fpinc.c @@ -0,0 +1,821 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* This source file is meant to be included by other source files + * (ecp_fp###.c, where ### is one of 160, 192, 224) and should not + * constitute an independent compilation unit. It requires the following + * preprocessor definitions be made: ECFP_BSIZE - the number of bits in + * the field's prime + * ECFP_NUMDOUBLES - the number of doubles to store one + * multi-precision integer in floating point + +/* Adds a prefix to a given token to give a unique token name. Prefixes + * with "ecfp" + ECFP_BSIZE + "_". e.g. if ECFP_BSIZE = 160, then + * PREFIX(hello) = ecfp160_hello This optimization allows static function + * linking and compiler loop unrolling without code duplication. */ +#ifndef PREFIX +#define PREFIX(b) PREFIX1(ECFP_BSIZE, b) +#define PREFIX1(bsize, b) PREFIX2(bsize, b) +#define PREFIX2(bsize, b) ecfp ## bsize ## _ ## b +#endif + +/* Returns true iff every double in d is 0. (If d == 0 and it is tidied, + * this will be true.) */ +mp_err PREFIX(isZero) (const double *d) { + int i; + + for (i = 0; i < ECFP_NUMDOUBLES; i++) { + if (d[i] != 0) + return MP_NO; + } + return MP_YES; +} + +/* Sets the multi-precision floating point number at t = 0 */ +void PREFIX(zero) (double *t) { + int i; + + for (i = 0; i < ECFP_NUMDOUBLES; i++) { + t[i] = 0; + } +} + +/* Sets the multi-precision floating point number at t = 1 */ +void PREFIX(one) (double *t) { + int i; + + t[0] = 1; + for (i = 1; i < ECFP_NUMDOUBLES; i++) { + t[i] = 0; + } +} + +/* Checks if point P(x, y, z) is at infinity. Uses Jacobian coordinates. */ +mp_err PREFIX(pt_is_inf_jac) (const ecfp_jac_pt * p) { + return PREFIX(isZero) (p->z); +} + +/* Sets the Jacobian point P to be at infinity. */ +void PREFIX(set_pt_inf_jac) (ecfp_jac_pt * p) { + PREFIX(zero) (p->z); +} + +/* Checks if point P(x, y) is at infinity. Uses Affine coordinates. */ +mp_err PREFIX(pt_is_inf_aff) (const ecfp_aff_pt * p) { + if (PREFIX(isZero) (p->x) == MP_YES && PREFIX(isZero) (p->y) == MP_YES) + return MP_YES; + return MP_NO; +} + +/* Sets the affine point P to be at infinity. */ +void PREFIX(set_pt_inf_aff) (ecfp_aff_pt * p) { + PREFIX(zero) (p->x); + PREFIX(zero) (p->y); +} + +/* Checks if point P(x, y, z, a*z^4) is at infinity. Uses Modified + * Jacobian coordinates. */ +mp_err PREFIX(pt_is_inf_jm) (const ecfp_jm_pt * p) { + return PREFIX(isZero) (p->z); +} + +/* Sets the Modified Jacobian point P to be at infinity. */ +void PREFIX(set_pt_inf_jm) (ecfp_jm_pt * p) { + PREFIX(zero) (p->z); +} + +/* Checks if point P(x, y, z, z^2, z^3) is at infinity. Uses Chudnovsky + * Jacobian coordinates */ +mp_err PREFIX(pt_is_inf_chud) (const ecfp_chud_pt * p) { + return PREFIX(isZero) (p->z); +} + +/* Sets the Chudnovsky Jacobian point P to be at infinity. */ +void PREFIX(set_pt_inf_chud) (ecfp_chud_pt * p) { + PREFIX(zero) (p->z); +} + +/* Copies a multi-precision floating point number, Setting dest = src */ +void PREFIX(copy) (double *dest, const double *src) { + int i; + + for (i = 0; i < ECFP_NUMDOUBLES; i++) { + dest[i] = src[i]; + } +} + +/* Sets dest = -src */ +void PREFIX(negLong) (double *dest, const double *src) { + int i; + + for (i = 0; i < 2 * ECFP_NUMDOUBLES; i++) { + dest[i] = -src[i]; + } +} + +/* Sets r = -p p = (x, y, z, z2, z3) r = (x, -y, z, z2, z3) Uses + * Chudnovsky Jacobian coordinates. */ +/* TODO reverse order */ +void PREFIX(pt_neg_chud) (const ecfp_chud_pt * p, ecfp_chud_pt * r) { + int i; + + PREFIX(copy) (r->x, p->x); + PREFIX(copy) (r->z, p->z); + PREFIX(copy) (r->z2, p->z2); + PREFIX(copy) (r->z3, p->z3); + for (i = 0; i < ECFP_NUMDOUBLES; i++) { + r->y[i] = -p->y[i]; + } +} + +/* Computes r = x + y. Does not tidy or reduce. Any combinations of r, x, + * y can point to the same data. Componentwise adds first ECFP_NUMDOUBLES + * doubles of x and y and stores the result in r. */ +void PREFIX(addShort) (double *r, const double *x, const double *y) { + int i; + + for (i = 0; i < ECFP_NUMDOUBLES; i++) { + *r++ = *x++ + *y++; + } +} + +/* Computes r = x + y. Does not tidy or reduce. Any combinations of r, x, + * y can point to the same data. Componentwise adds first + * 2*ECFP_NUMDOUBLES doubles of x and y and stores the result in r. */ +void PREFIX(addLong) (double *r, const double *x, const double *y) { + int i; + + for (i = 0; i < 2 * ECFP_NUMDOUBLES; i++) { + *r++ = *x++ + *y++; + } +} + +/* Computes r = x - y. Does not tidy or reduce. Any combinations of r, x, + * y can point to the same data. Componentwise subtracts first + * ECFP_NUMDOUBLES doubles of x and y and stores the result in r. */ +void PREFIX(subtractShort) (double *r, const double *x, const double *y) { + int i; + + for (i = 0; i < ECFP_NUMDOUBLES; i++) { + *r++ = *x++ - *y++; + } +} + +/* Computes r = x - y. Does not tidy or reduce. Any combinations of r, x, + * y can point to the same data. Componentwise subtracts first + * 2*ECFP_NUMDOUBLES doubles of x and y and stores the result in r. */ +void PREFIX(subtractLong) (double *r, const double *x, const double *y) { + int i; + + for (i = 0; i < 2 * ECFP_NUMDOUBLES; i++) { + *r++ = *x++ - *y++; + } +} + +/* Computes r = x*y. Both x and y should be tidied and reduced, + * r must be different (point to different memory) than x and y. + * Does not tidy or reduce. */ +void PREFIX(multiply)(double *r, const double *x, const double *y) { + int i, j; + + for(j=0;j<ECFP_NUMDOUBLES-1;j++) { + r[j] = x[0] * y[j]; + r[j+(ECFP_NUMDOUBLES-1)] = x[ECFP_NUMDOUBLES-1] * y[j]; + } + r[ECFP_NUMDOUBLES-1] = x[0] * y[ECFP_NUMDOUBLES-1]; + r[ECFP_NUMDOUBLES-1] += x[ECFP_NUMDOUBLES-1] * y[0]; + r[2*ECFP_NUMDOUBLES-2] = x[ECFP_NUMDOUBLES-1] * y[ECFP_NUMDOUBLES-1]; + r[2*ECFP_NUMDOUBLES-1] = 0; + + for(i=1;i<ECFP_NUMDOUBLES-1;i++) { + for(j=0;j<ECFP_NUMDOUBLES;j++) { + r[i+j] += (x[i] * y[j]); + } + } +} + +/* Computes the square of x and stores the result in r. x should be + * tidied & reduced, r will be neither tidied nor reduced. + * r should point to different memory than x */ +void PREFIX(square) (double *r, const double *x) { + PREFIX(multiply) (r, x, x); +} + +/* Perform a point doubling in Jacobian coordinates. Input and output + * should be multi-precision floating point integers. */ +void PREFIX(pt_dbl_jac) (const ecfp_jac_pt * dp, ecfp_jac_pt * dr, + const EC_group_fp * group) { + double t0[2 * ECFP_NUMDOUBLES], t1[2 * ECFP_NUMDOUBLES], + M[2 * ECFP_NUMDOUBLES], S[2 * ECFP_NUMDOUBLES]; + + /* Check for point at infinity */ + if (PREFIX(pt_is_inf_jac) (dp) == MP_YES) { + /* Set r = pt at infinity */ + PREFIX(set_pt_inf_jac) (dr); + goto CLEANUP; + } + + /* Perform typical point doubling operations */ + + /* TODO? is it worthwhile to do optimizations for when pz = 1? */ + + if (group->aIsM3) { + /* When a = -3, M = 3(px - pz^2)(px + pz^2) */ + PREFIX(square) (t1, dp->z); + group->ecfp_reduce(t1, t1, group); /* 2^23 since the negative + * rounding buys another bit */ + PREFIX(addShort) (t0, dp->x, t1); /* 2*2^23 */ + PREFIX(subtractShort) (t1, dp->x, t1); /* 2 * 2^23 */ + PREFIX(multiply) (M, t0, t1); /* 40 * 2^46 */ + PREFIX(addLong) (t0, M, M); /* 80 * 2^46 */ + PREFIX(addLong) (M, t0, M); /* 120 * 2^46 < 2^53 */ + group->ecfp_reduce(M, M, group); + } else { + /* Generic case */ + /* M = 3 (px^2) + a*(pz^4) */ + PREFIX(square) (t0, dp->x); + PREFIX(addLong) (M, t0, t0); + PREFIX(addLong) (t0, t0, M); /* t0 = 3(px^2) */ + PREFIX(square) (M, dp->z); + group->ecfp_reduce(M, M, group); + PREFIX(square) (t1, M); + group->ecfp_reduce(t1, t1, group); + PREFIX(multiply) (M, t1, group->curvea); /* M = a(pz^4) */ + PREFIX(addLong) (M, M, t0); + group->ecfp_reduce(M, M, group); + } + + /* rz = 2 * py * pz */ + PREFIX(multiply) (t1, dp->y, dp->z); + PREFIX(addLong) (t1, t1, t1); + group->ecfp_reduce(dr->z, t1, group); + + /* t0 = 2y^2 */ + PREFIX(square) (t0, dp->y); + group->ecfp_reduce(t0, t0, group); + PREFIX(addShort) (t0, t0, t0); + + /* S = 4 * px * py^2 = 2 * px * t0 */ + PREFIX(multiply) (S, dp->x, t0); + PREFIX(addLong) (S, S, S); + group->ecfp_reduce(S, S, group); + + /* rx = M^2 - 2 * S */ + PREFIX(square) (t1, M); + PREFIX(subtractShort) (t1, t1, S); + PREFIX(subtractShort) (t1, t1, S); + group->ecfp_reduce(dr->x, t1, group); + + /* ry = M * (S - rx) - 8 * py^4 */ + PREFIX(square) (t1, t0); /* t1 = 4y^4 */ + PREFIX(subtractShort) (S, S, dr->x); + PREFIX(multiply) (t0, M, S); + PREFIX(subtractLong) (t0, t0, t1); + PREFIX(subtractLong) (t0, t0, t1); + group->ecfp_reduce(dr->y, t0, group); + + CLEANUP: + return; +} + +/* Perform a point addition using coordinate system Jacobian + Affine -> + * Jacobian. Input and output should be multi-precision floating point + * integers. */ +void PREFIX(pt_add_jac_aff) (const ecfp_jac_pt * p, const ecfp_aff_pt * q, + ecfp_jac_pt * r, const EC_group_fp * group) { + /* Temporary storage */ + double A[2 * ECFP_NUMDOUBLES], B[2 * ECFP_NUMDOUBLES], + C[2 * ECFP_NUMDOUBLES], C2[2 * ECFP_NUMDOUBLES], + D[2 * ECFP_NUMDOUBLES], C3[2 * ECFP_NUMDOUBLES]; + + /* Check for point at infinity for p or q */ + if (PREFIX(pt_is_inf_aff) (q) == MP_YES) { + PREFIX(copy) (r->x, p->x); + PREFIX(copy) (r->y, p->y); + PREFIX(copy) (r->z, p->z); + goto CLEANUP; + } else if (PREFIX(pt_is_inf_jac) (p) == MP_YES) { + PREFIX(copy) (r->x, q->x); + PREFIX(copy) (r->y, q->y); + /* Since the affine point is not infinity, we can set r->z = 1 */ + PREFIX(one) (r->z); + goto CLEANUP; + } + + /* Calculates c = qx * pz^2 - px d = (qy * b - py) rx = d^2 - c^3 + 2 + * (px * c^2) ry = d * (c-rx) - py*c^3 rz = c * pz */ + + /* A = pz^2, B = pz^3 */ + PREFIX(square) (A, p->z); + group->ecfp_reduce(A, A, group); + PREFIX(multiply) (B, A, p->z); + group->ecfp_reduce(B, B, group); + + /* C = qx * A - px */ + PREFIX(multiply) (C, q->x, A); + PREFIX(subtractShort) (C, C, p->x); + group->ecfp_reduce(C, C, group); + + /* D = qy * B - py */ + PREFIX(multiply) (D, q->y, B); + PREFIX(subtractShort) (D, D, p->y); + group->ecfp_reduce(D, D, group); + + /* C2 = C^2, C3 = C^3 */ + PREFIX(square) (C2, C); + group->ecfp_reduce(C2, C2, group); + PREFIX(multiply) (C3, C2, C); + group->ecfp_reduce(C3, C3, group); + + /* rz = A = pz * C */ + PREFIX(multiply) (A, p->z, C); + group->ecfp_reduce(r->z, A, group); + + /* C = px * C^2, untidied, unreduced */ + PREFIX(multiply) (C, p->x, C2); + + /* A = D^2, untidied, unreduced */ + PREFIX(square) (A, D); + + /* rx = B = A - C3 - C - C = D^2 - (C^3 + 2 * (px * C^2) */ + PREFIX(subtractShort) (A, A, C3); + PREFIX(subtractLong) (A, A, C); + PREFIX(subtractLong) (A, A, C); + group->ecfp_reduce(r->x, A, group); + + /* B = py * C3, untidied, unreduced */ + PREFIX(multiply) (B, p->y, C3); + + /* C = px * C^2 - rx */ + PREFIX(subtractShort) (C, C, r->x); + group->ecfp_reduce(C, C, group); + + /* ry = A = D * C - py * C^3 */ + PREFIX(multiply) (A, D, C); + PREFIX(subtractLong) (A, A, B); + group->ecfp_reduce(r->y, A, group); + + CLEANUP: + return; +} + +/* Perform a point addition using Jacobian coordinate system. Input and + * output should be multi-precision floating point integers. */ +void PREFIX(pt_add_jac) (const ecfp_jac_pt * p, const ecfp_jac_pt * q, + ecfp_jac_pt * r, const EC_group_fp * group) { + + /* Temporary Storage */ + double t0[2 * ECFP_NUMDOUBLES], t1[2 * ECFP_NUMDOUBLES], + U[2 * ECFP_NUMDOUBLES], R[2 * ECFP_NUMDOUBLES], + S[2 * ECFP_NUMDOUBLES], H[2 * ECFP_NUMDOUBLES], + H3[2 * ECFP_NUMDOUBLES]; + + /* Check for point at infinity for p, if so set r = q */ + if (PREFIX(pt_is_inf_jac) (p) == MP_YES) { + PREFIX(copy) (r->x, q->x); + PREFIX(copy) (r->y, q->y); + PREFIX(copy) (r->z, q->z); + goto CLEANUP; + } + + /* Check for point at infinity for p, if so set r = q */ + if (PREFIX(pt_is_inf_jac) (q) == MP_YES) { + PREFIX(copy) (r->x, p->x); + PREFIX(copy) (r->y, p->y); + PREFIX(copy) (r->z, p->z); + goto CLEANUP; + } + + /* U = px * qz^2 , S = py * qz^3 */ + PREFIX(square) (t0, q->z); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (U, p->x, t0); + group->ecfp_reduce(U, U, group); + PREFIX(multiply) (t1, t0, q->z); + group->ecfp_reduce(t1, t1, group); + PREFIX(multiply) (t0, p->y, t1); + group->ecfp_reduce(S, t0, group); + + /* H = qx*(pz)^2 - U , R = (qy * pz^3 - S) */ + PREFIX(square) (t0, p->z); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (H, q->x, t0); + PREFIX(subtractShort) (H, H, U); + group->ecfp_reduce(H, H, group); + PREFIX(multiply) (t1, t0, p->z); /* t1 = pz^3 */ + group->ecfp_reduce(t1, t1, group); + PREFIX(multiply) (t0, t1, q->y); /* t0 = qy * pz^3 */ + PREFIX(subtractShort) (t0, t0, S); + group->ecfp_reduce(R, t0, group); + + /* U = U*H^2, H3 = H^3 */ + PREFIX(square) (t0, H); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (t1, U, t0); + group->ecfp_reduce(U, t1, group); + PREFIX(multiply) (H3, t0, H); + group->ecfp_reduce(H3, H3, group); + + /* rz = pz * qz * H */ + PREFIX(multiply) (t0, q->z, H); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (t1, t0, p->z); + group->ecfp_reduce(r->z, t1, group); + + /* rx = R^2 - H^3 - 2 * U */ + PREFIX(square) (t0, R); + PREFIX(subtractShort) (t0, t0, H3); + PREFIX(subtractShort) (t0, t0, U); + PREFIX(subtractShort) (t0, t0, U); + group->ecfp_reduce(r->x, t0, group); + + /* ry = R(U - rx) - S*H3 */ + PREFIX(subtractShort) (t1, U, r->x); + PREFIX(multiply) (t0, t1, R); + PREFIX(multiply) (t1, S, H3); + PREFIX(subtractLong) (t1, t0, t1); + group->ecfp_reduce(r->y, t1, group); + + CLEANUP: + return; +} + +/* Perform a point doubling in Modified Jacobian coordinates. Input and + * output should be multi-precision floating point integers. */ +void PREFIX(pt_dbl_jm) (const ecfp_jm_pt * p, ecfp_jm_pt * r, + const EC_group_fp * group) { + + /* Temporary storage */ + double t0[2 * ECFP_NUMDOUBLES], t1[2 * ECFP_NUMDOUBLES], + M[2 * ECFP_NUMDOUBLES], S[2 * ECFP_NUMDOUBLES], + U[2 * ECFP_NUMDOUBLES], T[2 * ECFP_NUMDOUBLES]; + + /* Check for point at infinity */ + if (PREFIX(pt_is_inf_jm) (p) == MP_YES) { + /* Set r = pt at infinity by setting rz = 0 */ + PREFIX(set_pt_inf_jm) (r); + goto CLEANUP; + } + + /* M = 3 (px^2) + a*(pz^4) */ + PREFIX(square) (t0, p->x); + PREFIX(addLong) (M, t0, t0); + PREFIX(addLong) (t0, t0, M); /* t0 = 3(px^2) */ + PREFIX(addShort) (t0, t0, p->az4); + group->ecfp_reduce(M, t0, group); + + /* rz = 2 * py * pz */ + PREFIX(multiply) (t1, p->y, p->z); + PREFIX(addLong) (t1, t1, t1); + group->ecfp_reduce(r->z, t1, group); + + /* t0 = 2y^2, U = 8y^4 */ + PREFIX(square) (t0, p->y); + group->ecfp_reduce(t0, t0, group); + PREFIX(addShort) (t0, t0, t0); + PREFIX(square) (U, t0); + group->ecfp_reduce(U, U, group); + PREFIX(addShort) (U, U, U); + + /* S = 4 * px * py^2 = 2 * px * t0 */ + PREFIX(multiply) (S, p->x, t0); + group->ecfp_reduce(S, S, group); + PREFIX(addShort) (S, S, S); + + /* rx = M^2 - 2S */ + PREFIX(square) (T, M); + PREFIX(subtractShort) (T, T, S); + PREFIX(subtractShort) (T, T, S); + group->ecfp_reduce(r->x, T, group); + + /* ry = M * (S - rx) - U */ + PREFIX(subtractShort) (S, S, r->x); + PREFIX(multiply) (t0, M, S); + PREFIX(subtractShort) (t0, t0, U); + group->ecfp_reduce(r->y, t0, group); + + /* ra*z^4 = 2*U*(apz4) */ + PREFIX(multiply) (t1, U, p->az4); + PREFIX(addLong) (t1, t1, t1); + group->ecfp_reduce(r->az4, t1, group); + + CLEANUP: + return; +} + +/* Perform a point doubling using coordinates Affine -> Chudnovsky + * Jacobian. Input and output should be multi-precision floating point + * integers. */ +void PREFIX(pt_dbl_aff2chud) (const ecfp_aff_pt * p, ecfp_chud_pt * r, + const EC_group_fp * group) { + double t0[2 * ECFP_NUMDOUBLES], t1[2 * ECFP_NUMDOUBLES], + M[2 * ECFP_NUMDOUBLES], twoY2[2 * ECFP_NUMDOUBLES], + S[2 * ECFP_NUMDOUBLES]; + + /* Check for point at infinity for p, if so set r = O */ + if (PREFIX(pt_is_inf_aff) (p) == MP_YES) { + PREFIX(set_pt_inf_chud) (r); + goto CLEANUP; + } + + /* M = 3(px)^2 + a */ + PREFIX(square) (t0, p->x); + PREFIX(addLong) (t1, t0, t0); + PREFIX(addLong) (t1, t1, t0); + PREFIX(addShort) (t1, t1, group->curvea); + group->ecfp_reduce(M, t1, group); + + /* twoY2 = 2*(py)^2, S = 4(px)(py)^2 */ + PREFIX(square) (twoY2, p->y); + PREFIX(addLong) (twoY2, twoY2, twoY2); + group->ecfp_reduce(twoY2, twoY2, group); + PREFIX(multiply) (S, p->x, twoY2); + PREFIX(addLong) (S, S, S); + group->ecfp_reduce(S, S, group); + + /* rx = M^2 - 2S */ + PREFIX(square) (t0, M); + PREFIX(subtractShort) (t0, t0, S); + PREFIX(subtractShort) (t0, t0, S); + group->ecfp_reduce(r->x, t0, group); + + /* ry = M(S-rx) - 8y^4 */ + PREFIX(subtractShort) (t0, S, r->x); + PREFIX(multiply) (t1, t0, M); + PREFIX(square) (t0, twoY2); + PREFIX(subtractLong) (t1, t1, t0); + PREFIX(subtractLong) (t1, t1, t0); + group->ecfp_reduce(r->y, t1, group); + + /* rz = 2py */ + PREFIX(addShort) (r->z, p->y, p->y); + + /* rz2 = rz^2 */ + PREFIX(square) (t0, r->z); + group->ecfp_reduce(r->z2, t0, group); + + /* rz3 = rz^3 */ + PREFIX(multiply) (t0, r->z, r->z2); + group->ecfp_reduce(r->z3, t0, group); + + CLEANUP: + return; +} + +/* Perform a point addition using coordinates: Modified Jacobian + + * Chudnovsky Jacobian -> Modified Jacobian. Input and output should be + * multi-precision floating point integers. */ +void PREFIX(pt_add_jm_chud) (ecfp_jm_pt * p, ecfp_chud_pt * q, + ecfp_jm_pt * r, const EC_group_fp * group) { + + double t0[2 * ECFP_NUMDOUBLES], t1[2 * ECFP_NUMDOUBLES], + U[2 * ECFP_NUMDOUBLES], R[2 * ECFP_NUMDOUBLES], + S[2 * ECFP_NUMDOUBLES], H[2 * ECFP_NUMDOUBLES], + H3[2 * ECFP_NUMDOUBLES], pz2[2 * ECFP_NUMDOUBLES]; + + /* Check for point at infinity for p, if so set r = q need to convert + * from Chudnovsky form to Modified Jacobian form */ + if (PREFIX(pt_is_inf_jm) (p) == MP_YES) { + PREFIX(copy) (r->x, q->x); + PREFIX(copy) (r->y, q->y); + PREFIX(copy) (r->z, q->z); + PREFIX(square) (t0, q->z2); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (t1, t0, group->curvea); + group->ecfp_reduce(r->az4, t1, group); + goto CLEANUP; + } + /* Check for point at infinity for q, if so set r = p */ + if (PREFIX(pt_is_inf_chud) (q) == MP_YES) { + PREFIX(copy) (r->x, p->x); + PREFIX(copy) (r->y, p->y); + PREFIX(copy) (r->z, p->z); + PREFIX(copy) (r->az4, p->az4); + goto CLEANUP; + } + + /* U = px * qz^2 */ + PREFIX(multiply) (U, p->x, q->z2); + group->ecfp_reduce(U, U, group); + + /* H = qx*(pz)^2 - U */ + PREFIX(square) (t0, p->z); + group->ecfp_reduce(pz2, t0, group); + PREFIX(multiply) (H, pz2, q->x); + group->ecfp_reduce(H, H, group); + PREFIX(subtractShort) (H, H, U); + + /* U = U*H^2, H3 = H^3 */ + PREFIX(square) (t0, H); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (t1, U, t0); + group->ecfp_reduce(U, t1, group); + PREFIX(multiply) (H3, t0, H); + group->ecfp_reduce(H3, H3, group); + + /* S = py * qz^3 */ + PREFIX(multiply) (S, p->y, q->z3); + group->ecfp_reduce(S, S, group); + + /* R = (qy * z1^3 - s) */ + PREFIX(multiply) (t0, pz2, p->z); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (R, t0, q->y); + PREFIX(subtractShort) (R, R, S); + group->ecfp_reduce(R, R, group); + + /* rz = pz * qz * H */ + PREFIX(multiply) (t1, q->z, H); + group->ecfp_reduce(t1, t1, group); + PREFIX(multiply) (t0, p->z, t1); + group->ecfp_reduce(r->z, t0, group); + + /* rx = R^2 - H^3 - 2 * U */ + PREFIX(square) (t0, R); + PREFIX(subtractShort) (t0, t0, H3); + PREFIX(subtractShort) (t0, t0, U); + PREFIX(subtractShort) (t0, t0, U); + group->ecfp_reduce(r->x, t0, group); + + /* ry = R(U - rx) - S*H3 */ + PREFIX(subtractShort) (t1, U, r->x); + PREFIX(multiply) (t0, t1, R); + PREFIX(multiply) (t1, S, H3); + PREFIX(subtractLong) (t1, t0, t1); + group->ecfp_reduce(r->y, t1, group); + + if (group->aIsM3) { /* a == -3 */ + /* a(rz^4) = -3 * ((rz^2)^2) */ + PREFIX(square) (t0, r->z); + group->ecfp_reduce(t0, t0, group); + PREFIX(square) (t1, t0); + PREFIX(addLong) (t0, t1, t1); + PREFIX(addLong) (t0, t0, t1); + PREFIX(negLong) (t0, t0); + group->ecfp_reduce(r->az4, t0, group); + } else { /* Generic case */ + /* a(rz^4) = a * ((rz^2)^2) */ + PREFIX(square) (t0, r->z); + group->ecfp_reduce(t0, t0, group); + PREFIX(square) (t1, t0); + group->ecfp_reduce(t1, t1, group); + PREFIX(multiply) (t0, group->curvea, t1); + group->ecfp_reduce(r->az4, t0, group); + } + CLEANUP: + return; +} + +/* Perform a point addition using Chudnovsky Jacobian coordinates. Input + * and output should be multi-precision floating point integers. */ +void PREFIX(pt_add_chud) (const ecfp_chud_pt * p, const ecfp_chud_pt * q, + ecfp_chud_pt * r, const EC_group_fp * group) { + + /* Temporary Storage */ + double t0[2 * ECFP_NUMDOUBLES], t1[2 * ECFP_NUMDOUBLES], + U[2 * ECFP_NUMDOUBLES], R[2 * ECFP_NUMDOUBLES], + S[2 * ECFP_NUMDOUBLES], H[2 * ECFP_NUMDOUBLES], + H3[2 * ECFP_NUMDOUBLES]; + + /* Check for point at infinity for p, if so set r = q */ + if (PREFIX(pt_is_inf_chud) (p) == MP_YES) { + PREFIX(copy) (r->x, q->x); + PREFIX(copy) (r->y, q->y); + PREFIX(copy) (r->z, q->z); + PREFIX(copy) (r->z2, q->z2); + PREFIX(copy) (r->z3, q->z3); + goto CLEANUP; + } + + /* Check for point at infinity for p, if so set r = q */ + if (PREFIX(pt_is_inf_chud) (q) == MP_YES) { + PREFIX(copy) (r->x, p->x); + PREFIX(copy) (r->y, p->y); + PREFIX(copy) (r->z, p->z); + PREFIX(copy) (r->z2, p->z2); + PREFIX(copy) (r->z3, p->z3); + goto CLEANUP; + } + + /* U = px * qz^2 */ + PREFIX(multiply) (U, p->x, q->z2); + group->ecfp_reduce(U, U, group); + + /* H = qx*(pz)^2 - U */ + PREFIX(multiply) (H, q->x, p->z2); + PREFIX(subtractShort) (H, H, U); + group->ecfp_reduce(H, H, group); + + /* U = U*H^2, H3 = H^3 */ + PREFIX(square) (t0, H); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (t1, U, t0); + group->ecfp_reduce(U, t1, group); + PREFIX(multiply) (H3, t0, H); + group->ecfp_reduce(H3, H3, group); + + /* S = py * qz^3 */ + PREFIX(multiply) (S, p->y, q->z3); + group->ecfp_reduce(S, S, group); + + /* rz = pz * qz * H */ + PREFIX(multiply) (t0, q->z, H); + group->ecfp_reduce(t0, t0, group); + PREFIX(multiply) (t1, t0, p->z); + group->ecfp_reduce(r->z, t1, group); + + /* R = (qy * z1^3 - s) */ + PREFIX(multiply) (t0, q->y, p->z3); + PREFIX(subtractShort) (t0, t0, S); + group->ecfp_reduce(R, t0, group); + + /* rx = R^2 - H^3 - 2 * U */ + PREFIX(square) (t0, R); + PREFIX(subtractShort) (t0, t0, H3); + PREFIX(subtractShort) (t0, t0, U); + PREFIX(subtractShort) (t0, t0, U); + group->ecfp_reduce(r->x, t0, group); + + /* ry = R(U - rx) - S*H3 */ + PREFIX(subtractShort) (t1, U, r->x); + PREFIX(multiply) (t0, t1, R); + PREFIX(multiply) (t1, S, H3); + PREFIX(subtractLong) (t1, t0, t1); + group->ecfp_reduce(r->y, t1, group); + + /* rz2 = rz^2 */ + PREFIX(square) (t0, r->z); + group->ecfp_reduce(r->z2, t0, group); + + /* rz3 = rz^3 */ + PREFIX(multiply) (t0, r->z, r->z2); + group->ecfp_reduce(r->z3, t0, group); + + CLEANUP: + return; +} + +/* Expects out to be an array of size 16 of Chudnovsky Jacobian points. + * Fills in Chudnovsky Jacobian form (x, y, z, z^2, z^3), for -15P, -13P, + * -11P, -9P, -7P, -5P, -3P, -P, P, 3P, 5P, 7P, 9P, 11P, 13P, 15P */ +void PREFIX(precompute_chud) (ecfp_chud_pt * out, const ecfp_aff_pt * p, + const EC_group_fp * group) { + + ecfp_chud_pt p2; + + /* Set out[8] = P */ + PREFIX(copy) (out[8].x, p->x); + PREFIX(copy) (out[8].y, p->y); + PREFIX(one) (out[8].z); + PREFIX(one) (out[8].z2); + PREFIX(one) (out[8].z3); + + /* Set p2 = 2P */ + PREFIX(pt_dbl_aff2chud) (p, &p2, group); + + /* Set 3P, 5P, ..., 15P */ + PREFIX(pt_add_chud) (&out[8], &p2, &out[9], group); + PREFIX(pt_add_chud) (&out[9], &p2, &out[10], group); + PREFIX(pt_add_chud) (&out[10], &p2, &out[11], group); + PREFIX(pt_add_chud) (&out[11], &p2, &out[12], group); + PREFIX(pt_add_chud) (&out[12], &p2, &out[13], group); + PREFIX(pt_add_chud) (&out[13], &p2, &out[14], group); + PREFIX(pt_add_chud) (&out[14], &p2, &out[15], group); + + /* Set -15P, -13P, ..., -P */ + PREFIX(pt_neg_chud) (&out[8], &out[7]); + PREFIX(pt_neg_chud) (&out[9], &out[6]); + PREFIX(pt_neg_chud) (&out[10], &out[5]); + PREFIX(pt_neg_chud) (&out[11], &out[4]); + PREFIX(pt_neg_chud) (&out[12], &out[3]); + PREFIX(pt_neg_chud) (&out[13], &out[2]); + PREFIX(pt_neg_chud) (&out[14], &out[1]); + PREFIX(pt_neg_chud) (&out[15], &out[0]); +} + +/* Expects out to be an array of size 16 of Jacobian points. Fills in + * Jacobian form (x, y, z), for O, P, 2P, ... 15P */ +void PREFIX(precompute_jac) (ecfp_jac_pt * precomp, const ecfp_aff_pt * p, + const EC_group_fp * group) { + int i; + + /* fill precomputation table */ + /* set precomp[0] */ + PREFIX(set_pt_inf_jac) (&precomp[0]); + /* set precomp[1] */ + PREFIX(copy) (precomp[1].x, p->x); + PREFIX(copy) (precomp[1].y, p->y); + if (PREFIX(pt_is_inf_aff) (p) == MP_YES) { + PREFIX(zero) (precomp[1].z); + } else { + PREFIX(one) (precomp[1].z); + } + /* set precomp[2] */ + group->pt_dbl_jac(&precomp[1], &precomp[2], group); + + /* set rest of precomp */ + for (i = 3; i < 16; i++) { + group->pt_add_jac_aff(&precomp[i - 1], p, &precomp[i], group); + } +} diff --git a/nss/lib/freebl/ecl/ecp_jac.c b/nss/lib/freebl/ecl/ecp_jac.c new file mode 100644 index 0000000..f174b16 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_jac.c @@ -0,0 +1,529 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp.h" +#include "mplogic.h" +#include <stdlib.h> +#ifdef ECL_DEBUG +#include <assert.h> +#endif + +/* Converts a point P(px, py) from affine coordinates to Jacobian + * projective coordinates R(rx, ry, rz). Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. */ +mp_err +ec_GFp_pt_aff2jac(const mp_int *px, const mp_int *py, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + + if (ec_GFp_pt_is_inf_aff(px, py) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); + } else { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + MP_CHECKOK(mp_set_int(rz, 1)); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth->field_enc(rz, rz, group->meth)); + } + } + CLEANUP: + return res; +} + +/* Converts a point P(px, py, pz) from Jacobian projective coordinates to + * affine coordinates R(rx, ry). P and R can share x and y coordinates. + * Assumes input is already field-encoded using field_enc, and returns + * output that is still field-encoded. */ +mp_err +ec_GFp_pt_jac2aff(const mp_int *px, const mp_int *py, const mp_int *pz, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int z1, z2, z3; + + MP_DIGITS(&z1) = 0; + MP_DIGITS(&z2) = 0; + MP_DIGITS(&z3) = 0; + MP_CHECKOK(mp_init(&z1)); + MP_CHECKOK(mp_init(&z2)); + MP_CHECKOK(mp_init(&z3)); + + /* if point at infinity, then set point at infinity and exit */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_set_inf_aff(rx, ry)); + goto CLEANUP; + } + + /* transform (px, py, pz) into (px / pz^2, py / pz^3) */ + if (mp_cmp_d(pz, 1) == 0) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + } else { + MP_CHECKOK(group->meth->field_div(NULL, pz, &z1, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&z1, &z2, group->meth)); + MP_CHECKOK(group->meth->field_mul(&z1, &z2, &z3, group->meth)); + MP_CHECKOK(group->meth->field_mul(px, &z2, rx, group->meth)); + MP_CHECKOK(group->meth->field_mul(py, &z3, ry, group->meth)); + } + + CLEANUP: + mp_clear(&z1); + mp_clear(&z2); + mp_clear(&z3); + return res; +} + +/* Checks if point P(px, py, pz) is at infinity. Uses Jacobian + * coordinates. */ +mp_err +ec_GFp_pt_is_inf_jac(const mp_int *px, const mp_int *py, const mp_int *pz) +{ + return mp_cmp_z(pz); +} + +/* Sets P(px, py, pz) to be the point at infinity. Uses Jacobian + * coordinates. */ +mp_err +ec_GFp_pt_set_inf_jac(mp_int *px, mp_int *py, mp_int *pz) +{ + mp_zero(pz); + return MP_OKAY; +} + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, 1). Elliptic curve points P, Q, and R can all be identical. + * Uses mixed Jacobian-affine coordinates. Assumes input is already + * field-encoded using field_enc, and returns output that is still + * field-encoded. Uses equation (2) from Brown, Hankerson, Lopez, and + * Menezes. Software Implementation of the NIST Elliptic Curves Over Prime + * Fields. */ +mp_err +ec_GFp_pt_add_jac_aff(const mp_int *px, const mp_int *py, const mp_int *pz, + const mp_int *qx, const mp_int *qy, mp_int *rx, + mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int A, B, C, D, C2, C3; + + MP_DIGITS(&A) = 0; + MP_DIGITS(&B) = 0; + MP_DIGITS(&C) = 0; + MP_DIGITS(&D) = 0; + MP_DIGITS(&C2) = 0; + MP_DIGITS(&C3) = 0; + MP_CHECKOK(mp_init(&A)); + MP_CHECKOK(mp_init(&B)); + MP_CHECKOK(mp_init(&C)); + MP_CHECKOK(mp_init(&D)); + MP_CHECKOK(mp_init(&C2)); + MP_CHECKOK(mp_init(&C3)); + + /* If either P or Q is the point at infinity, then return the other + * point */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_aff2jac(qx, qy, rx, ry, rz, group)); + goto CLEANUP; + } + if (ec_GFp_pt_is_inf_aff(qx, qy) == MP_YES) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + MP_CHECKOK(mp_copy(pz, rz)); + goto CLEANUP; + } + + /* A = qx * pz^2, B = qy * pz^3 */ + MP_CHECKOK(group->meth->field_sqr(pz, &A, group->meth)); + MP_CHECKOK(group->meth->field_mul(&A, pz, &B, group->meth)); + MP_CHECKOK(group->meth->field_mul(&A, qx, &A, group->meth)); + MP_CHECKOK(group->meth->field_mul(&B, qy, &B, group->meth)); + + /* C = A - px, D = B - py */ + MP_CHECKOK(group->meth->field_sub(&A, px, &C, group->meth)); + MP_CHECKOK(group->meth->field_sub(&B, py, &D, group->meth)); + + if (mp_cmp_z(&C) == 0) { + /* P == Q or P == -Q */ + if (mp_cmp_z(&D) == 0) { + /* P == Q */ + /* It is cheaper to double (qx, qy, 1) than (px, py, pz). */ + MP_DIGIT(&D, 0) = 1; /* Set D to 1. */ + MP_CHECKOK(ec_GFp_pt_dbl_jac(qx, qy, &D, rx, ry, rz, group)); + } else { + /* P == -Q */ + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); + } + goto CLEANUP; + } + + /* C2 = C^2, C3 = C^3 */ + MP_CHECKOK(group->meth->field_sqr(&C, &C2, group->meth)); + MP_CHECKOK(group->meth->field_mul(&C, &C2, &C3, group->meth)); + + /* rz = pz * C */ + MP_CHECKOK(group->meth->field_mul(pz, &C, rz, group->meth)); + + /* C = px * C^2 */ + MP_CHECKOK(group->meth->field_mul(px, &C2, &C, group->meth)); + /* A = D^2 */ + MP_CHECKOK(group->meth->field_sqr(&D, &A, group->meth)); + + /* rx = D^2 - (C^3 + 2 * (px * C^2)) */ + MP_CHECKOK(group->meth->field_add(&C, &C, rx, group->meth)); + MP_CHECKOK(group->meth->field_add(&C3, rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(&A, rx, rx, group->meth)); + + /* C3 = py * C^3 */ + MP_CHECKOK(group->meth->field_mul(py, &C3, &C3, group->meth)); + + /* ry = D * (px * C^2 - rx) - py * C^3 */ + MP_CHECKOK(group->meth->field_sub(&C, rx, ry, group->meth)); + MP_CHECKOK(group->meth->field_mul(&D, ry, ry, group->meth)); + MP_CHECKOK(group->meth->field_sub(ry, &C3, ry, group->meth)); + + CLEANUP: + mp_clear(&A); + mp_clear(&B); + mp_clear(&C); + mp_clear(&D); + mp_clear(&C2); + mp_clear(&C3); + return res; +} + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * Jacobian coordinates. + * + * Assumes input is already field-encoded using field_enc, and returns + * output that is still field-encoded. + * + * This routine implements Point Doubling in the Jacobian Projective + * space as described in the paper "Efficient elliptic curve exponentiation + * using mixed coordinates", by H. Cohen, A Miyaji, T. Ono. + */ +mp_err +ec_GFp_pt_dbl_jac(const mp_int *px, const mp_int *py, const mp_int *pz, + mp_int *rx, mp_int *ry, mp_int *rz, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int t0, t1, M, S; + + MP_DIGITS(&t0) = 0; + MP_DIGITS(&t1) = 0; + MP_DIGITS(&M) = 0; + MP_DIGITS(&S) = 0; + MP_CHECKOK(mp_init(&t0)); + MP_CHECKOK(mp_init(&t1)); + MP_CHECKOK(mp_init(&M)); + MP_CHECKOK(mp_init(&S)); + + /* P == inf or P == -P */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES || mp_cmp_z(py) == 0) { + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); + goto CLEANUP; + } + + if (mp_cmp_d(pz, 1) == 0) { + /* M = 3 * px^2 + a */ + MP_CHECKOK(group->meth->field_sqr(px, &t0, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &t0, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &M, &t0, group->meth)); + MP_CHECKOK(group->meth-> + field_add(&t0, &group->curvea, &M, group->meth)); + } else if (mp_cmp_int(&group->curvea, -3) == 0) { + /* M = 3 * (px + pz^2) * (px - pz^2) */ + MP_CHECKOK(group->meth->field_sqr(pz, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(px, &M, &t0, group->meth)); + MP_CHECKOK(group->meth->field_sub(px, &M, &t1, group->meth)); + MP_CHECKOK(group->meth->field_mul(&t0, &t1, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(&M, &M, &t0, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &M, &M, group->meth)); + } else { + /* M = 3 * (px^2) + a * (pz^4) */ + MP_CHECKOK(group->meth->field_sqr(px, &t0, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &t0, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(&t0, &M, &t0, group->meth)); + MP_CHECKOK(group->meth->field_sqr(pz, &M, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&M, &M, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(&M, &group->curvea, &M, group->meth)); + MP_CHECKOK(group->meth->field_add(&M, &t0, &M, group->meth)); + } + + /* rz = 2 * py * pz */ + /* t0 = 4 * py^2 */ + if (mp_cmp_d(pz, 1) == 0) { + MP_CHECKOK(group->meth->field_add(py, py, rz, group->meth)); + MP_CHECKOK(group->meth->field_sqr(rz, &t0, group->meth)); + } else { + MP_CHECKOK(group->meth->field_add(py, py, &t0, group->meth)); + MP_CHECKOK(group->meth->field_mul(&t0, pz, rz, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&t0, &t0, group->meth)); + } + + /* S = 4 * px * py^2 = px * (2 * py)^2 */ + MP_CHECKOK(group->meth->field_mul(px, &t0, &S, group->meth)); + + /* rx = M^2 - 2 * S */ + MP_CHECKOK(group->meth->field_add(&S, &S, &t1, group->meth)); + MP_CHECKOK(group->meth->field_sqr(&M, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(rx, &t1, rx, group->meth)); + + /* ry = M * (S - rx) - 8 * py^4 */ + MP_CHECKOK(group->meth->field_sqr(&t0, &t1, group->meth)); + if (mp_isodd(&t1)) { + MP_CHECKOK(mp_add(&t1, &group->meth->irr, &t1)); + } + MP_CHECKOK(mp_div_2(&t1, &t1)); + MP_CHECKOK(group->meth->field_sub(&S, rx, &S, group->meth)); + MP_CHECKOK(group->meth->field_mul(&M, &S, &M, group->meth)); + MP_CHECKOK(group->meth->field_sub(&M, &t1, ry, group->meth)); + + CLEANUP: + mp_clear(&t0); + mp_clear(&t1); + mp_clear(&M); + mp_clear(&S); + return res; +} + +/* by default, this routine is unused and thus doesn't need to be compiled */ +#ifdef ECL_ENABLE_GFP_PT_MUL_JAC +/* Computes R = nP where R is (rx, ry) and P is (px, py). The parameters + * a, b and p are the elliptic curve coefficients and the prime that + * determines the field GFp. Elliptic curve points P and R can be + * identical. Uses mixed Jacobian-affine coordinates. Assumes input is + * already field-encoded using field_enc, and returns output that is still + * field-encoded. Uses 4-bit window method. */ +mp_err +ec_GFp_pt_mul_jac(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[16][2], rz; + int i, ni, d; + + MP_DIGITS(&rz) = 0; + for (i = 0; i < 16; i++) { + MP_DIGITS(&precomp[i][0]) = 0; + MP_DIGITS(&precomp[i][1]) = 0; + } + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); + + /* initialize precomputation table */ + for (i = 0; i < 16; i++) { + MP_CHECKOK(mp_init(&precomp[i][0])); + MP_CHECKOK(mp_init(&precomp[i][1])); + } + + /* fill precomputation table */ + mp_zero(&precomp[0][0]); + mp_zero(&precomp[0][1]); + MP_CHECKOK(mp_copy(px, &precomp[1][0])); + MP_CHECKOK(mp_copy(py, &precomp[1][1])); + for (i = 2; i < 16; i++) { + MP_CHECKOK(group-> + point_add(&precomp[1][0], &precomp[1][1], + &precomp[i - 1][0], &precomp[i - 1][1], + &precomp[i][0], &precomp[i][1], group)); + } + + d = (mpl_significant_bits(n) + 3) / 4; + + /* R = inf */ + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); + + for (i = d - 1; i >= 0; i--) { + /* compute window ni */ + ni = MP_GET_BIT(n, 4 * i + 3); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 2); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i + 1); + ni <<= 1; + ni |= MP_GET_BIT(n, 4 * i); + /* R = 2^4 * R */ + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + /* R = R + (ni * P) */ + MP_CHECKOK(ec_GFp_pt_add_jac_aff + (rx, ry, &rz, &precomp[ni][0], &precomp[ni][1], rx, ry, + &rz, group)); + } + + /* convert result S to affine coordinates */ + MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, group)); + + CLEANUP: + mp_clear(&rz); + for (i = 0; i < 16; i++) { + mp_clear(&precomp[i][0]); + mp_clear(&precomp[i][1]); + } + return res; +} +#endif + +/* Elliptic curve scalar-point multiplication. Computes R(x, y) = k1 * G + + * k2 * P(x, y), where G is the generator (base point) of the group of + * points on the elliptic curve. Allows k1 = NULL or { k2, P } = NULL. + * Uses mixed Jacobian-affine coordinates. Input and output values are + * assumed to be NOT field-encoded. Uses algorithm 15 (simultaneous + * multiple point multiplication) from Brown, Hankerson, Lopez, Menezes. + * Software Implementation of the NIST Elliptic Curves over Prime Fields. */ +mp_err +ec_GFp_pts_mul_jac(const mp_int *k1, const mp_int *k2, const mp_int *px, + const mp_int *py, mp_int *rx, mp_int *ry, + const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[4][4][2]; + mp_int rz; + const mp_int *a, *b; + unsigned int i, j; + int ai, bi, d; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + MP_DIGITS(&precomp[i][j][0]) = 0; + MP_DIGITS(&precomp[i][j][1]) = 0; + } + } + MP_DIGITS(&rz) = 0; + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK(!((k1 == NULL) + && ((k2 == NULL) || (px == NULL) + || (py == NULL))), MP_BADARG); + + /* if some arguments are not defined used ECPoint_mul */ + if (k1 == NULL) { + return ECPoint_mul(group, k2, px, py, rx, ry); + } else if ((k2 == NULL) || (px == NULL) || (py == NULL)) { + return ECPoint_mul(group, k1, NULL, NULL, rx, ry); + } + + /* initialize precomputation table */ + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + MP_CHECKOK(mp_init(&precomp[i][j][0])); + MP_CHECKOK(mp_init(&precomp[i][j][1])); + } + } + + /* fill precomputation table */ + /* assign {k1, k2} = {a, b} such that len(a) >= len(b) */ + if (mpl_significant_bits(k1) < mpl_significant_bits(k2)) { + a = k2; + b = k1; + if (group->meth->field_enc) { + MP_CHECKOK(group->meth-> + field_enc(px, &precomp[1][0][0], group->meth)); + MP_CHECKOK(group->meth-> + field_enc(py, &precomp[1][0][1], group->meth)); + } else { + MP_CHECKOK(mp_copy(px, &precomp[1][0][0])); + MP_CHECKOK(mp_copy(py, &precomp[1][0][1])); + } + MP_CHECKOK(mp_copy(&group->genx, &precomp[0][1][0])); + MP_CHECKOK(mp_copy(&group->geny, &precomp[0][1][1])); + } else { + a = k1; + b = k2; + MP_CHECKOK(mp_copy(&group->genx, &precomp[1][0][0])); + MP_CHECKOK(mp_copy(&group->geny, &precomp[1][0][1])); + if (group->meth->field_enc) { + MP_CHECKOK(group->meth-> + field_enc(px, &precomp[0][1][0], group->meth)); + MP_CHECKOK(group->meth-> + field_enc(py, &precomp[0][1][1], group->meth)); + } else { + MP_CHECKOK(mp_copy(px, &precomp[0][1][0])); + MP_CHECKOK(mp_copy(py, &precomp[0][1][1])); + } + } + /* precompute [*][0][*] */ + mp_zero(&precomp[0][0][0]); + mp_zero(&precomp[0][0][1]); + MP_CHECKOK(group-> + point_dbl(&precomp[1][0][0], &precomp[1][0][1], + &precomp[2][0][0], &precomp[2][0][1], group)); + MP_CHECKOK(group-> + point_add(&precomp[1][0][0], &precomp[1][0][1], + &precomp[2][0][0], &precomp[2][0][1], + &precomp[3][0][0], &precomp[3][0][1], group)); + /* precompute [*][1][*] */ + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][1][0], &precomp[0][1][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][1][0], &precomp[i][1][1], group)); + } + /* precompute [*][2][*] */ + MP_CHECKOK(group-> + point_dbl(&precomp[0][1][0], &precomp[0][1][1], + &precomp[0][2][0], &precomp[0][2][1], group)); + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][2][0], &precomp[0][2][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][2][0], &precomp[i][2][1], group)); + } + /* precompute [*][3][*] */ + MP_CHECKOK(group-> + point_add(&precomp[0][1][0], &precomp[0][1][1], + &precomp[0][2][0], &precomp[0][2][1], + &precomp[0][3][0], &precomp[0][3][1], group)); + for (i = 1; i < 4; i++) { + MP_CHECKOK(group-> + point_add(&precomp[0][3][0], &precomp[0][3][1], + &precomp[i][0][0], &precomp[i][0][1], + &precomp[i][3][0], &precomp[i][3][1], group)); + } + + d = (mpl_significant_bits(a) + 1) / 2; + + /* R = inf */ + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); + + for (i = d; i-- > 0;) { + ai = MP_GET_BIT(a, 2 * i + 1); + ai <<= 1; + ai |= MP_GET_BIT(a, 2 * i); + bi = MP_GET_BIT(b, 2 * i + 1); + bi <<= 1; + bi |= MP_GET_BIT(b, 2 * i); + /* R = 2^2 * R */ + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + MP_CHECKOK(ec_GFp_pt_dbl_jac(rx, ry, &rz, rx, ry, &rz, group)); + /* R = R + (ai * A + bi * B) */ + MP_CHECKOK(ec_GFp_pt_add_jac_aff + (rx, ry, &rz, &precomp[ai][bi][0], &precomp[ai][bi][1], + rx, ry, &rz, group)); + } + + MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, group)); + + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_dec(ry, ry, group->meth)); + } + + CLEANUP: + mp_clear(&rz); + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + mp_clear(&precomp[i][j][0]); + mp_clear(&precomp[i][j][1]); + } + } + return res; +} diff --git a/nss/lib/freebl/ecl/ecp_jm.c b/nss/lib/freebl/ecl/ecp_jm.c new file mode 100644 index 0000000..2d56412 --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_jm.c @@ -0,0 +1,289 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp.h" +#include "ecl-priv.h" +#include "mplogic.h" +#include <stdlib.h> + +#define MAX_SCRATCH 6 + +/* Computes R = 2P. Elliptic curve points P and R can be identical. Uses + * Modified Jacobian coordinates. + * + * Assumes input is already field-encoded using field_enc, and returns + * output that is still field-encoded. + * + */ +static mp_err +ec_GFp_pt_dbl_jm(const mp_int *px, const mp_int *py, const mp_int *pz, + const mp_int *paz4, mp_int *rx, mp_int *ry, mp_int *rz, + mp_int *raz4, mp_int scratch[], const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int *t0, *t1, *M, *S; + + t0 = &scratch[0]; + t1 = &scratch[1]; + M = &scratch[2]; + S = &scratch[3]; + +#if MAX_SCRATCH < 4 +#error "Scratch array defined too small " +#endif + + /* Check for point at infinity */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + /* Set r = pt at infinity by setting rz = 0 */ + + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, rz)); + goto CLEANUP; + } + + /* M = 3 (px^2) + a*(pz^4) */ + MP_CHECKOK(group->meth->field_sqr(px, t0, group->meth)); + MP_CHECKOK(group->meth->field_add(t0, t0, M, group->meth)); + MP_CHECKOK(group->meth->field_add(t0, M, t0, group->meth)); + MP_CHECKOK(group->meth->field_add(t0, paz4, M, group->meth)); + + /* rz = 2 * py * pz */ + MP_CHECKOK(group->meth->field_mul(py, pz, S, group->meth)); + MP_CHECKOK(group->meth->field_add(S, S, rz, group->meth)); + + /* t0 = 2y^2 , t1 = 8y^4 */ + MP_CHECKOK(group->meth->field_sqr(py, t0, group->meth)); + MP_CHECKOK(group->meth->field_add(t0, t0, t0, group->meth)); + MP_CHECKOK(group->meth->field_sqr(t0, t1, group->meth)); + MP_CHECKOK(group->meth->field_add(t1, t1, t1, group->meth)); + + /* S = 4 * px * py^2 = 2 * px * t0 */ + MP_CHECKOK(group->meth->field_mul(px, t0, S, group->meth)); + MP_CHECKOK(group->meth->field_add(S, S, S, group->meth)); + + + /* rx = M^2 - 2S */ + MP_CHECKOK(group->meth->field_sqr(M, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(rx, S, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(rx, S, rx, group->meth)); + + /* ry = M * (S - rx) - t1 */ + MP_CHECKOK(group->meth->field_sub(S, rx, S, group->meth)); + MP_CHECKOK(group->meth->field_mul(S, M, ry, group->meth)); + MP_CHECKOK(group->meth->field_sub(ry, t1, ry, group->meth)); + + /* ra*z^4 = 2*t1*(apz4) */ + MP_CHECKOK(group->meth->field_mul(paz4, t1, raz4, group->meth)); + MP_CHECKOK(group->meth->field_add(raz4, raz4, raz4, group->meth)); + + + CLEANUP: + return res; +} + +/* Computes R = P + Q where R is (rx, ry, rz), P is (px, py, pz) and Q is + * (qx, qy, 1). Elliptic curve points P, Q, and R can all be identical. + * Uses mixed Modified_Jacobian-affine coordinates. Assumes input is + * already field-encoded using field_enc, and returns output that is still + * field-encoded. */ +static mp_err +ec_GFp_pt_add_jm_aff(const mp_int *px, const mp_int *py, const mp_int *pz, + const mp_int *paz4, const mp_int *qx, + const mp_int *qy, mp_int *rx, mp_int *ry, mp_int *rz, + mp_int *raz4, mp_int scratch[], const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int *A, *B, *C, *D, *C2, *C3; + + A = &scratch[0]; + B = &scratch[1]; + C = &scratch[2]; + D = &scratch[3]; + C2 = &scratch[4]; + C3 = &scratch[5]; + +#if MAX_SCRATCH < 6 +#error "Scratch array defined too small " +#endif + + /* If either P or Q is the point at infinity, then return the other + * point */ + if (ec_GFp_pt_is_inf_jac(px, py, pz) == MP_YES) { + MP_CHECKOK(ec_GFp_pt_aff2jac(qx, qy, rx, ry, rz, group)); + MP_CHECKOK(group->meth->field_sqr(rz, raz4, group->meth)); + MP_CHECKOK(group->meth->field_sqr(raz4, raz4, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(raz4, &group->curvea, raz4, group->meth)); + goto CLEANUP; + } + if (ec_GFp_pt_is_inf_aff(qx, qy) == MP_YES) { + MP_CHECKOK(mp_copy(px, rx)); + MP_CHECKOK(mp_copy(py, ry)); + MP_CHECKOK(mp_copy(pz, rz)); + MP_CHECKOK(mp_copy(paz4, raz4)); + goto CLEANUP; + } + + /* A = qx * pz^2, B = qy * pz^3 */ + MP_CHECKOK(group->meth->field_sqr(pz, A, group->meth)); + MP_CHECKOK(group->meth->field_mul(A, pz, B, group->meth)); + MP_CHECKOK(group->meth->field_mul(A, qx, A, group->meth)); + MP_CHECKOK(group->meth->field_mul(B, qy, B, group->meth)); + + /* C = A - px, D = B - py */ + MP_CHECKOK(group->meth->field_sub(A, px, C, group->meth)); + MP_CHECKOK(group->meth->field_sub(B, py, D, group->meth)); + + /* C2 = C^2, C3 = C^3 */ + MP_CHECKOK(group->meth->field_sqr(C, C2, group->meth)); + MP_CHECKOK(group->meth->field_mul(C, C2, C3, group->meth)); + + /* rz = pz * C */ + MP_CHECKOK(group->meth->field_mul(pz, C, rz, group->meth)); + + /* C = px * C^2 */ + MP_CHECKOK(group->meth->field_mul(px, C2, C, group->meth)); + /* A = D^2 */ + MP_CHECKOK(group->meth->field_sqr(D, A, group->meth)); + + /* rx = D^2 - (C^3 + 2 * (px * C^2)) */ + MP_CHECKOK(group->meth->field_add(C, C, rx, group->meth)); + MP_CHECKOK(group->meth->field_add(C3, rx, rx, group->meth)); + MP_CHECKOK(group->meth->field_sub(A, rx, rx, group->meth)); + + /* C3 = py * C^3 */ + MP_CHECKOK(group->meth->field_mul(py, C3, C3, group->meth)); + + /* ry = D * (px * C^2 - rx) - py * C^3 */ + MP_CHECKOK(group->meth->field_sub(C, rx, ry, group->meth)); + MP_CHECKOK(group->meth->field_mul(D, ry, ry, group->meth)); + MP_CHECKOK(group->meth->field_sub(ry, C3, ry, group->meth)); + + /* raz4 = a * rz^4 */ + MP_CHECKOK(group->meth->field_sqr(rz, raz4, group->meth)); + MP_CHECKOK(group->meth->field_sqr(raz4, raz4, group->meth)); + MP_CHECKOK(group->meth-> + field_mul(raz4, &group->curvea, raz4, group->meth)); +CLEANUP: + return res; +} + +/* Computes R = nP where R is (rx, ry) and P is the base point. Elliptic + * curve points P and R can be identical. Uses mixed Modified-Jacobian + * co-ordinates for doubling and Chudnovsky Jacobian coordinates for + * additions. Assumes input is already field-encoded using field_enc, and + * returns output that is still field-encoded. Uses 5-bit window NAF + * method (algorithm 11) for scalar-point multiplication from Brown, + * Hankerson, Lopez, Menezes. Software Implementation of the NIST Elliptic + * Curves Over Prime Fields. */ +mp_err +ec_GFp_pt_mul_jm_wNAF(const mp_int *n, const mp_int *px, const mp_int *py, + mp_int *rx, mp_int *ry, const ECGroup *group) +{ + mp_err res = MP_OKAY; + mp_int precomp[16][2], rz, tpx, tpy; + mp_int raz4; + mp_int scratch[MAX_SCRATCH]; + signed char *naf = NULL; + int i, orderBitSize; + + MP_DIGITS(&rz) = 0; + MP_DIGITS(&raz4) = 0; + MP_DIGITS(&tpx) = 0; + MP_DIGITS(&tpy) = 0; + for (i = 0; i < 16; i++) { + MP_DIGITS(&precomp[i][0]) = 0; + MP_DIGITS(&precomp[i][1]) = 0; + } + for (i = 0; i < MAX_SCRATCH; i++) { + MP_DIGITS(&scratch[i]) = 0; + } + + ARGCHK(group != NULL, MP_BADARG); + ARGCHK((n != NULL) && (px != NULL) && (py != NULL), MP_BADARG); + + /* initialize precomputation table */ + MP_CHECKOK(mp_init(&tpx)); + MP_CHECKOK(mp_init(&tpy));; + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(mp_init(&raz4)); + + for (i = 0; i < 16; i++) { + MP_CHECKOK(mp_init(&precomp[i][0])); + MP_CHECKOK(mp_init(&precomp[i][1])); + } + for (i = 0; i < MAX_SCRATCH; i++) { + MP_CHECKOK(mp_init(&scratch[i])); + } + + /* Set out[8] = P */ + MP_CHECKOK(mp_copy(px, &precomp[8][0])); + MP_CHECKOK(mp_copy(py, &precomp[8][1])); + + /* Set (tpx, tpy) = 2P */ + MP_CHECKOK(group-> + point_dbl(&precomp[8][0], &precomp[8][1], &tpx, &tpy, + group)); + + /* Set 3P, 5P, ..., 15P */ + for (i = 8; i < 15; i++) { + MP_CHECKOK(group-> + point_add(&precomp[i][0], &precomp[i][1], &tpx, &tpy, + &precomp[i + 1][0], &precomp[i + 1][1], + group)); + } + + /* Set -15P, -13P, ..., -P */ + for (i = 0; i < 8; i++) { + MP_CHECKOK(mp_copy(&precomp[15 - i][0], &precomp[i][0])); + MP_CHECKOK(group->meth-> + field_neg(&precomp[15 - i][1], &precomp[i][1], + group->meth)); + } + + /* R = inf */ + MP_CHECKOK(ec_GFp_pt_set_inf_jac(rx, ry, &rz)); + + orderBitSize = mpl_significant_bits(&group->order); + + /* Allocate memory for NAF */ + naf = (signed char *) malloc(sizeof(signed char) * (orderBitSize + 1)); + if (naf == NULL) { + res = MP_MEM; + goto CLEANUP; + } + + /* Compute 5NAF */ + ec_compute_wNAF(naf, orderBitSize, n, 5); + + /* wNAF method */ + for (i = orderBitSize; i >= 0; i--) { + /* R = 2R */ + ec_GFp_pt_dbl_jm(rx, ry, &rz, &raz4, rx, ry, &rz, + &raz4, scratch, group); + if (naf[i] != 0) { + ec_GFp_pt_add_jm_aff(rx, ry, &rz, &raz4, + &precomp[(naf[i] + 15) / 2][0], + &precomp[(naf[i] + 15) / 2][1], rx, ry, + &rz, &raz4, scratch, group); + } + } + + /* convert result S to affine coordinates */ + MP_CHECKOK(ec_GFp_pt_jac2aff(rx, ry, &rz, rx, ry, group)); + + CLEANUP: + for (i = 0; i < MAX_SCRATCH; i++) { + mp_clear(&scratch[i]); + } + for (i = 0; i < 16; i++) { + mp_clear(&precomp[i][0]); + mp_clear(&precomp[i][1]); + } + mp_clear(&tpx); + mp_clear(&tpy); + mp_clear(&rz); + mp_clear(&raz4); + free(naf); + return res; +} diff --git a/nss/lib/freebl/ecl/ecp_mont.c b/nss/lib/freebl/ecl/ecp_mont.c new file mode 100644 index 0000000..6b8462e --- /dev/null +++ b/nss/lib/freebl/ecl/ecp_mont.c @@ -0,0 +1,155 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* Uses Montgomery reduction for field arithmetic. See mpi/mpmontg.c for + * code implementation. */ + +#include "mpi.h" +#include "mplogic.h" +#include "mpi-priv.h" +#include "ecl-priv.h" +#include "ecp.h" +#include <stdlib.h> +#include <stdio.h> + +/* Construct a generic GFMethod for arithmetic over prime fields with + * irreducible irr. */ +GFMethod * +GFMethod_consGFp_mont(const mp_int *irr) +{ + mp_err res = MP_OKAY; + GFMethod *meth = NULL; + mp_mont_modulus *mmm; + + meth = GFMethod_consGFp(irr); + if (meth == NULL) + return NULL; + + mmm = (mp_mont_modulus *) malloc(sizeof(mp_mont_modulus)); + if (mmm == NULL) { + res = MP_MEM; + goto CLEANUP; + } + + meth->field_mul = &ec_GFp_mul_mont; + meth->field_sqr = &ec_GFp_sqr_mont; + meth->field_div = &ec_GFp_div_mont; + meth->field_enc = &ec_GFp_enc_mont; + meth->field_dec = &ec_GFp_dec_mont; + meth->extra1 = mmm; + meth->extra2 = NULL; + meth->extra_free = &ec_GFp_extra_free_mont; + + mmm->N = meth->irr; + mmm->n0prime = 0 - s_mp_invmod_radix(MP_DIGIT(&meth->irr, 0)); + + CLEANUP: + if (res != MP_OKAY) { + GFMethod_free(meth); + return NULL; + } + return meth; +} + +/* Wrapper functions for generic prime field arithmetic. */ + +/* Field multiplication using Montgomery reduction. */ +mp_err +ec_GFp_mul_mont(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + +#ifdef MP_MONT_USE_MP_MUL + /* if MP_MONT_USE_MP_MUL is defined, then the function s_mp_mul_mont + * is not implemented and we have to use mp_mul and s_mp_redc directly + */ + MP_CHECKOK(mp_mul(a, b, r)); + MP_CHECKOK(s_mp_redc(r, (mp_mont_modulus *) meth->extra1)); +#else + mp_int s; + + MP_DIGITS(&s) = 0; + /* s_mp_mul_mont doesn't allow source and destination to be the same */ + if ((a == r) || (b == r)) { + MP_CHECKOK(mp_init(&s)); + MP_CHECKOK(s_mp_mul_mont + (a, b, &s, (mp_mont_modulus *) meth->extra1)); + MP_CHECKOK(mp_copy(&s, r)); + mp_clear(&s); + } else { + return s_mp_mul_mont(a, b, r, (mp_mont_modulus *) meth->extra1); + } +#endif + CLEANUP: + return res; +} + +/* Field squaring using Montgomery reduction. */ +mp_err +ec_GFp_sqr_mont(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + return ec_GFp_mul_mont(a, a, r, meth); +} + +/* Field division using Montgomery reduction. */ +mp_err +ec_GFp_div_mont(const mp_int *a, const mp_int *b, mp_int *r, + const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + /* if A=aZ represents a encoded in montgomery coordinates with Z and # + * and \ respectively represent multiplication and division in + * montgomery coordinates, then A\B = (a/b)Z = (A/B)Z and Binv = + * (1/b)Z = (1/B)(Z^2) where B # Binv = Z */ + MP_CHECKOK(ec_GFp_div(a, b, r, meth)); + MP_CHECKOK(ec_GFp_enc_mont(r, r, meth)); + if (a == NULL) { + MP_CHECKOK(ec_GFp_enc_mont(r, r, meth)); + } + CLEANUP: + return res; +} + +/* Encode a field element in Montgomery form. See s_mp_to_mont in + * mpi/mpmontg.c */ +mp_err +ec_GFp_enc_mont(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_mont_modulus *mmm; + mp_err res = MP_OKAY; + + mmm = (mp_mont_modulus *) meth->extra1; + MP_CHECKOK(mp_copy(a, r)); + MP_CHECKOK(s_mp_lshd(r, MP_USED(&mmm->N))); + MP_CHECKOK(mp_mod(r, &mmm->N, r)); + CLEANUP: + return res; +} + +/* Decode a field element from Montgomery form. */ +mp_err +ec_GFp_dec_mont(const mp_int *a, mp_int *r, const GFMethod *meth) +{ + mp_err res = MP_OKAY; + + if (a != r) { + MP_CHECKOK(mp_copy(a, r)); + } + MP_CHECKOK(s_mp_redc(r, (mp_mont_modulus *) meth->extra1)); + CLEANUP: + return res; +} + +/* Free the memory allocated to the extra fields of Montgomery GFMethod + * object. */ +void +ec_GFp_extra_free_mont(GFMethod *meth) +{ + if (meth->extra1 != NULL) { + free(meth->extra1); + meth->extra1 = NULL; + } +} diff --git a/nss/lib/freebl/ecl/tests/ec2_test.c b/nss/lib/freebl/ecl/tests/ec2_test.c new file mode 100644 index 0000000..1b4d8c3 --- /dev/null +++ b/nss/lib/freebl/ecl/tests/ec2_test.c @@ -0,0 +1,482 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mpi.h" +#include "mplogic.h" +#include "mpprime.h" +#include "mp_gf2m.h" +#include "ecl.h" +#include "ecl-curve.h" +#include "ec2.h" +#include <stdio.h> +#include <strings.h> +#include <assert.h> + +#include <time.h> +#include <sys/time.h> +#include <sys/resource.h> + +/* Time k repetitions of operation op. */ +#define M_TimeOperation(op, k) { \ + double dStart, dNow, dUserTime; \ + struct rusage ru; \ + int i; \ + getrusage(RUSAGE_SELF, &ru); \ + dStart = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + for (i = 0; i < k; i++) { \ + { op; } \ + }; \ + getrusage(RUSAGE_SELF, &ru); \ + dNow = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + dUserTime = dNow-dStart; \ + if (dUserTime) printf(" %-45s k: %6i, t: %6.2f sec\n", #op, k, dUserTime); \ +} + +/* Test curve using generic field arithmetic. */ +#define ECTEST_GENERIC_GF2M(name_c, name) \ + printf("Testing %s using generic implementation...\n", name_c); \ + params = EC_GetNamedCurveParams(name); \ + if (params == NULL) { \ + printf(" Error: could not construct params.\n"); \ + res = MP_NO; \ + goto CLEANUP; \ + } \ + ECGroup_free(group); \ + group = ECGroup_fromHex(params); \ + if (group == NULL) { \ + printf(" Error: could not construct group.\n"); \ + res = MP_NO; \ + goto CLEANUP; \ + } \ + MP_CHECKOK( ectest_curve_GF2m(group, ectestPrint, ectestTime, 1) ); \ + printf("... okay.\n"); + +/* Test curve using specific field arithmetic. */ +#define ECTEST_NAMED_GF2M(name_c, name) \ + printf("Testing %s using specific implementation...\n", name_c); \ + ECGroup_free(group); \ + group = ECGroup_fromName(name); \ + if (group == NULL) { \ + printf(" Warning: could not construct group.\n"); \ + printf("... failed; continuing with remaining tests.\n"); \ + } else { \ + MP_CHECKOK( ectest_curve_GF2m(group, ectestPrint, ectestTime, 0) ); \ + printf("... okay.\n"); \ + } + +/* Performs basic tests of elliptic curve cryptography over binary + * polynomial fields. If tests fail, then it prints an error message, + * aborts, and returns an error code. Otherwise, returns 0. */ +int +ectest_curve_GF2m(ECGroup *group, int ectestPrint, int ectestTime, + int generic) +{ + + mp_int one, order_1, gx, gy, rx, ry, n; + int size; + mp_err res; + char s[1000]; + + /* initialize values */ + MP_CHECKOK(mp_init(&one)); + MP_CHECKOK(mp_init(&order_1)); + MP_CHECKOK(mp_init(&gx)); + MP_CHECKOK(mp_init(&gy)); + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&n)); + + MP_CHECKOK(mp_set_int(&one, 1)); + MP_CHECKOK(mp_sub(&group->order, &one, &order_1)); + + /* encode base point */ + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(&group->genx, &gx, group->meth)); + MP_CHECKOK(group->meth->field_dec(&group->geny, &gy, group->meth)); + } else { + MP_CHECKOK(mp_copy(&group->genx, &gx)); + MP_CHECKOK(mp_copy(&group->geny, &gy)); + } + + if (ectestPrint) { + /* output base point */ + printf(" base point P:\n"); + MP_CHECKOK(mp_toradix(&gx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&gy, s, 16)); + printf(" %s\n", s); + if (group->meth->field_enc) { + printf(" base point P (encoded):\n"); + MP_CHECKOK(mp_toradix(&group->genx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&group->geny, s, 16)); + printf(" %s\n", s); + } + } + +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GF2m_pt_mul_aff + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (affine):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GF2m_pt_mul_mont + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (montgomery):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + +#ifdef ECL_ENABLE_GF2M_PROJ + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GF2m_pt_mul_proj + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (projective):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ECPoint_mul(group, &order_1, NULL, NULL, &rx, &ry)); + if (ectestPrint) { + printf(" (order-1)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(ec_GF2m_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ECPoint_mul(group, &order_1, &gx, &gy, &rx, &ry)); + if (ectestPrint) { + printf(" (order-1)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(ec_GF2m_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GF2m_pt_mul_aff + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (affine):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GF2m_pt_mul_mont + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (montgomery):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + +#ifdef ECL_ENABLE_GF2M_PROJ + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GF2m_pt_mul_proj + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (projective):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ECPoint_mul(group, &group->order, NULL, NULL, &rx, &ry)); + if (ectestPrint) { + printf(" (order)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ECPoint_mul(group, &group->order, &gx, &gy, &rx, &ry)); + if (ectestPrint) { + printf(" (order)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GF2m_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* check that (order-1)P + (order-1)P + P == (order-1)P */ + MP_CHECKOK(ECPoints_mul + (group, &order_1, &order_1, &gx, &gy, &rx, &ry)); + MP_CHECKOK(ECPoints_mul(group, &one, &one, &rx, &ry, &rx, &ry)); + if (ectestPrint) { + printf + (" (order-1)*P + (order-1)*P + P == (order-1)*P (ECPoints_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(ec_GF2m_add(&ry, &rx, &ry, group->meth)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* test validate_point function */ + if (ECPoint_validate(group, &gx, &gy) != MP_YES) { + printf(" Error: validate point on base point failed.\n"); + res = MP_NO; + goto CLEANUP; + } + MP_CHECKOK(mp_add_d(&gy, 1, &ry)); + if (ECPoint_validate(group, &gx, &ry) != MP_NO) { + printf(" Error: validate point on invalid point passed.\n"); + res = MP_NO; + goto CLEANUP; + } + + if (ectestTime) { + /* compute random scalar */ + size = mpl_significant_bits(&group->meth->irr); + if (size < MP_OKAY) { + goto CLEANUP; + } + MP_CHECKOK(mpp_random_size(&n, (size + ECL_BITS - 1) / ECL_BITS)); + MP_CHECKOK(group->meth->field_mod(&n, &n, group->meth)); + /* timed test */ + if (generic) { +#ifdef ECL_ENABLE_GF2M_PT_MUL_AFF + M_TimeOperation(MP_CHECKOK + (ec_GF2m_pt_mul_aff + (&n, &group->genx, &group->geny, &rx, &ry, + group)), 100); +#endif + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoints_mul + (group, &n, &n, &gx, &gy, &rx, &ry)), 100); + } else { + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, &gx, &gy, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoints_mul + (group, &n, &n, &gx, &gy, &rx, &ry)), 100); + } + } + + CLEANUP: + mp_clear(&one); + mp_clear(&order_1); + mp_clear(&gx); + mp_clear(&gy); + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&n); + if (res != MP_OKAY) { + printf(" Error: exiting with error value %i\n", res); + } + return res; +} + +/* Prints help information. */ +void +printUsage() +{ + printf("Usage: ecp_test [--print] [--time]\n"); + printf + (" --print Print out results of each point arithmetic test.\n"); + printf + (" --time Benchmark point operations and print results.\n"); +} + +/* Performs tests of elliptic curve cryptography over binary polynomial + * fields. If tests fail, then it prints an error message, aborts, and + * returns an error code. Otherwise, returns 0. */ +int +main(int argv, char **argc) +{ + + int ectestTime = 0; + int ectestPrint = 0; + int i; + ECGroup *group = NULL; + ECCurveParams *params = NULL; + mp_err res; + + /* read command-line arguments */ + for (i = 1; i < argv; i++) { + if ((strcasecmp(argc[i], "time") == 0) + || (strcasecmp(argc[i], "-time") == 0) + || (strcasecmp(argc[i], "--time") == 0)) { + ectestTime = 1; + } else if ((strcasecmp(argc[i], "print") == 0) + || (strcasecmp(argc[i], "-print") == 0) + || (strcasecmp(argc[i], "--print") == 0)) { + ectestPrint = 1; + } else { + printUsage(); + return 0; + } + } + + /* generic arithmetic tests */ + ECTEST_GENERIC_GF2M("SECT-131R1", ECCurve_SECG_CHAR2_131R1); + + /* specific arithmetic tests */ + ECTEST_NAMED_GF2M("NIST-K163", ECCurve_NIST_K163); + ECTEST_NAMED_GF2M("NIST-B163", ECCurve_NIST_B163); + ECTEST_NAMED_GF2M("NIST-K233", ECCurve_NIST_K233); + ECTEST_NAMED_GF2M("NIST-B233", ECCurve_NIST_B233); + ECTEST_NAMED_GF2M("NIST-K283", ECCurve_NIST_K283); + ECTEST_NAMED_GF2M("NIST-B283", ECCurve_NIST_B283); + ECTEST_NAMED_GF2M("NIST-K409", ECCurve_NIST_K409); + ECTEST_NAMED_GF2M("NIST-B409", ECCurve_NIST_B409); + ECTEST_NAMED_GF2M("NIST-K571", ECCurve_NIST_K571); + ECTEST_NAMED_GF2M("NIST-B571", ECCurve_NIST_B571); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB163V1", ECCurve_X9_62_CHAR2_PNB163V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB163V2", ECCurve_X9_62_CHAR2_PNB163V2); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB163V3", ECCurve_X9_62_CHAR2_PNB163V3); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB176V1", ECCurve_X9_62_CHAR2_PNB176V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB191V1", ECCurve_X9_62_CHAR2_TNB191V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB191V2", ECCurve_X9_62_CHAR2_TNB191V2); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB191V3", ECCurve_X9_62_CHAR2_TNB191V3); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB208W1", ECCurve_X9_62_CHAR2_PNB208W1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB239V1", ECCurve_X9_62_CHAR2_TNB239V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB239V2", ECCurve_X9_62_CHAR2_TNB239V2); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB239V3", ECCurve_X9_62_CHAR2_TNB239V3); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB272W1", ECCurve_X9_62_CHAR2_PNB272W1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB304W1", ECCurve_X9_62_CHAR2_PNB304W1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB359V1", ECCurve_X9_62_CHAR2_TNB359V1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2PNB368W1", ECCurve_X9_62_CHAR2_PNB368W1); + ECTEST_NAMED_GF2M("ANSI X9.62 C2TNB431R1", ECCurve_X9_62_CHAR2_TNB431R1); + ECTEST_NAMED_GF2M("SECT-113R1", ECCurve_SECG_CHAR2_113R1); + ECTEST_NAMED_GF2M("SECT-113R2", ECCurve_SECG_CHAR2_113R2); + ECTEST_NAMED_GF2M("SECT-131R1", ECCurve_SECG_CHAR2_131R1); + ECTEST_NAMED_GF2M("SECT-131R2", ECCurve_SECG_CHAR2_131R2); + ECTEST_NAMED_GF2M("SECT-163K1", ECCurve_SECG_CHAR2_163K1); + ECTEST_NAMED_GF2M("SECT-163R1", ECCurve_SECG_CHAR2_163R1); + ECTEST_NAMED_GF2M("SECT-163R2", ECCurve_SECG_CHAR2_163R2); + ECTEST_NAMED_GF2M("SECT-193R1", ECCurve_SECG_CHAR2_193R1); + ECTEST_NAMED_GF2M("SECT-193R2", ECCurve_SECG_CHAR2_193R2); + ECTEST_NAMED_GF2M("SECT-233K1", ECCurve_SECG_CHAR2_233K1); + ECTEST_NAMED_GF2M("SECT-233R1", ECCurve_SECG_CHAR2_233R1); + ECTEST_NAMED_GF2M("SECT-239K1", ECCurve_SECG_CHAR2_239K1); + ECTEST_NAMED_GF2M("SECT-283K1", ECCurve_SECG_CHAR2_283K1); + ECTEST_NAMED_GF2M("SECT-283R1", ECCurve_SECG_CHAR2_283R1); + ECTEST_NAMED_GF2M("SECT-409K1", ECCurve_SECG_CHAR2_409K1); + ECTEST_NAMED_GF2M("SECT-409R1", ECCurve_SECG_CHAR2_409R1); + ECTEST_NAMED_GF2M("SECT-571K1", ECCurve_SECG_CHAR2_571K1); + ECTEST_NAMED_GF2M("SECT-571R1", ECCurve_SECG_CHAR2_571R1); + ECTEST_NAMED_GF2M("WTLS-1 (113)", ECCurve_WTLS_1); + ECTEST_NAMED_GF2M("WTLS-3 (163)", ECCurve_WTLS_3); + ECTEST_NAMED_GF2M("WTLS-4 (113)", ECCurve_WTLS_4); + ECTEST_NAMED_GF2M("WTLS-5 (163)", ECCurve_WTLS_5); + ECTEST_NAMED_GF2M("WTLS-10 (233)", ECCurve_WTLS_10); + ECTEST_NAMED_GF2M("WTLS-11 (233)", ECCurve_WTLS_11); + + CLEANUP: + EC_FreeCurveParams(params); + ECGroup_free(group); + if (res != MP_OKAY) { + printf("Error: exiting with error value %i\n", res); + } + return res; +} diff --git a/nss/lib/freebl/ecl/tests/ec_naft.c b/nss/lib/freebl/ecl/tests/ec_naft.c new file mode 100644 index 0000000..833daea --- /dev/null +++ b/nss/lib/freebl/ecl/tests/ec_naft.c @@ -0,0 +1,117 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mpi.h" +#include "mplogic.h" +#include "ecl.h" +#include "ecp.h" +#include "ecl-priv.h" + +#include <sys/types.h> +#include <stdio.h> +#include <time.h> +#include <sys/time.h> +#include <sys/resource.h> + +/* Returns 2^e as an integer. This is meant to be used for small powers of + * two. */ +int ec_twoTo(int e); + +/* Number of bits of scalar to test */ +#define BITSIZE 160 + +/* Time k repetitions of operation op. */ +#define M_TimeOperation(op, k) { \ + double dStart, dNow, dUserTime; \ + struct rusage ru; \ + int i; \ + getrusage(RUSAGE_SELF, &ru); \ + dStart = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + for (i = 0; i < k; i++) { \ + { op; } \ + }; \ + getrusage(RUSAGE_SELF, &ru); \ + dNow = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + dUserTime = dNow-dStart; \ + if (dUserTime) printf(" %-45s\n k: %6i, t: %6.2f sec\n", #op, k, dUserTime); \ +} + +/* Tests wNAF computation. Non-adjacent-form is discussed in the paper: D. + * Hankerson, J. Hernandez and A. Menezes, "Software implementation of + * elliptic curve cryptography over binary fields", Proc. CHES 2000. */ + +mp_err +main(void) +{ + signed char naf[BITSIZE + 1]; + ECGroup *group = NULL; + mp_int k; + mp_int *scalar; + int i, count; + int res; + int w = 5; + char s[1000]; + + /* Get a 160 bit scalar to compute wNAF from */ + group = ECGroup_fromName(ECCurve_SECG_PRIME_160R1); + scalar = &group->genx; + + /* Compute wNAF representation of scalar */ + ec_compute_wNAF(naf, BITSIZE, scalar, w); + + /* Verify correctness of representation */ + mp_init(&k); /* init k to 0 */ + + for (i = BITSIZE; i >= 0; i--) { + mp_add(&k, &k, &k); + /* digits in mp_???_d are unsigned */ + if (naf[i] >= 0) { + mp_add_d(&k, naf[i], &k); + } else { + mp_sub_d(&k, -naf[i], &k); + } + } + + if (mp_cmp(&k, scalar) != 0) { + printf("Error: incorrect NAF value.\n"); + MP_CHECKOK(mp_toradix(&k, s, 16)); + printf("NAF value %s\n", s); + MP_CHECKOK(mp_toradix(scalar, s, 16)); + printf("original value %s\n", s); + goto CLEANUP; + } + + /* Verify digits of representation are valid */ + for (i = 0; i <= BITSIZE; i++) { + if (naf[i] % 2 == 0 && naf[i] != 0) { + printf("Error: Even non-zero digit found.\n"); + goto CLEANUP; + } + if (naf[i] < -(ec_twoTo(w - 1)) || naf[i] >= ec_twoTo(w - 1)) { + printf("Error: Magnitude of naf digit too large.\n"); + goto CLEANUP; + } + } + + /* Verify sparsity of representation */ + count = w - 1; + for (i = 0; i <= BITSIZE; i++) { + if (naf[i] != 0) { + if (count < w - 1) { + printf("Error: Sparsity failed.\n"); + goto CLEANUP; + } + count = 0; + } else + count++; + } + + /* Check timing */ + M_TimeOperation(ec_compute_wNAF(naf, BITSIZE, scalar, w), 10000); + + printf("Test passed.\n"); + CLEANUP: + ECGroup_free(group); + return MP_OKAY; +} diff --git a/nss/lib/freebl/ecl/tests/ecp_fpt.c b/nss/lib/freebl/ecl/tests/ecp_fpt.c new file mode 100644 index 0000000..3c91796 --- /dev/null +++ b/nss/lib/freebl/ecl/tests/ecp_fpt.c @@ -0,0 +1,1088 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ecp_fp.h" +#include "mpprime.h" + +#include <stdio.h> +#include <time.h> +#include <sys/time.h> +#include <sys/resource.h> + +/* Time k repetitions of operation op. */ +#define M_TimeOperation(op, k) { \ + double dStart, dNow, dUserTime; \ + struct rusage ru; \ + int i; \ + getrusage(RUSAGE_SELF, &ru); \ + dStart = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + for (i = 0; i < k; i++) { \ + { op; } \ + }; \ + getrusage(RUSAGE_SELF, &ru); \ + dNow = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + dUserTime = dNow-dStart; \ + if (dUserTime) printf(" %-45s\n k: %6i, t: %6.2f sec, k/t: %6.2f ops/sec\n", #op, k, dUserTime, k/dUserTime); \ +} + +/* Test curve using specific floating point field arithmetic. */ +#define M_TestCurve(name_c, name) { \ + printf("Testing %s using specific floating point implementation...\n", name_c); \ + ECGroup_free(ecgroup); \ + ecgroup = ECGroup_fromName(name); \ + if (ecgroup == NULL) { \ + printf(" Warning: could not construct group.\n"); \ + printf("%s failed.\n", name_c); \ + res = MP_NO; \ + goto CLEANUP; \ + } else { \ + MP_CHECKOK( testCurve(ecgroup)); \ + printf("%s passed.\n", name_c); \ + } \ +} + +/* Outputs a floating point double (currently not used) */ +void +d_output(const double *u, int len, char *name, const EC_group_fp * group) +{ + int i; + + printf("%s: ", name); + for (i = 0; i < len; i++) { + printf("+ %.2f * 2^%i ", u[i] / ecfp_exp[i], + group->doubleBitSize * i); + } + printf("\n"); +} + +/* Tests a point p in Jacobian coordinates, comparing against the + * expected affine result (x, y). */ +mp_err +testJacPoint(ecfp_jac_pt * p, mp_int *x, mp_int *y, ECGroup *ecgroup) +{ + char s[1000]; + mp_int rx, ry, rz; + mp_err res = MP_OKAY; + + MP_DIGITS(&rx) = 0; + MP_DIGITS(&ry) = 0; + MP_DIGITS(&rz) = 0; + + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&rz)); + + ecfp_fp2i(&rx, p->x, ecgroup); + ecfp_fp2i(&ry, p->y, ecgroup); + ecfp_fp2i(&rz, p->z, ecgroup); + + /* convert result R to affine coordinates */ + ec_GFp_pt_jac2aff(&rx, &ry, &rz, &rx, &ry, ecgroup); + + /* Compare to expected result */ + if ((mp_cmp(&rx, x) != 0) || (mp_cmp(&ry, y) != 0)) { + printf(" Error: Jacobian Floating Point Incorrect.\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("floating point result\nrx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + MP_CHECKOK(mp_toradix(x, s, 16)); + printf("integer result\nx %s\n", s); + MP_CHECKOK(mp_toradix(y, s, 16)); + printf("y %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + CLEANUP: + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&rz); + + return res; +} + +/* Tests a point p in Chudnovsky Jacobian coordinates, comparing against + * the expected affine result (x, y). */ +mp_err +testChudPoint(ecfp_chud_pt * p, mp_int *x, mp_int *y, ECGroup *ecgroup) +{ + + char s[1000]; + mp_int rx, ry, rz, rz2, rz3, test; + mp_err res = MP_OKAY; + + /* Initialization */ + MP_DIGITS(&rx) = 0; + MP_DIGITS(&ry) = 0; + MP_DIGITS(&rz) = 0; + MP_DIGITS(&rz2) = 0; + MP_DIGITS(&rz3) = 0; + MP_DIGITS(&test) = 0; + + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(mp_init(&rz2)); + MP_CHECKOK(mp_init(&rz3)); + MP_CHECKOK(mp_init(&test)); + + /* Convert to integers */ + ecfp_fp2i(&rx, p->x, ecgroup); + ecfp_fp2i(&ry, p->y, ecgroup); + ecfp_fp2i(&rz, p->z, ecgroup); + ecfp_fp2i(&rz2, p->z2, ecgroup); + ecfp_fp2i(&rz3, p->z3, ecgroup); + + /* Verify z2, z3 are valid */ + mp_sqrmod(&rz, &ecgroup->meth->irr, &test); + if (mp_cmp(&test, &rz2) != 0) { + printf(" Error: rzp2 not valid\n"); + res = MP_NO; + goto CLEANUP; + } + mp_mulmod(&test, &rz, &ecgroup->meth->irr, &test); + if (mp_cmp(&test, &rz3) != 0) { + printf(" Error: rzp2 not valid\n"); + res = MP_NO; + goto CLEANUP; + } + + /* convert result R to affine coordinates */ + ec_GFp_pt_jac2aff(&rx, &ry, &rz, &rx, &ry, ecgroup); + + /* Compare against expected result */ + if ((mp_cmp(&rx, x) != 0) || (mp_cmp(&ry, y) != 0)) { + printf(" Error: Chudnovsky Floating Point Incorrect.\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("floating point result\nrx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + MP_CHECKOK(mp_toradix(x, s, 16)); + printf("integer result\nx %s\n", s); + MP_CHECKOK(mp_toradix(y, s, 16)); + printf("y %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + CLEANUP: + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&rz); + mp_clear(&rz2); + mp_clear(&rz3); + mp_clear(&test); + + return res; +} + +/* Tests a point p in Modified Jacobian coordinates, comparing against the + * expected affine result (x, y). */ +mp_err +testJmPoint(ecfp_jm_pt * r, mp_int *x, mp_int *y, ECGroup *ecgroup) +{ + + char s[1000]; + mp_int rx, ry, rz, raz4, test; + mp_err res = MP_OKAY; + + /* Initialization */ + MP_DIGITS(&rx) = 0; + MP_DIGITS(&ry) = 0; + MP_DIGITS(&rz) = 0; + MP_DIGITS(&raz4) = 0; + MP_DIGITS(&test) = 0; + + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(mp_init(&raz4)); + MP_CHECKOK(mp_init(&test)); + + /* Convert to integer */ + ecfp_fp2i(&rx, r->x, ecgroup); + ecfp_fp2i(&ry, r->y, ecgroup); + ecfp_fp2i(&rz, r->z, ecgroup); + ecfp_fp2i(&raz4, r->az4, ecgroup); + + /* Verify raz4 = rz^4 * a */ + mp_sqrmod(&rz, &ecgroup->meth->irr, &test); + mp_sqrmod(&test, &ecgroup->meth->irr, &test); + mp_mulmod(&test, &ecgroup->curvea, &ecgroup->meth->irr, &test); + if (mp_cmp(&test, &raz4) != 0) { + printf(" Error: a*z^4 not valid\n"); + MP_CHECKOK(mp_toradix(&ecgroup->curvea, s, 16)); + printf("a %s\n", s); + MP_CHECKOK(mp_toradix(&rz, s, 16)); + printf("rz %s\n", s); + MP_CHECKOK(mp_toradix(&raz4, s, 16)); + printf("raz4 %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + /* convert result R to affine coordinates */ + ec_GFp_pt_jac2aff(&rx, &ry, &rz, &rx, &ry, ecgroup); + + /* Compare against expected result */ + if ((mp_cmp(&rx, x) != 0) || (mp_cmp(&ry, y) != 0)) { + printf(" Error: Modified Jacobian Floating Point Incorrect.\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("floating point result\nrx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + MP_CHECKOK(mp_toradix(x, s, 16)); + printf("integer result\nx %s\n", s); + MP_CHECKOK(mp_toradix(y, s, 16)); + printf("y %s\n", s); + res = MP_NO; + goto CLEANUP; + } + CLEANUP: + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&rz); + mp_clear(&raz4); + mp_clear(&test); + + return res; +} + +/* Tests point addition of Jacobian + Affine -> Jacobian */ +mp_err +testPointAddJacAff(ECGroup *ecgroup) +{ + mp_err res; + mp_int pz, rx2, ry2, rz2; + ecfp_jac_pt p, r; + ecfp_aff_pt q; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + /* Init */ + MP_DIGITS(&pz) = 0; + MP_DIGITS(&rx2) = 0; + MP_DIGITS(&ry2) = 0; + MP_DIGITS(&rz2) = 0; + MP_CHECKOK(mp_init(&pz)); + MP_CHECKOK(mp_init(&rx2)); + MP_CHECKOK(mp_init(&ry2)); + MP_CHECKOK(mp_init(&rz2)); + + MP_CHECKOK(mp_set_int(&pz, 5)); + + /* Set p */ + ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); + ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); + ecfp_i2fp(p.z, &pz, ecgroup); + /* Set q */ + ecfp_i2fp(q.x, &ecgroup->geny, ecgroup); + ecfp_i2fp(q.y, &ecgroup->genx, ecgroup); + + /* Do calculations */ + group->pt_add_jac_aff(&p, &q, &r, group); + + /* Do calculation in integer to compare against */ + MP_CHECKOK(ec_GFp_pt_add_jac_aff + (&ecgroup->genx, &ecgroup->geny, &pz, &ecgroup->geny, + &ecgroup->genx, &rx2, &ry2, &rz2, ecgroup)); + /* convert result R to affine coordinates */ + ec_GFp_pt_jac2aff(&rx2, &ry2, &rz2, &rx2, &ry2, ecgroup); + + MP_CHECKOK(testJacPoint(&r, &rx2, &ry2, ecgroup)); + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Addition - Jacobian & Affine\n"); + else + printf("TEST FAILED - Point Addition - Jacobian & Affine\n"); + + mp_clear(&pz); + mp_clear(&rx2); + mp_clear(&ry2); + mp_clear(&rz2); + + return res; +} + +/* Tests point addition in Jacobian coordinates */ +mp_err +testPointAddJac(ECGroup *ecgroup) +{ + mp_err res; + mp_int pz, qz, qx, qy, rx2, ry2, rz2; + ecfp_jac_pt p, q, r; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + /* Init */ + MP_DIGITS(&pz) = 0; + MP_DIGITS(&qx) = 0; + MP_DIGITS(&qy) = 0; + MP_DIGITS(&qz) = 0; + MP_DIGITS(&rx2) = 0; + MP_DIGITS(&ry2) = 0; + MP_DIGITS(&rz2) = 0; + MP_CHECKOK(mp_init(&pz)); + MP_CHECKOK(mp_init(&qx)); + MP_CHECKOK(mp_init(&qy)); + MP_CHECKOK(mp_init(&qz)); + MP_CHECKOK(mp_init(&rx2)); + MP_CHECKOK(mp_init(&ry2)); + MP_CHECKOK(mp_init(&rz2)); + + MP_CHECKOK(mp_set_int(&pz, 5)); + MP_CHECKOK(mp_set_int(&qz, 105)); + + /* Set p */ + ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); + ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); + ecfp_i2fp(p.z, &pz, ecgroup); + /* Set q */ + ecfp_i2fp(q.x, &ecgroup->geny, ecgroup); + ecfp_i2fp(q.y, &ecgroup->genx, ecgroup); + ecfp_i2fp(q.z, &qz, ecgroup); + + /* Do calculations */ + group->pt_add_jac(&p, &q, &r, group); + + /* Do calculation in integer to compare against */ + ec_GFp_pt_jac2aff(&ecgroup->geny, &ecgroup->genx, &qz, &qx, &qy, + ecgroup); + MP_CHECKOK(ec_GFp_pt_add_jac_aff + (&ecgroup->genx, &ecgroup->geny, &pz, &qx, &qy, &rx2, &ry2, + &rz2, ecgroup)); + /* convert result R to affine coordinates */ + ec_GFp_pt_jac2aff(&rx2, &ry2, &rz2, &rx2, &ry2, ecgroup); + + MP_CHECKOK(testJacPoint(&r, &rx2, &ry2, ecgroup)); + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Addition - Jacobian\n"); + else + printf("TEST FAILED - Point Addition - Jacobian\n"); + + mp_clear(&pz); + mp_clear(&qx); + mp_clear(&qy); + mp_clear(&qz); + mp_clear(&rx2); + mp_clear(&ry2); + mp_clear(&rz2); + + return res; +} + +/* Tests point addition in Chudnovsky Jacobian Coordinates */ +mp_err +testPointAddChud(ECGroup *ecgroup) +{ + mp_err res; + mp_int rx2, ry2, ix, iy, iz, test, pz, qx, qy, qz; + ecfp_chud_pt p, q, r; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + MP_DIGITS(&qx) = 0; + MP_DIGITS(&qy) = 0; + MP_DIGITS(&qz) = 0; + MP_DIGITS(&pz) = 0; + MP_DIGITS(&rx2) = 0; + MP_DIGITS(&ry2) = 0; + MP_DIGITS(&ix) = 0; + MP_DIGITS(&iy) = 0; + MP_DIGITS(&iz) = 0; + MP_DIGITS(&test) = 0; + + MP_CHECKOK(mp_init(&qx)); + MP_CHECKOK(mp_init(&qy)); + MP_CHECKOK(mp_init(&qz)); + MP_CHECKOK(mp_init(&pz)); + MP_CHECKOK(mp_init(&rx2)); + MP_CHECKOK(mp_init(&ry2)); + MP_CHECKOK(mp_init(&ix)); + MP_CHECKOK(mp_init(&iy)); + MP_CHECKOK(mp_init(&iz)); + MP_CHECKOK(mp_init(&test)); + + /* Test Chudnovsky form addition */ + /* Set p */ + MP_CHECKOK(mp_set_int(&pz, 5)); + ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); + ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); + ecfp_i2fp(p.z, &pz, ecgroup); + mp_sqrmod(&pz, &ecgroup->meth->irr, &test); + ecfp_i2fp(p.z2, &test, ecgroup); + mp_mulmod(&test, &pz, &ecgroup->meth->irr, &test); + ecfp_i2fp(p.z3, &test, ecgroup); + + /* Set q */ + MP_CHECKOK(mp_set_int(&qz, 105)); + ecfp_i2fp(q.x, &ecgroup->geny, ecgroup); + ecfp_i2fp(q.y, &ecgroup->genx, ecgroup); + ecfp_i2fp(q.z, &qz, ecgroup); + mp_sqrmod(&qz, &ecgroup->meth->irr, &test); + ecfp_i2fp(q.z2, &test, ecgroup); + mp_mulmod(&test, &qz, &ecgroup->meth->irr, &test); + ecfp_i2fp(q.z3, &test, ecgroup); + + group->pt_add_chud(&p, &q, &r, group); + + /* Calculate addition to compare against */ + ec_GFp_pt_jac2aff(&ecgroup->geny, &ecgroup->genx, &qz, &qx, &qy, + ecgroup); + ec_GFp_pt_add_jac_aff(&ecgroup->genx, &ecgroup->geny, &pz, &qx, &qy, + &ix, &iy, &iz, ecgroup); + ec_GFp_pt_jac2aff(&ix, &iy, &iz, &rx2, &ry2, ecgroup); + + MP_CHECKOK(testChudPoint(&r, &rx2, &ry2, ecgroup)); + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Addition - Chudnovsky Jacobian\n"); + else + printf("TEST FAILED - Point Addition - Chudnovsky Jacobian\n"); + + mp_clear(&qx); + mp_clear(&qy); + mp_clear(&qz); + mp_clear(&pz); + mp_clear(&rx2); + mp_clear(&ry2); + mp_clear(&ix); + mp_clear(&iy); + mp_clear(&iz); + mp_clear(&test); + + return res; +} + +/* Tests point addition in Modified Jacobian + Chudnovsky Jacobian -> + * Modified Jacobian coordinates. */ +mp_err +testPointAddJmChud(ECGroup *ecgroup) +{ + mp_err res; + mp_int rx2, ry2, ix, iy, iz, test, pz, paz4, qx, qy, qz; + ecfp_chud_pt q; + ecfp_jm_pt p, r; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + MP_DIGITS(&qx) = 0; + MP_DIGITS(&qy) = 0; + MP_DIGITS(&qz) = 0; + MP_DIGITS(&pz) = 0; + MP_DIGITS(&paz4) = 0; + MP_DIGITS(&iz) = 0; + MP_DIGITS(&rx2) = 0; + MP_DIGITS(&ry2) = 0; + MP_DIGITS(&ix) = 0; + MP_DIGITS(&iy) = 0; + MP_DIGITS(&iz) = 0; + MP_DIGITS(&test) = 0; + + MP_CHECKOK(mp_init(&qx)); + MP_CHECKOK(mp_init(&qy)); + MP_CHECKOK(mp_init(&qz)); + MP_CHECKOK(mp_init(&pz)); + MP_CHECKOK(mp_init(&paz4)); + MP_CHECKOK(mp_init(&rx2)); + MP_CHECKOK(mp_init(&ry2)); + MP_CHECKOK(mp_init(&ix)); + MP_CHECKOK(mp_init(&iy)); + MP_CHECKOK(mp_init(&iz)); + MP_CHECKOK(mp_init(&test)); + + /* Test Modified Jacobian form addition */ + /* Set p */ + ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); + ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); + ecfp_i2fp(group->curvea, &ecgroup->curvea, ecgroup); + /* paz4 = az^4 */ + MP_CHECKOK(mp_set_int(&pz, 5)); + mp_sqrmod(&pz, &ecgroup->meth->irr, &paz4); + mp_sqrmod(&paz4, &ecgroup->meth->irr, &paz4); + mp_mulmod(&paz4, &ecgroup->curvea, &ecgroup->meth->irr, &paz4); + ecfp_i2fp(p.z, &pz, ecgroup); + ecfp_i2fp(p.az4, &paz4, ecgroup); + + /* Set q */ + MP_CHECKOK(mp_set_int(&qz, 105)); + ecfp_i2fp(q.x, &ecgroup->geny, ecgroup); + ecfp_i2fp(q.y, &ecgroup->genx, ecgroup); + ecfp_i2fp(q.z, &qz, ecgroup); + mp_sqrmod(&qz, &ecgroup->meth->irr, &test); + ecfp_i2fp(q.z2, &test, ecgroup); + mp_mulmod(&test, &qz, &ecgroup->meth->irr, &test); + ecfp_i2fp(q.z3, &test, ecgroup); + + /* Do calculation */ + group->pt_add_jm_chud(&p, &q, &r, group); + + /* Calculate addition to compare against */ + ec_GFp_pt_jac2aff(&ecgroup->geny, &ecgroup->genx, &qz, &qx, &qy, + ecgroup); + ec_GFp_pt_add_jac_aff(&ecgroup->genx, &ecgroup->geny, &pz, &qx, &qy, + &ix, &iy, &iz, ecgroup); + ec_GFp_pt_jac2aff(&ix, &iy, &iz, &rx2, &ry2, ecgroup); + + MP_CHECKOK(testJmPoint(&r, &rx2, &ry2, ecgroup)); + + CLEANUP: + if (res == MP_OKAY) + printf + (" Test Passed - Point Addition - Modified & Chudnovsky Jacobian\n"); + else + printf + ("TEST FAILED - Point Addition - Modified & Chudnovsky Jacobian\n"); + + mp_clear(&qx); + mp_clear(&qy); + mp_clear(&qz); + mp_clear(&pz); + mp_clear(&paz4); + mp_clear(&rx2); + mp_clear(&ry2); + mp_clear(&ix); + mp_clear(&iy); + mp_clear(&iz); + mp_clear(&test); + + return res; +} + +/* Tests point doubling in Modified Jacobian coordinates */ +mp_err +testPointDoubleJm(ECGroup *ecgroup) +{ + mp_err res; + mp_int pz, paz4, rx2, ry2, rz2, raz4; + ecfp_jm_pt p, r; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + MP_DIGITS(&pz) = 0; + MP_DIGITS(&paz4) = 0; + MP_DIGITS(&rx2) = 0; + MP_DIGITS(&ry2) = 0; + MP_DIGITS(&rz2) = 0; + MP_DIGITS(&raz4) = 0; + + MP_CHECKOK(mp_init(&pz)); + MP_CHECKOK(mp_init(&paz4)); + MP_CHECKOK(mp_init(&rx2)); + MP_CHECKOK(mp_init(&ry2)); + MP_CHECKOK(mp_init(&rz2)); + MP_CHECKOK(mp_init(&raz4)); + + /* Set p */ + ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); + ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); + ecfp_i2fp(group->curvea, &ecgroup->curvea, ecgroup); + + /* paz4 = az^4 */ + MP_CHECKOK(mp_set_int(&pz, 5)); + mp_sqrmod(&pz, &ecgroup->meth->irr, &paz4); + mp_sqrmod(&paz4, &ecgroup->meth->irr, &paz4); + mp_mulmod(&paz4, &ecgroup->curvea, &ecgroup->meth->irr, &paz4); + + ecfp_i2fp(p.z, &pz, ecgroup); + ecfp_i2fp(p.az4, &paz4, ecgroup); + + group->pt_dbl_jm(&p, &r, group); + + M_TimeOperation(group->pt_dbl_jm(&p, &r, group), 100000); + + /* Calculate doubling to compare against */ + ec_GFp_pt_dbl_jac(&ecgroup->genx, &ecgroup->geny, &pz, &rx2, &ry2, + &rz2, ecgroup); + ec_GFp_pt_jac2aff(&rx2, &ry2, &rz2, &rx2, &ry2, ecgroup); + + /* Do comparison and check az^4 */ + MP_CHECKOK(testJmPoint(&r, &rx2, &ry2, ecgroup)); + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Doubling - Modified Jacobian\n"); + else + printf("TEST FAILED - Point Doubling - Modified Jacobian\n"); + mp_clear(&pz); + mp_clear(&paz4); + mp_clear(&rx2); + mp_clear(&ry2); + mp_clear(&rz2); + mp_clear(&raz4); + + return res; + +} + +/* Tests point doubling in Chudnovsky Jacobian coordinates */ +mp_err +testPointDoubleChud(ECGroup *ecgroup) +{ + mp_err res; + mp_int px, py, pz, rx2, ry2, rz2; + ecfp_aff_pt p; + ecfp_chud_pt p2; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + MP_DIGITS(&rx2) = 0; + MP_DIGITS(&ry2) = 0; + MP_DIGITS(&rz2) = 0; + MP_DIGITS(&px) = 0; + MP_DIGITS(&py) = 0; + MP_DIGITS(&pz) = 0; + + MP_CHECKOK(mp_init(&rx2)); + MP_CHECKOK(mp_init(&ry2)); + MP_CHECKOK(mp_init(&rz2)); + MP_CHECKOK(mp_init(&px)); + MP_CHECKOK(mp_init(&py)); + MP_CHECKOK(mp_init(&pz)); + + /* Set p2 = 2P */ + ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); + ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); + ecfp_i2fp(group->curvea, &ecgroup->curvea, ecgroup); + + group->pt_dbl_aff2chud(&p, &p2, group); + + /* Calculate doubling to compare against */ + MP_CHECKOK(mp_set_int(&pz, 1)); + ec_GFp_pt_dbl_jac(&ecgroup->genx, &ecgroup->geny, &pz, &rx2, &ry2, + &rz2, ecgroup); + ec_GFp_pt_jac2aff(&rx2, &ry2, &rz2, &rx2, &ry2, ecgroup); + + /* Do comparison and check az^4 */ + MP_CHECKOK(testChudPoint(&p2, &rx2, &ry2, ecgroup)); + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Doubling - Chudnovsky Jacobian\n"); + else + printf("TEST FAILED - Point Doubling - Chudnovsky Jacobian\n"); + + mp_clear(&rx2); + mp_clear(&ry2); + mp_clear(&rz2); + mp_clear(&px); + mp_clear(&py); + mp_clear(&pz); + + return res; +} + +/* Test point doubling in Jacobian coordinates */ +mp_err +testPointDoubleJac(ECGroup *ecgroup) +{ + mp_err res; + mp_int pz, rx, ry, rz, rx2, ry2, rz2; + ecfp_jac_pt p, p2; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + MP_DIGITS(&pz) = 0; + MP_DIGITS(&rx) = 0; + MP_DIGITS(&ry) = 0; + MP_DIGITS(&rz) = 0; + MP_DIGITS(&rx2) = 0; + MP_DIGITS(&ry2) = 0; + MP_DIGITS(&rz2) = 0; + + MP_CHECKOK(mp_init(&pz)); + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&rz)); + MP_CHECKOK(mp_init(&rx2)); + MP_CHECKOK(mp_init(&ry2)); + MP_CHECKOK(mp_init(&rz2)); + + MP_CHECKOK(mp_set_int(&pz, 5)); + + /* Set p2 = 2P */ + ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); + ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); + ecfp_i2fp(p.z, &pz, ecgroup); + ecfp_i2fp(group->curvea, &ecgroup->curvea, ecgroup); + + group->pt_dbl_jac(&p, &p2, group); + M_TimeOperation(group->pt_dbl_jac(&p, &p2, group), 100000); + + /* Calculate doubling to compare against */ + ec_GFp_pt_dbl_jac(&ecgroup->genx, &ecgroup->geny, &pz, &rx2, &ry2, + &rz2, ecgroup); + ec_GFp_pt_jac2aff(&rx2, &ry2, &rz2, &rx2, &ry2, ecgroup); + + /* Do comparison */ + MP_CHECKOK(testJacPoint(&p2, &rx2, &ry2, ecgroup)); + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Doubling - Jacobian\n"); + else + printf("TEST FAILED - Point Doubling - Jacobian\n"); + + mp_clear(&pz); + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&rz); + mp_clear(&rx2); + mp_clear(&ry2); + mp_clear(&rz2); + + return res; +} + +/* Tests a point multiplication (various algorithms) */ +mp_err +testPointMul(ECGroup *ecgroup) +{ + mp_err res; + char s[1000]; + mp_int rx, ry, order_1; + + /* Init */ + MP_DIGITS(&rx) = 0; + MP_DIGITS(&ry) = 0; + MP_DIGITS(&order_1) = 0; + + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&order_1)); + + MP_CHECKOK(mp_set_int(&order_1, 1)); + MP_CHECKOK(mp_sub(&ecgroup->order, &order_1, &order_1)); + + /* Test Algorithm 1: Jacobian-Affine Double & Add */ + ec_GFp_pt_mul_jac_fp(&order_1, &ecgroup->genx, &ecgroup->geny, &rx, + &ry, ecgroup); + MP_CHECKOK(ecgroup->meth->field_neg(&ry, &ry, ecgroup->meth)); + if ((mp_cmp(&rx, &ecgroup->genx) != 0) + || (mp_cmp(&ry, &ecgroup->geny) != 0)) { + printf + (" Error: ec_GFp_pt_mul_jac_fp invalid result (expected (- base point)).\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("rx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + ec_GFp_pt_mul_jac_fp(&ecgroup->order, &ecgroup->genx, &ecgroup->geny, + &rx, &ry, ecgroup); + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf + (" Error: ec_GFp_pt_mul_jac_fp invalid result (expected point at infinity.\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("rx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + /* Test Algorithm 2: 4-bit Window in Jacobian */ + ec_GFp_point_mul_jac_4w_fp(&order_1, &ecgroup->genx, &ecgroup->geny, + &rx, &ry, ecgroup); + MP_CHECKOK(ecgroup->meth->field_neg(&ry, &ry, ecgroup->meth)); + if ((mp_cmp(&rx, &ecgroup->genx) != 0) + || (mp_cmp(&ry, &ecgroup->geny) != 0)) { + printf + (" Error: ec_GFp_point_mul_jac_4w_fp invalid result (expected (- base point)).\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("rx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + ec_GFp_point_mul_jac_4w_fp(&ecgroup->order, &ecgroup->genx, + &ecgroup->geny, &rx, &ry, ecgroup); + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf + (" Error: ec_GFp_point_mul_jac_4w_fp invalid result (expected point at infinity.\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("rx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + /* Test Algorithm 3: wNAF with modified Jacobian coordinates */ + ec_GFp_point_mul_wNAF_fp(&order_1, &ecgroup->genx, &ecgroup->geny, &rx, + &ry, ecgroup); + MP_CHECKOK(ecgroup->meth->field_neg(&ry, &ry, ecgroup->meth)); + if ((mp_cmp(&rx, &ecgroup->genx) != 0) + || (mp_cmp(&ry, &ecgroup->geny) != 0)) { + printf + (" Error: ec_GFp_pt_mul_wNAF_fp invalid result (expected (- base point)).\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("rx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + ec_GFp_point_mul_wNAF_fp(&ecgroup->order, &ecgroup->genx, + &ecgroup->geny, &rx, &ry, ecgroup); + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf + (" Error: ec_GFp_pt_mul_wNAF_fp invalid result (expected point at infinity.\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf("rx %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf("ry %s\n", s); + res = MP_NO; + goto CLEANUP; + } + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Multiplication\n"); + else + printf("TEST FAILED - Point Multiplication\n"); + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&order_1); + + return res; +} + +/* Tests point multiplication with a random scalar repeatedly, comparing + * for consistency within different algorithms. */ +mp_err +testPointMulRandom(ECGroup *ecgroup) +{ + mp_err res; + mp_int rx, ry, rx2, ry2, n; + int i, size; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + + MP_DIGITS(&rx) = 0; + MP_DIGITS(&ry) = 0; + MP_DIGITS(&rx2) = 0; + MP_DIGITS(&ry2) = 0; + MP_DIGITS(&n) = 0; + + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&rx2)); + MP_CHECKOK(mp_init(&ry2)); + MP_CHECKOK(mp_init(&n)); + + for (i = 0; i < 100; i++) { + /* compute random scalar */ + size = mpl_significant_bits(&ecgroup->meth->irr); + if (size < MP_OKAY) { + res = MP_NO; + goto CLEANUP; + } + MP_CHECKOK(mpp_random_size(&n, group->orderBitSize)); + MP_CHECKOK(mp_mod(&n, &ecgroup->order, &n)); + + ec_GFp_pt_mul_jac(&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, + ecgroup); + ec_GFp_pt_mul_jac_fp(&n, &ecgroup->genx, &ecgroup->geny, &rx2, + &ry2, ecgroup); + + if ((mp_cmp(&rx, &rx2) != 0) || (mp_cmp(&ry, &ry2) != 0)) { + printf + (" Error: different results for Point Multiplication - Double & Add.\n"); + res = MP_NO; + goto CLEANUP; + } + + ec_GFp_point_mul_wNAF_fp(&n, &ecgroup->genx, &ecgroup->geny, &rx, + &ry, ecgroup); + if ((mp_cmp(&rx, &rx2) != 0) || (mp_cmp(&ry, &ry2) != 0)) { + printf + (" Error: different results for Point Multiplication - wNAF.\n"); + res = MP_NO; + goto CLEANUP; + } + + ec_GFp_point_mul_jac_4w_fp(&n, &ecgroup->genx, &ecgroup->geny, &rx, + &ry, ecgroup); + if ((mp_cmp(&rx, &rx2) != 0) || (mp_cmp(&ry, &ry2) != 0)) { + printf + (" Error: different results for Point Multiplication - 4 bit window.\n"); + res = MP_NO; + goto CLEANUP; + } + + } + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Random Multiplication\n"); + else + printf("TEST FAILED - Point Random Multiplication\n"); + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&rx2); + mp_clear(&ry2); + mp_clear(&n); + + return res; +} + +/* Tests the time required for a point multiplication */ +mp_err +testPointMulTime(ECGroup *ecgroup) +{ + mp_err res = MP_OKAY; + mp_int rx, ry, n; + int size; + + MP_DIGITS(&rx) = 0; + MP_DIGITS(&ry) = 0; + MP_DIGITS(&n) = 0; + + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&n)); + + /* compute random scalar */ + size = mpl_significant_bits(&ecgroup->meth->irr); + if (size < MP_OKAY) { + res = MP_NO; + goto CLEANUP; + } + + MP_CHECKOK(mpp_random_size(&n, (size + ECL_BITS - 1) / ECL_BITS)); + MP_CHECKOK(ecgroup->meth->field_mod(&n, &n, ecgroup->meth)); + + M_TimeOperation(ec_GFp_pt_mul_jac_fp + (&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, + ecgroup), 1000); + + M_TimeOperation(ec_GFp_point_mul_jac_4w_fp + (&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, + ecgroup), 1000); + + M_TimeOperation(ec_GFp_point_mul_wNAF_fp + (&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, + ecgroup), 1000); + + M_TimeOperation(ec_GFp_pt_mul_jac + (&n, &ecgroup->genx, &ecgroup->geny, &rx, &ry, + ecgroup), 100); + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Point Multiplication Timing\n"); + else + printf("TEST FAILED - Point Multiplication Timing\n"); + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&n); + + return res; +} + +/* Tests pre computation of Chudnovsky Jacobian points used in wNAF form */ +mp_err +testPreCompute(ECGroup *ecgroup) +{ + ecfp_chud_pt precomp[16]; + ecfp_aff_pt p; + EC_group_fp *group = (EC_group_fp *) ecgroup->extra1; + int i; + mp_err res; + + mp_int x, y, ny, x2, y2; + + MP_DIGITS(&x) = 0; + MP_DIGITS(&y) = 0; + MP_DIGITS(&ny) = 0; + MP_DIGITS(&x2) = 0; + MP_DIGITS(&y2) = 0; + + MP_CHECKOK(mp_init(&x)); + MP_CHECKOK(mp_init(&y)); + MP_CHECKOK(mp_init(&ny)); + MP_CHECKOK(mp_init(&x2)); + MP_CHECKOK(mp_init(&y2)); + + ecfp_i2fp(p.x, &ecgroup->genx, ecgroup); + ecfp_i2fp(p.y, &ecgroup->geny, ecgroup); + ecfp_i2fp(group->curvea, &(ecgroup->curvea), ecgroup); + + /* Perform precomputation */ + group->precompute_chud(precomp, &p, group); + + M_TimeOperation(group->precompute_chud(precomp, &p, group), 10000); + + /* Calculate addition to compare against */ + MP_CHECKOK(mp_copy(&ecgroup->genx, &x)); + MP_CHECKOK(mp_copy(&ecgroup->geny, &y)); + MP_CHECKOK(ecgroup->meth->field_neg(&y, &ny, ecgroup->meth)); + + ec_GFp_pt_dbl_aff(&x, &y, &x2, &y2, ecgroup); + + for (i = 0; i < 8; i++) { + MP_CHECKOK(testChudPoint(&precomp[8 + i], &x, &y, ecgroup)); + MP_CHECKOK(testChudPoint(&precomp[7 - i], &x, &ny, ecgroup)); + ec_GFp_pt_add_aff(&x, &y, &x2, &y2, &x, &y, ecgroup); + MP_CHECKOK(ecgroup->meth->field_neg(&y, &ny, ecgroup->meth)); + } + + CLEANUP: + if (res == MP_OKAY) + printf(" Test Passed - Precomputation\n"); + else + printf("TEST FAILED - Precomputation\n"); + + mp_clear(&x); + mp_clear(&y); + mp_clear(&ny); + mp_clear(&x2); + mp_clear(&y2); + return res; +} + +/* Given a curve using floating point arithmetic, test it. This method + * specifies which of the above tests to run. */ +mp_err +testCurve(ECGroup *ecgroup) +{ + int res = MP_OKAY; + + MP_CHECKOK(testPointAddJacAff(ecgroup)); + MP_CHECKOK(testPointAddJac(ecgroup)); + MP_CHECKOK(testPointAddChud(ecgroup)); + MP_CHECKOK(testPointAddJmChud(ecgroup)); + MP_CHECKOK(testPointDoubleJac(ecgroup)); + MP_CHECKOK(testPointDoubleChud(ecgroup)); + MP_CHECKOK(testPointDoubleJm(ecgroup)); + MP_CHECKOK(testPreCompute(ecgroup)); + MP_CHECKOK(testPointMul(ecgroup)); + MP_CHECKOK(testPointMulRandom(ecgroup)); + MP_CHECKOK(testPointMulTime(ecgroup)); + CLEANUP: + return res; +} + +/* Tests a number of curves optimized using floating point arithmetic */ +int +main(void) +{ + mp_err res = MP_OKAY; + ECGroup *ecgroup = NULL; + + /* specific arithmetic tests */ + M_TestCurve("SECG-160R1", ECCurve_SECG_PRIME_160R1); + M_TestCurve("SECG-192R1", ECCurve_SECG_PRIME_192R1); + M_TestCurve("SEGC-224R1", ECCurve_SECG_PRIME_224R1); + + CLEANUP: + ECGroup_free(ecgroup); + if (res != MP_OKAY) { + printf("Error: exiting with error value %i\n", res); + } + return res; +} diff --git a/nss/lib/freebl/ecl/tests/ecp_test.c b/nss/lib/freebl/ecl/tests/ecp_test.c new file mode 100644 index 0000000..e9a448e --- /dev/null +++ b/nss/lib/freebl/ecl/tests/ecp_test.c @@ -0,0 +1,426 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mpi.h" +#include "mplogic.h" +#include "mpprime.h" +#include "ecl.h" +#include "ecl-curve.h" +#include "ecp.h" +#include <stdio.h> +#include <strings.h> +#include <assert.h> + +#include <time.h> +#include <sys/time.h> +#include <sys/resource.h> + +/* Time k repetitions of operation op. */ +#define M_TimeOperation(op, k) { \ + double dStart, dNow, dUserTime; \ + struct rusage ru; \ + int i; \ + getrusage(RUSAGE_SELF, &ru); \ + dStart = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + for (i = 0; i < k; i++) { \ + { op; } \ + }; \ + getrusage(RUSAGE_SELF, &ru); \ + dNow = (double)ru.ru_utime.tv_sec+(double)ru.ru_utime.tv_usec*0.000001; \ + dUserTime = dNow-dStart; \ + if (dUserTime) printf(" %-45s k: %6i, t: %6.2f sec\n", #op, k, dUserTime); \ +} + +/* Test curve using generic field arithmetic. */ +#define ECTEST_GENERIC_GFP(name_c, name) \ + printf("Testing %s using generic implementation...\n", name_c); \ + params = EC_GetNamedCurveParams(name); \ + if (params == NULL) { \ + printf(" Error: could not construct params.\n"); \ + res = MP_NO; \ + goto CLEANUP; \ + } \ + ECGroup_free(group); \ + group = ECGroup_fromHex(params); \ + if (group == NULL) { \ + printf(" Error: could not construct group.\n"); \ + res = MP_NO; \ + goto CLEANUP; \ + } \ + MP_CHECKOK( ectest_curve_GFp(group, ectestPrint, ectestTime, 1) ); \ + printf("... okay.\n"); + +/* Test curve using specific field arithmetic. */ +#define ECTEST_NAMED_GFP(name_c, name) \ + printf("Testing %s using specific implementation...\n", name_c); \ + ECGroup_free(group); \ + group = ECGroup_fromName(name); \ + if (group == NULL) { \ + printf(" Warning: could not construct group.\n"); \ + printf("... failed; continuing with remaining tests.\n"); \ + } else { \ + MP_CHECKOK( ectest_curve_GFp(group, ectestPrint, ectestTime, 0) ); \ + printf("... okay.\n"); \ + } + +/* Performs basic tests of elliptic curve cryptography over prime fields. + * If tests fail, then it prints an error message, aborts, and returns an + * error code. Otherwise, returns 0. */ +int +ectest_curve_GFp(ECGroup *group, int ectestPrint, int ectestTime, + int generic) +{ + + mp_int one, order_1, gx, gy, rx, ry, n; + int size; + mp_err res; + char s[1000]; + + /* initialize values */ + MP_CHECKOK(mp_init(&one)); + MP_CHECKOK(mp_init(&order_1)); + MP_CHECKOK(mp_init(&gx)); + MP_CHECKOK(mp_init(&gy)); + MP_CHECKOK(mp_init(&rx)); + MP_CHECKOK(mp_init(&ry)); + MP_CHECKOK(mp_init(&n)); + + MP_CHECKOK(mp_set_int(&one, 1)); + MP_CHECKOK(mp_sub(&group->order, &one, &order_1)); + + /* encode base point */ + if (group->meth->field_dec) { + MP_CHECKOK(group->meth->field_dec(&group->genx, &gx, group->meth)); + MP_CHECKOK(group->meth->field_dec(&group->geny, &gy, group->meth)); + } else { + MP_CHECKOK(mp_copy(&group->genx, &gx)); + MP_CHECKOK(mp_copy(&group->geny, &gy)); + } + if (ectestPrint) { + /* output base point */ + printf(" base point P:\n"); + MP_CHECKOK(mp_toradix(&gx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&gy, s, 16)); + printf(" %s\n", s); + if (group->meth->field_enc) { + printf(" base point P (encoded):\n"); + MP_CHECKOK(mp_toradix(&group->genx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&group->geny, s, 16)); + printf(" %s\n", s); + } + } + +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GFp_pt_mul_aff + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (affine):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_neg(&ry, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ec_GFp_pt_mul_jac + (&order_1, &group->genx, &group->geny, &rx, &ry, group)); + if (ectestPrint) { + printf(" (order-1)*P (jacobian):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(group->meth->field_neg(&ry, &ry, group->meth)); + if ((mp_cmp(&rx, &group->genx) != 0) + || (mp_cmp(&ry, &group->geny) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ECPoint_mul(group, &order_1, NULL, NULL, &rx, &ry)); + if (ectestPrint) { + printf(" (order-1)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* multiply base point by order - 1 and check for negative of base + * point */ + MP_CHECKOK(ECPoint_mul(group, &order_1, &gx, &gy, &rx, &ry)); + if (ectestPrint) { + printf(" (order-1)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GFp_pt_mul_aff + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (affine):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + +#ifdef ECL_ENABLE_GFP_PT_MUL_JAC + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ec_GFp_pt_mul_jac + (&group->order, &group->genx, &group->geny, &rx, &ry, + group)); + if (ectestPrint) { + printf(" (order)*P (jacobian):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } +#endif + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ECPoint_mul(group, &group->order, NULL, NULL, &rx, &ry)); + if (ectestPrint) { + printf(" (order)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* multiply base point by order and check for point at infinity */ + MP_CHECKOK(ECPoint_mul(group, &group->order, &gx, &gy, &rx, &ry)); + if (ectestPrint) { + printf(" (order)*P (ECPoint_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + if (ec_GFp_pt_is_inf_aff(&rx, &ry) != MP_YES) { + printf(" Error: invalid result (expected point at infinity).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* check that (order-1)P + (order-1)P + P == (order-1)P */ + MP_CHECKOK(ECPoints_mul + (group, &order_1, &order_1, &gx, &gy, &rx, &ry)); + MP_CHECKOK(ECPoints_mul(group, &one, &one, &rx, &ry, &rx, &ry)); + if (ectestPrint) { + printf + (" (order-1)*P + (order-1)*P + P == (order-1)*P (ECPoints_mul):\n"); + MP_CHECKOK(mp_toradix(&rx, s, 16)); + printf(" %s\n", s); + MP_CHECKOK(mp_toradix(&ry, s, 16)); + printf(" %s\n", s); + } + MP_CHECKOK(mp_submod(&group->meth->irr, &ry, &group->meth->irr, &ry)); + if ((mp_cmp(&rx, &gx) != 0) || (mp_cmp(&ry, &gy) != 0)) { + printf(" Error: invalid result (expected (- base point)).\n"); + res = MP_NO; + goto CLEANUP; + } + + /* test validate_point function */ + if (ECPoint_validate(group, &gx, &gy) != MP_YES) { + printf(" Error: validate point on base point failed.\n"); + res = MP_NO; + goto CLEANUP; + } + MP_CHECKOK(mp_add_d(&gy, 1, &ry)); + if (ECPoint_validate(group, &gx, &ry) != MP_NO) { + printf(" Error: validate point on invalid point passed.\n"); + res = MP_NO; + goto CLEANUP; + } + + if (ectestTime) { + /* compute random scalar */ + size = mpl_significant_bits(&group->meth->irr); + if (size < MP_OKAY) { + goto CLEANUP; + } + MP_CHECKOK(mpp_random_size(&n, (size + ECL_BITS - 1) / ECL_BITS)); + MP_CHECKOK(group->meth->field_mod(&n, &n, group->meth)); + /* timed test */ + if (generic) { +#ifdef ECL_ENABLE_GFP_PT_MUL_AFF + M_TimeOperation(MP_CHECKOK + (ec_GFp_pt_mul_aff + (&n, &group->genx, &group->geny, &rx, &ry, + group)), 100); +#endif + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoints_mul + (group, &n, &n, &gx, &gy, &rx, &ry)), 100); + } else { + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, NULL, NULL, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoint_mul(group, &n, &gx, &gy, &rx, &ry)), + 100); + M_TimeOperation(MP_CHECKOK + (ECPoints_mul + (group, &n, &n, &gx, &gy, &rx, &ry)), 100); + } + } + + CLEANUP: + mp_clear(&one); + mp_clear(&order_1); + mp_clear(&gx); + mp_clear(&gy); + mp_clear(&rx); + mp_clear(&ry); + mp_clear(&n); + if (res != MP_OKAY) { + printf(" Error: exiting with error value %i\n", res); + } + return res; +} + +/* Prints help information. */ +void +printUsage() +{ + printf("Usage: ecp_test [--print] [--time]\n"); + printf + (" --print Print out results of each point arithmetic test.\n"); + printf + (" --time Benchmark point operations and print results.\n"); +} + +/* Performs tests of elliptic curve cryptography over prime fields If + * tests fail, then it prints an error message, aborts, and returns an + * error code. Otherwise, returns 0. */ +int +main(int argv, char **argc) +{ + + int ectestTime = 0; + int ectestPrint = 0; + int i; + ECGroup *group = NULL; + ECCurveParams *params = NULL; + mp_err res; + + /* read command-line arguments */ + for (i = 1; i < argv; i++) { + if ((strcasecmp(argc[i], "time") == 0) + || (strcasecmp(argc[i], "-time") == 0) + || (strcasecmp(argc[i], "--time") == 0)) { + ectestTime = 1; + } else if ((strcasecmp(argc[i], "print") == 0) + || (strcasecmp(argc[i], "-print") == 0) + || (strcasecmp(argc[i], "--print") == 0)) { + ectestPrint = 1; + } else { + printUsage(); + return 0; + } + } + + /* generic arithmetic tests */ + ECTEST_GENERIC_GFP("SECP-160R1", ECCurve_SECG_PRIME_160R1); + + /* specific arithmetic tests */ + ECTEST_NAMED_GFP("NIST-P192", ECCurve_NIST_P192); + ECTEST_NAMED_GFP("NIST-P224", ECCurve_NIST_P224); + ECTEST_NAMED_GFP("NIST-P256", ECCurve_NIST_P256); + ECTEST_NAMED_GFP("NIST-P384", ECCurve_NIST_P384); + ECTEST_NAMED_GFP("NIST-P521", ECCurve_NIST_P521); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME192v1", ECCurve_X9_62_PRIME_192V1); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME192v2", ECCurve_X9_62_PRIME_192V2); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME192v3", ECCurve_X9_62_PRIME_192V3); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME239v1", ECCurve_X9_62_PRIME_239V1); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME239v2", ECCurve_X9_62_PRIME_239V2); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME239v3", ECCurve_X9_62_PRIME_239V3); + ECTEST_NAMED_GFP("ANSI X9.62 PRIME256v1", ECCurve_X9_62_PRIME_256V1); + ECTEST_NAMED_GFP("SECP-112R1", ECCurve_SECG_PRIME_112R1); + ECTEST_NAMED_GFP("SECP-112R2", ECCurve_SECG_PRIME_112R2); + ECTEST_NAMED_GFP("SECP-128R1", ECCurve_SECG_PRIME_128R1); + ECTEST_NAMED_GFP("SECP-128R2", ECCurve_SECG_PRIME_128R2); + ECTEST_NAMED_GFP("SECP-160K1", ECCurve_SECG_PRIME_160K1); + ECTEST_NAMED_GFP("SECP-160R1", ECCurve_SECG_PRIME_160R1); + ECTEST_NAMED_GFP("SECP-160R2", ECCurve_SECG_PRIME_160R2); + ECTEST_NAMED_GFP("SECP-192K1", ECCurve_SECG_PRIME_192K1); + ECTEST_NAMED_GFP("SECP-192R1", ECCurve_SECG_PRIME_192R1); + ECTEST_NAMED_GFP("SECP-224K1", ECCurve_SECG_PRIME_224K1); + ECTEST_NAMED_GFP("SECP-224R1", ECCurve_SECG_PRIME_224R1); + ECTEST_NAMED_GFP("SECP-256K1", ECCurve_SECG_PRIME_256K1); + ECTEST_NAMED_GFP("SECP-256R1", ECCurve_SECG_PRIME_256R1); + ECTEST_NAMED_GFP("SECP-384R1", ECCurve_SECG_PRIME_384R1); + ECTEST_NAMED_GFP("SECP-521R1", ECCurve_SECG_PRIME_521R1); + ECTEST_NAMED_GFP("WTLS-6 (112)", ECCurve_WTLS_6); + ECTEST_NAMED_GFP("WTLS-7 (160)", ECCurve_WTLS_7); + ECTEST_NAMED_GFP("WTLS-8 (112)", ECCurve_WTLS_8); + ECTEST_NAMED_GFP("WTLS-9 (160)", ECCurve_WTLS_9); + ECTEST_NAMED_GFP("WTLS-12 (224)", ECCurve_WTLS_12); + + CLEANUP: + EC_FreeCurveParams(params); + ECGroup_free(group); + if (res != MP_OKAY) { + printf("Error: exiting with error value %i\n", res); + } + return res; +} |