Skip to content

Commit

Permalink
Merge magnitude/normalized fields, move/improve comments
Browse files Browse the repository at this point in the history
Also split secp256k1_fe_verify into a generic and an implementation
specific part.
  • Loading branch information
sipa committed May 11, 2023
1 parent 341cc19 commit b29566c
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 61 deletions.
45 changes: 34 additions & 11 deletions src/field.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,36 @@
#ifndef SECP256K1_FIELD_H
#define SECP256K1_FIELD_H

/** Field element module.
*
* Field elements can be represented in several ways, but code accessing
* it (and implementations) need to take certain properties into account:
* - Each field element can be normalized or not.
* - Each field element has a magnitude, which represents how far away
* its representation is away from normalization. Normalized elements
* always have a magnitude of 0 or 1, but a magnitude of 1 doesn't
* imply normality.
*/

#include "util.h"

/* This file defines the generic interface for working with secp256k1_fe
* objects, which represent field elements (integers modulo 2^256 - 2^32 - 977).
*
* The actual definition of the secp256k1_fe type depends on the chosen field
* implementation; see the field_5x52.h and field_10x26.h files for details.
*
* All secp256k1_fe objects have implicit properties that determine what
* operations are permitted on it. These are purely a function of what
* secp256k1_fe_ operations are applied on it, generally (implicitly) fixed at
* compile time, and do not depend on the chosen field implementation. Despite
* that, what these properties actually entail for the field representation
* values depends on the chosen field implementation. These properties are:
* - magnitude: an integer in [0,32]
* - normalized: 0 or 1; normalized=1 implies magnitude <= 1.
*
* In VERIFY mode, they are materialized explicitly as fields in the struct,
* allowing run-time verification of these properties. In that case, the field
* implementation also provides a secp256k1_fe_verify routine to verify that
* these fields match the run-time value and perform internal consistency
* checks. */
#ifdef VERIFY
# define SECP256K1_FE_VERIFY_FIELDS \
int magnitude; \
int normalized;
#else
# define SECP256K1_FE_VERIFY_FIELDS
#endif

#if defined(SECP256K1_WIDEMUL_INT128)
#include "field_5x52.h"
#elif defined(SECP256K1_WIDEMUL_INT64)
Expand All @@ -34,6 +51,12 @@ static const secp256k1_fe secp256k1_const_beta = SECP256K1_FE_CONST(
0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul
);

#ifndef VERIFY
/* In non-VERIFY mode, we #define the fe operations to be identical to their
* internal field implementation, to avoid the potential overhead of a
* function call (even though presumably inlinable). */
#endif /* !defined(VERIFY) */

/** Normalize a field element. This brings the field element to a canonical representation, reduces
* its magnitude to 1, and reduces it modulo field size `p`.
*/
Expand Down
27 changes: 20 additions & 7 deletions src/field_10x26.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,28 @@

#include <stdint.h>

/** This field implementation represents the value as 10 uint32_t limbs in base
* 2^26. */
typedef struct {
/* X = sum(i=0..9, n[i]*2^(i*26)) mod p
* where p = 2^256 - 0x1000003D1
*/
/* A field element f represents the sum(i=0..9, f.n[i] << (i*26)) mod p,
* where p is the field modulus, 2^256 - 2^32 - 977.
*
* The individual limbs f.n[i] can exceed 2^26; the field's magnitude roughly
* corresponds to how much excess is allowed. The value
* sum(i=0..9, f.n[i] << (i*26)) may exceed p, unless the field element is
* normalized. */
uint32_t n[10];
#ifdef VERIFY
int magnitude;
int normalized;
#endif
/*
* Magnitude m requires:
* n[i] <= 2 * m * (2^26 - 1) for i=0..8
* n[9] <= 2 * m * (2^22 - 1)
*
* Normalized requires:
* n[i] <= (2^26 - 1) for i=0..8
* sum(i=0..9, n[i] << (i*26)) < p
* (together these imply n[9] <= 2^22 - 1)
*/
SECP256K1_FE_VERIFY_FIELDS
} secp256k1_fe;

/* Unpacks a constant into a overlapping multi-limbed FE element. */
Expand Down
17 changes: 2 additions & 15 deletions src/field_10x26_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,8 @@
#include "field.h"
#include "modinv32_impl.h"

/** See the comment at the top of field_5x52_impl.h for more details.
*
* Here, we represent field elements as 10 uint32_t's in base 2^26, least significant first,
* where limbs can contain >26 bits.
* A magnitude M means:
* - 2*M*(2^22-1) is the max (inclusive) of the most significant limb
* - 2*M*(2^26-1) is the max (inclusive) of the remaining limbs
*/

