UltrafastSecp256k1 3.50.0
Ultra high-performance secp256k1 elliptic curve cryptography library
Loading...
Searching...
No Matches
field_26.hpp
Go to the documentation of this file.
1#ifndef SECP256K1_FIELD_26_HPP
2#define SECP256K1_FIELD_26_HPP
3#pragma once
4
5// ============================================================================
6// 10x26-bit Field Element for secp256k1 (Lazy-Reduction for 32-bit Platforms)
7// ============================================================================
8//
9// Alternative representation: 10 limbs x 26 bits each in uint32_t[10].
10// The upper 6 bits per limb provide "headroom" for lazy reduction:
11//
12// Addition = 10 plain adds (NO carry propagation!)
13// Sub = 10 adds (pre-add 2p to avoid underflow, NO borrow!)
14// Mul/Sqr = native 10x26 with inline secp256k1 reduction
15//
16// Headroom budget: 6 bits -> up to 64 additions without normalization.
17// In practice, ECC point operations need <=50 chained additions.
18//
19// This is the 32-bit counterpart of FieldElement52 (5x52 for 64-bit CPUs).
20// Targets: ESP32 (Xtensa LX6/LX7), STM32 (Cortex-M3/M4), any 32-bit CPU.
21//
22// FieldElement (4x64) -> optimal for: 64-bit CPUs, serialization, I/O
23// FieldElement52 (5x52) -> optimal for: 64-bit ECC point ops (lazy adds)
24// FieldElement26 (10x26)-> optimal for: 32-bit ECC point ops (lazy adds)
25//
26// Multiplication/squaring adapted from bitcoin-core/secp256k1 field_10x26_impl.h
27// (MIT license, Copyright (c) 2013-2024 Pieter Wuille and contributors)
28// ============================================================================
29
30#include <cstdint>
31#include <array>
32#include "secp256k1/field.hpp"
33
34namespace secp256k1::fast {
35
36// -- Constants ----------------------------------------------------------------
37namespace fe26_constants {
38 // Mask for 26 bits
39 constexpr std::uint32_t M26 = 0x3FFFFFFU; // (1 << 26) - 1
40 // Mask for 22 bits (top limb)
41 constexpr std::uint32_t M22 = 0x3FFFFFU; // (1 << 22) - 1
42
43 // p in 10x26 representation
44 // p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
45 //
46 // Split into 26-bit chunks from LSB:
47 // bits [0..25] = 0x3FFFC2F
48 // bits [26..51] = 0x3FFFFBF (note: FFFFFFEFFFFC2F -> ...03FFFFBF at this position)
49 // bits [52..77] = 0x3FFFFFF
50 // ...
51 // bits [234..255] = 0x3FFFFF (22 bits)
52 //
53 // Actually: p = 2^256 - 0x1000003D1
54 // limb 0: bits[0..25] -> lower 26 of 0xFFFFFC2F = 0x3FFFC2F
55 // limb 1: bits[26..51] -> (0xFFFFFFFEFFFFFC2F >> 26) & M26 = 0x3FFFFBF
56 // limb 2: bits[52..77] -> all 1s = 0x3FFFFFF
57 // limb 3..8: all 1s = 0x3FFFFFF
58 // limb 9: bits[234..255] -> 22 bits, all 1s = 0x3FFFFF
59 constexpr std::uint32_t P0 = 0x3FFFC2FU;
60 constexpr std::uint32_t P1 = 0x3FFFFBFU;
61 constexpr std::uint32_t P2 = 0x3FFFFFFU;
62 constexpr std::uint32_t P3 = 0x3FFFFFFU;
63 constexpr std::uint32_t P4 = 0x3FFFFFFU;
64 constexpr std::uint32_t P5 = 0x3FFFFFFU;
65 constexpr std::uint32_t P6 = 0x3FFFFFFU;
66 constexpr std::uint32_t P7 = 0x3FFFFFFU;
67 constexpr std::uint32_t P8 = 0x3FFFFFFU;
68 constexpr std::uint32_t P9 = 0x3FFFFFU; // 22 bits
69}
70
71// -- 10x26 Field Element ------------------------------------------------------
72struct alignas(4) FieldElement26 {
73 std::uint32_t n[10]; // Each limb holds <=26 bits when normalized (magnitude=1)
74
75 // -- Construction -------------------------------------------------
76 static FieldElement26 zero() noexcept;
77 static FieldElement26 one() noexcept;
78
79 // -- Conversion (4x64 <-> 10x26) -----------------------------------
80 //
81 // 4x64 bit layout: [bits 0..63] [64..127] [128..191] [192..255]
82 // 10x26 bit layout: [bits 0..25] [26..51] [52..77] [78..103]
83 // [104..129] [130..155] [156..181] [182..207]
84 // [208..233] [234..255]
85 //
86 static FieldElement26 from_fe(const FieldElement& fe) noexcept;
87 FieldElement to_fe() const noexcept; // Normalizes first!
88
89 // -- Normalization ------------------------------------------------
90 // Weak: carry-propagate so each limb <= 26 bits, but result may be >= p
91 void normalize_weak() noexcept;
92 // Full: canonical result in [0, p)
93 void normalize() noexcept;
94
95 // -- Lazy Arithmetic (NO carry propagation!) ----------------------
96 // These just do 10 plain adds per operation. Caller is responsible
97 // for normalizing before limbs would exceed 32 bits (after ~64 adds).
98
99 FieldElement26 operator+(const FieldElement26& rhs) const noexcept;
100 void add_assign(const FieldElement26& rhs) noexcept;
101
102 // -- Negate -------------------------------------------------------
103 // Computes (magnitude+1)*p - a to ensure positive result.
104 FieldElement26 negate(unsigned magnitude) const noexcept;
105 void negate_assign(unsigned magnitude) noexcept;
106
107 // -- Fully-Reduced Arithmetic -------------------------------------
108 // Multiplication and squaring produce normalized output (magnitude=1).
109 FieldElement26 operator*(const FieldElement26& rhs) const noexcept;
110 FieldElement26 square() const noexcept;
111
112 // In-place variants
113 void mul_assign(const FieldElement26& rhs) noexcept;
114 void square_inplace() noexcept;
115
116 // -- Comparison (requires normalized inputs!) ---------------------
117 bool is_zero() const noexcept;
118 bool operator==(const FieldElement26& rhs) const noexcept;
119
120 // -- Half ---------------------------------------------------------
121 // Computes a/2 mod p. Branchless.
122 FieldElement26 half() const noexcept;
123};
124
125// -- Free Functions (hot-path, avoid vtable/method overhead) ------------------
126void fe26_mul_inner(std::uint32_t* r, const std::uint32_t* a,
127 const std::uint32_t* b) noexcept;
128void fe26_sqr_inner(std::uint32_t* r, const std::uint32_t* a) noexcept;
129void fe26_normalize(std::uint32_t* r) noexcept;
130void fe26_normalize_weak(std::uint32_t* r) noexcept;
131
132// Compile-time layout verification
133static_assert(sizeof(FieldElement26) == 40, "FieldElement26 must be 40 bytes (10x4)");
134
135} // namespace secp256k1::fast
136
137#endif // SECP256K1_FIELD_26_HPP
constexpr std::uint32_t P8
Definition field_26.hpp:67
constexpr std::uint32_t P0
Definition field_26.hpp:59
constexpr std::uint32_t P4
Definition field_26.hpp:63
constexpr std::uint32_t P5
Definition field_26.hpp:64
constexpr std::uint32_t M22
Definition field_26.hpp:41
constexpr std::uint32_t P7
Definition field_26.hpp:66
constexpr std::uint32_t P2
Definition field_26.hpp:61
constexpr std::uint32_t P1
Definition field_26.hpp:60
constexpr std::uint32_t P9
Definition field_26.hpp:68
constexpr std::uint32_t P3
Definition field_26.hpp:62
constexpr std::uint32_t P6
Definition field_26.hpp:65
constexpr std::uint32_t M26
Definition field_26.hpp:39
void fe26_normalize_weak(std::uint32_t *r) noexcept
void fe26_normalize(std::uint32_t *r) noexcept
void fe26_mul_inner(std::uint32_t *r, const std::uint32_t *a, const std::uint32_t *b) noexcept
void fe26_sqr_inner(std::uint32_t *r, const std::uint32_t *a) noexcept
FieldElement26 square() const noexcept
void mul_assign(const FieldElement26 &rhs) noexcept
bool is_zero() const noexcept
static FieldElement26 from_fe(const FieldElement &fe) noexcept
FieldElement26 half() const noexcept
void negate_assign(unsigned magnitude) noexcept
static FieldElement26 zero() noexcept
static FieldElement26 one() noexcept
FieldElement to_fe() const noexcept
void add_assign(const FieldElement26 &rhs) noexcept
FieldElement26 negate(unsigned magnitude) const noexcept