static void secp256k1_fe_verify(const secp256k1_fe *a) {
#ifdef VERIFY
static void secp256k1_fe_impl_verify(const secp256k1_fe *a) {
const uint32_t *d = a->n;
int m = a->normalized ? 1 : 2 * a->magnitude, r = 1;
r &= (d[0] <= 0x3FFFFFFUL * m);
Expand All @@ -35,10 +26,7 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
r &= (d[7] <= 0x3FFFFFFUL * m);
r &= (d[8] <= 0x3FFFFFFUL * m);
r &= (d[9] <= 0x03FFFFFUL * m);
r &= (a->magnitude >= 0);
r &= (a->magnitude <= 32);
if (a->normalized) {
r &= (a->magnitude <= 1);
if (r && (d[9] == 0x03FFFFFUL)) {
uint32_t mid = d[8] & d[7] & d[6] & d[5] & d[4] & d[3] & d[2];
if (mid == 0x3FFFFFFUL) {
Expand All @@ -47,9 +35,8 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
}
}
VERIFY_CHECK(r == 1);
#endif
(void)a;
}
#endif

static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) {
VERIFY_CHECK(m >= 0);
Expand Down
27 changes: 20 additions & 7 deletions src/field_5x52.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,28 @@

#include <stdint.h>

/** This field implementation represents the value as 5 uint64_t limbs in base
* 2^52. */
typedef struct {
/* X = sum(i=0..4, n[i]*2^(i*52)) mod p
* where p = 2^256 - 0x1000003D1
*/
/* A field element f represents the sum(i=0..4, f.n[i] << (i*52)) mod p,
* where p is the field modulus, 2^256 - 2^32 - 977.
*
* The individual limbs f.n[i] can exceed 2^52; the field's magnitude roughly
* corresponds to how much excess is allowed. The value
* sum(i=0..4, f.n[i] << (i*52)) may exceed p, unless the field element is
* normalized. */
uint64_t n[5];
#ifdef VERIFY
int magnitude;
int normalized;
#endif
/*
* Magnitude m requires:
* n[i] <= 2 * m * (2^52 - 1) for i=0..3
* n[4] <= 2 * m * (2^48 - 1)
*
* Normalized requires:
* n[i] <= (2^52 - 1) for i=0..3
* sum(i=0..4, n[i] << (i*52)) < p
* (together these imply n[4] <= 2^48 - 1)
*/
SECP256K1_FE_VERIFY_FIELDS
} secp256k1_fe;

/* Unpacks a constant into a overlapping multi-limbed FE element. */
Expand Down
23 changes: 2 additions & 21 deletions src/field_5x52_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,8 @@
#include "field_5x52_int128_impl.h"
#endif

/** Implements arithmetic modulo FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F,
* represented as 5 uint64_t's in base 2^52, least significant first. Note that the limbs are allowed to
* contain >52 bits each.
*
* Each field element has a 'magnitude' associated with it. Internally, a magnitude M means:
* - 2*M*(2^48-1) is the max (inclusive) of the most significant limb
* - 2*M*(2^52-1) is the max (inclusive) of the remaining limbs
*
* Operations have different rules for propagating magnitude to their outputs. If an operation takes a
* magnitude M as a parameter, that means the magnitude of input field elements can be at most M (inclusive).
*
* Each field element also has a 'normalized' flag. A field element is normalized if its magnitude is either
* 0 or 1, and its value is already reduced modulo the order of the field.
*/

static void secp256k1_fe_verify(const secp256k1_fe *a) {
#ifdef VERIFY
static void secp256k1_fe_impl_verify(const secp256k1_fe *a) {
const uint64_t *d = a->n;
int m = a->normalized ? 1 : 2 * a->magnitude, r = 1;
/* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */
Expand All @@ -43,18 +28,14 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m);
r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m);
r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m);
r &= (a->magnitude >= 0);
r &= (a->magnitude <= 2048);
if (a->normalized) {
r &= (a->magnitude <= 1);
if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) {
r &= (d[0] < 0xFFFFEFFFFFC2FULL);
}
}
VERIFY_CHECK(r == 1);
#endif
(void)a;
}
#endif

static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) {
VERIFY_CHECK(m >= 0);
Expand Down
18 changes: 18 additions & 0 deletions src/field_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#ifndef SECP256K1_FIELD_IMPL_H
#define SECP256K1_FIELD_IMPL_H

#include "field.h"
#include "util.h"

#if defined(SECP256K1_WIDEMUL_INT128)
Expand Down Expand Up @@ -131,4 +132,21 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
return secp256k1_fe_equal(&t1, a);
}

#ifndef VERIFY
static void secp256k1_fe_verify(const secp256k1_fe *a) { (void)a; }
#else
static void secp256k1_fe_impl_verify(const secp256k1_fe *a);
static void secp256k1_fe_verify(const secp256k1_fe *a) {
/* Magnitude between 0 and 32. */
int r = (a->magnitude >= 0) & (a->magnitude <= 32);
/* Normalized is 0 or 1. */
r &= (a->normalized == 0) | (a->normalized == 1);
/* If normalized, magnitude must be 0 or 1. */
if (a->normalized) r &= (a->magnitude <= 1);
VERIFY_CHECK(r == 1);
/* Invoke implementation-specific checks. */
secp256k1_fe_impl_verify(a);
}
#endif /* defined(VERIFY) */

#endif /* SECP256K1_FIELD_IMPL_H */

0 comments on commit b29566c

Please sign in to comment.