├── LICENSE ├── README.md ├── doc ├── ecgfp5.pdf ├── ecgfp5.tex └── llncs.cls ├── python └── ecGFp5.py └── rust ├── Cargo.toml ├── benches ├── curve.rs ├── field.rs └── scalar.rs └── src ├── curve.rs ├── field.rs ├── lib.rs ├── multab.rs └── scalar.rs /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Thomas Pornin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Curve ecGFp5 2 | 3 | EcGFp5 is an elliptic curve defined over the field GF(p^5), for the 4 | prime p = 2^64 - 2^32 + 1. This curve was designed to efficiently 5 | support systems with a special compute model where operations in GF(p) 6 | are especially efficient. One such system is the [Miden 7 | VM](https://hackmd.io/YDbjUVHTRn64F4LPelC-NA), which is used to produce 8 | and verify zero-knowledge proofs (of the 9 | [STARK](https://eprint.iacr.org/2018/046) kind). EcGFp5 is not 10 | inherently tied to Miden, but that compute model served as inspiration 11 | for the curve choice. 12 | 13 | - The curve design and choice is explained in the 14 | [whitepaper](doc/ecgfp5.pdf). The paper is now also on 15 | [eprint](https://eprint.iacr.org/2022/274). 16 | 17 | - A test implementation, in Python, is provided in the [python](python/) 18 | directory. It emulates operations as they are meant to occur within 19 | the target compute model (the "VM") and counts costs, expressed in 20 | such operations. 21 | 22 | - A reference implementation in Rust (optimized and constant-time, but 23 | portable) is provided in the [rust](rust/) directory. It implements 24 | all operations on curve elements needed to implement, for instance, 25 | digital signatures. 26 | 27 | # Curve Parameters 28 | 29 | EcGFp5 is a [double-odd curve](https://doubleodd.group/): it is best 30 | understood as a prime order group with efficient and constant-time group 31 | operations, as well as canonical encoding and decoding procedures. 32 | 33 | Base modulus is `p = 2^64 - 2^32 + 1 = 18446744069414584321`. 34 | 35 | Field GF(p^5) is defined as polynomials in GF(p)[z], for a symbolic 36 | variable z, of degree at most 4, and all computations performed 37 | modulo the irreducible polynomial M = z^5 - 3. 38 | 39 | The curve equation is: `y^2 = x*(x^2 + a*x + b)` for the two constants 40 | `a = 2` and `b = 263*z`. The total curve order is `2*n` for a big 41 | 319-bit prime `n`: 42 | 43 | n = 1067993516717146951041484916571792702745057740581727230159139685185762082554198619328292418486241 44 | 45 | EcGFp5 itself is defined as a group of order exactly `n`, consisting of 46 | the points of the curve which are _not_ points of `n`-torsion. The 47 | neutral element is the point `N = (0, 0)` (on the curve, this is the 48 | unique point of order 2). The sum of two points `P` and `Q` in the 49 | group is defined as `P + Q + N` on the curve. This yields a group with 50 | all needed properties for defining cryptographic operations such as 51 | key exchange or signatures: 52 | 53 | - The group has a prime order (no cofactor!). 54 | 55 | - Encoding and decoding of elements is canonical: each element can be 56 | encoded into a single sequence of bytes, and the decoding process 57 | succeeds only if provided that exact sequence of bytes. 58 | 59 | - The group law can be applied with complete formulas, with no 60 | special case; the formulas are furthermore efficient (generic 61 | addition in 10M, amortized per-doubling cost in 2M+5S). 62 | 63 | The conventional generator `G` is the unique point of the group such 64 | that `y/x = 4` (there are exactly two such points on the base curve, 65 | but only one of them is in the prime-order group). Its coordinates 66 | are: 67 | 68 | x = 12883135586176881569 69 | + 4356519642755055268*z 70 | + 5248930565894896907*z^2 71 | + 2165973894480315022*z^3 72 | + 2448410071095648785*z^4 73 | y = 4*x 74 | 75 | As described in the [double-odd curves](https://doubleodd.group/), 76 | there are several choices for internal coordinates. In general, the 77 | _fractional (x,u)_ coordinates are recommended: an element is 78 | represented as the quadruplet `(X:Z:U:T)` with `x = X/Z` and 79 | `u = x/y = U/T` (with `u = 0` for the neutral `N`); elements `Z` and 80 | `T` are never zero. 81 | 82 | # Rust Implementation 83 | 84 | The [rust](rust/) directory contains a reference implementation of 85 | ecGFp5. It is portable: the code uses only `core` (not `std`) and has no 86 | architecture-dependent features (no assembly, intrinsics or anything 87 | tagged `unsafe`); it is nonetheless quite optimized. It does not require 88 | the "nightly" compiler. The main types are `GFp` (integers modulo the 89 | 64-bit integer `p`), `GFp5` (elements of the GF(p^5) field), `Point` 90 | (group elements) and `Scalar` (integers modulo the prime group order 91 | `n`). The structures implement the usual traits (e.g. `Add` and 92 | `AddAssign`) so that arithmetic operators can be used on such values. 93 | 94 | The implementation is considered secure (notably, everything is 95 | constant-time, except the functions meant to directly support signature 96 | verification, which nominally uses only public data). It shall be noted 97 | that there is no attempt whatsoever at "wiping memory", an action is 98 | believed in some circles to be an important security feature. At least, 99 | being a `core`-only implementation, there should be no dynamic memory 100 | allocation except on the stack, so that a blanket stack-wiping strategy 101 | after execution of curve-related code is feasible. 102 | 103 | Compilation is straightforward (`cargo build` and so on). It can be 104 | beneficial to performance to add explicit compilation flags in order to 105 | leverage some architecture-specific opcodes, e.g. `mulx` on recent x86 106 | CPUs (Intel Skylake and later); this is done by setting the `RUSTFLAGS` 107 | environment variable with the value `-C target-cpu=native` (but this, of 108 | course, prevents execution of the resulting binary on older CPUs that do 109 | not feature such opcodes). 110 | 111 | There are benchmarks (`cargo bench`). These benchmarks use the intrinsic 112 | functions `_mm_lfence()` and `_rdtsc()` and will compile only on 64-bit 113 | x86 architectures. Moreover, they use the CPU cycle counter, which is 114 | known *not* to actually count cycles on CPUs that indulge in frequency 115 | scaling (aka "TurboBoost"): if you run the benchmarks on a machine that 116 | has TurboBoost enabled, the numbers you get are meaningless. 117 | 118 | # Signatures 119 | 120 | The provided implementations do _not_ include signature primitives. The 121 | main reason is that defining a signature scheme entails choosing a hash 122 | function, and it is expected that in-VM implementations will want to use 123 | a specialized hash function such as [Poseidon](https://www.poseidon-hash.info/) 124 | or [Rescue](https://www.esat.kuleuven.be/cosic/sites/rescue/). 125 | 126 | Given the hash function `H` with an output size of at least 320 bits, 127 | the _Schnorr signatures_ work as follows: 128 | 129 | - The private key is `d`, a non-zero scalar (integer modulo `n`). 130 | The public key is `Q = d*G`. 131 | 132 | - To sign a message `m` (which may itself be already a hash value): 133 | 134 | 1. Generate a new random non-zero `k` modulo `n`. The choice must be 135 | uniform (or indinstinguishable from uniform) amoung all integers 136 | modulo `n`. If a secure random source is not available, 137 | derandomization techniques can be used, such as described in 138 | [RFC 6979](https://datatracker.ietf.org/doc/html/rfc6979). 139 | In this case, it may be convenient to hash together the private 140 | key, the public key and the message, and interpret the hash 141 | output as an integer, which is then reduced modulo `n` (assuming 142 | that the hash output is large enough that the reduction does not 143 | induce any notable bias; a 384-bit output is enough, since `n` 144 | has size 319 bits). In the Rust implementation, 145 | `Scalar::decode_reduce()` can perform the decoding and modular 146 | reduction. 147 | 148 | 2. Compute point `R = k*G`. Since this uses the conventional generator, 149 | precomputed tables can be used to speed up the process; in the 150 | Rust code, use `Point::mulgen()`. 151 | 152 | 3. Encode `R` into the 40-byte sequence `rbuf` (use `Point::encode()` 153 | to encode the point `R` into an element of GF(p^5), then 154 | `GFp5::encode() to obtain 40 bytes). 155 | 156 | 4. Hash (with `H`) the concatenation of `rbuf`, the encoding of `Q`, 157 | and the message `m`. The hash output is interpreted as an integer 158 | (e.g. with little-endian convention) which is reduced modulo `n`, 159 | yielding the scalar `e`. 160 | 161 | 5. Compute the scalar `s = k + d*e` (all computations modulo `n`; 162 | in Rust, use the arithmetic operations on `Scalar` instances). 163 | The scalar `s` is encoded into the 40-byte value `sbuf` 164 | (with `Scalar::encode()`). 165 | 166 | 6. The signature is the concatenation of `rbuf` and `sbuf`. 167 | 168 | - To verify a signature over a message `m`, against a public key `Q`: 169 | 170 | 1. Check that the signature has length exactly 80 bytes; split it 171 | into `rbuf` and `sbuf`. 172 | 173 | 2. Decode `rbuf` into the point `R` (`GFp5::decode()`, then 174 | `Point::decode()`). Each decoding may fail, in case the value 175 | is invalid; a failed decoding implies that the signature is to 176 | be rejected. 177 | 178 | 3. Decode `sbuf` into the scalar `s` (with `Scalar::decode()`, which 179 | may also fail if `sbuf` is not in the proper range). 180 | 181 | 4. Hash the concatenation of `rbuf`, the encoding of `Q`, and the 182 | message `m`, and reduce the output modulo `n`. This recomputes 183 | the value `e` as in step 4 of the signature generation. 184 | 185 | 5. Check that `s*G - e*Q = R`. In the Rust code, the function 186 | `Point::verify_muladd_vartime()` can be used (with scalars 187 | `s` and `-e`, and point `R`): this function applies an 188 | [optimized process](https://eprint.iacr.org/2020/454) to 189 | speed up this operation. As the name indicate, this function 190 | is _not_ constant-time, which is normally not a problem for 191 | signature verification, which uses only public data. 192 | -------------------------------------------------------------------------------- /doc/ecgfp5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pornin/ecgfp5/ce059c6d1e1662db437aecbf3db6bb67fe63c716/doc/ecgfp5.pdf -------------------------------------------------------------------------------- /doc/llncs.cls: -------------------------------------------------------------------------------- 1 | % LLNCS DOCUMENT CLASS -- version 2.20 (10-Mar-2018) 2 | % Springer Verlag LaTeX2e support for Lecture Notes in Computer Science 3 | % 4 | %% 5 | %% \CharacterTable 6 | %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z 7 | %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z 8 | %% Digits \0\1\2\3\4\5\6\7\8\9 9 | %% Exclamation \! Double quote \" Hash (number) \# 10 | %% Dollar \$ Percent \% Ampersand \& 11 | %% Acute accent \' Left paren \( Right paren \) 12 | %% Asterisk \* Plus \+ Comma \, 13 | %% Minus \- Point \. Solidus \/ 14 | %% Colon \: Semicolon \; Less than \< 15 | %% Equals \= Greater than \> Question mark \? 16 | %% Commercial at \@ Left bracket \[ Backslash \\ 17 | %% Right bracket \] Circumflex \^ Underscore \_ 18 | %% Grave accent \` Left brace \{ Vertical bar \| 19 | %% Right brace \} Tilde \~} 20 | %% 21 | \NeedsTeXFormat{LaTeX2e}[1995/12/01] 22 | \ProvidesClass{llncs}[2018/03/10 v2.20 23 | ^^J LaTeX document class for Lecture Notes in Computer Science] 24 | % Options 25 | \let\if@envcntreset\iffalse 26 | \DeclareOption{envcountreset}{\let\if@envcntreset\iftrue} 27 | \DeclareOption{citeauthoryear}{\let\citeauthoryear=Y} 28 | \DeclareOption{oribibl}{\let\oribibl=Y} 29 | \let\if@custvec\iftrue 30 | \DeclareOption{orivec}{\let\if@custvec\iffalse} 31 | \let\if@envcntsame\iffalse 32 | \DeclareOption{envcountsame}{\let\if@envcntsame\iftrue} 33 | \let\if@envcntsect\iffalse 34 | \DeclareOption{envcountsect}{\let\if@envcntsect\iftrue} 35 | \let\if@runhead\iffalse 36 | \DeclareOption{runningheads}{\let\if@runhead\iftrue} 37 | 38 | \let\if@openright\iftrue 39 | \let\if@openbib\iffalse 40 | \DeclareOption{openbib}{\let\if@openbib\iftrue} 41 | 42 | % languages 43 | \let\switcht@@therlang\relax 44 | \def\ds@deutsch{\def\switcht@@therlang{\switcht@deutsch}} 45 | \def\ds@francais{\def\switcht@@therlang{\switcht@francais}} 46 | 47 | \DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}} 48 | 49 | \ProcessOptions 50 | 51 | \LoadClass[twoside]{article} 52 | \RequirePackage{multicol} % needed for the list of participants, index 53 | \RequirePackage{aliascnt} 54 | 55 | \setlength{\textwidth}{12.2cm} 56 | \setlength{\textheight}{19.3cm} 57 | \renewcommand\@pnumwidth{2em} 58 | \renewcommand\@tocrmarg{3.5em} 59 | % 60 | \def\@dottedtocline#1#2#3#4#5{% 61 | \ifnum #1>\c@tocdepth \else 62 | \vskip \z@ \@plus.2\p@ 63 | {\leftskip #2\relax \rightskip \@tocrmarg \advance\rightskip by 0pt plus 2cm 64 | \parfillskip -\rightskip \pretolerance=10000 65 | \parindent #2\relax\@afterindenttrue 66 | \interlinepenalty\@M 67 | \leavevmode 68 | \@tempdima #3\relax 69 | \advance\leftskip \@tempdima \null\nobreak\hskip -\leftskip 70 | {#4}\nobreak 71 | \leaders\hbox{$\m@th 72 | \mkern \@dotsep mu\hbox{.}\mkern \@dotsep 73 | mu$}\hfill 74 | \nobreak 75 | \hb@xt@\@pnumwidth{\hfil\normalfont \normalcolor #5}% 76 | \par}% 77 | \fi} 78 | % 79 | \def\switcht@albion{% 80 | \def\abstractname{Abstract.} 81 | \def\ackname{Acknowledgement.} 82 | \def\andname{and} 83 | \def\lastandname{\unskip, and} 84 | \def\appendixname{Appendix} 85 | \def\chaptername{Chapter} 86 | \def\claimname{Claim} 87 | \def\conjecturename{Conjecture} 88 | \def\contentsname{Table of Contents} 89 | \def\corollaryname{Corollary} 90 | \def\definitionname{Definition} 91 | \def\examplename{Example} 92 | \def\exercisename{Exercise} 93 | \def\figurename{Fig.} 94 | \def\keywordname{{\bf Keywords:}} 95 | \def\indexname{Index} 96 | \def\lemmaname{Lemma} 97 | \def\contriblistname{List of Contributors} 98 | \def\listfigurename{List of Figures} 99 | \def\listtablename{List of Tables} 100 | \def\mailname{{\it Correspondence to\/}:} 101 | \def\noteaddname{Note added in proof} 102 | \def\notename{Note} 103 | \def\partname{Part} 104 | \def\problemname{Problem} 105 | \def\proofname{Proof} 106 | \def\propertyname{Property} 107 | \def\propositionname{Proposition} 108 | \def\questionname{Question} 109 | \def\remarkname{Remark} 110 | \def\seename{see} 111 | \def\solutionname{Solution} 112 | \def\subclassname{{\it Subject Classifications\/}:} 113 | \def\tablename{Table} 114 | \def\theoremname{Theorem}} 115 | \switcht@albion 116 | % Names of theorem like environments are already defined 117 | % but must be translated if another language is chosen 118 | % 119 | % French section 120 | \def\switcht@francais{%\typeout{On parle francais.}% 121 | \def\abstractname{R\'esum\'e.}% 122 | \def\ackname{Remerciements.}% 123 | \def\andname{et}% 124 | \def\lastandname{ et}% 125 | \def\appendixname{Appendice} 126 | \def\chaptername{Chapitre}% 127 | \def\claimname{Pr\'etention}% 128 | \def\conjecturename{Hypoth\`ese}% 129 | \def\contentsname{Table des mati\`eres}% 130 | \def\corollaryname{Corollaire}% 131 | \def\definitionname{D\'efinition}% 132 | \def\examplename{Exemple}% 133 | \def\exercisename{Exercice}% 134 | \def\figurename{Fig.}% 135 | \def\keywordname{{\bf Mots-cl\'e:}} 136 | \def\indexname{Index} 137 | \def\lemmaname{Lemme}% 138 | \def\contriblistname{Liste des contributeurs} 139 | \def\listfigurename{Liste des figures}% 140 | \def\listtablename{Liste des tables}% 141 | \def\mailname{{\it Correspondence to\/}:} 142 | \def\noteaddname{Note ajout\'ee \`a l'\'epreuve}% 143 | \def\notename{Remarque}% 144 | \def\partname{Partie}% 145 | \def\problemname{Probl\`eme}% 146 | \def\proofname{Preuve}% 147 | \def\propertyname{Caract\'eristique}% 148 | %\def\propositionname{Proposition}% 149 | \def\questionname{Question}% 150 | \def\remarkname{Remarque}% 151 | \def\seename{voir} 152 | \def\solutionname{Solution}% 153 | \def\subclassname{{\it Subject Classifications\/}:} 154 | \def\tablename{Tableau}% 155 | \def\theoremname{Th\'eor\`eme}% 156 | } 157 | % 158 | % German section 159 | \def\switcht@deutsch{%\typeout{Man spricht deutsch.}% 160 | \def\abstractname{Zusammenfassung.}% 161 | \def\ackname{Danksagung.}% 162 | \def\andname{und}% 163 | \def\lastandname{ und}% 164 | \def\appendixname{Anhang}% 165 | \def\chaptername{Kapitel}% 166 | \def\claimname{Behauptung}% 167 | \def\conjecturename{Hypothese}% 168 | \def\contentsname{Inhaltsverzeichnis}% 169 | \def\corollaryname{Korollar}% 170 | %\def\definitionname{Definition}% 171 | \def\examplename{Beispiel}% 172 | \def\exercisename{\"Ubung}% 173 | \def\figurename{Abb.}% 174 | \def\keywordname{{\bf Schl\"usselw\"orter:}} 175 | \def\indexname{Index} 176 | %\def\lemmaname{Lemma}% 177 | \def\contriblistname{Mitarbeiter} 178 | \def\listfigurename{Abbildungsverzeichnis}% 179 | \def\listtablename{Tabellenverzeichnis}% 180 | \def\mailname{{\it Correspondence to\/}:} 181 | \def\noteaddname{Nachtrag}% 182 | \def\notename{Anmerkung}% 183 | \def\partname{Teil}% 184 | %\def\problemname{Problem}% 185 | \def\proofname{Beweis}% 186 | \def\propertyname{Eigenschaft}% 187 | %\def\propositionname{Proposition}% 188 | \def\questionname{Frage}% 189 | \def\remarkname{Anmerkung}% 190 | \def\seename{siehe} 191 | \def\solutionname{L\"osung}% 192 | \def\subclassname{{\it Subject Classifications\/}:} 193 | \def\tablename{Tabelle}% 194 | %\def\theoremname{Theorem}% 195 | } 196 | 197 | % Ragged bottom for the actual page 198 | \def\thisbottomragged{\def\@textbottom{\vskip\z@ plus.0001fil 199 | \global\let\@textbottom\relax}} 200 | 201 | \renewcommand\small{% 202 | \@setfontsize\small\@ixpt{11}% 203 | \abovedisplayskip 8.5\p@ \@plus3\p@ \@minus4\p@ 204 | \abovedisplayshortskip \z@ \@plus2\p@ 205 | \belowdisplayshortskip 4\p@ \@plus2\p@ \@minus2\p@ 206 | \def\@listi{\leftmargin\leftmargini 207 | \parsep 0\p@ \@plus1\p@ \@minus\p@ 208 | \topsep 8\p@ \@plus2\p@ \@minus4\p@ 209 | \itemsep0\p@}% 210 | \belowdisplayskip \abovedisplayskip 211 | } 212 | 213 | \frenchspacing 214 | \widowpenalty=10000 215 | \clubpenalty=10000 216 | 217 | \setlength\oddsidemargin {63\p@} 218 | \setlength\evensidemargin {63\p@} 219 | \setlength\marginparwidth {90\p@} 220 | 221 | \setlength\headsep {16\p@} 222 | 223 | \setlength\footnotesep{7.7\p@} 224 | \setlength\textfloatsep{8mm\@plus 2\p@ \@minus 4\p@} 225 | \setlength\intextsep {8mm\@plus 2\p@ \@minus 2\p@} 226 | 227 | \setcounter{secnumdepth}{2} 228 | 229 | \newcounter {chapter} 230 | \renewcommand\thechapter {\@arabic\c@chapter} 231 | 232 | \newif\if@mainmatter \@mainmattertrue 233 | \newcommand\frontmatter{\cleardoublepage 234 | \@mainmatterfalse\pagenumbering{Roman}} 235 | \newcommand\mainmatter{\cleardoublepage 236 | \@mainmattertrue\pagenumbering{arabic}} 237 | \newcommand\backmatter{\if@openright\cleardoublepage\else\clearpage\fi 238 | \@mainmatterfalse} 239 | 240 | \renewcommand\part{\cleardoublepage 241 | \thispagestyle{empty}% 242 | \if@twocolumn 243 | \onecolumn 244 | \@tempswatrue 245 | \else 246 | \@tempswafalse 247 | \fi 248 | \null\vfil 249 | \secdef\@part\@spart} 250 | 251 | \def\@part[#1]#2{% 252 | \ifnum \c@secnumdepth >-2\relax 253 | \refstepcounter{part}% 254 | \addcontentsline{toc}{part}{\thepart\hspace{1em}#1}% 255 | \else 256 | \addcontentsline{toc}{part}{#1}% 257 | \fi 258 | \markboth{}{}% 259 | {\centering 260 | \interlinepenalty \@M 261 | \normalfont 262 | \ifnum \c@secnumdepth >-2\relax 263 | \huge\bfseries \partname~\thepart 264 | \par 265 | \vskip 20\p@ 266 | \fi 267 | \Huge \bfseries #2\par}% 268 | \@endpart} 269 | \def\@spart#1{% 270 | {\centering 271 | \interlinepenalty \@M 272 | \normalfont 273 | \Huge \bfseries #1\par}% 274 | \@endpart} 275 | \def\@endpart{\vfil\newpage 276 | \if@twoside 277 | \null 278 | \thispagestyle{empty}% 279 | \newpage 280 | \fi 281 | \if@tempswa 282 | \twocolumn 283 | \fi} 284 | 285 | \newcommand\chapter{\clearpage 286 | \thispagestyle{empty}% 287 | \global\@topnum\z@ 288 | \@afterindentfalse 289 | \secdef\@chapter\@schapter} 290 | \def\@chapter[#1]#2{\ifnum \c@secnumdepth >\m@ne 291 | \if@mainmatter 292 | \refstepcounter{chapter}% 293 | \typeout{\@chapapp\space\thechapter.}% 294 | \addcontentsline{toc}{chapter}% 295 | {\protect\numberline{\thechapter}#1}% 296 | \else 297 | \addcontentsline{toc}{chapter}{#1}% 298 | \fi 299 | \else 300 | \addcontentsline{toc}{chapter}{#1}% 301 | \fi 302 | \chaptermark{#1}% 303 | \addtocontents{lof}{\protect\addvspace{10\p@}}% 304 | \addtocontents{lot}{\protect\addvspace{10\p@}}% 305 | \if@twocolumn 306 | \@topnewpage[\@makechapterhead{#2}]% 307 | \else 308 | \@makechapterhead{#2}% 309 | \@afterheading 310 | \fi} 311 | \def\@makechapterhead#1{% 312 | % \vspace*{50\p@}% 313 | {\centering 314 | \ifnum \c@secnumdepth >\m@ne 315 | \if@mainmatter 316 | \large\bfseries \@chapapp{} \thechapter 317 | \par\nobreak 318 | \vskip 20\p@ 319 | \fi 320 | \fi 321 | \interlinepenalty\@M 322 | \Large \bfseries #1\par\nobreak 323 | \vskip 40\p@ 324 | }} 325 | \def\@schapter#1{\if@twocolumn 326 | \@topnewpage[\@makeschapterhead{#1}]% 327 | \else 328 | \@makeschapterhead{#1}% 329 | \@afterheading 330 | \fi} 331 | \def\@makeschapterhead#1{% 332 | % \vspace*{50\p@}% 333 | {\centering 334 | \normalfont 335 | \interlinepenalty\@M 336 | \Large \bfseries #1\par\nobreak 337 | \vskip 40\p@ 338 | }} 339 | 340 | \renewcommand\section{\@startsection{section}{1}{\z@}% 341 | {-18\p@ \@plus -4\p@ \@minus -4\p@}% 342 | {12\p@ \@plus 4\p@ \@minus 4\p@}% 343 | {\normalfont\large\bfseries\boldmath 344 | \rightskip=\z@ \@plus 8em\pretolerance=10000 }} 345 | \renewcommand\subsection{\@startsection{subsection}{2}{\z@}% 346 | {-18\p@ \@plus -4\p@ \@minus -4\p@}% 347 | {8\p@ \@plus 4\p@ \@minus 4\p@}% 348 | {\normalfont\normalsize\bfseries\boldmath 349 | \rightskip=\z@ \@plus 8em\pretolerance=10000 }} 350 | \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}% 351 | {-18\p@ \@plus -4\p@ \@minus -4\p@}% 352 | {-0.5em \@plus -0.22em \@minus -0.1em}% 353 | {\normalfont\normalsize\bfseries\boldmath}} 354 | \renewcommand\paragraph{\@startsection{paragraph}{4}{\z@}% 355 | {-12\p@ \@plus -4\p@ \@minus -4\p@}% 356 | {-0.5em \@plus -0.22em \@minus -0.1em}% 357 | {\normalfont\normalsize\itshape}} 358 | \renewcommand\subparagraph[1]{\typeout{LLNCS warning: You should not use 359 | \string\subparagraph\space with this class}\vskip0.5cm 360 | You should not use \verb|\subparagraph| with this class.\vskip0.5cm} 361 | 362 | \DeclareMathSymbol{\Gamma}{\mathalpha}{letters}{"00} 363 | \DeclareMathSymbol{\Delta}{\mathalpha}{letters}{"01} 364 | \DeclareMathSymbol{\Theta}{\mathalpha}{letters}{"02} 365 | \DeclareMathSymbol{\Lambda}{\mathalpha}{letters}{"03} 366 | \DeclareMathSymbol{\Xi}{\mathalpha}{letters}{"04} 367 | \DeclareMathSymbol{\Pi}{\mathalpha}{letters}{"05} 368 | \DeclareMathSymbol{\Sigma}{\mathalpha}{letters}{"06} 369 | \DeclareMathSymbol{\Upsilon}{\mathalpha}{letters}{"07} 370 | \DeclareMathSymbol{\Phi}{\mathalpha}{letters}{"08} 371 | \DeclareMathSymbol{\Psi}{\mathalpha}{letters}{"09} 372 | \DeclareMathSymbol{\Omega}{\mathalpha}{letters}{"0A} 373 | 374 | \let\footnotesize\small 375 | 376 | \if@custvec 377 | \def\vec#1{\mathchoice{\mbox{\boldmath$\displaystyle#1$}} 378 | {\mbox{\boldmath$\textstyle#1$}} 379 | {\mbox{\boldmath$\scriptstyle#1$}} 380 | {\mbox{\boldmath$\scriptscriptstyle#1$}}} 381 | \fi 382 | 383 | \def\squareforqed{\hbox{\rlap{$\sqcap$}$\sqcup$}} 384 | \def\qed{\ifmmode\squareforqed\else{\unskip\nobreak\hfil 385 | \penalty50\hskip1em\null\nobreak\hfil\squareforqed 386 | \parfillskip=0pt\finalhyphendemerits=0\endgraf}\fi} 387 | 388 | \def\getsto{\mathrel{\mathchoice {\vcenter{\offinterlineskip 389 | \halign{\hfil 390 | $\displaystyle##$\hfil\cr\gets\cr\to\cr}}} 391 | {\vcenter{\offinterlineskip\halign{\hfil$\textstyle##$\hfil\cr\gets 392 | \cr\to\cr}}} 393 | {\vcenter{\offinterlineskip\halign{\hfil$\scriptstyle##$\hfil\cr\gets 394 | \cr\to\cr}}} 395 | {\vcenter{\offinterlineskip\halign{\hfil$\scriptscriptstyle##$\hfil\cr 396 | \gets\cr\to\cr}}}}} 397 | \def\lid{\mathrel{\mathchoice {\vcenter{\offinterlineskip\halign{\hfil 398 | $\displaystyle##$\hfil\cr<\cr\noalign{\vskip1.2pt}=\cr}}} 399 | {\vcenter{\offinterlineskip\halign{\hfil$\textstyle##$\hfil\cr<\cr 400 | \noalign{\vskip1.2pt}=\cr}}} 401 | {\vcenter{\offinterlineskip\halign{\hfil$\scriptstyle##$\hfil\cr<\cr 402 | \noalign{\vskip1pt}=\cr}}} 403 | {\vcenter{\offinterlineskip\halign{\hfil$\scriptscriptstyle##$\hfil\cr 404 | <\cr 405 | \noalign{\vskip0.9pt}=\cr}}}}} 406 | \def\gid{\mathrel{\mathchoice {\vcenter{\offinterlineskip\halign{\hfil 407 | $\displaystyle##$\hfil\cr>\cr\noalign{\vskip1.2pt}=\cr}}} 408 | {\vcenter{\offinterlineskip\halign{\hfil$\textstyle##$\hfil\cr>\cr 409 | \noalign{\vskip1.2pt}=\cr}}} 410 | {\vcenter{\offinterlineskip\halign{\hfil$\scriptstyle##$\hfil\cr>\cr 411 | \noalign{\vskip1pt}=\cr}}} 412 | {\vcenter{\offinterlineskip\halign{\hfil$\scriptscriptstyle##$\hfil\cr 413 | >\cr 414 | \noalign{\vskip0.9pt}=\cr}}}}} 415 | \def\grole{\mathrel{\mathchoice {\vcenter{\offinterlineskip 416 | \halign{\hfil 417 | $\displaystyle##$\hfil\cr>\cr\noalign{\vskip-1pt}<\cr}}} 418 | {\vcenter{\offinterlineskip\halign{\hfil$\textstyle##$\hfil\cr 419 | >\cr\noalign{\vskip-1pt}<\cr}}} 420 | {\vcenter{\offinterlineskip\halign{\hfil$\scriptstyle##$\hfil\cr 421 | >\cr\noalign{\vskip-0.8pt}<\cr}}} 422 | {\vcenter{\offinterlineskip\halign{\hfil$\scriptscriptstyle##$\hfil\cr 423 | >\cr\noalign{\vskip-0.3pt}<\cr}}}}} 424 | \def\bbbr{{\rm I\!R}} %reelle Zahlen 425 | \def\bbbm{{\rm I\!M}} 426 | \def\bbbn{{\rm I\!N}} %natuerliche Zahlen 427 | \def\bbbf{{\rm I\!F}} 428 | \def\bbbh{{\rm I\!H}} 429 | \def\bbbk{{\rm I\!K}} 430 | \def\bbbp{{\rm I\!P}} 431 | \def\bbbone{{\mathchoice {\rm 1\mskip-4mu l} {\rm 1\mskip-4mu l} 432 | {\rm 1\mskip-4.5mu l} {\rm 1\mskip-5mu l}}} 433 | \def\bbbc{{\mathchoice {\setbox0=\hbox{$\displaystyle\rm C$}\hbox{\hbox 434 | to0pt{\kern0.4\wd0\vrule height0.9\ht0\hss}\box0}} 435 | {\setbox0=\hbox{$\textstyle\rm C$}\hbox{\hbox 436 | to0pt{\kern0.4\wd0\vrule height0.9\ht0\hss}\box0}} 437 | {\setbox0=\hbox{$\scriptstyle\rm C$}\hbox{\hbox 438 | to0pt{\kern0.4\wd0\vrule height0.9\ht0\hss}\box0}} 439 | {\setbox0=\hbox{$\scriptscriptstyle\rm C$}\hbox{\hbox 440 | to0pt{\kern0.4\wd0\vrule height0.9\ht0\hss}\box0}}}} 441 | \def\bbbq{{\mathchoice {\setbox0=\hbox{$\displaystyle\rm 442 | Q$}\hbox{\raise 443 | 0.15\ht0\hbox to0pt{\kern0.4\wd0\vrule height0.8\ht0\hss}\box0}} 444 | {\setbox0=\hbox{$\textstyle\rm Q$}\hbox{\raise 445 | 0.15\ht0\hbox to0pt{\kern0.4\wd0\vrule height0.8\ht0\hss}\box0}} 446 | {\setbox0=\hbox{$\scriptstyle\rm Q$}\hbox{\raise 447 | 0.15\ht0\hbox to0pt{\kern0.4\wd0\vrule height0.7\ht0\hss}\box0}} 448 | {\setbox0=\hbox{$\scriptscriptstyle\rm Q$}\hbox{\raise 449 | 0.15\ht0\hbox to0pt{\kern0.4\wd0\vrule height0.7\ht0\hss}\box0}}}} 450 | \def\bbbt{{\mathchoice {\setbox0=\hbox{$\displaystyle\rm 451 | T$}\hbox{\hbox to0pt{\kern0.3\wd0\vrule height0.9\ht0\hss}\box0}} 452 | {\setbox0=\hbox{$\textstyle\rm T$}\hbox{\hbox 453 | to0pt{\kern0.3\wd0\vrule height0.9\ht0\hss}\box0}} 454 | {\setbox0=\hbox{$\scriptstyle\rm T$}\hbox{\hbox 455 | to0pt{\kern0.3\wd0\vrule height0.9\ht0\hss}\box0}} 456 | {\setbox0=\hbox{$\scriptscriptstyle\rm T$}\hbox{\hbox 457 | to0pt{\kern0.3\wd0\vrule height0.9\ht0\hss}\box0}}}} 458 | \def\bbbs{{\mathchoice 459 | {\setbox0=\hbox{$\displaystyle \rm S$}\hbox{\raise0.5\ht0\hbox 460 | to0pt{\kern0.35\wd0\vrule height0.45\ht0\hss}\hbox 461 | to0pt{\kern0.55\wd0\vrule height0.5\ht0\hss}\box0}} 462 | {\setbox0=\hbox{$\textstyle \rm S$}\hbox{\raise0.5\ht0\hbox 463 | to0pt{\kern0.35\wd0\vrule height0.45\ht0\hss}\hbox 464 | to0pt{\kern0.55\wd0\vrule height0.5\ht0\hss}\box0}} 465 | {\setbox0=\hbox{$\scriptstyle \rm S$}\hbox{\raise0.5\ht0\hbox 466 | to0pt{\kern0.35\wd0\vrule height0.45\ht0\hss}\raise0.05\ht0\hbox 467 | to0pt{\kern0.5\wd0\vrule height0.45\ht0\hss}\box0}} 468 | {\setbox0=\hbox{$\scriptscriptstyle\rm S$}\hbox{\raise0.5\ht0\hbox 469 | to0pt{\kern0.4\wd0\vrule height0.45\ht0\hss}\raise0.05\ht0\hbox 470 | to0pt{\kern0.55\wd0\vrule height0.45\ht0\hss}\box0}}}} 471 | \def\bbbz{{\mathchoice {\hbox{$\mathsf\textstyle Z\kern-0.4em Z$}} 472 | {\hbox{$\mathsf\textstyle Z\kern-0.4em Z$}} 473 | {\hbox{$\mathsf\scriptstyle Z\kern-0.3em Z$}} 474 | {\hbox{$\mathsf\scriptscriptstyle Z\kern-0.2em Z$}}}} 475 | 476 | \let\ts\, 477 | 478 | \setlength\leftmargini {17\p@} 479 | \setlength\leftmargin {\leftmargini} 480 | \setlength\leftmarginii {\leftmargini} 481 | \setlength\leftmarginiii {\leftmargini} 482 | \setlength\leftmarginiv {\leftmargini} 483 | \setlength \labelsep {.5em} 484 | \setlength \labelwidth{\leftmargini} 485 | \addtolength\labelwidth{-\labelsep} 486 | 487 | \def\@listI{\leftmargin\leftmargini 488 | \parsep 0\p@ \@plus1\p@ \@minus\p@ 489 | \topsep 8\p@ \@plus2\p@ \@minus4\p@ 490 | \itemsep0\p@} 491 | \let\@listi\@listI 492 | \@listi 493 | \def\@listii {\leftmargin\leftmarginii 494 | \labelwidth\leftmarginii 495 | \advance\labelwidth-\labelsep 496 | \topsep 0\p@ \@plus2\p@ \@minus\p@} 497 | \def\@listiii{\leftmargin\leftmarginiii 498 | \labelwidth\leftmarginiii 499 | \advance\labelwidth-\labelsep 500 | \topsep 0\p@ \@plus\p@\@minus\p@ 501 | \parsep \z@ 502 | \partopsep \p@ \@plus\z@ \@minus\p@} 503 | 504 | \renewcommand\labelitemi{\normalfont\bfseries --} 505 | \renewcommand\labelitemii{$\m@th\bullet$} 506 | 507 | \setlength\arraycolsep{1.4\p@} 508 | \setlength\tabcolsep{1.4\p@} 509 | 510 | \def\tableofcontents{\chapter*{\contentsname\@mkboth{{\contentsname}}% 511 | {{\contentsname}}} 512 | \def\authcount##1{\setcounter{auco}{##1}\setcounter{@auth}{1}} 513 | \def\lastand{\ifnum\value{auco}=2\relax 514 | \unskip{} \andname\ 515 | \else 516 | \unskip \lastandname\ 517 | \fi}% 518 | \def\and{\stepcounter{@auth}\relax 519 | \ifnum\value{@auth}=\value{auco}% 520 | \lastand 521 | \else 522 | \unskip, 523 | \fi}% 524 | \@starttoc{toc}\if@restonecol\twocolumn\fi} 525 | 526 | \def\l@part#1#2{\addpenalty{\@secpenalty}% 527 | \addvspace{2em plus\p@}% % space above part line 528 | \begingroup 529 | \parindent \z@ 530 | \rightskip \z@ plus 5em 531 | \hrule\vskip5pt 532 | \large % same size as for a contribution heading 533 | \bfseries\boldmath % set line in boldface 534 | \leavevmode % TeX command to enter horizontal mode. 535 | #1\par 536 | \vskip5pt 537 | \hrule 538 | \vskip1pt 539 | \nobreak % Never break after part entry 540 | \endgroup} 541 | 542 | \def\@dotsep{2} 543 | 544 | \let\phantomsection=\relax 545 | 546 | \def\hyperhrefextend{\ifx\hyper@anchor\@undefined\else 547 | {}\fi} 548 | 549 | \def\addnumcontentsmark#1#2#3{% 550 | \addtocontents{#1}{\protect\contentsline{#2}{\protect\numberline 551 | {\thechapter}#3}{\thepage}\hyperhrefextend}}% 552 | \def\addcontentsmark#1#2#3{% 553 | \addtocontents{#1}{\protect\contentsline{#2}{#3}{\thepage}\hyperhrefextend}}% 554 | \def\addcontentsmarkwop#1#2#3{% 555 | \addtocontents{#1}{\protect\contentsline{#2}{#3}{0}\hyperhrefextend}}% 556 | 557 | \def\@adcmk[#1]{\ifcase #1 \or 558 | \def\@gtempa{\addnumcontentsmark}% 559 | \or \def\@gtempa{\addcontentsmark}% 560 | \or \def\@gtempa{\addcontentsmarkwop}% 561 | \fi\@gtempa{toc}{chapter}% 562 | } 563 | \def\addtocmark{% 564 | \phantomsection 565 | \@ifnextchar[{\@adcmk}{\@adcmk[3]}% 566 | } 567 | 568 | \def\l@chapter#1#2{\addpenalty{-\@highpenalty} 569 | \vskip 1.0em plus 1pt \@tempdima 1.5em \begingroup 570 | \parindent \z@ \rightskip \@tocrmarg 571 | \advance\rightskip by 0pt plus 2cm 572 | \parfillskip -\rightskip \pretolerance=10000 573 | \leavevmode \advance\leftskip\@tempdima \hskip -\leftskip 574 | {\large\bfseries\boldmath#1}\ifx0#2\hfil\null 575 | \else 576 | \nobreak 577 | \leaders\hbox{$\m@th \mkern \@dotsep mu.\mkern 578 | \@dotsep mu$}\hfill 579 | \nobreak\hbox to\@pnumwidth{\hss #2}% 580 | \fi\par 581 | \penalty\@highpenalty \endgroup} 582 | 583 | \def\l@title#1#2{\addpenalty{-\@highpenalty} 584 | \addvspace{8pt plus 1pt} 585 | \@tempdima \z@ 586 | \begingroup 587 | \parindent \z@ \rightskip \@tocrmarg 588 | \advance\rightskip by 0pt plus 2cm 589 | \parfillskip -\rightskip \pretolerance=10000 590 | \leavevmode \advance\leftskip\@tempdima \hskip -\leftskip 591 | #1\nobreak 592 | \leaders\hbox{$\m@th \mkern \@dotsep mu.\mkern 593 | \@dotsep mu$}\hfill 594 | \nobreak\hbox to\@pnumwidth{\hss #2}\par 595 | \penalty\@highpenalty \endgroup} 596 | 597 | \def\l@author#1#2{\addpenalty{\@highpenalty} 598 | \@tempdima=15\p@ %\z@ 599 | \begingroup 600 | \parindent \z@ \rightskip \@tocrmarg 601 | \advance\rightskip by 0pt plus 2cm 602 | \pretolerance=10000 603 | \leavevmode \advance\leftskip\@tempdima %\hskip -\leftskip 604 | \textit{#1}\par 605 | \penalty\@highpenalty \endgroup} 606 | 607 | \setcounter{tocdepth}{0} 608 | \newdimen\tocchpnum 609 | \newdimen\tocsecnum 610 | \newdimen\tocsectotal 611 | \newdimen\tocsubsecnum 612 | \newdimen\tocsubsectotal 613 | \newdimen\tocsubsubsecnum 614 | \newdimen\tocsubsubsectotal 615 | \newdimen\tocparanum 616 | \newdimen\tocparatotal 617 | \newdimen\tocsubparanum 618 | \tocchpnum=\z@ % no chapter numbers 619 | \tocsecnum=15\p@ % section 88. plus 2.222pt 620 | \tocsubsecnum=23\p@ % subsection 88.8 plus 2.222pt 621 | \tocsubsubsecnum=27\p@ % subsubsection 88.8.8 plus 1.444pt 622 | \tocparanum=35\p@ % paragraph 88.8.8.8 plus 1.666pt 623 | \tocsubparanum=43\p@ % subparagraph 88.8.8.8.8 plus 1.888pt 624 | \def\calctocindent{% 625 | \tocsectotal=\tocchpnum 626 | \advance\tocsectotal by\tocsecnum 627 | \tocsubsectotal=\tocsectotal 628 | \advance\tocsubsectotal by\tocsubsecnum 629 | \tocsubsubsectotal=\tocsubsectotal 630 | \advance\tocsubsubsectotal by\tocsubsubsecnum 631 | \tocparatotal=\tocsubsubsectotal 632 | \advance\tocparatotal by\tocparanum} 633 | \calctocindent 634 | 635 | \def\l@section{\@dottedtocline{1}{\tocchpnum}{\tocsecnum}} 636 | \def\l@subsection{\@dottedtocline{2}{\tocsectotal}{\tocsubsecnum}} 637 | \def\l@subsubsection{\@dottedtocline{3}{\tocsubsectotal}{\tocsubsubsecnum}} 638 | \def\l@paragraph{\@dottedtocline{4}{\tocsubsubsectotal}{\tocparanum}} 639 | \def\l@subparagraph{\@dottedtocline{5}{\tocparatotal}{\tocsubparanum}} 640 | 641 | \def\listoffigures{\@restonecolfalse\if@twocolumn\@restonecoltrue\onecolumn 642 | \fi\section*{\listfigurename\@mkboth{{\listfigurename}}{{\listfigurename}}} 643 | \@starttoc{lof}\if@restonecol\twocolumn\fi} 644 | \def\l@figure{\@dottedtocline{1}{0em}{1.5em}} 645 | 646 | \def\listoftables{\@restonecolfalse\if@twocolumn\@restonecoltrue\onecolumn 647 | \fi\section*{\listtablename\@mkboth{{\listtablename}}{{\listtablename}}} 648 | \@starttoc{lot}\if@restonecol\twocolumn\fi} 649 | \let\l@table\l@figure 650 | 651 | \renewcommand\listoffigures{% 652 | \section*{\listfigurename 653 | \@mkboth{\listfigurename}{\listfigurename}}% 654 | \@starttoc{lof}% 655 | } 656 | 657 | \renewcommand\listoftables{% 658 | \section*{\listtablename 659 | \@mkboth{\listtablename}{\listtablename}}% 660 | \@starttoc{lot}% 661 | } 662 | 663 | \ifx\oribibl\undefined 664 | \ifx\citeauthoryear\undefined 665 | \renewenvironment{thebibliography}[1] 666 | {\section*{\refname} 667 | \def\@biblabel##1{##1.} 668 | \small 669 | \list{\@biblabel{\@arabic\c@enumiv}}% 670 | {\settowidth\labelwidth{\@biblabel{#1}}% 671 | \leftmargin\labelwidth 672 | \advance\leftmargin\labelsep 673 | \if@openbib 674 | \advance\leftmargin\bibindent 675 | \itemindent -\bibindent 676 | \listparindent \itemindent 677 | \parsep \z@ 678 | \fi 679 | \usecounter{enumiv}% 680 | \let\p@enumiv\@empty 681 | \renewcommand\theenumiv{\@arabic\c@enumiv}}% 682 | \if@openbib 683 | \renewcommand\newblock{\par}% 684 | \else 685 | \renewcommand\newblock{\hskip .11em \@plus.33em \@minus.07em}% 686 | \fi 687 | \sloppy\clubpenalty4000\widowpenalty4000% 688 | \sfcode`\.=\@m} 689 | {\def\@noitemerr 690 | {\@latex@warning{Empty `thebibliography' environment}}% 691 | \endlist} 692 | \def\@lbibitem[#1]#2{\item[{[#1]}\hfill]\if@filesw 693 | {\let\protect\noexpand\immediate 694 | \write\@auxout{\string\bibcite{#2}{#1}}}\fi\ignorespaces} 695 | \newcount\@tempcntc 696 | \def\@citex[#1]#2{\if@filesw\immediate\write\@auxout{\string\citation{#2}}\fi 697 | \@tempcnta\z@\@tempcntb\m@ne\def\@citea{}\@cite{\@for\@citeb:=#2\do 698 | {\@ifundefined 699 | {b@\@citeb}{\@citeo\@tempcntb\m@ne\@citea\def\@citea{,}{\bfseries 700 | ?}\@warning 701 | {Citation `\@citeb' on page \thepage \space undefined}}% 702 | {\setbox\z@\hbox{\global\@tempcntc0\csname b@\@citeb\endcsname\relax}% 703 | \ifnum\@tempcntc=\z@ \@citeo\@tempcntb\m@ne 704 | \@citea\def\@citea{,}\hbox{\csname b@\@citeb\endcsname}% 705 | \else 706 | \advance\@tempcntb\@ne 707 | \ifnum\@tempcntb=\@tempcntc 708 | \else\advance\@tempcntb\m@ne\@citeo 709 | \@tempcnta\@tempcntc\@tempcntb\@tempcntc\fi\fi}}\@citeo}{#1}} 710 | \def\@citeo{\ifnum\@tempcnta>\@tempcntb\else 711 | \@citea\def\@citea{,\,\hskip\z@skip}% 712 | \ifnum\@tempcnta=\@tempcntb\the\@tempcnta\else 713 | {\advance\@tempcnta\@ne\ifnum\@tempcnta=\@tempcntb \else 714 | \def\@citea{--}\fi 715 | \advance\@tempcnta\m@ne\the\@tempcnta\@citea\the\@tempcntb}\fi\fi} 716 | \else 717 | \renewenvironment{thebibliography}[1] 718 | {\section*{\refname} 719 | \small 720 | \list{}% 721 | {\settowidth\labelwidth{}% 722 | \leftmargin\parindent 723 | \itemindent=-\parindent 724 | \labelsep=\z@ 725 | \if@openbib 726 | \advance\leftmargin\bibindent 727 | \itemindent -\bibindent 728 | \listparindent \itemindent 729 | \parsep \z@ 730 | \fi 731 | \usecounter{enumiv}% 732 | \let\p@enumiv\@empty 733 | \renewcommand\theenumiv{}}% 734 | \if@openbib 735 | \renewcommand\newblock{\par}% 736 | \else 737 | \renewcommand\newblock{\hskip .11em \@plus.33em \@minus.07em}% 738 | \fi 739 | \sloppy\clubpenalty4000\widowpenalty4000% 740 | \sfcode`\.=\@m} 741 | {\def\@noitemerr 742 | {\@latex@warning{Empty `thebibliography' environment}}% 743 | \endlist} 744 | \def\@cite#1{#1}% 745 | \def\@lbibitem[#1]#2{\item[]\if@filesw 746 | {\def\protect##1{\string ##1\space}\immediate 747 | \write\@auxout{\string\bibcite{#2}{#1}}}\fi\ignorespaces} 748 | \fi 749 | \else 750 | \@cons\@openbib@code{\noexpand\small} 751 | \fi 752 | 753 | \def\idxquad{\hskip 10\p@}% space that divides entry from number 754 | 755 | \def\@idxitem{\par\hangindent 10\p@} 756 | 757 | \def\subitem{\par\setbox0=\hbox{--\enspace}% second order 758 | \noindent\hangindent\wd0\box0}% index entry 759 | 760 | \def\subsubitem{\par\setbox0=\hbox{--\,--\enspace}% third 761 | \noindent\hangindent\wd0\box0}% order index entry 762 | 763 | \def\indexspace{\par \vskip 10\p@ plus5\p@ minus3\p@\relax} 764 | 765 | \renewenvironment{theindex} 766 | {\@mkboth{\indexname}{\indexname}% 767 | \thispagestyle{empty}\parindent\z@ 768 | \parskip\z@ \@plus .3\p@\relax 769 | \let\item\par 770 | \def\,{\relax\ifmmode\mskip\thinmuskip 771 | \else\hskip0.2em\ignorespaces\fi}% 772 | \normalfont\small 773 | \begin{multicols}{2}[\@makeschapterhead{\indexname}]% 774 | } 775 | {\end{multicols}} 776 | 777 | \renewcommand\footnoterule{% 778 | \kern-3\p@ 779 | \hrule\@width 2truecm 780 | \kern2.6\p@} 781 | \newdimen\fnindent 782 | \fnindent1em 783 | \long\def\@makefntext#1{% 784 | \parindent \fnindent% 785 | \leftskip \fnindent% 786 | \noindent 787 | \llap{\hb@xt@1em{\hss\@makefnmark\ }}\ignorespaces#1} 788 | 789 | \long\def\@makecaption#1#2{% 790 | \small 791 | \vskip\abovecaptionskip 792 | \sbox\@tempboxa{{\bfseries #1.} #2}% 793 | \ifdim \wd\@tempboxa >\hsize 794 | {\bfseries #1.} #2\par 795 | \else 796 | \global \@minipagefalse 797 | \hb@xt@\hsize{\hfil\box\@tempboxa\hfil}% 798 | \fi 799 | \vskip\belowcaptionskip} 800 | 801 | \def\fps@figure{htbp} 802 | \def\fnum@figure{\figurename\thinspace\thefigure} 803 | \def \@floatboxreset {% 804 | \reset@font 805 | \small 806 | \@setnobreak 807 | \@setminipage 808 | } 809 | \def\fps@table{htbp} 810 | \def\fnum@table{\tablename~\thetable} 811 | \renewenvironment{table} 812 | {\setlength\abovecaptionskip{0\p@}% 813 | \setlength\belowcaptionskip{10\p@}% 814 | \@float{table}} 815 | {\end@float} 816 | \renewenvironment{table*} 817 | {\setlength\abovecaptionskip{0\p@}% 818 | \setlength\belowcaptionskip{10\p@}% 819 | \@dblfloat{table}} 820 | {\end@dblfloat} 821 | 822 | \long\def\@caption#1[#2]#3{\par\addcontentsline{\csname 823 | ext@#1\endcsname}{#1}{\protect\numberline{\csname 824 | the#1\endcsname}{\ignorespaces #2}}\begingroup 825 | \@parboxrestore 826 | \@makecaption{\csname fnum@#1\endcsname}{\ignorespaces #3}\par 827 | \endgroup} 828 | 829 | % LaTeX does not provide a command to enter the authors institute 830 | % addresses. The \institute command is defined here. 831 | 832 | \newcounter{@inst} 833 | \newcounter{@auth} 834 | \newcounter{auco} 835 | \newdimen\instindent 836 | \newbox\authrun 837 | \newtoks\authorrunning 838 | \newtoks\tocauthor 839 | \newbox\titrun 840 | \newtoks\titlerunning 841 | \newtoks\toctitle 842 | 843 | \def\clearheadinfo{\gdef\@author{No Author Given}% 844 | \gdef\@title{No Title Given}% 845 | \gdef\@subtitle{}% 846 | \gdef\@institute{No Institute Given}% 847 | \gdef\@thanks{}% 848 | \global\titlerunning={}\global\authorrunning={}% 849 | \global\toctitle={}\global\tocauthor={}} 850 | 851 | \def\institute#1{\gdef\@institute{#1}} 852 | 853 | \def\institutename{\par 854 | \begingroup 855 | \parskip=\z@ 856 | \parindent=\z@ 857 | \setcounter{@inst}{1}% 858 | \def\and{\par\stepcounter{@inst}% 859 | \noindent$^{\the@inst}$\enspace\ignorespaces}% 860 | \setbox0=\vbox{\def\thanks##1{}\@institute}% 861 | \ifnum\c@@inst=1\relax 862 | \gdef\fnnstart{0}% 863 | \else 864 | \xdef\fnnstart{\c@@inst}% 865 | \setcounter{@inst}{1}% 866 | \noindent$^{\the@inst}$\enspace 867 | \fi 868 | \ignorespaces 869 | \@institute\par 870 | \endgroup} 871 | 872 | \def\@fnsymbol#1{\ensuremath{\ifcase#1\or\star\or{\star\star}\or 873 | {\star\star\star}\or \dagger\or \ddagger\or 874 | \mathchar "278\or \mathchar "27B\or \|\or **\or \dagger\dagger 875 | \or \ddagger\ddagger \else\@ctrerr\fi}} 876 | 877 | \def\inst#1{\unskip$^{#1}$} 878 | \def\orcidID#1{\unskip$^{[#1]}$} % added MR 2018-03-10 879 | \def\fnmsep{\unskip$^,$} 880 | \def\email#1{{\tt#1}} 881 | 882 | \AtBeginDocument{\@ifundefined{url}{\def\url#1{#1}}{}% 883 | \@ifpackageloaded{babel}{% 884 | \@ifundefined{extrasenglish}{}{\addto\extrasenglish{\switcht@albion}}% 885 | \@ifundefined{extrasfrenchb}{}{\addto\extrasfrenchb{\switcht@francais}}% 886 | \@ifundefined{extrasgerman}{}{\addto\extrasgerman{\switcht@deutsch}}% 887 | \@ifundefined{extrasngerman}{}{\addto\extrasngerman{\switcht@deutsch}}% 888 | }{\switcht@@therlang}% 889 | \providecommand{\keywords}[1]{\def\and{{\textperiodcentered} }% 890 | \par\addvspace\baselineskip 891 | \noindent\keywordname\enspace\ignorespaces#1}% 892 | \@ifpackageloaded{hyperref}{% 893 | \def\doi#1{\href{https://doi.org/#1}{https://doi.org/#1}}}{ 894 | \def\doi#1{https://doi.org/#1}} 895 | } 896 | \def\homedir{\~{ }} 897 | 898 | \def\subtitle#1{\gdef\@subtitle{#1}} 899 | \clearheadinfo 900 | % 901 | %%% to avoid hyperref warnings 902 | \providecommand*{\toclevel@author}{999} 903 | %%% to make title-entry parent of section-entries 904 | \providecommand*{\toclevel@title}{0} 905 | % 906 | \renewcommand\maketitle{\newpage 907 | \phantomsection 908 | \refstepcounter{chapter}% 909 | \stepcounter{section}% 910 | \setcounter{section}{0}% 911 | \setcounter{subsection}{0}% 912 | \setcounter{figure}{0} 913 | \setcounter{table}{0} 914 | \setcounter{equation}{0} 915 | \setcounter{footnote}{0}% 916 | \begingroup 917 | \parindent=\z@ 918 | \renewcommand\thefootnote{\@fnsymbol\c@footnote}% 919 | \if@twocolumn 920 | \ifnum \col@number=\@ne 921 | \@maketitle 922 | \else 923 | \twocolumn[\@maketitle]% 924 | \fi 925 | \else 926 | \newpage 927 | \global\@topnum\z@ % Prevents figures from going at top of page. 928 | \@maketitle 929 | \fi 930 | \thispagestyle{empty}\@thanks 931 | % 932 | \def\\{\unskip\ \ignorespaces}\def\inst##1{\unskip{}}% 933 | \def\thanks##1{\unskip{}}\def\fnmsep{\unskip}% 934 | \instindent=\hsize 935 | \advance\instindent by-\headlineindent 936 | \if!\the\toctitle!\addcontentsline{toc}{title}{\@title}\else 937 | \addcontentsline{toc}{title}{\the\toctitle}\fi 938 | \if@runhead 939 | \if!\the\titlerunning!\else 940 | \edef\@title{\the\titlerunning}% 941 | \fi 942 | \global\setbox\titrun=\hbox{\small\rm\unboldmath\ignorespaces\@title}% 943 | \ifdim\wd\titrun>\instindent 944 | \typeout{Title too long for running head. Please supply}% 945 | \typeout{a shorter form with \string\titlerunning\space prior to 946 | \string\maketitle}% 947 | \global\setbox\titrun=\hbox{\small\rm 948 | Title Suppressed Due to Excessive Length}% 949 | \fi 950 | \xdef\@title{\copy\titrun}% 951 | \fi 952 | % 953 | \if!\the\tocauthor!\relax 954 | {\def\and{\noexpand\protect\noexpand\and}% 955 | \def\inst##1{}% added MR 2017-09-20 to remove inst numbers from the TOC 956 | \def\orcidID##1{}% added MR 2017-09-20 to remove ORCID ids from the TOC 957 | \protected@xdef\toc@uthor{\@author}}% 958 | \else 959 | \def\\{\noexpand\protect\noexpand\newline}% 960 | \protected@xdef\scratch{\the\tocauthor}% 961 | \protected@xdef\toc@uthor{\scratch}% 962 | \fi 963 | \addtocontents{toc}{\noexpand\protect\noexpand\authcount{\the\c@auco}}% 964 | \addcontentsline{toc}{author}{\toc@uthor}% 965 | \if@runhead 966 | \if!\the\authorrunning! 967 | \value{@inst}=\value{@auth}% 968 | \setcounter{@auth}{1}% 969 | \else 970 | \edef\@author{\the\authorrunning}% 971 | \fi 972 | \global\setbox\authrun=\hbox{\def\inst##1{}% added MR 2017-09-20 to remove inst numbers from the runninghead 973 | \def\orcidID##1{}% added MR 2017-09-20 to remove ORCID ids from the runninghead 974 | \small\unboldmath\@author\unskip}% 975 | \ifdim\wd\authrun>\instindent 976 | \typeout{Names of authors too long for running head. Please supply}% 977 | \typeout{a shorter form with \string\authorrunning\space prior to 978 | \string\maketitle}% 979 | \global\setbox\authrun=\hbox{\small\rm 980 | Authors Suppressed Due to Excessive Length}% 981 | \fi 982 | \xdef\@author{\copy\authrun}% 983 | \markboth{\@author}{\@title}% 984 | \fi 985 | \endgroup 986 | \setcounter{footnote}{\fnnstart}% 987 | \clearheadinfo} 988 | % 989 | \def\@maketitle{\newpage 990 | \markboth{}{}% 991 | \def\lastand{\ifnum\value{@inst}=2\relax 992 | \unskip{} \andname\ 993 | \else 994 | \unskip \lastandname\ 995 | \fi}% 996 | \def\and{\stepcounter{@auth}\relax 997 | \ifnum\value{@auth}=\value{@inst}% 998 | \lastand 999 | \else 1000 | \unskip, 1001 | \fi}% 1002 | \begin{center}% 1003 | \let\newline\\ 1004 | {\Large \bfseries\boldmath 1005 | \pretolerance=10000 1006 | \@title \par}\vskip .8cm 1007 | \if!\@subtitle!\else {\large \bfseries\boldmath 1008 | \vskip -.65cm 1009 | \pretolerance=10000 1010 | \@subtitle \par}\vskip .8cm\fi 1011 | \setbox0=\vbox{\setcounter{@auth}{1}\def\and{\stepcounter{@auth}}% 1012 | \def\thanks##1{}\@author}% 1013 | \global\value{@inst}=\value{@auth}% 1014 | \global\value{auco}=\value{@auth}% 1015 | \setcounter{@auth}{1}% 1016 | {\lineskip .5em 1017 | \noindent\ignorespaces 1018 | \@author\vskip.35cm} 1019 | {\small\institutename} 1020 | \end{center}% 1021 | } 1022 | 1023 | % definition of the "\spnewtheorem" command. 1024 | % 1025 | % Usage: 1026 | % 1027 | % \spnewtheorem{env_nam}{caption}[within]{cap_font}{body_font} 1028 | % or \spnewtheorem{env_nam}[numbered_like]{caption}{cap_font}{body_font} 1029 | % or \spnewtheorem*{env_nam}{caption}{cap_font}{body_font} 1030 | % 1031 | % New is "cap_font" and "body_font". It stands for 1032 | % fontdefinition of the caption and the text itself. 1033 | % 1034 | % "\spnewtheorem*" gives a theorem without number. 1035 | % 1036 | % A defined spnewthoerem environment is used as described 1037 | % by Lamport. 1038 | % 1039 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1040 | 1041 | \def\@thmcountersep{} 1042 | \def\@thmcounterend{.} 1043 | 1044 | \def\spnewtheorem{\@ifstar{\@sthm}{\@Sthm}} 1045 | 1046 | % definition of \spnewtheorem with number 1047 | 1048 | \def\@spnthm#1#2{% 1049 | \@ifnextchar[{\@spxnthm{#1}{#2}}{\@spynthm{#1}{#2}}} 1050 | \def\@Sthm#1{\@ifnextchar[{\@spothm{#1}}{\@spnthm{#1}}} 1051 | 1052 | \def\@spxnthm#1#2[#3]#4#5{\expandafter\@ifdefinable\csname #1\endcsname 1053 | {\@definecounter{#1}\@addtoreset{#1}{#3}% 1054 | \expandafter\xdef\csname the#1\endcsname{\expandafter\noexpand 1055 | \csname the#3\endcsname \noexpand\@thmcountersep \@thmcounter{#1}}% 1056 | \expandafter\xdef\csname #1name\endcsname{#2}% 1057 | \global\@namedef{#1}{\@spthm{#1}{\csname #1name\endcsname}{#4}{#5}}% 1058 | \global\@namedef{end#1}{\@endtheorem}}} 1059 | 1060 | \def\@spynthm#1#2#3#4{\expandafter\@ifdefinable\csname #1\endcsname 1061 | {\@definecounter{#1}% 1062 | \expandafter\xdef\csname the#1\endcsname{\@thmcounter{#1}}% 1063 | \expandafter\xdef\csname #1name\endcsname{#2}% 1064 | \global\@namedef{#1}{\@spthm{#1}{\csname #1name\endcsname}{#3}{#4}}% 1065 | \global\@namedef{end#1}{\@endtheorem}}} 1066 | 1067 | \def\@spothm#1[#2]#3#4#5{% 1068 | \@ifundefined{c@#2}{\@latexerr{No theorem environment `#2' defined}\@eha}% 1069 | {\expandafter\@ifdefinable\csname #1\endcsname 1070 | {\newaliascnt{#1}{#2}% 1071 | \expandafter\xdef\csname #1name\endcsname{#3}% 1072 | \global\@namedef{#1}{\@spthm{#1}{\csname #1name\endcsname}{#4}{#5}}% 1073 | \global\@namedef{end#1}{\@endtheorem}}}} 1074 | 1075 | \def\@spthm#1#2#3#4{\topsep 7\p@ \@plus2\p@ \@minus4\p@ 1076 | \refstepcounter{#1}% 1077 | \@ifnextchar[{\@spythm{#1}{#2}{#3}{#4}}{\@spxthm{#1}{#2}{#3}{#4}}} 1078 | 1079 | \def\@spxthm#1#2#3#4{\@spbegintheorem{#2}{\csname the#1\endcsname}{#3}{#4}% 1080 | \ignorespaces} 1081 | 1082 | \def\@spythm#1#2#3#4[#5]{\@spopargbegintheorem{#2}{\csname 1083 | the#1\endcsname}{#5}{#3}{#4}\ignorespaces} 1084 | 1085 | \def\@spbegintheorem#1#2#3#4{\trivlist 1086 | \item[\hskip\labelsep{#3#1\ #2\@thmcounterend}]#4} 1087 | 1088 | \def\@spopargbegintheorem#1#2#3#4#5{\trivlist 1089 | \item[\hskip\labelsep{#4#1\ #2}]{#4(#3)\@thmcounterend\ }#5} 1090 | 1091 | % definition of \spnewtheorem* without number 1092 | 1093 | \def\@sthm#1#2{\@Ynthm{#1}{#2}} 1094 | 1095 | \def\@Ynthm#1#2#3#4{\expandafter\@ifdefinable\csname #1\endcsname 1096 | {\global\@namedef{#1}{\@Thm{\csname #1name\endcsname}{#3}{#4}}% 1097 | \expandafter\xdef\csname #1name\endcsname{#2}% 1098 | \global\@namedef{end#1}{\@endtheorem}}} 1099 | 1100 | \def\@Thm#1#2#3{\topsep 7\p@ \@plus2\p@ \@minus4\p@ 1101 | \@ifnextchar[{\@Ythm{#1}{#2}{#3}}{\@Xthm{#1}{#2}{#3}}} 1102 | 1103 | \def\@Xthm#1#2#3{\@Begintheorem{#1}{#2}{#3}\ignorespaces} 1104 | 1105 | \def\@Ythm#1#2#3[#4]{\@Opargbegintheorem{#1} 1106 | {#4}{#2}{#3}\ignorespaces} 1107 | 1108 | \def\@Begintheorem#1#2#3{#3\trivlist 1109 | \item[\hskip\labelsep{#2#1\@thmcounterend}]} 1110 | 1111 | \def\@Opargbegintheorem#1#2#3#4{#4\trivlist 1112 | \item[\hskip\labelsep{#3#1}]{#3(#2)\@thmcounterend\ }} 1113 | 1114 | \if@envcntsect 1115 | \def\@thmcountersep{.} 1116 | \spnewtheorem{theorem}{Theorem}[section]{\bfseries}{\itshape} 1117 | \else 1118 | \spnewtheorem{theorem}{Theorem}{\bfseries}{\itshape} 1119 | \if@envcntreset 1120 | \@addtoreset{theorem}{section} 1121 | \else 1122 | \@addtoreset{theorem}{chapter} 1123 | \fi 1124 | \fi 1125 | 1126 | %definition of divers theorem environments 1127 | \spnewtheorem*{claim}{Claim}{\itshape}{\rmfamily} 1128 | \spnewtheorem*{proof}{Proof}{\itshape}{\rmfamily} 1129 | \if@envcntsame % alle Umgebungen wie Theorem. 1130 | \def\spn@wtheorem#1#2#3#4{\@spothm{#1}[theorem]{#2}{#3}{#4}} 1131 | \else % alle Umgebungen mit eigenem Zaehler 1132 | \if@envcntsect % mit section numeriert 1133 | \def\spn@wtheorem#1#2#3#4{\@spxnthm{#1}{#2}[section]{#3}{#4}} 1134 | \else % nicht mit section numeriert 1135 | \if@envcntreset 1136 | \def\spn@wtheorem#1#2#3#4{\@spynthm{#1}{#2}{#3}{#4} 1137 | \@addtoreset{#1}{section}} 1138 | \else 1139 | \def\spn@wtheorem#1#2#3#4{\@spynthm{#1}{#2}{#3}{#4} 1140 | \@addtoreset{#1}{chapter}}% 1141 | \fi 1142 | \fi 1143 | \fi 1144 | \spn@wtheorem{case}{Case}{\itshape}{\rmfamily} 1145 | \spn@wtheorem{conjecture}{Conjecture}{\itshape}{\rmfamily} 1146 | \spn@wtheorem{corollary}{Corollary}{\bfseries}{\itshape} 1147 | \spn@wtheorem{definition}{Definition}{\bfseries}{\itshape} 1148 | \spn@wtheorem{example}{Example}{\itshape}{\rmfamily} 1149 | \spn@wtheorem{exercise}{Exercise}{\itshape}{\rmfamily} 1150 | \spn@wtheorem{lemma}{Lemma}{\bfseries}{\itshape} 1151 | \spn@wtheorem{note}{Note}{\itshape}{\rmfamily} 1152 | \spn@wtheorem{problem}{Problem}{\itshape}{\rmfamily} 1153 | \spn@wtheorem{property}{Property}{\itshape}{\rmfamily} 1154 | \spn@wtheorem{proposition}{Proposition}{\bfseries}{\itshape} 1155 | \spn@wtheorem{question}{Question}{\itshape}{\rmfamily} 1156 | \spn@wtheorem{solution}{Solution}{\itshape}{\rmfamily} 1157 | \spn@wtheorem{remark}{Remark}{\itshape}{\rmfamily} 1158 | 1159 | \def\@takefromreset#1#2{% 1160 | \def\@tempa{#1}% 1161 | \let\@tempd\@elt 1162 | \def\@elt##1{% 1163 | \def\@tempb{##1}% 1164 | \ifx\@tempa\@tempb\else 1165 | \@addtoreset{##1}{#2}% 1166 | \fi}% 1167 | \expandafter\expandafter\let\expandafter\@tempc\csname cl@#2\endcsname 1168 | \expandafter\def\csname cl@#2\endcsname{}% 1169 | \@tempc 1170 | \let\@elt\@tempd} 1171 | 1172 | \def\theopargself{\def\@spopargbegintheorem##1##2##3##4##5{\trivlist 1173 | \item[\hskip\labelsep{##4##1\ ##2}]{##4##3\@thmcounterend\ }##5} 1174 | \def\@Opargbegintheorem##1##2##3##4{##4\trivlist 1175 | \item[\hskip\labelsep{##3##1}]{##3##2\@thmcounterend\ }} 1176 | } 1177 | 1178 | \renewenvironment{abstract}{% 1179 | \list{}{\advance\topsep by0.35cm\relax\small 1180 | \leftmargin=1cm 1181 | \labelwidth=\z@ 1182 | \listparindent=\z@ 1183 | \itemindent\listparindent 1184 | \rightmargin\leftmargin}\item[\hskip\labelsep 1185 | \bfseries\abstractname]} 1186 | {\endlist} 1187 | 1188 | \newdimen\headlineindent % dimension for space between 1189 | \headlineindent=1.166cm % number and text of headings. 1190 | 1191 | \def\ps@headings{\let\@mkboth\@gobbletwo 1192 | \let\@oddfoot\@empty\let\@evenfoot\@empty 1193 | \def\@evenhead{\normalfont\small\rlap{\thepage}\hspace{\headlineindent}% 1194 | \leftmark\hfil} 1195 | \def\@oddhead{\normalfont\small\hfil\rightmark\hspace{\headlineindent}% 1196 | \llap{\thepage}} 1197 | \def\chaptermark##1{}% 1198 | \def\sectionmark##1{}% 1199 | \def\subsectionmark##1{}} 1200 | 1201 | \def\ps@titlepage{\let\@mkboth\@gobbletwo 1202 | \let\@oddfoot\@empty\let\@evenfoot\@empty 1203 | \def\@evenhead{\normalfont\small\rlap{\thepage}\hspace{\headlineindent}% 1204 | \hfil} 1205 | \def\@oddhead{\normalfont\small\hfil\hspace{\headlineindent}% 1206 | \llap{\thepage}} 1207 | \def\chaptermark##1{}% 1208 | \def\sectionmark##1{}% 1209 | \def\subsectionmark##1{}} 1210 | 1211 | \if@runhead\ps@headings\else 1212 | \ps@empty\fi 1213 | 1214 | \setlength\arraycolsep{1.4\p@} 1215 | \setlength\tabcolsep{1.4\p@} 1216 | 1217 | \endinput 1218 | %end of file llncs.cls 1219 | -------------------------------------------------------------------------------- /rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ecgfp5" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [[bench]] 11 | name = "field" 12 | path = "benches/field.rs" 13 | harness = false 14 | 15 | [[bench]] 16 | name = "curve" 17 | path = "benches/curve.rs" 18 | harness = false 19 | 20 | [[bench]] 21 | name = "scalar" 22 | path = "benches/scalar.rs" 23 | harness = false 24 | -------------------------------------------------------------------------------- /rust/benches/curve.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use ecgfp5::curve::Point; 4 | use ecgfp5::scalar::Scalar; 5 | use core::arch::x86_64::{_mm_lfence, _rdtsc}; 6 | 7 | fn core_cycles() -> u64 { 8 | unsafe { 9 | _mm_lfence(); 10 | _rdtsc() 11 | } 12 | } 13 | 14 | // A custom PRNG; not cryptographically secure, but good enough 15 | // for tests. 16 | struct PRNG(u128); 17 | 18 | impl PRNG { 19 | 20 | // A is a random prime. B is random. 21 | const A: u128 = 87981536952642681582438141175044346919; 22 | const B: u128 = 331203846847999889118488772711684568728; 23 | 24 | fn next_u64(&mut self) -> u64 { 25 | self.0 = PRNG::A.wrapping_mul(self.0).wrapping_add(PRNG::B); 26 | (self.0 >> 32) as u64 27 | } 28 | 29 | fn next(&mut self, buf: &mut [u8]) { 30 | let mut acc: u64 = 0; 31 | for i in 0..buf.len() { 32 | if (i & 7) == 0 { 33 | acc = self.next_u64(); 34 | } 35 | buf[i] = acc as u8; 36 | acc >>= 8; 37 | } 38 | } 39 | } 40 | 41 | fn bench_curve_add() { 42 | let mut P = Point::GENERATOR; 43 | let mut ebuf = [0u8; 40]; 44 | for i in 0..ebuf.len() { 45 | ebuf[i] = core_cycles() as u8; 46 | } 47 | let mut Q = P * Scalar::decode_reduce(&ebuf); 48 | let mut tt = [0; 10]; 49 | for i in 0..10 { 50 | let begin = core_cycles(); 51 | for _ in 0..1000 { 52 | Q += P; 53 | P += Q; 54 | Q += P; 55 | P += Q; 56 | Q += P; 57 | P += Q; 58 | } 59 | let end = core_cycles(); 60 | tt[i] = end.wrapping_sub(begin); 61 | } 62 | tt.sort(); 63 | let x = P.encode().0[0]; 64 | println!("Curve add: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 65 | } 66 | 67 | fn bench_curve_double() { 68 | let mut P = Point::GENERATOR; 69 | let mut ebuf = [0u8; 40]; 70 | for i in 0..ebuf.len() { 71 | ebuf[i] = core_cycles() as u8; 72 | } 73 | P *= Scalar::decode_reduce(&ebuf); 74 | let mut tt = [0; 10]; 75 | for i in 0..10 { 76 | let begin = core_cycles(); 77 | for _ in 0..1000 { 78 | P = P.double(); 79 | P = P.double(); 80 | P = P.double(); 81 | P = P.double(); 82 | P = P.double(); 83 | P = P.double(); 84 | } 85 | let end = core_cycles(); 86 | tt[i] = end.wrapping_sub(begin); 87 | } 88 | tt.sort(); 89 | let x = P.encode().0[0]; 90 | println!("Curve double: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 91 | } 92 | 93 | fn bench_curve_double_x5() { 94 | let mut P = Point::GENERATOR; 95 | let mut ebuf = [0u8; 40]; 96 | for i in 0..ebuf.len() { 97 | ebuf[i] = core_cycles() as u8; 98 | } 99 | P *= Scalar::decode_reduce(&ebuf); 100 | let mut tt = [0; 10]; 101 | for i in 0..10 { 102 | let begin = core_cycles(); 103 | for _ in 0..1000 { 104 | P = P.mdouble(5); 105 | P = P.mdouble(5); 106 | P = P.mdouble(5); 107 | P = P.mdouble(5); 108 | P = P.mdouble(5); 109 | P = P.mdouble(5); 110 | } 111 | let end = core_cycles(); 112 | tt[i] = end.wrapping_sub(begin); 113 | } 114 | tt.sort(); 115 | let x = P.encode().0[0]; 116 | println!("Curve double x5: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 117 | } 118 | 119 | fn bench_curve_mul() { 120 | let mut P = Point::GENERATOR; 121 | let mut ebuf = [0u8; 40]; 122 | for i in 0..ebuf.len() { 123 | ebuf[i] = core_cycles() as u8; 124 | } 125 | let mut e = Scalar::decode_reduce(&ebuf); 126 | let mut tt = [0; 10]; 127 | for i in 0..10 { 128 | let begin = core_cycles(); 129 | for _ in 0..1000 { 130 | P = P * e; 131 | } 132 | let end = core_cycles(); 133 | tt[i] = end.wrapping_sub(begin); 134 | e += e; 135 | } 136 | tt.sort(); 137 | let x = P.encode().0[0]; 138 | println!("Curve mul: {:11.2} ({})", (tt[4] as f64) / 1000.0, x.to_u64()); 139 | } 140 | 141 | fn bench_curve_mulgen() { 142 | let mut ebuf = [0u8; 40]; 143 | for i in 0..ebuf.len() { 144 | ebuf[i] = core_cycles() as u8; 145 | } 146 | let mut e = Scalar::decode_reduce(&ebuf); 147 | let mut tt = [0; 10]; 148 | for i in 0..10 { 149 | let begin = core_cycles(); 150 | for _ in 0..1000 { 151 | let P = Point::mulgen(e); 152 | if (P.encode().0[0].to_u64() & 1) == 0 { 153 | e += e; 154 | } 155 | } 156 | let end = core_cycles(); 157 | tt[i] = end.wrapping_sub(begin); 158 | e += e; 159 | } 160 | tt.sort(); 161 | let x = e.encode()[0]; 162 | println!("Curve mulgen: {:11.2} ({})", (tt[4] as f64) / 1000.0, x); 163 | } 164 | 165 | fn bench_curve_verify_muladd_vartime() { 166 | let mut prng = PRNG(core_cycles() as u128); 167 | let mut QQ = [Point::NEUTRAL; 50]; 168 | let mut RR = [Point::NEUTRAL; 50]; 169 | let mut ss = [Scalar::ZERO; 50]; 170 | let mut kk = [Scalar::ZERO; 50]; 171 | let mut tt = [0; 100]; 172 | for i in 0..100 { 173 | for j in 0..QQ.len() { 174 | let mut ebuf = [0u8; 40]; 175 | let mut sbuf = [0u8; 40]; 176 | let mut kbuf = [0u8; 40]; 177 | prng.next(&mut ebuf); 178 | prng.next(&mut sbuf); 179 | prng.next(&mut kbuf); 180 | QQ[j] = Point::mulgen(Scalar::decode_reduce(&ebuf)); 181 | ss[j] = Scalar::decode_reduce(&sbuf); 182 | kk[j] = Scalar::decode_reduce(&kbuf); 183 | RR[j] = Point::mulgen(ss[j]) + kk[j]*QQ[j]; 184 | if (prng.next_u64() & 1) != 0 { 185 | RR[j] = RR[j].double(); 186 | } 187 | } 188 | let begin = core_cycles(); 189 | for j in 0..QQ.len() { 190 | if QQ[j].verify_muladd_vartime(ss[j], kk[j], RR[j]) { 191 | prng.0 = prng.0.wrapping_add(j as u128); 192 | } 193 | } 194 | let end = core_cycles(); 195 | tt[i] = end.wrapping_sub(begin); 196 | } 197 | tt.sort(); 198 | let x = prng.next_u64(); 199 | println!("Curve verify: {:11.2} ({})", (tt[49] as f64) / (QQ.len() as f64), x); 200 | } 201 | 202 | fn main() { 203 | bench_curve_add(); 204 | bench_curve_double(); 205 | bench_curve_double_x5(); 206 | bench_curve_mul(); 207 | bench_curve_mulgen(); 208 | bench_curve_verify_muladd_vartime(); 209 | } 210 | -------------------------------------------------------------------------------- /rust/benches/field.rs: -------------------------------------------------------------------------------- 1 | use ecgfp5::field::{GFp, GFp5}; 2 | use core::arch::x86_64::{_mm_lfence, _rdtsc}; 3 | 4 | fn core_cycles() -> u64 { 5 | unsafe { 6 | _mm_lfence(); 7 | _rdtsc() 8 | } 9 | } 10 | 11 | fn bench_gfp_add() { 12 | let mut x = GFp::from_u64_reduce(core_cycles()); 13 | let mut y = x + GFp::ONE; 14 | let mut tt = [0; 10]; 15 | for i in 0..10 { 16 | let begin = core_cycles(); 17 | for _ in 0..1000 { 18 | x += y; 19 | y += x; 20 | x += y; 21 | y += x; 22 | x += y; 23 | y += x; 24 | } 25 | let end = core_cycles(); 26 | tt[i] = end.wrapping_sub(begin); 27 | } 28 | tt.sort(); 29 | println!("GFp add: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 30 | } 31 | 32 | fn bench_gfp_sub() { 33 | let mut x = GFp::from_u64_reduce(core_cycles()); 34 | let mut y = x + GFp::ONE; 35 | let mut tt = [0; 10]; 36 | for i in 0..10 { 37 | let begin = core_cycles(); 38 | for _ in 0..1000 { 39 | x -= y; 40 | y -= x; 41 | x -= y; 42 | y -= x; 43 | x -= y; 44 | y -= x; 45 | } 46 | let end = core_cycles(); 47 | tt[i] = end.wrapping_sub(begin); 48 | } 49 | tt.sort(); 50 | println!("GFp sub: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 51 | } 52 | 53 | fn bench_gfp_mul() { 54 | let mut x = GFp::from_u64_reduce(core_cycles()); 55 | let mut y = x + GFp::ONE; 56 | let mut tt = [0; 10]; 57 | for i in 0..10 { 58 | let begin = core_cycles(); 59 | for _ in 0..1000 { 60 | x *= y; 61 | y *= x; 62 | x *= y; 63 | y *= x; 64 | x *= y; 65 | y *= x; 66 | } 67 | let end = core_cycles(); 68 | tt[i] = end.wrapping_sub(begin); 69 | } 70 | tt.sort(); 71 | println!("GFp mul: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 72 | } 73 | 74 | fn bench_gfp_invert() { 75 | let mut x = GFp::from_u64_reduce(core_cycles()); 76 | let mut tt = [0; 10]; 77 | for i in 0..10 { 78 | let begin = core_cycles(); 79 | for _ in 0..1000 { 80 | x = x.invert(); 81 | x = x.invert(); 82 | x = x.invert(); 83 | x = x.invert(); 84 | x = x.invert(); 85 | x = x.invert(); 86 | } 87 | let end = core_cycles(); 88 | tt[i] = end.wrapping_sub(begin); 89 | } 90 | tt.sort(); 91 | println!("GFp invert: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 92 | } 93 | 94 | fn bench_gfp_legendre() { 95 | let mut x = GFp::from_u64_reduce(core_cycles()); 96 | let mut tt = [0; 10]; 97 | for i in 0..10 { 98 | let begin = core_cycles(); 99 | for _ in 0..1000 { 100 | x = GFp::from_u64_reduce(x.legendre().to_u64().wrapping_add(x.to_u64())); 101 | x = GFp::from_u64_reduce(x.legendre().to_u64().wrapping_add(x.to_u64())); 102 | x = GFp::from_u64_reduce(x.legendre().to_u64().wrapping_add(x.to_u64())); 103 | x = GFp::from_u64_reduce(x.legendre().to_u64().wrapping_add(x.to_u64())); 104 | x = GFp::from_u64_reduce(x.legendre().to_u64().wrapping_add(x.to_u64())); 105 | x = GFp::from_u64_reduce(x.legendre().to_u64().wrapping_add(x.to_u64())); 106 | } 107 | let end = core_cycles(); 108 | tt[i] = end.wrapping_sub(begin); 109 | } 110 | tt.sort(); 111 | println!("GFp legendre: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 112 | } 113 | 114 | fn bench_gfp_sqrt() { 115 | let mut x = GFp::from_u64_reduce(core_cycles()); 116 | let mut tt = [0; 10]; 117 | for i in 0..10 { 118 | let begin = core_cycles(); 119 | for _ in 0..1000 { 120 | let (r0, c0) = x.sqrt(); x = GFp::from_u64_reduce(x.to_u64().wrapping_add(r0.to_u64()).wrapping_add(c0)); 121 | let (r1, c1) = x.sqrt(); x = GFp::from_u64_reduce(x.to_u64().wrapping_add(r1.to_u64()).wrapping_add(c1)); 122 | let (r2, c2) = x.sqrt(); x = GFp::from_u64_reduce(x.to_u64().wrapping_add(r2.to_u64()).wrapping_add(c2)); 123 | let (r3, c3) = x.sqrt(); x = GFp::from_u64_reduce(x.to_u64().wrapping_add(r3.to_u64()).wrapping_add(c3)); 124 | let (r4, c4) = x.sqrt(); x = GFp::from_u64_reduce(x.to_u64().wrapping_add(r4.to_u64()).wrapping_add(c4)); 125 | let (r5, c5) = x.sqrt(); x = GFp::from_u64_reduce(x.to_u64().wrapping_add(r5.to_u64()).wrapping_add(c5)); 126 | } 127 | let end = core_cycles(); 128 | tt[i] = end.wrapping_sub(begin); 129 | } 130 | tt.sort(); 131 | println!("GFp sqrt: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.to_u64()); 132 | } 133 | 134 | fn bench_gfp5_add() { 135 | let x0 = GFp::from_u64_reduce(core_cycles()); 136 | let x1 = GFp::from_u64_reduce(core_cycles()); 137 | let x2 = GFp::from_u64_reduce(core_cycles()); 138 | let x3 = GFp::from_u64_reduce(core_cycles()); 139 | let x4 = GFp::from_u64_reduce(core_cycles()); 140 | let mut x = GFp5([x0, x1, x2, x3, x4]); 141 | let mut y = GFp5([x4, x3, x2, x1, x0]); 142 | let mut tt = [0; 10]; 143 | for i in 0..10 { 144 | let begin = core_cycles(); 145 | for _ in 0..1000 { 146 | x += y; 147 | y += x; 148 | x += y; 149 | y += x; 150 | x += y; 151 | y += x; 152 | } 153 | let end = core_cycles(); 154 | tt[i] = end.wrapping_sub(begin); 155 | } 156 | tt.sort(); 157 | let z = (x.0[0] + x.0[1] + x.0[2] + x.0[3] + x.0[4]).to_u64(); 158 | println!("GFp5 add: {:11.2} ({})", (tt[4] as f64) / 6000.0, z); 159 | } 160 | 161 | fn bench_gfp5_sub() { 162 | let x0 = GFp::from_u64_reduce(core_cycles()); 163 | let x1 = GFp::from_u64_reduce(core_cycles()); 164 | let x2 = GFp::from_u64_reduce(core_cycles()); 165 | let x3 = GFp::from_u64_reduce(core_cycles()); 166 | let x4 = GFp::from_u64_reduce(core_cycles()); 167 | let mut x = GFp5([x0, x1, x2, x3, x4]); 168 | let mut y = GFp5([x4, x3, x2, x1, x0]); 169 | let mut tt = [0; 10]; 170 | for i in 0..10 { 171 | let begin = core_cycles(); 172 | for _ in 0..1000 { 173 | x -= y; 174 | y -= x; 175 | x -= y; 176 | y -= x; 177 | x -= y; 178 | y -= x; 179 | } 180 | let end = core_cycles(); 181 | tt[i] = end.wrapping_sub(begin); 182 | } 183 | tt.sort(); 184 | let z = (x.0[0] + x.0[1] + x.0[2] + x.0[3] + x.0[4]).to_u64(); 185 | println!("GFp5 sub: {:11.2} ({})", (tt[4] as f64) / 6000.0, z); 186 | } 187 | 188 | fn bench_gfp5_mul() { 189 | let x0 = GFp::from_u64_reduce(core_cycles()); 190 | let x1 = GFp::from_u64_reduce(core_cycles()); 191 | let x2 = GFp::from_u64_reduce(core_cycles()); 192 | let x3 = GFp::from_u64_reduce(core_cycles()); 193 | let x4 = GFp::from_u64_reduce(core_cycles()); 194 | let mut x = GFp5([x0, x1, x2, x3, x4]); 195 | let mut y = GFp5([x4, x3, x2, x1, x0]); 196 | let mut tt = [0; 10]; 197 | for i in 0..10 { 198 | let begin = core_cycles(); 199 | for _ in 0..1000 { 200 | x *= y; 201 | y *= x; 202 | x *= y; 203 | y *= x; 204 | x *= y; 205 | y *= x; 206 | } 207 | let end = core_cycles(); 208 | tt[i] = end.wrapping_sub(begin); 209 | } 210 | tt.sort(); 211 | let z = (x.0[0] + x.0[1] + x.0[2] + x.0[3] + x.0[4]).to_u64(); 212 | println!("GFp5 mul: {:11.2} ({})", (tt[4] as f64) / 6000.0, z); 213 | } 214 | 215 | fn bench_gfp5_square() { 216 | let x0 = GFp::from_u64_reduce(core_cycles()); 217 | let x1 = GFp::from_u64_reduce(core_cycles()); 218 | let x2 = GFp::from_u64_reduce(core_cycles()); 219 | let x3 = GFp::from_u64_reduce(core_cycles()); 220 | let x4 = GFp::from_u64_reduce(core_cycles()); 221 | let mut x = GFp5([x0, x1, x2, x3, x4]); 222 | let mut tt = [0; 10]; 223 | for i in 0..10 { 224 | let begin = core_cycles(); 225 | for _ in 0..1000 { 226 | x = x.square(); 227 | x = x.square(); 228 | x = x.square(); 229 | x = x.square(); 230 | x = x.square(); 231 | x = x.square(); 232 | } 233 | let end = core_cycles(); 234 | tt[i] = end.wrapping_sub(begin); 235 | } 236 | tt.sort(); 237 | let z = (x.0[0] + x.0[1] + x.0[2] + x.0[3] + x.0[4]).to_u64(); 238 | println!("GFp5 square: {:11.2} ({})", (tt[4] as f64) / 6000.0, z); 239 | } 240 | 241 | fn bench_gfp5_invert() { 242 | let x0 = GFp::from_u64_reduce(core_cycles()); 243 | let x1 = GFp::from_u64_reduce(core_cycles()); 244 | let x2 = GFp::from_u64_reduce(core_cycles()); 245 | let x3 = GFp::from_u64_reduce(core_cycles()); 246 | let x4 = GFp::from_u64_reduce(core_cycles()); 247 | let mut x = GFp5([x0, x1, x2, x3, x4]); 248 | let mut tt = [0; 10]; 249 | for i in 0..10 { 250 | let begin = core_cycles(); 251 | for _ in 0..1000 { 252 | x = x.invert(); 253 | x = x.invert(); 254 | x = x.invert(); 255 | x = x.invert(); 256 | x = x.invert(); 257 | x = x.invert(); 258 | } 259 | let end = core_cycles(); 260 | tt[i] = end.wrapping_sub(begin); 261 | } 262 | tt.sort(); 263 | println!("GFp5 invert: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.0[0].to_u64()); 264 | } 265 | 266 | fn bench_gfp5_legendre() { 267 | let x0 = GFp::from_u64_reduce(core_cycles()); 268 | let x1 = GFp::from_u64_reduce(core_cycles()); 269 | let x2 = GFp::from_u64_reduce(core_cycles()); 270 | let x3 = GFp::from_u64_reduce(core_cycles()); 271 | let x4 = GFp::from_u64_reduce(core_cycles()); 272 | let mut x = GFp5([x0, x1, x2, x3, x4]); 273 | let mut tt = [0; 10]; 274 | for i in 0..10 { 275 | let begin = core_cycles(); 276 | for _ in 0..1000 { 277 | let v0 = x.legendre(); x = GFp5([GFp::from_u64_reduce(x.0[0].to_u64().wrapping_add(v0.to_u64())), GFp::from_u64_reduce(x.0[1].to_u64().wrapping_sub(v0.to_u64())), x.0[2], x.0[3], x.0[4]]); 278 | let v1 = x.legendre(); x = GFp5([GFp::from_u64_reduce(x.0[0].to_u64().wrapping_add(v1.to_u64())), GFp::from_u64_reduce(x.0[1].to_u64().wrapping_sub(v1.to_u64())), x.0[2], x.0[3], x.0[4]]); 279 | let v2 = x.legendre(); x = GFp5([GFp::from_u64_reduce(x.0[0].to_u64().wrapping_add(v2.to_u64())), GFp::from_u64_reduce(x.0[1].to_u64().wrapping_sub(v2.to_u64())), x.0[2], x.0[3], x.0[4]]); 280 | let v3 = x.legendre(); x = GFp5([GFp::from_u64_reduce(x.0[0].to_u64().wrapping_add(v3.to_u64())), GFp::from_u64_reduce(x.0[1].to_u64().wrapping_sub(v3.to_u64())), x.0[2], x.0[3], x.0[4]]); 281 | let v4 = x.legendre(); x = GFp5([GFp::from_u64_reduce(x.0[0].to_u64().wrapping_add(v4.to_u64())), GFp::from_u64_reduce(x.0[1].to_u64().wrapping_sub(v4.to_u64())), x.0[2], x.0[3], x.0[4]]); 282 | let v5 = x.legendre(); x = GFp5([GFp::from_u64_reduce(x.0[0].to_u64().wrapping_add(v5.to_u64())), GFp::from_u64_reduce(x.0[1].to_u64().wrapping_sub(v5.to_u64())), x.0[2], x.0[3], x.0[4]]); 283 | } 284 | let end = core_cycles(); 285 | tt[i] = end.wrapping_sub(begin); 286 | } 287 | tt.sort(); 288 | println!("GFp5 legendre: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.0[0].to_u64()); 289 | } 290 | 291 | fn bench_gfp5_sqrt() { 292 | let x0 = GFp::from_u64_reduce(core_cycles()); 293 | let x1 = GFp::from_u64_reduce(core_cycles()); 294 | let x2 = GFp::from_u64_reduce(core_cycles()); 295 | let x3 = GFp::from_u64_reduce(core_cycles()); 296 | let x4 = GFp::from_u64_reduce(core_cycles()); 297 | let mut x = GFp5([x0, x1, x2, x3, x4]); 298 | let mut tt = [0; 10]; 299 | for i in 0..10 { 300 | let begin = core_cycles(); 301 | for _ in 0..1000 { 302 | let (s0, c0) = x.sqrt(); x = GFp5([GFp::from_u64_reduce(s0.0[0].to_u64().wrapping_add(c0)), s0.0[1], s0.0[2], s0.0[3], s0.0[4]]); 303 | let (s1, c1) = x.sqrt(); x = GFp5([GFp::from_u64_reduce(s1.0[0].to_u64().wrapping_add(c1)), s1.0[1], s1.0[2], s1.0[3], s1.0[4]]); 304 | let (s2, c2) = x.sqrt(); x = GFp5([GFp::from_u64_reduce(s2.0[0].to_u64().wrapping_add(c2)), s2.0[1], s2.0[2], s2.0[3], s2.0[4]]); 305 | let (s3, c3) = x.sqrt(); x = GFp5([GFp::from_u64_reduce(s3.0[0].to_u64().wrapping_add(c3)), s3.0[1], s3.0[2], s3.0[3], s3.0[4]]); 306 | let (s4, c4) = x.sqrt(); x = GFp5([GFp::from_u64_reduce(s4.0[0].to_u64().wrapping_add(c4)), s4.0[1], s4.0[2], s4.0[3], s4.0[4]]); 307 | let (s5, c5) = x.sqrt(); x = GFp5([GFp::from_u64_reduce(s5.0[0].to_u64().wrapping_add(c5)), s5.0[1], s5.0[2], s5.0[3], s5.0[4]]); 308 | } 309 | let end = core_cycles(); 310 | tt[i] = end.wrapping_sub(begin); 311 | } 312 | tt.sort(); 313 | println!("GFp5 sqrt: {:11.2} ({})", (tt[4] as f64) / 6000.0, x.0[0].to_u64()); 314 | } 315 | 316 | fn main() { 317 | bench_gfp_add(); 318 | bench_gfp_sub(); 319 | bench_gfp_mul(); 320 | bench_gfp_invert(); 321 | bench_gfp_legendre(); 322 | bench_gfp_sqrt(); 323 | 324 | bench_gfp5_add(); 325 | bench_gfp5_sub(); 326 | bench_gfp5_mul(); 327 | bench_gfp5_square(); 328 | bench_gfp5_invert(); 329 | bench_gfp5_legendre(); 330 | bench_gfp5_sqrt(); 331 | } 332 | -------------------------------------------------------------------------------- /rust/benches/scalar.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use ecgfp5::scalar::Scalar; 4 | use core::arch::x86_64::{_mm_lfence, _rdtsc}; 5 | 6 | fn core_cycles() -> u64 { 7 | unsafe { 8 | _mm_lfence(); 9 | _rdtsc() 10 | } 11 | } 12 | 13 | // A custom PRNG; not cryptographically secure, but good enough 14 | // for tests. 15 | struct PRNG(u128); 16 | 17 | impl PRNG { 18 | 19 | // A is a random prime. B is random. 20 | const A: u128 = 87981536952642681582438141175044346919; 21 | const B: u128 = 331203846847999889118488772711684568728; 22 | 23 | fn next_u64(&mut self) -> u64 { 24 | self.0 = PRNG::A.wrapping_mul(self.0).wrapping_add(PRNG::B); 25 | (self.0 >> 32) as u64 26 | } 27 | 28 | fn next(&mut self, buf: &mut [u8]) { 29 | let mut acc: u64 = 0; 30 | for i in 0..buf.len() { 31 | if (i & 7) == 0 { 32 | acc = self.next_u64(); 33 | } 34 | buf[i] = acc as u8; 35 | acc >>= 8; 36 | } 37 | } 38 | } 39 | 40 | fn bench_scalar_lagrange() { 41 | // Since Lagrange's algorithm execution time is variable, we need to 42 | // make some random inputs out-of-band. 43 | let mut prng = PRNG(core_cycles() as u128); 44 | let mut tt = [0; 10]; 45 | for i in 0..10 { 46 | let mut kk = [Scalar::ZERO; 300]; 47 | for j in 0..kk.len() { 48 | let mut kbuf = [0u8; 40]; 49 | prng.next(&mut kbuf); 50 | kk[j] = Scalar::decode_reduce(&kbuf); 51 | } 52 | let begin = core_cycles(); 53 | for j in 0..300 { 54 | let (c0, c1) = kk[j].lagrange(); 55 | let tmp0 = c0.to_u192(); 56 | let tmp1 = c1.to_u192(); 57 | prng.0 = prng.0.wrapping_add(tmp0[0] as u128); 58 | prng.0 = prng.0.wrapping_add(tmp1[0] as u128); 59 | } 60 | let end = core_cycles(); 61 | tt[i] = end.wrapping_sub(begin); 62 | } 63 | tt.sort(); 64 | let x = prng.next_u64(); 65 | println!("Scalar lagrange: {:11.2} ({})", (tt[4] as f64) / 300.0, x); 66 | } 67 | 68 | fn main() { 69 | bench_scalar_lagrange(); 70 | } 71 | -------------------------------------------------------------------------------- /rust/src/curve.rs: -------------------------------------------------------------------------------- 1 | // We want uppercase X, Z, U and T to match formal descriptions of point 2 | // addition formulas. In this file, lowercase 'x', 'u' and 'w' are for 3 | // affine coordinates; uppercase is used for fractional coordinates. 4 | #![allow(non_snake_case)] 5 | 6 | use core::ops::{Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign}; 7 | use super::field::{GFp, GFp5}; 8 | use super::scalar::Scalar; 9 | use super::multab::{G0, G40, G80, G120, G160, G200, G240, G280}; 10 | 11 | // ======================================================================== 12 | 13 | /// A curve point. 14 | #[derive(Clone, Copy, Debug)] 15 | pub struct Point { 16 | // Internally, we use the (x,u) fractional coordinates: for curve 17 | // point (x,y), we have (x,u) = (x,x/y) = (X/Z,U/T) (for the neutral 18 | // N, the u coordinate is 0). 19 | X: GFp5, 20 | Z: GFp5, 21 | U: GFp5, 22 | T: GFp5, 23 | } 24 | 25 | impl Point { 26 | 27 | // Curve equation 'a' constant. 28 | const A: GFp5 = GFp5([ 29 | GFp::from_u64_reduce(2), 30 | GFp::ZERO, 31 | GFp::ZERO, 32 | GFp::ZERO, 33 | GFp::ZERO, 34 | ]); 35 | 36 | // Curve equation 'b' constant is equal to B1*z. 37 | const B1: u32 = 263; 38 | 39 | /* unused 40 | // Curve equation 'b' constant. 41 | const B: GFp5 = GFp5([ 42 | GFp::ZERO, 43 | GFp::from_u64_reduce(Self::B1 as u64), 44 | GFp::ZERO, 45 | GFp::ZERO, 46 | GFp::ZERO, 47 | ]); 48 | */ 49 | 50 | // 4*b 51 | const B_MUL4: GFp5 = GFp5([ 52 | GFp::ZERO, 53 | GFp::from_u64_reduce((4 * Self::B1) as u64), 54 | GFp::ZERO, 55 | GFp::ZERO, 56 | GFp::ZERO, 57 | ]); 58 | 59 | /// The neutral point (neutral of the group law). 60 | pub const NEUTRAL: Self = Self { 61 | X: GFp5::ZERO, 62 | Z: GFp5::ONE, 63 | U: GFp5::ZERO, 64 | T: GFp5::ONE, 65 | }; 66 | 67 | /// The conventional generator (corresponding to encoding w = 4). 68 | pub const GENERATOR: Self = Self { 69 | X: GFp5::from_u64_reduce( 70 | 12883135586176881569, 71 | 4356519642755055268, 72 | 5248930565894896907, 73 | 2165973894480315022, 74 | 2448410071095648785), 75 | Z: GFp5::ONE, 76 | U: GFp5::from_u64_reduce( 77 | 13835058052060938241, 78 | 0, 79 | 0, 80 | 0, 81 | 0), 82 | T: GFp5::ONE, 83 | }; 84 | 85 | /// Encode this point into a field element. Encoding is always 86 | /// canonical. 87 | pub fn encode(self) -> GFp5 { 88 | // Encoded form is the value w = 1/u. For the neutral (u == 0), 89 | // the encoded form is 0. Since our inversion over GF(p^5) already 90 | // yields 0 in that case, there is no need for any special code. 91 | self.T / self.U 92 | } 93 | 94 | /// Test whether a field element can be decoded into a point. Returned 95 | /// value is 0xFFFFFFFFFFFFFFFF if decoding would work, 0 otherwise. 96 | pub fn validate(w: GFp5) -> u64 { 97 | // Value w can be decoded if and only if it is zero, or 98 | // (w^2 - a)^2 - 4*b is a quadratic residue. 99 | let e = w.square() - Self::A; 100 | let delta = e.square() - Self::B_MUL4; 101 | w.iszero() | delta.legendre().isone() 102 | } 103 | 104 | /// Decode a point from a field element. Returned value is (P, c): if 105 | /// decoding succeeds, then P is the point, and c == 0xFFFFFFFFFFFFFFFF; 106 | /// otherwise, P is set to the neutral, and c == 0. 107 | pub fn decode(w: GFp5) -> (Self, u64) { 108 | // Curve equation is y^2 = x*(x^2 + a*x + b); encoded value 109 | // is w = y/x. Dividing by x, we get the equation: 110 | // x^2 - (w^2 - a)*x + b = 0 111 | // We solve for x and keep the solution which is not itself a 112 | // square (if there are solutions, exactly one of them will be 113 | // a square, and the other will not be a square). 114 | 115 | let e = w.square() - Self::A; 116 | let delta = e.square() - Self::B_MUL4; 117 | let (r, c) = delta.sqrt(); 118 | let x1 = (e + r).half(); 119 | let x2 = (e - r).half(); 120 | let x = GFp5::select(x1.legendre().isone(), x1, x2); 121 | 122 | // If c == 0, then we want to get the neutral here; note that if 123 | // w == 0, then delta = a^2 - 4*b, which is not a square, and 124 | // thus we also get c == 0. 125 | let X = GFp5::select(c, GFp5::ZERO, x); 126 | let Z = GFp5::ONE; 127 | let U = GFp5::select(c, GFp5::ZERO, GFp5::ONE); 128 | let T = GFp5::select(c, GFp5::ONE, w); 129 | 130 | // If w == 0 then this is in fact a success. 131 | (Self { X, Z, U, T }, c | w.iszero()) 132 | } 133 | 134 | // General point addition. Formulas are complete (no special case). 135 | fn set_add(&mut self, rhs: &Self) { 136 | // cost: 10M 137 | let (X1, Z1, U1, T1) = (&self.X, &self.Z, &self.U, &self.T); 138 | let (X2, Z2, U2, T2) = (&rhs.X, &rhs.Z, &rhs.U, &rhs.T); 139 | 140 | let t1 = X1 * X2; 141 | let t2 = Z1 * Z2; 142 | let t3 = U1 * U2; 143 | let t4 = T1 * T2; 144 | let t5 = (X1 + Z1) * (X2 + Z2) - t1 - t2; 145 | let t6 = (U1 + T1) * (U2 + T2) - t3 - t4; 146 | let t7 = t1 + t2.mul_small_k1(Self::B1); 147 | let t8 = t4 * t7; 148 | let t9 = t3 * (t5.mul_small_k1(2 * Self::B1) + t7.double()); 149 | let t10 = (t4 + t3.double()) * (t5 + t7); 150 | self.X = (t10 - t8).mul_small_k1(Self::B1); 151 | self.Z = t8 - t9; 152 | self.U = t6 * (t2.mul_small_k1(Self::B1) - t1); 153 | self.T = t8 + t9; 154 | } 155 | 156 | // Add a point in affine coordinates to this one. 157 | fn set_add_affine(&mut self, rhs: &PointAffine) { 158 | // cost: 8M 159 | let (X1, Z1, U1, T1) = (&self.X, &self.Z, &self.U, &self.T); 160 | let (x2, u2) = (&rhs.x, &rhs.u); 161 | 162 | let t1 = X1 * x2; 163 | let t2 = Z1; 164 | let t3 = U1 * u2; 165 | let t4 = T1; 166 | let t5 = X1 + x2 * Z1; 167 | let t6 = U1 + u2 * T1; 168 | let t7 = t1 + t2.mul_small_k1(Self::B1); 169 | let t8 = t4 * t7; 170 | let t9 = t3 * (t5.mul_small_k1(2 * Self::B1) + t7.double()); 171 | let t10 = (t4 + t3.double()) * (t5 + t7); 172 | self.X = (t10 - t8).mul_small_k1(Self::B1); 173 | self.U = t6 * (t2.mul_small_k1(Self::B1) - t1); 174 | self.Z = t8 - t9; 175 | self.T = t8 + t9; 176 | } 177 | 178 | // Subtract a point in affine coordinates from this one. 179 | fn set_sub_affine(&mut self, rhs: &PointAffine) { 180 | self.set_add_affine(&PointAffine { x: rhs.x, u: -rhs.u }) 181 | } 182 | 183 | fn set_neg(&mut self) { 184 | self.U.set_neg(); 185 | } 186 | 187 | fn set_sub(&mut self, rhs: &Self) { 188 | self.set_add(&rhs.neg()) 189 | } 190 | 191 | /// Specialized point doubling function (faster than using general 192 | /// addition on the point and itself). 193 | pub fn double(self) -> Self { 194 | let mut r = self; 195 | r.set_double(); 196 | r 197 | } 198 | 199 | fn set_double(&mut self) { 200 | // cost: 4M+5S 201 | let (X, Z, U, T) = (&self.X, &self.Z, &self.U, &self.T); 202 | 203 | let t1 = Z * T; 204 | let t2 = t1 * T; 205 | let X1 = t2.square(); 206 | let Z1 = t1 * U; 207 | let t3 = U.square(); 208 | let W1 = t2 - (X + Z).double() * t3; 209 | let t4 = Z1.square(); 210 | self.X = t4.mul_small_k1(4 * Self::B1); 211 | self.Z = W1.square(); 212 | self.U = (W1 + Z1).square() - t4 - self.Z; 213 | self.T = X1.double() - t4.mul_small(4) - self.Z; 214 | } 215 | 216 | /// Multiply this point by 2^n (i.e. n successive doublings). This is 217 | /// faster than calling the double() function n times. 218 | pub fn mdouble(self, n: u32) -> Self { 219 | let mut r = self; 220 | r.set_mdouble(n); 221 | r 222 | } 223 | 224 | fn set_mdouble(&mut self, n: u32) { 225 | // Handle corner cases (0 or 1 double). 226 | if n == 0 { 227 | return; 228 | } 229 | if n == 1 { 230 | self.set_double(); 231 | return; 232 | } 233 | 234 | // cost: n*(2M+5S) + 2M+1S 235 | let (X0, Z0, U0, T0) = (&self.X, &self.Z, &self.U, &self.T); 236 | let mut t1 = Z0 * T0; 237 | let mut t2 = t1 * T0; 238 | let X1 = t2.square(); 239 | let Z1 = t1 * U0; 240 | let mut t3 = U0.square(); 241 | let mut W1 = t2 - (X0 + Z0).double() * t3; 242 | let mut t4 = W1.square(); 243 | let mut t5 = Z1.square(); 244 | let mut X = t5.square().mul_small_k1(16 * Self::B1); 245 | let mut W = X1.double() - t5.mul_small(4) - t4; 246 | let mut Z = (W1 + Z1).square() - t4 - t5; 247 | 248 | for _ in 2..n { 249 | t1 = Z.square(); 250 | t2 = t1.square(); 251 | t3 = W.square(); 252 | t4 = t3.square(); 253 | t5 = (W + Z).square() - t1 - t3; 254 | Z = t5 * ((X + t1).double() - t3); 255 | X = (t2 * t4).mul_small_k1(16 * Self::B1); 256 | W = -t4 - t2.mul_small_kn01(4, 4 * Self::B1); 257 | } 258 | 259 | t1 = W.square(); 260 | t2 = Z.square(); 261 | t3 = (W + Z).square() - t1 - t2; 262 | W1 = t1 - (X + t2).double(); 263 | self.X = t3.square().mul_small_k1(Self::B1); 264 | self.Z = W1.square(); 265 | self.U = t3 * W1; 266 | self.T = t1.double() * (t1 - t2.double()) - self.Z; 267 | } 268 | 269 | /// Return 0xFFFFFFFFFFFFFFFF if this point is the neutral, 0 otherwise. 270 | pub fn isneutral(self) -> u64 { 271 | self.U.iszero() 272 | } 273 | 274 | /// Compare this point with another; returned value is 0xFFFFFFFFFFFFFFFF 275 | /// if the two points are equal, 0 otherwise. 276 | pub fn equals(self, rhs: Self) -> u64 { 277 | (self.U * rhs.T).equals(rhs.U * self.T) 278 | } 279 | 280 | // Convert points to affine coordinates. The source and destination 281 | // slices MUST have the same length. 282 | fn to_affine_array(src: &[Self], dst: &mut [PointAffine]) { 283 | // We use a trick due to Montgomery: to compute the inverse of 284 | // x and of y, a single inversion suffices, with: 285 | // 1/x = y*(1/(x*y)) 286 | // 1/y = x*(1/(x*y)) 287 | // This extends to the case of inverting n values, with a total 288 | // cost of 1 inversion and 3*(n-1) multiplications. 289 | let n = src.len(); 290 | 291 | // Handle edge cases (empty slices, and 1-value slices). 292 | if n == 0 { 293 | return; 294 | } 295 | if n == 1 { 296 | let P = src[0]; 297 | let m1 = (P.Z * P.T).invert(); 298 | dst[0] = PointAffine { 299 | x: P.X * P.T * m1, 300 | u: P.U * P.Z * m1, 301 | }; 302 | return; 303 | } 304 | 305 | // Compute product of all values to invert, and invert it. 306 | // We also use the x and u coordinates of the points in the 307 | // destination slice to keep track of the partial products. 308 | let mut m = src[0].Z * src[0].T; 309 | for i in 1..n { 310 | dst[i].x = m; 311 | m *= src[i].Z; 312 | dst[i].u = m; 313 | m *= src[i].T; 314 | } 315 | m = m.invert(); 316 | 317 | // Propagate back inverses. 318 | for i in (1..n).rev() { 319 | dst[i].u = src[i].U * dst[i].u * m; 320 | m *= src[i].T; 321 | dst[i].x = src[i].X * dst[i].x * m; 322 | m *= src[i].Z; 323 | } 324 | dst[0].u = src[0].U * src[0].Z * m; 325 | m *= src[0].T; 326 | dst[0].x = src[0].X * m; 327 | } 328 | 329 | // Optimal window size should be 4 or 5 bits, depending on target 330 | // architecture. On an Intel i5-8259U ("Coffee Lake" core), a 5-bit 331 | // window seems very slightly better. 332 | const WINDOW: usize = 5; 333 | const WIN_SIZE: usize = 1 << ((Self::WINDOW - 1) as i32); 334 | 335 | fn make_window_affine(self) -> [PointAffine; Self::WIN_SIZE] { 336 | let mut tmp = [Self::NEUTRAL; Self::WIN_SIZE]; 337 | tmp[0] = self; 338 | for i in 1..Self::WIN_SIZE { 339 | if (i & 1) == 0 { 340 | tmp[i] = self.add(tmp[i - 1]); 341 | } else { 342 | tmp[i] = tmp[i >> 1].double(); 343 | } 344 | } 345 | let mut win = [PointAffine::NEUTRAL; Self::WIN_SIZE]; 346 | Self::to_affine_array(&tmp, &mut win); 347 | win 348 | } 349 | 350 | // Multiply this point by a scalar. 351 | fn set_mul(&mut self, s: &Scalar) { 352 | // Make a window with affine points. 353 | let win = self.make_window_affine(); 354 | let mut ss = [0i32; (319 + Self::WINDOW) / Self::WINDOW]; 355 | s.recode_signed(&mut ss, Self::WINDOW as i32); 356 | let n = ss.len() - 1; 357 | *self = PointAffine::lookup(&win, ss[n]).to_point(); 358 | for i in (0..n).rev() { 359 | self.set_mdouble(Self::WINDOW as u32); 360 | *self += PointAffine::lookup(&win, ss[i]); 361 | } 362 | } 363 | 364 | /// Multiply the conventional generator by a scalar. 365 | /// This function is faster than using the multiplication operator 366 | /// on the generator point. 367 | pub fn mulgen(s: Scalar) -> Self { 368 | // Precomputed tables are for j*(2^(80*i))*G, for i = 0 to 3 369 | // and j = 1 to 16, i.e. 5-bit windows. 370 | let mut ss = [0i32; 64]; 371 | s.recode_signed(&mut ss, 5); 372 | let mut P = PointAffine::lookup(&G0, ss[7]).to_point(); 373 | P += PointAffine::lookup(&G40, ss[15]); 374 | P += PointAffine::lookup(&G80, ss[23]); 375 | P += PointAffine::lookup(&G120, ss[31]); 376 | P += PointAffine::lookup(&G160, ss[39]); 377 | P += PointAffine::lookup(&G200, ss[47]); 378 | P += PointAffine::lookup(&G240, ss[55]); 379 | P += PointAffine::lookup(&G280, ss[63]); 380 | for i in (0..7).rev() { 381 | P.set_mdouble(5); 382 | P += PointAffine::lookup(&G0, ss[i]); 383 | P += PointAffine::lookup(&G40, ss[i + 8]); 384 | P += PointAffine::lookup(&G80, ss[i + 16]); 385 | P += PointAffine::lookup(&G120, ss[i + 24]); 386 | P += PointAffine::lookup(&G160, ss[i + 32]); 387 | P += PointAffine::lookup(&G200, ss[i + 40]); 388 | P += PointAffine::lookup(&G240, ss[i + 48]); 389 | P += PointAffine::lookup(&G280, ss[i + 56]); 390 | } 391 | P 392 | } 393 | 394 | fn make_window_5(self) -> [Self; 16] { 395 | let mut win = [Self::NEUTRAL; 16]; 396 | win[0] = self; 397 | for i in 1..win.len() { 398 | if (i & 1) == 0 { 399 | win[i] = self.add(win[i - 1]); 400 | } else { 401 | win[i] = win[i >> 1].double(); 402 | } 403 | } 404 | win 405 | } 406 | 407 | fn lookup_vartime(win: &[Self], k: i32) -> Self { 408 | if k > 0 { 409 | return win[(k - 1) as usize]; 410 | } else if k == 0 { 411 | return Self::NEUTRAL; 412 | } else { 413 | return -win[(-k - 1) as usize]; 414 | } 415 | } 416 | 417 | /// Given scalars s and k, and point R, verify whether s*G + k*Q = R 418 | /// (with G being the curve conventional generator, and Q this instance). 419 | /// This is the main operation in Schnorr signature verification. 420 | /// WARNING: this function is not constant-time; use only on 421 | /// public data. 422 | pub fn verify_muladd_vartime(self, s: Scalar, k: Scalar, R:Self) -> bool { 423 | // We use a method by Antipa et al (SAC 2005), following the 424 | // description in: https://eprint.iacr.org/2020/454 425 | // We split k into two (signed) integers c0 and c1 such 426 | // that k = c0/c1 mod n; the integers c0 and c1 fit on 161 bits 427 | // each (including the signed bit). The verification is then: 428 | // (s*c1)*G + c0*Q - c1*R = 0 429 | // We split s*c1 into two 160-bit halves, and use the precomputed 430 | // tables for G; thus, all scalars fit on 160 bits (+sign). 431 | // 432 | // Since formulas for multiple doublings favour long runs of 433 | // doublings, we do not use a wNAF representation; instead, we 434 | // make regular 5-bit (signed) windows. 435 | // 436 | // We use fractional coordinates for the Q and R windows; it is 437 | // not worth it converting them to affine. 438 | 439 | // Compute c0 and c1. 440 | let (c0, c1) = k.lagrange(); 441 | 442 | // Compute t <- s*c1. 443 | let t = s * c1.to_scalar_vartime(); 444 | 445 | // Recode multipliers. 446 | let mut tt = [0i32; 64]; 447 | t.recode_signed(&mut tt, 5); 448 | let tt0 = &tt[..32]; 449 | let tt1 = &tt[32..]; 450 | let ss0 = c0.recode_signed_5(); 451 | let ss1 = c1.recode_signed_5(); 452 | 453 | // Make windows for this point (Q) and for -R. 454 | let winQ = self.make_window_5(); 455 | let winR = (-R).make_window_5(); 456 | 457 | let mut P = Self::lookup_vartime(&winQ, ss0[32]); 458 | if ss1[32] != 0 { 459 | P += Self::lookup_vartime(&winR, ss1[32]); 460 | } 461 | for i in (0..32).rev() { 462 | P.set_mdouble(5); 463 | if tt0[i] != 0 { 464 | P += PointAffine::lookup_vartime(&G0, tt0[i]); 465 | } 466 | if tt1[i] != 0 { 467 | P += PointAffine::lookup_vartime(&G160, tt1[i]); 468 | } 469 | if ss0[i] != 0 { 470 | P += Self::lookup_vartime(&winQ, ss0[i]); 471 | } 472 | if ss1[i] != 0 { 473 | P += Self::lookup_vartime(&winR, ss1[i]); 474 | } 475 | } 476 | P.isneutral() != 0 477 | } 478 | } 479 | 480 | // A curve point in affine (x,u) coordinates. This is used internally 481 | // to make "windows" that speed up point multiplications. 482 | #[derive(Clone, Copy, Debug)] 483 | pub(crate) struct PointAffine { 484 | pub(crate) x: GFp5, 485 | pub(crate) u: GFp5, 486 | } 487 | 488 | impl PointAffine { 489 | 490 | const NEUTRAL: Self = Self { x: GFp5::ZERO, u: GFp5::ZERO }; 491 | 492 | fn to_point(self) -> Point { 493 | Point { X: self.x, Z: GFp5::ONE, U: self.u, T: GFp5::ONE } 494 | } 495 | 496 | fn set_neg(&mut self) { 497 | self.u.set_neg(); 498 | } 499 | 500 | // Lookup a point in a window. The win[] slice must contain values 501 | // i*P for i = 1 to n (win[0] contains P, win[1] contains 2*P, and 502 | // so on). Index value k is an integer in the -n to n range; returned 503 | // point is k*P. 504 | fn set_lookup(&mut self, win: &[Self], k: i32) { 505 | // sign = 0xFFFFFFFF if k < 0, 0x00000000 otherwise 506 | let sign = (k >> 31) as u32; 507 | // ka = abs(k) 508 | let ka = ((k as u32) ^ sign).wrapping_sub(sign); 509 | // km1 = ka - 1 510 | let km1 = ka.wrapping_sub(1); 511 | 512 | let mut x = GFp5::ZERO; 513 | let mut u = GFp5::ZERO; 514 | for i in 0..win.len() { 515 | let m = km1.wrapping_sub(i as u32); 516 | let c = (((m | m.wrapping_neg()) >> 31) as u64).wrapping_sub(1); 517 | x.set_partial_lookup(win[i].x, c); 518 | u.set_partial_lookup(win[i].u, c); 519 | } 520 | 521 | // If k < 0, then we must negate the point. 522 | let c = (sign as u64) | ((sign as u64) << 32); 523 | self.x = x; 524 | self.u = GFp5::select(c, u, -u); 525 | } 526 | 527 | fn lookup(win: &[Self], k: i32) -> Self { 528 | let mut r = Self::NEUTRAL; 529 | r.set_lookup(win, k); 530 | r 531 | } 532 | 533 | // Same as lookup(), except this implementation is variable-time. 534 | fn lookup_vartime(win: &[Self], k: i32) -> Self { 535 | if k > 0 { 536 | return win[(k - 1) as usize]; 537 | } else if k == 0 { 538 | return Self::NEUTRAL; 539 | } else { 540 | return -win[(-k - 1) as usize]; 541 | } 542 | } 543 | } 544 | 545 | // We implement all the needed traits to allow use of the arithmetic 546 | // operators on points. We support all combinations of operands 547 | // either as Point structures, or pointers to Point structures. Some 548 | // operations with PointAffine structures are also implemented. 549 | 550 | impl Add for Point { 551 | type Output = Point; 552 | 553 | #[inline(always)] 554 | fn add(self, other: Point) -> Point { 555 | let mut r = self; 556 | r.set_add(&other); 557 | r 558 | } 559 | } 560 | 561 | impl Add<&Point> for Point { 562 | type Output = Point; 563 | 564 | #[inline(always)] 565 | fn add(self, other: &Point) -> Point { 566 | let mut r = self; 567 | r.set_add(other); 568 | r 569 | } 570 | } 571 | 572 | impl Add for &Point { 573 | type Output = Point; 574 | 575 | #[inline(always)] 576 | fn add(self, other: Point) -> Point { 577 | let mut r = *self; 578 | r.set_add(&other); 579 | r 580 | } 581 | } 582 | 583 | impl Add<&Point> for &Point { 584 | type Output = Point; 585 | 586 | #[inline(always)] 587 | fn add(self, other: &Point) -> Point { 588 | let mut r = *self; 589 | r.set_add(other); 590 | r 591 | } 592 | } 593 | 594 | impl Add for Point { 595 | type Output = Point; 596 | 597 | #[inline(always)] 598 | fn add(self, other: PointAffine) -> Point { 599 | let mut r = self; 600 | r.set_add_affine(&other); 601 | r 602 | } 603 | } 604 | 605 | impl Add<&PointAffine> for Point { 606 | type Output = Point; 607 | 608 | #[inline(always)] 609 | fn add(self, other: &PointAffine) -> Point { 610 | let mut r = self; 611 | r.set_add_affine(other); 612 | r 613 | } 614 | } 615 | 616 | impl Add for &Point { 617 | type Output = Point; 618 | 619 | #[inline(always)] 620 | fn add(self, other: PointAffine) -> Point { 621 | let mut r = *self; 622 | r.set_add_affine(&other); 623 | r 624 | } 625 | } 626 | 627 | impl Add<&PointAffine> for &Point { 628 | type Output = Point; 629 | 630 | #[inline(always)] 631 | fn add(self, other: &PointAffine) -> Point { 632 | let mut r = *self; 633 | r.set_add_affine(other); 634 | r 635 | } 636 | } 637 | 638 | impl Add for PointAffine { 639 | type Output = Point; 640 | 641 | #[inline(always)] 642 | fn add(self, other: Point) -> Point { 643 | let mut r = other; 644 | r.set_add_affine(&self); 645 | r 646 | } 647 | } 648 | 649 | impl Add<&Point> for PointAffine { 650 | type Output = Point; 651 | 652 | #[inline(always)] 653 | fn add(self, other: &Point) -> Point { 654 | let mut r = *other; 655 | r.set_add_affine(&self); 656 | r 657 | } 658 | } 659 | 660 | impl Add for &PointAffine { 661 | type Output = Point; 662 | 663 | #[inline(always)] 664 | fn add(self, other: Point) -> Point { 665 | let mut r = other; 666 | r.set_add_affine(self); 667 | r 668 | } 669 | } 670 | 671 | impl Add<&Point> for &PointAffine { 672 | type Output = Point; 673 | 674 | #[inline(always)] 675 | fn add(self, other: &Point) -> Point { 676 | let mut r = *other; 677 | r.set_add_affine(self); 678 | r 679 | } 680 | } 681 | 682 | impl AddAssign for Point { 683 | #[inline(always)] 684 | fn add_assign(&mut self, other: Point) { 685 | self.set_add(&other); 686 | } 687 | } 688 | 689 | impl AddAssign<&Point> for Point { 690 | #[inline(always)] 691 | fn add_assign(&mut self, other: &Point) { 692 | self.set_add(other); 693 | } 694 | } 695 | 696 | impl AddAssign for Point { 697 | #[inline(always)] 698 | fn add_assign(&mut self, other: PointAffine) { 699 | self.set_add_affine(&other); 700 | } 701 | } 702 | 703 | impl AddAssign<&PointAffine> for Point { 704 | #[inline(always)] 705 | fn add_assign(&mut self, other: &PointAffine) { 706 | self.set_add_affine(other); 707 | } 708 | } 709 | 710 | impl Sub for Point { 711 | type Output = Point; 712 | 713 | #[inline(always)] 714 | fn sub(self, other: Point) -> Point { 715 | let mut r = self; 716 | r.set_sub(&other); 717 | r 718 | } 719 | } 720 | 721 | impl Sub<&Point> for Point { 722 | type Output = Point; 723 | 724 | #[inline(always)] 725 | fn sub(self, other: &Point) -> Point { 726 | let mut r = self; 727 | r.set_sub(other); 728 | r 729 | } 730 | } 731 | 732 | impl Sub for &Point { 733 | type Output = Point; 734 | 735 | #[inline(always)] 736 | fn sub(self, other: Point) -> Point { 737 | let mut r = *self; 738 | r.set_sub(&other); 739 | r 740 | } 741 | } 742 | 743 | impl Sub<&Point> for &Point { 744 | type Output = Point; 745 | 746 | #[inline(always)] 747 | fn sub(self, other: &Point) -> Point { 748 | let mut r = *self; 749 | r.set_sub(other); 750 | r 751 | } 752 | } 753 | 754 | impl Sub for Point { 755 | type Output = Point; 756 | 757 | #[inline(always)] 758 | fn sub(self, other: PointAffine) -> Point { 759 | let mut r = self; 760 | r.set_sub_affine(&other); 761 | r 762 | } 763 | } 764 | 765 | impl Sub<&PointAffine> for Point { 766 | type Output = Point; 767 | 768 | #[inline(always)] 769 | fn sub(self, other: &PointAffine) -> Point { 770 | let mut r = self; 771 | r.set_sub_affine(other); 772 | r 773 | } 774 | } 775 | 776 | impl Sub for &Point { 777 | type Output = Point; 778 | 779 | #[inline(always)] 780 | fn sub(self, other: PointAffine) -> Point { 781 | let mut r = *self; 782 | r.set_sub_affine(&other); 783 | r 784 | } 785 | } 786 | 787 | impl Sub<&PointAffine> for &Point { 788 | type Output = Point; 789 | 790 | #[inline(always)] 791 | fn sub(self, other: &PointAffine) -> Point { 792 | let mut r = *self; 793 | r.set_sub_affine(other); 794 | r 795 | } 796 | } 797 | 798 | impl Sub for PointAffine { 799 | type Output = Point; 800 | 801 | #[inline(always)] 802 | fn sub(self, other: Point) -> Point { 803 | let mut r = other; 804 | r.set_sub_affine(&self); 805 | r 806 | } 807 | } 808 | 809 | impl Sub<&Point> for PointAffine { 810 | type Output = Point; 811 | 812 | #[inline(always)] 813 | fn sub(self, other: &Point) -> Point { 814 | let mut r = *other; 815 | r.set_sub_affine(&self); 816 | r 817 | } 818 | } 819 | 820 | impl Sub for &PointAffine { 821 | type Output = Point; 822 | 823 | #[inline(always)] 824 | fn sub(self, other: Point) -> Point { 825 | let mut r = other; 826 | r.set_sub_affine(self); 827 | r 828 | } 829 | } 830 | 831 | impl Sub<&Point> for &PointAffine { 832 | type Output = Point; 833 | 834 | #[inline(always)] 835 | fn sub(self, other: &Point) -> Point { 836 | let mut r = *other; 837 | r.set_sub_affine(self); 838 | r 839 | } 840 | } 841 | 842 | impl SubAssign for Point { 843 | #[inline(always)] 844 | fn sub_assign(&mut self, other: Point) { 845 | self.set_sub(&other); 846 | } 847 | } 848 | 849 | impl SubAssign<&Point> for Point { 850 | #[inline(always)] 851 | fn sub_assign(&mut self, other: &Point) { 852 | self.set_sub(other); 853 | } 854 | } 855 | 856 | impl SubAssign for Point { 857 | #[inline(always)] 858 | fn sub_assign(&mut self, other: PointAffine) { 859 | self.set_sub_affine(&other); 860 | } 861 | } 862 | 863 | impl SubAssign<&PointAffine> for Point { 864 | #[inline(always)] 865 | fn sub_assign(&mut self, other: &PointAffine) { 866 | self.set_sub_affine(other); 867 | } 868 | } 869 | 870 | impl Neg for Point { 871 | type Output = Point; 872 | 873 | #[inline(always)] 874 | fn neg(self) -> Point { 875 | let mut r = self; 876 | r.set_neg(); 877 | r 878 | } 879 | } 880 | 881 | impl Neg for &Point { 882 | type Output = Point; 883 | 884 | #[inline(always)] 885 | fn neg(self) -> Point { 886 | let mut r = *self; 887 | r.set_neg(); 888 | r 889 | } 890 | } 891 | 892 | impl Neg for PointAffine { 893 | type Output = PointAffine; 894 | 895 | #[inline(always)] 896 | fn neg(self) -> PointAffine { 897 | let mut r = self; 898 | r.set_neg(); 899 | r 900 | } 901 | } 902 | 903 | impl Neg for &PointAffine { 904 | type Output = PointAffine; 905 | 906 | #[inline(always)] 907 | fn neg(self) -> PointAffine { 908 | let mut r = *self; 909 | r.set_neg(); 910 | r 911 | } 912 | } 913 | 914 | impl Mul for Point { 915 | type Output = Point; 916 | 917 | #[inline(always)] 918 | fn mul(self, other: Scalar) -> Point { 919 | let mut r = self; 920 | r.set_mul(&other); 921 | r 922 | } 923 | } 924 | 925 | impl Mul<&Scalar> for Point { 926 | type Output = Point; 927 | 928 | #[inline(always)] 929 | fn mul(self, other: &Scalar) -> Point { 930 | let mut r = self; 931 | r.set_mul(other); 932 | r 933 | } 934 | } 935 | 936 | impl Mul for &Point { 937 | type Output = Point; 938 | 939 | #[inline(always)] 940 | fn mul(self, other: Scalar) -> Point { 941 | let mut r = *self; 942 | r.set_mul(&other); 943 | r 944 | } 945 | } 946 | 947 | impl Mul<&Scalar> for &Point { 948 | type Output = Point; 949 | 950 | #[inline(always)] 951 | fn mul(self, other: &Scalar) -> Point { 952 | let mut r = *self; 953 | r.set_mul(other); 954 | r 955 | } 956 | } 957 | 958 | impl MulAssign for Point { 959 | #[inline(always)] 960 | fn mul_assign(&mut self, other: Scalar) { 961 | self.set_mul(&other); 962 | } 963 | } 964 | 965 | impl MulAssign<&Scalar> for Point { 966 | #[inline(always)] 967 | fn mul_assign(&mut self, other: &Scalar) { 968 | self.set_mul(other); 969 | } 970 | } 971 | 972 | impl Mul for Scalar { 973 | type Output = Point; 974 | 975 | #[inline(always)] 976 | fn mul(self, other: Point) -> Point { 977 | let mut r = other; 978 | r.set_mul(&self); 979 | r 980 | } 981 | } 982 | 983 | impl Mul<&Point> for Scalar { 984 | type Output = Point; 985 | 986 | #[inline(always)] 987 | fn mul(self, other: &Point) -> Point { 988 | let mut r = *other; 989 | r.set_mul(&self); 990 | r 991 | } 992 | } 993 | 994 | impl Mul for &Scalar { 995 | type Output = Point; 996 | 997 | #[inline(always)] 998 | fn mul(self, other: Point) -> Point { 999 | let mut r = other; 1000 | r.set_mul(self); 1001 | r 1002 | } 1003 | } 1004 | 1005 | impl Mul<&Point> for &Scalar { 1006 | type Output = Point; 1007 | 1008 | #[inline(always)] 1009 | fn mul(self, other: &Point) -> Point { 1010 | let mut r = *other; 1011 | r.set_mul(self); 1012 | r 1013 | } 1014 | } 1015 | 1016 | // ======================================================================== 1017 | // Unit tests. 1018 | 1019 | #[cfg(test)] 1020 | mod tests { 1021 | use super::{Point, PointAffine}; 1022 | use super::super::field::GFp5; 1023 | use super::super::scalar::Scalar; 1024 | use super::super::PRNG; 1025 | 1026 | #[test] 1027 | fn ecgfp5_ops() { 1028 | // Test vectors generated with Sage. 1029 | // P0 is neutral of G. 1030 | // P1 is a random point in G (encoded as w1) 1031 | // P2 = e*P1 in G (encoded as w2) 1032 | // P3 = P1 + P2 (in G) (encoded as w3) 1033 | // P4 = 2*P1 (in G) (encoded as w4) 1034 | // P5 = 2*P2 (in G) (encoded as w5) 1035 | // P6 = 2*P1 + P2 (in G) (encoded as w6) 1036 | // P7 = P1 + 2*P2 (in G) (encoded as w7) 1037 | 1038 | let w0 = GFp5::from_u64_reduce(0, 0, 0, 0, 0); 1039 | let w1 = GFp5::from_u64_reduce(12539254003028696409, 15524144070600887654, 15092036948424041984, 11398871370327264211, 10958391180505708567); 1040 | let w2 = GFp5::from_u64_reduce(11001943240060308920, 17075173755187928434, 3940989555384655766, 15017795574860011099, 5548543797011402287); 1041 | let w3 = GFp5::from_u64_reduce(246872606398642312, 4900963247917836450, 7327006728177203977, 13945036888436667069, 3062018119121328861); 1042 | let w4 = GFp5::from_u64_reduce(8058035104653144162, 16041715455419993830, 7448530016070824199, 11253639182222911208, 6228757819849640866); 1043 | let w5 = GFp5::from_u64_reduce(10523134687509281194, 11148711503117769087, 9056499921957594891, 13016664454465495026, 16494247923890248266); 1044 | let w6 = GFp5::from_u64_reduce(12173306542237620, 6587231965341539782, 17027985748515888117, 17194831817613584995, 10056734072351459010); 1045 | let w7 = GFp5::from_u64_reduce(9420857400785992333, 4695934009314206363, 14471922162341187302, 13395190104221781928, 16359223219913018041); 1046 | 1047 | // Values that should not decode successfully. 1048 | let bww: [GFp5; 6] = [ 1049 | GFp5::from_u64_reduce(13557832913345268708, 15669280705791538619, 8534654657267986396, 12533218303838131749, 5058070698878426028), 1050 | GFp5::from_u64_reduce(135036726621282077, 17283229938160287622, 13113167081889323961, 1653240450380825271, 520025869628727862), 1051 | GFp5::from_u64_reduce(6727960962624180771, 17240764188796091916, 3954717247028503753, 1002781561619501488, 4295357288570643789), 1052 | GFp5::from_u64_reduce(4578929270179684956, 3866930513245945042, 7662265318638150701, 9503686272550423634, 12241691520798116285), 1053 | GFp5::from_u64_reduce(16890297404904119082, 6169724643582733633, 9725973298012340311, 5977049210035183790, 11379332130141664883), 1054 | GFp5::from_u64_reduce(13777379982711219130, 14715168412651470168, 17942199593791635585, 6188824164976547520, 15461469634034461986), 1055 | ]; 1056 | 1057 | assert!(Point::validate(w0) == 0xFFFFFFFFFFFFFFFF); 1058 | assert!(Point::validate(w1) == 0xFFFFFFFFFFFFFFFF); 1059 | assert!(Point::validate(w2) == 0xFFFFFFFFFFFFFFFF); 1060 | assert!(Point::validate(w3) == 0xFFFFFFFFFFFFFFFF); 1061 | assert!(Point::validate(w4) == 0xFFFFFFFFFFFFFFFF); 1062 | assert!(Point::validate(w5) == 0xFFFFFFFFFFFFFFFF); 1063 | assert!(Point::validate(w6) == 0xFFFFFFFFFFFFFFFF); 1064 | assert!(Point::validate(w7) == 0xFFFFFFFFFFFFFFFF); 1065 | 1066 | let (P0, c0) = Point::decode(w0); 1067 | let (P1, c1) = Point::decode(w1); 1068 | let (P2, c2) = Point::decode(w2); 1069 | let (P3, c3) = Point::decode(w3); 1070 | let (P4, c4) = Point::decode(w4); 1071 | let (P5, c5) = Point::decode(w5); 1072 | let (P6, c6) = Point::decode(w6); 1073 | let (P7, c7) = Point::decode(w7); 1074 | 1075 | assert!(c0 == 0xFFFFFFFFFFFFFFFF); 1076 | assert!(c1 == 0xFFFFFFFFFFFFFFFF); 1077 | assert!(c2 == 0xFFFFFFFFFFFFFFFF); 1078 | assert!(c3 == 0xFFFFFFFFFFFFFFFF); 1079 | assert!(c4 == 0xFFFFFFFFFFFFFFFF); 1080 | assert!(c5 == 0xFFFFFFFFFFFFFFFF); 1081 | assert!(c6 == 0xFFFFFFFFFFFFFFFF); 1082 | assert!(c7 == 0xFFFFFFFFFFFFFFFF); 1083 | 1084 | assert!(P0.isneutral() == 0xFFFFFFFFFFFFFFFF); 1085 | assert!(P1.isneutral() == 0); 1086 | assert!(P2.isneutral() == 0); 1087 | assert!(P3.isneutral() == 0); 1088 | assert!(P4.isneutral() == 0); 1089 | assert!(P5.isneutral() == 0); 1090 | assert!(P6.isneutral() == 0); 1091 | assert!(P7.isneutral() == 0); 1092 | 1093 | assert!(P0.equals(P0) == 0xFFFFFFFFFFFFFFFF); 1094 | assert!(P0.equals(P1) == 0); 1095 | assert!(P1.equals(P0) == 0); 1096 | assert!(P1.equals(P1) == 0xFFFFFFFFFFFFFFFF); 1097 | assert!(P1.equals(P2) == 0); 1098 | 1099 | assert!(P0.encode().equals(w0) == 0xFFFFFFFFFFFFFFFF); 1100 | assert!(P1.encode().equals(w1) == 0xFFFFFFFFFFFFFFFF); 1101 | assert!(P2.encode().equals(w2) == 0xFFFFFFFFFFFFFFFF); 1102 | assert!(P3.encode().equals(w3) == 0xFFFFFFFFFFFFFFFF); 1103 | assert!(P4.encode().equals(w4) == 0xFFFFFFFFFFFFFFFF); 1104 | assert!(P5.encode().equals(w5) == 0xFFFFFFFFFFFFFFFF); 1105 | assert!(P6.encode().equals(w6) == 0xFFFFFFFFFFFFFFFF); 1106 | assert!(P7.encode().equals(w7) == 0xFFFFFFFFFFFFFFFF); 1107 | 1108 | for w in bww.iter() { 1109 | assert!(Point::validate(*w) == 0); 1110 | let (P, c) = Point::decode(*w); 1111 | assert!(P.isneutral() == 0xFFFFFFFFFFFFFFFF); 1112 | assert!(c == 0); 1113 | } 1114 | 1115 | assert!((P1 + P2).encode().equals(w3) == 0xFFFFFFFFFFFFFFFF); 1116 | assert!((P1 + P1).encode().equals(w4) == 0xFFFFFFFFFFFFFFFF); 1117 | assert!(P2.double().encode().equals(w5) == 0xFFFFFFFFFFFFFFFF); 1118 | assert!((P1.double() + P2).encode().equals(w6) == 0xFFFFFFFFFFFFFFFF); 1119 | assert!((P1 + P2 + P2).encode().equals(w7) == 0xFFFFFFFFFFFFFFFF); 1120 | 1121 | assert!((P0.double()).encode().iszero() == 0xFFFFFFFFFFFFFFFF); 1122 | assert!((P0 + P0).encode().iszero() == 0xFFFFFFFFFFFFFFFF); 1123 | assert!((P0 + P1).encode().equals(w1) == 0xFFFFFFFFFFFFFFFF); 1124 | assert!((P1 + P0).encode().equals(w1) == 0xFFFFFFFFFFFFFFFF); 1125 | 1126 | for i in 0..10 { 1127 | let Q1 = P1.mdouble(i); 1128 | let mut Q2 = P1; 1129 | for _ in 0..i { 1130 | Q2 = Q2.double(); 1131 | } 1132 | assert!(Q1.equals(Q2) == 0xFFFFFFFFFFFFFFFF); 1133 | } 1134 | 1135 | let P2a = PointAffine { x: P2.X / P2.Z, u: P2.U / P2.T }; 1136 | assert!((P1 + P2a).equals(P1 + P2) == 0xFFFFFFFFFFFFFFFF); 1137 | } 1138 | 1139 | #[test] 1140 | fn ecgfp5_affine_window() { 1141 | let w = GFp5::from_u64_reduce(12539254003028696409, 15524144070600887654, 15092036948424041984, 11398871370327264211, 10958391180505708567); 1142 | let (P, c) = Point::decode(w); 1143 | assert!(c == 0xFFFFFFFFFFFFFFFF); 1144 | 1145 | // Create an array of 8 points. 1146 | let mut tab1 = [Point::NEUTRAL; 8]; 1147 | tab1[0] = P.double(); 1148 | for i in 1..tab1.len() { 1149 | tab1[i] = tab1[0] + tab1[i - 1]; 1150 | } 1151 | 1152 | // Test conversion to affine coordinates. 1153 | for n in 1..(tab1.len() + 1) { 1154 | let mut tab2 = [PointAffine::NEUTRAL; 8]; 1155 | Point::to_affine_array(&tab1[0..n], &mut tab2[0..n]); 1156 | for i in 0..n { 1157 | assert!((tab1[i].Z * tab2[i].x).equals(tab1[i].X) != 0); 1158 | assert!((tab1[i].T * tab2[i].u).equals(tab1[i].U) != 0); 1159 | } 1160 | } 1161 | 1162 | // Test lookup. 1163 | let mut win = [PointAffine::NEUTRAL; 8]; 1164 | Point::to_affine_array(&tab1, &mut win); 1165 | let Pa = PointAffine::lookup(&win, 0); 1166 | assert!(Pa.x.iszero() != 0); 1167 | assert!(Pa.u.iszero() != 0); 1168 | for i in 1..9 { 1169 | let Pb = PointAffine::lookup(&win, i as i32); 1170 | assert!((tab1[i - 1].Z * Pb.x).equals(tab1[i - 1].X) != 0); 1171 | assert!((tab1[i - 1].T * Pb.u).equals(tab1[i - 1].U) != 0); 1172 | let Pc = PointAffine::lookup(&win, -(i as i32)); 1173 | assert!((tab1[i - 1].Z * Pc.x).equals(tab1[i - 1].X) != 0); 1174 | assert!((tab1[i - 1].T * Pc.u).equals(-tab1[i - 1].U) != 0); 1175 | } 1176 | } 1177 | 1178 | #[test] 1179 | fn ecgfp5_mul() { 1180 | // w1 = encoding of a random point P1 1181 | // ebuf = encoding of a random scalar e 1182 | // w2 = encoding of P2 = e*P1 1183 | let w1 = GFp5::from_u64_reduce(7534507442095725921, 16658460051907528927, 12417574136563175256, 2750788641759288856, 620002843272906439); 1184 | let ebuf: [u8; 40] = [ 1185 | 0x1B, 0x18, 0x51, 0xC8, 0x1D, 0x22, 0xD4, 0x0D, 1186 | 0x6D, 0x36, 0xEC, 0xCE, 0x54, 0x27, 0x41, 0x66, 1187 | 0x08, 0x14, 0x2F, 0x8F, 0xFF, 0x64, 0xB4, 0x76, 1188 | 0x28, 0xCD, 0x3F, 0xF8, 0xAA, 0x25, 0x16, 0xD4, 1189 | 0xBA, 0xD0, 0xCC, 0x02, 0x1A, 0x44, 0x7C, 0x03, 1190 | ]; 1191 | let w2 = GFp5::from_u64_reduce(9486104512504676657, 14312981644741144668, 5159846406177847664, 15978863787033795628, 3249948839313771192); 1192 | 1193 | let (P1, c1) = Point::decode(w1); 1194 | let (P2, c2) = Point::decode(w2); 1195 | assert!(c1 == 0xFFFFFFFFFFFFFFFF); 1196 | assert!(c2 == 0xFFFFFFFFFFFFFFFF); 1197 | let (e, ce) = Scalar::decode(&ebuf); 1198 | assert!(ce == 0xFFFFFFFFFFFFFFFF); 1199 | let Q1 = P1 * e; 1200 | assert!(Q1.equals(P2) == 0xFFFFFFFFFFFFFFFF); 1201 | assert!(Q1.encode().equals(w2) == 0xFFFFFFFFFFFFFFFF); 1202 | let Q2 = e * P1; 1203 | assert!(Q2.equals(P2) == 0xFFFFFFFFFFFFFFFF); 1204 | assert!(Q2.encode().equals(w2) == 0xFFFFFFFFFFFFFFFF); 1205 | } 1206 | 1207 | #[test] 1208 | fn ecgfp5_mulgen() { 1209 | let mut prng = PRNG(0); 1210 | for _ in 0..20 { 1211 | let mut ebuf = [0u8; 48]; 1212 | prng.next(&mut ebuf); 1213 | let e = Scalar::decode_reduce(&ebuf); 1214 | let P1 = Point::GENERATOR * e; 1215 | let P2 = Point::mulgen(e); 1216 | assert!(P1.equals(P2) == 0xFFFFFFFFFFFFFFFF); 1217 | } 1218 | } 1219 | 1220 | #[test] 1221 | fn ecgfp5_verify_muladd() { 1222 | let mut prng = PRNG(0); 1223 | for _ in 0..100 { 1224 | let mut ebuf = [0u8; 48]; 1225 | let mut sbuf = [0u8; 48]; 1226 | let mut kbuf = [0u8; 48]; 1227 | prng.next(&mut ebuf); 1228 | prng.next(&mut sbuf); 1229 | prng.next(&mut kbuf); 1230 | let e = Scalar::decode_reduce(&ebuf); 1231 | let s = Scalar::decode_reduce(&sbuf); 1232 | let k = Scalar::decode_reduce(&kbuf); 1233 | let Q = Point::mulgen(e); 1234 | let R = Point::mulgen(s) + k*Q; 1235 | assert!(Q.verify_muladd_vartime(s, k, R)); 1236 | let R2 = R + Point::GENERATOR; 1237 | assert!(!Q.verify_muladd_vartime(s, k, R2)); 1238 | } 1239 | } 1240 | } 1241 | -------------------------------------------------------------------------------- /rust/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | pub mod field; 4 | pub mod curve; 5 | pub mod scalar; 6 | pub mod multab; 7 | 8 | // A custom PRNG; not cryptographically secure, but good enough 9 | // for tests. 10 | #[cfg(test)] 11 | struct PRNG(u128); 12 | 13 | #[cfg(test)] 14 | impl PRNG { 15 | // A: a randomly selected prime integer. 16 | // B: a randomly selected odd integer. 17 | const A: u128 = 87981536952642681582438141175044346919; 18 | const B: u128 = 331203846847999889118488772711684568729; 19 | 20 | // Get the next pseudo-random 64-bit integer. 21 | fn next_u64(&mut self) -> u64 { 22 | self.0 = PRNG::A.wrapping_mul(self.0).wrapping_add(PRNG::B); 23 | (self.0 >> 64) as u64 24 | } 25 | 26 | // Fill buf[] with pseudo-random bytes. 27 | fn next(&mut self, buf: &mut [u8]) { 28 | let mut acc: u64 = 0; 29 | for i in 0..buf.len() { 30 | if (i & 7) == 0 { 31 | acc = self.next_u64(); 32 | } 33 | buf[i] = acc as u8; 34 | acc >>= 8; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /rust/src/multab.rs: -------------------------------------------------------------------------------- 1 | use super::field::GFp5; 2 | use super::curve::PointAffine; 3 | 4 | // ======================================================================== 5 | // For k = 40*j (j = 0 to 7), constant Gk[] is an array of 16 points in 6 | // affine coordinates, with Gk[i] = (i+1)*(2^k)*G for the conventional 7 | // generator point G. 8 | 9 | pub(crate) const G0: [PointAffine; 16] = [ 10 | PointAffine { /* 1 */ 11 | x: GFp5::from_u64_reduce(0xB2CA178ECF4453A1, 0x3C757788836D3EA4, 12 | 0x48D7F28A26DAFD0B, 0x1E0F15C7FD44C28E, 0x21FA7FFCC8252211), 13 | u: GFp5::from_u64_reduce(0xBFFFFFFF40000001, 0x0000000000000000, 14 | 0x0000000000000000, 0x0000000000000000, 0x0000000000000000), 15 | }, 16 | PointAffine { /* 2 */ 17 | x: GFp5::from_u64_reduce(0xE53A14C4FA645562, 0x60A03B3FCD006755, 18 | 0xA8957CB5D9603510, 0xE6E3A7D2AE140D1F, 0x2067DD994D85BE92), 19 | u: GFp5::from_u64_reduce(0x755F62AABB0862CC, 0x01EC60924033842A, 20 | 0x2C3C144F634BCC44, 0xFC49506D49454DA8, 0x832FD0BD80667CB8), 21 | }, 22 | PointAffine { /* 3 */ 23 | x: GFp5::from_u64_reduce(0x3F1721A8B4BFE250, 0x135784DB6D866068, 24 | 0xD30DD0109FFB1561, 0x6DADE466D752E9A2, 0xACF8C6DE680A22CE), 25 | u: GFp5::from_u64_reduce(0xE33B8641C16CB542, 0x51DF13BC6EFD7D24, 26 | 0xF47A6CBCB19B08F4, 0xF4D93DDB98BE42CD, 0x2751D54472A53C2D), 27 | }, 28 | PointAffine { /* 4 */ 29 | x: GFp5::from_u64_reduce(0x3C414CE1F8AAE143, 0x7E727D66648035F2, 30 | 0x6C3B9846D5F6B1EE, 0xAF2022C48F8C580D, 0xB513380B614CD936), 31 | u: GFp5::from_u64_reduce(0x31BFB83AE0B6F4DD, 0x17D63BD29B7ED7A0, 32 | 0xE5A9E36118531AC2, 0xF38EBF5B61B910B7, 0xFF010361346C74E0), 33 | }, 34 | PointAffine { /* 5 */ 35 | x: GFp5::from_u64_reduce(0xFB7AFB9F0749C52F, 0x2F1B08170040D1C4, 36 | 0x24C0E6F01ACB4244, 0xB940E85295A4BDD9, 0x2F934B66CC631967), 37 | u: GFp5::from_u64_reduce(0x80916CD6733E31AD, 0x670F5AE14E0CBD35, 38 | 0x46E278D15F6E2188, 0x8252B7241D444762, 0x87D69BD8B06A37BE), 39 | }, 40 | PointAffine { /* 6 */ 41 | x: GFp5::from_u64_reduce(0x99C65351B868D074, 0x2B549ABB45AE3B05, 42 | 0xE6D26BBB7D6983C7, 0xCE8D90C97E847FFD, 0x90490A2371237297), 43 | u: GFp5::from_u64_reduce(0xC5D015B9654827DF, 0xDA4537A57CA4CE22, 44 | 0x36C70DD003F9210E, 0x41CB4479D87846E6, 0xF142FD633CB37552), 45 | }, 46 | PointAffine { /* 7 */ 47 | x: GFp5::from_u64_reduce(0x4219C8AAA886A320, 0xF821E49F695A9B94, 48 | 0xBC7223437FA3F3BA, 0x77D483207195D99F, 0xBE37D76AD12515C5), 49 | u: GFp5::from_u64_reduce(0x2E8E911431F24635, 0xC019A6C715B9A781, 50 | 0x417911CA4F5C8C33, 0x6EBA314D3B0B4D84, 0x8C6DC80B62684832), 51 | }, 52 | PointAffine { /* 8 */ 53 | x: GFp5::from_u64_reduce(0x37E2A1C7E50102C2, 0xBCAEA8E86E48319C, 54 | 0x9F9654BF22E51108, 0x8B0C420979D809A1, 0xA659D6B7DA77C522), 55 | u: GFp5::from_u64_reduce(0xC9AEF44D3444F179, 0x84FCE8B4923CF2A1, 56 | 0x2AC63DF0EAD62DFA, 0x27AF8ADF6D3054C2, 0x33FCA2A42C8C4310), 57 | }, 58 | PointAffine { /* 9 */ 59 | x: GFp5::from_u64_reduce(0xDD27B15F06D34600, 0x784D56F0C1D26C1F, 60 | 0x06D180D9AD8C6590, 0x5B62326A633F7A02, 0xABE5478A46E4902C), 61 | u: GFp5::from_u64_reduce(0xB76F2D032EE42A38, 0x00B792BF8263FB30, 62 | 0xD5BA9B5F40896B2D, 0x5DB4A555E3DFA714, 0x27A01C4CFCAF6622), 63 | }, 64 | PointAffine { /* 10 */ 65 | x: GFp5::from_u64_reduce(0x7597F149CD01FFB1, 0x21F55DA1987F38CE, 66 | 0xF94895E941D11482, 0x6071F8478A6FB91A, 0x8147478709A8AD5F), 67 | u: GFp5::from_u64_reduce(0x47C5FA108C16AF99, 0x29FC9D7035795EA7, 68 | 0x064A742C800C7F1E, 0xC42AB1D12867714F, 0x237A1D635AD89D2C), 69 | }, 70 | PointAffine { /* 11 */ 71 | x: GFp5::from_u64_reduce(0x36F591486C28B83C, 0x334A430D55CF5093, 72 | 0x2872A90BFB945CBF, 0x805E6320AE5B5820, 0xF39AA3FF0F843A88), 73 | u: GFp5::from_u64_reduce(0x29D7F90C194EE89D, 0x94E8BD911C76E114, 74 | 0x2DFE8D94E8698B00, 0x1B89FD3E133A360C, 0xBF423DD058BA8584), 75 | }, 76 | PointAffine { /* 12 */ 77 | x: GFp5::from_u64_reduce(0xA0A63453B2520E76, 0x154B9F3B3DED29A2, 78 | 0x68A11C1528013C8F, 0x903B80A6F09AE901, 0x940A7401F7737C15), 79 | u: GFp5::from_u64_reduce(0xE7801DEA8AE5C94D, 0x9CA6A99FC677B56D, 80 | 0x99A02F2C85B98888, 0xD6EF067B29F73B09, 0xBAE7DF0908F3326C), 81 | }, 82 | PointAffine { /* 13 */ 83 | x: GFp5::from_u64_reduce(0x9B529BDD12BA8168, 0x315948D3E2C24095, 84 | 0x4A32AAD7F2BD5D71, 0x3EA68814C835F7AC, 0x3693DC2BFE7A0305), 85 | u: GFp5::from_u64_reduce(0x4B3D8724805D3595, 0x02D5D1434E2B7FA0, 86 | 0x5433B329F514B8D9, 0xE0B27774E7FD8AD5, 0x39740438FA4DE60D), 87 | }, 88 | PointAffine { /* 14 */ 89 | x: GFp5::from_u64_reduce(0xB70B88550BCDC40A, 0x79CE838FD1512FC2, 90 | 0xF2D254FD78B4176A, 0xD21D143BD0C3D6E8, 0xD7586F2424CD8A89), 91 | u: GFp5::from_u64_reduce(0x0E7078E4323A9AF6, 0x808BB98BE0705106, 92 | 0x29802C432FD1B88F, 0xFB97D7787351836F, 0xAD55A82A983E13BE), 93 | }, 94 | PointAffine { /* 15 */ 95 | x: GFp5::from_u64_reduce(0xF5DDAE987FE1A8B7, 0xC72A5894CAE32911, 96 | 0x329407834833459F, 0x47C4368ABADAFC42, 0x04175D3818D989BA), 97 | u: GFp5::from_u64_reduce(0x68927FC54DE30BB1, 0xCBC1374AC68435D3, 98 | 0x0AE217DB1A860A3D, 0x49486982ED44B94C, 0xC7F1D337344580B9), 99 | }, 100 | PointAffine { /* 16 */ 101 | x: GFp5::from_u64_reduce(0x787A277B60DFF343, 0x6B93CDCC42380C6F, 102 | 0xE359042B24517973, 0x67599C1234CA257E, 0x5F334A5914C62FFF), 103 | u: GFp5::from_u64_reduce(0x8642ADFD967547F1, 0x48D8168E1347C6DB, 104 | 0x321FA2D8EF93D21F, 0xFFDDFAA10ED7545D, 0x31195BCBF821882E), 105 | }, 106 | ]; 107 | 108 | pub(crate) const G40: [PointAffine; 16] = [ 109 | PointAffine { /* 1 */ 110 | x: GFp5::from_u64_reduce(0x611A5359FCF0A4BF, 0x12370F87B8C02B88, 111 | 0x1B4C636C579F5ACE, 0xAC89FBCF88F6C0A9, 0x71A4E222268D96E3), 112 | u: GFp5::from_u64_reduce(0x87B686E43F300726, 0xB2051F0C83DF5ECD, 113 | 0x732BFAACD02E6493, 0x8B93DC17B6D61A39, 0x84B2CEFEC769CBE1), 114 | }, 115 | PointAffine { /* 2 */ 116 | x: GFp5::from_u64_reduce(0xB0A217943742E147, 0xA294DEAA61794F12, 117 | 0x9C3CD78D82554027, 0x44A5E5721FF637AF, 0x94222A04B9B09CFC), 118 | u: GFp5::from_u64_reduce(0xC1DA484EEA20E61B, 0x87839986D2C0C417, 119 | 0x536726F207AF8AE9, 0x0CE2D8784C4C06BD, 0xF38780BCCCE10933), 120 | }, 121 | PointAffine { /* 3 */ 122 | x: GFp5::from_u64_reduce(0x141D1DC8EB53D8F2, 0x119B541DACDB7E25, 123 | 0x00F4E51C4122258A, 0x070D3841B7C30D81, 0xCFB2F9E418BF898A), 124 | u: GFp5::from_u64_reduce(0xADBCFA8D2CD4BD91, 0x196805299FC754A5, 125 | 0x730C5BD275BD1F10, 0x60A2F4A66ECAF807, 0x45C489829D03437C), 126 | }, 127 | PointAffine { /* 4 */ 128 | x: GFp5::from_u64_reduce(0x68AE5601EAC10B82, 0xB68D5A1D87381951, 129 | 0xB4F2849EE7337B90, 0xF45AFE402B9F2354, 0x38931DA82C2AB7AD), 130 | u: GFp5::from_u64_reduce(0xE4A064DCEDA49780, 0x65E6A29DBEF8A1DD, 131 | 0xA4131B06679D9E21, 0xDBCE53BCE947882D, 0x7466B758C333EAAD), 132 | }, 133 | PointAffine { /* 5 */ 134 | x: GFp5::from_u64_reduce(0x6089B3824D410C77, 0x8FD29FDE1A3D8D78, 135 | 0x167564B36436BBBA, 0xEA5A00784D95E6DB, 0x452719F72FD61C13), 136 | u: GFp5::from_u64_reduce(0xBDF5B501AA8CC54C, 0xF912B0B754DA2189, 137 | 0x63127D35D8F1D3D9, 0xCA1FB0958F64D4D4, 0x394442A46629C061), 138 | }, 139 | PointAffine { /* 6 */ 140 | x: GFp5::from_u64_reduce(0x0B1C9B08197AB917, 0x99363DE503CCC6DC, 141 | 0x14595D2EF0327468, 0x7431486E378FD529, 0x96EB7445B9FB8E96), 142 | u: GFp5::from_u64_reduce(0x4146FC8E3963FC23, 0xDEDCFDC1FDA931B6, 143 | 0x7419E78EDA5EEA0A, 0xAAFFC23F3059EB35, 0x219031A52BC58CC8), 144 | }, 145 | PointAffine { /* 7 */ 146 | x: GFp5::from_u64_reduce(0xADBAB9D522BD3724, 0xFD7D5EB6BE3578B6, 147 | 0x3C70C562176C61FE, 0xE2AADA2E4890AE6C, 0x6D1743861BC53911), 148 | u: GFp5::from_u64_reduce(0xA8E2A8F79FE34EA2, 0x13D0507BDC9A9E5A, 149 | 0x2FE75772503CEDC2, 0x3FC74C6C8B738B8E, 0x4E38E48C25CEEB31), 150 | }, 151 | PointAffine { /* 8 */ 152 | x: GFp5::from_u64_reduce(0x615EB8E1F0C83E68, 0xCB81875E029323C6, 153 | 0x65286DE9000965E7, 0x33CFFDAE8BC911A6, 0xB397069155631D82), 154 | u: GFp5::from_u64_reduce(0x52BDE21F53DBF866, 0x6BA936C397782D19, 155 | 0xF6636580FC790962, 0x4DD42818F1555407, 0x8CFBEE063AEAED35), 156 | }, 157 | PointAffine { /* 9 */ 158 | x: GFp5::from_u64_reduce(0x8606EDC9D5E9113E, 0x182007E52EFE27C2, 159 | 0x2DEF1CF3FF3D0E56, 0x48C88C73DD199CD7, 0x9A0633769D643C8E), 160 | u: GFp5::from_u64_reduce(0x8D3ADAB2EB7C1CE5, 0xEDFFACF33E5EF7AC, 161 | 0xE5C9256D106262D2, 0x58F2A9B6BCBCFDE6, 0x06342E1E589A293F), 162 | }, 163 | PointAffine { /* 10 */ 164 | x: GFp5::from_u64_reduce(0x1BC27D027A958173, 0x6EE3BD06429418FE, 165 | 0xC0C81C8D10D4357F, 0x2F316BBF8AD1914C, 0xF4F1D345AFAADF4A), 166 | u: GFp5::from_u64_reduce(0x7D0DBC22D7972122, 0x86C7FA7CE07AA8D6, 167 | 0x4821B6A52390D398, 0xCB3C24236F5242FD, 0x46A8A25CDA5D4331), 168 | }, 169 | PointAffine { /* 11 */ 170 | x: GFp5::from_u64_reduce(0x3C6B69452DF20DA5, 0xA16382DA71339FF0, 171 | 0x0734ED8AF2FF492E, 0xF231730C6F123884, 0x6FC56D7052CBD46A), 172 | u: GFp5::from_u64_reduce(0xE179905DE5B7E0C4, 0xB8DDC95767B72473, 173 | 0xB78DD85B25EEBB13, 0xDBB1F2E1B16D69AF, 0x8D29614950F6815F), 174 | }, 175 | PointAffine { /* 12 */ 176 | x: GFp5::from_u64_reduce(0x7799965532068142, 0x28B108BB3C43E88C, 177 | 0xC2DB5F750355BEA8, 0x7681956BF5AA1319, 0x721E95D6212552E9), 178 | u: GFp5::from_u64_reduce(0x1D6543850C7B0929, 0xEC2A26A51B23B809, 179 | 0x350CA7888D6D1BB6, 0xF9F9E18245C88EC2, 0xC9FE9387C28FB541), 180 | }, 181 | PointAffine { /* 13 */ 182 | x: GFp5::from_u64_reduce(0xC3BCC4F02EAC0C83, 0x40D4CC7AD230AF3B, 183 | 0xCA8CF9F2155561E1, 0xAA99A9AD55042AB3, 0x99CFB2EA5C310F59), 184 | u: GFp5::from_u64_reduce(0xB4982A468FA68497, 0x5865337073A89564, 185 | 0xBE56C3BFCB41F3DE, 0x0B8EF2F8E362697E, 0x27551DED6C8B2F69), 186 | }, 187 | PointAffine { /* 14 */ 188 | x: GFp5::from_u64_reduce(0xF465CD82F6782E19, 0x7C27481748425D26, 189 | 0xC31289FF743E003B, 0x5DB208B4B28FBD05, 0x92D7B2A86701447B), 190 | u: GFp5::from_u64_reduce(0xE976DFF362834259, 0x6192BCD9481DE3B8, 191 | 0x1940FE2ED27A5D23, 0x14809DF701E212E2, 0x2A7A9E074D9C514E), 192 | }, 193 | PointAffine { /* 15 */ 194 | x: GFp5::from_u64_reduce(0xE35C62CF3F65D6EF, 0xF098AA0FC2F2329C, 195 | 0x8EB38143A4A67389, 0x7B8DA3DDC3B09AA7, 0x8B499F766BF7DF6F), 196 | u: GFp5::from_u64_reduce(0x0D9C66B7D75AA0BA, 0x7A711CACC51F2CDD, 197 | 0x114F33E3793D5E1E, 0x6FB4FE75D138043F, 0x4FDD0F0E91EAE708), 198 | }, 199 | PointAffine { /* 16 */ 200 | x: GFp5::from_u64_reduce(0x4284BDEAB4B3C711, 0x8F76C066935BD65F, 201 | 0x3CA785B64D9CA29F, 0x734FB37B0D88E69B, 0xA615CD2231C23F7F), 202 | u: GFp5::from_u64_reduce(0xF40EA9D64BB8DB38, 0x988BA6A1E479BAD6, 203 | 0x3B728B804E9FB190, 0x93151A8A49C7F10D, 0xB878EB1AFF80BAA4), 204 | }, 205 | ]; 206 | 207 | pub(crate) const G80: [PointAffine; 16] = [ 208 | PointAffine { /* 1 */ 209 | x: GFp5::from_u64_reduce(0xBFF791CA85BC028C, 0xF9FB2901AC55CA28, 210 | 0x12104EC08515E4D1, 0x7ED4B2E7EEB96EBD, 0x3E86614EA1297F5F), 211 | u: GFp5::from_u64_reduce(0x66234F2978C62E9A, 0xE797BC8963F28598, 212 | 0x3D0FEDC88533EFA9, 0x4928C92B69FA6381, 0xA1B5CC952764418D), 213 | }, 214 | PointAffine { /* 2 */ 215 | x: GFp5::from_u64_reduce(0x981134C4BE7EDF34, 0xD40A50866D224AC8, 216 | 0xE6DEF6024A2A53C2, 0x01EF1CE227EDAFC2, 0xCEF67CBF79ABFB65), 217 | u: GFp5::from_u64_reduce(0x61341049363D9806, 0x08DE86F9A1D453B4, 218 | 0xF0B229C09A150C3C, 0x9A6B56C97677B746, 0x609BEF57EDA9C360), 219 | }, 220 | PointAffine { /* 3 */ 221 | x: GFp5::from_u64_reduce(0x174A2490B64AA7D7, 0x90ED1C03BE0A1E10, 222 | 0xDA36BBE81409E8AD, 0x067986860F1796AA, 0x1337390B4D19D913), 223 | u: GFp5::from_u64_reduce(0x6DCBDC9D8B6FFCF1, 0x7D047179B73ACFA1, 224 | 0x7A85AA3FC5AFCEFE, 0xB1B14799AB054684, 0x47BB870DD0B97BBA), 225 | }, 226 | PointAffine { /* 4 */ 227 | x: GFp5::from_u64_reduce(0x862E7BAEF49C57BE, 0x16763810A8E92363, 228 | 0xDB5D6E0A97F30C0B, 0xC95A211538BFD9C5, 0x74954F9DA117998E), 229 | u: GFp5::from_u64_reduce(0x1CBC9CCD138E89C8, 0x56BA4D9517BDF76A, 230 | 0x4875ABC85E225A46, 0xB20EAC89A5DE3234, 0x5E583F59E343D970), 231 | }, 232 | PointAffine { /* 5 */ 233 | x: GFp5::from_u64_reduce(0x0883BCD672D03A45, 0xBF9DA799E22B0280, 234 | 0x44844BD86742B4F4, 0x2A4187BE0A3291E4, 0x93EF3D4D5B9FEF68), 235 | u: GFp5::from_u64_reduce(0x9C628A98C225201A, 0xC3E274C30E326B21, 236 | 0xD20919EC01919CD1, 0x708BE53371BB1513, 0x2CF0872ABFC8DE56), 237 | }, 238 | PointAffine { /* 6 */ 239 | x: GFp5::from_u64_reduce(0x32B323FDE01834E5, 0xEB7D58926002D7A5, 240 | 0x311976F6B1DE8C50, 0x53F0D01BCE04FC3B, 0xB83C631FB53E6835), 241 | u: GFp5::from_u64_reduce(0xD49D9B793D4C47C3, 0x05E243E0ADC4C074, 242 | 0x87CF12FFB6194074, 0x0194C81951ADBCE2, 0xF2574B54F7601426), 243 | }, 244 | PointAffine { /* 7 */ 245 | x: GFp5::from_u64_reduce(0x4D60ADBD2CF65DC1, 0x1E61B2DF00932777, 246 | 0x17669D53561D21B9, 0x5D7DBF43E4DA3098, 0xF1F1C35ADBA8C435), 247 | u: GFp5::from_u64_reduce(0x61F2BD2C307F98D0, 0xE5E6ACC9F0B7C41D, 248 | 0xB2DE8FB853425F4A, 0xC80F67CF92860DA9, 0x0FEB48F9807E881F), 249 | }, 250 | PointAffine { /* 8 */ 251 | x: GFp5::from_u64_reduce(0xD94CCFEBFE7376A8, 0x1F69B506843A9FB5, 252 | 0x25AF59D5AC2D3E6E, 0xA59BDF1CDDF32601, 0xF6994D2E8166A9ED), 253 | u: GFp5::from_u64_reduce(0xE8646785E9539A66, 0x8458539C65A3863B, 254 | 0xAD8A7E5ACC5827C1, 0x09C7A04F5884FF56, 0xCA3F64D3D9836222), 255 | }, 256 | PointAffine { /* 9 */ 257 | x: GFp5::from_u64_reduce(0xBE252F638D10A319, 0xA01D12686C403F4B, 258 | 0xFE05FE819EC8DC6D, 0xEBCADE7DE489A748, 0x733123271E24BC37), 259 | u: GFp5::from_u64_reduce(0xB3AB56CE983235E8, 0xDE6FDB91D66A50EA, 260 | 0xC86EAB8154E9F48C, 0x71DC9D64B9F0DB6D, 0xB49651B0086141ED), 261 | }, 262 | PointAffine { /* 10 */ 263 | x: GFp5::from_u64_reduce(0x7C97A1B3D2BCF519, 0x745E8A6D42E74374, 264 | 0x59A5C1467B27CE5E, 0x91B9E0010A0F5E0A, 0x7116292D5BF4DB68), 265 | u: GFp5::from_u64_reduce(0x08FFA357F0044133, 0x1C01E00B47B320B5, 266 | 0x997AB99060DA5999, 0xEE4D13B68B36E2F8, 0x0A0052F08B6D6A49), 267 | }, 268 | PointAffine { /* 11 */ 269 | x: GFp5::from_u64_reduce(0x0768481A730111E7, 0x7EBFBA3F4B985E9B, 270 | 0x5A29ABF499EE8220, 0x465E398A5E5D9F80, 0x8B24E98561F1E7B6), 271 | u: GFp5::from_u64_reduce(0xF03F941053C2F189, 0xAF15B126F1DD2362, 272 | 0xE0C1F9661069145C, 0x70AC53657D214DA1, 0xE8CB1591772591D3), 273 | }, 274 | PointAffine { /* 12 */ 275 | x: GFp5::from_u64_reduce(0x7C9805E37AFB8FDA, 0x7EC6E5AEC3944FD6, 276 | 0x799AD8712625A151, 0xCCF07EEE9356056F, 0x8000967388385792), 277 | u: GFp5::from_u64_reduce(0xD3E9E6243AAD543B, 0xA593BC22A29B572D, 278 | 0x1B7927E3CFFD9E34, 0x8BEC6E12A8095B62, 0x3F79034163A87374), 279 | }, 280 | PointAffine { /* 13 */ 281 | x: GFp5::from_u64_reduce(0xB2703BF5809085B7, 0xA1A318C3972A7206, 282 | 0xF198687ADB77A5EE, 0x521D68C0984AAA30, 0x0E87E4AF29F07F30), 283 | u: GFp5::from_u64_reduce(0x00D595A13DC09A16, 0x1718E2432CE9AA64, 284 | 0x6232C6A30D7280C9, 0xC1EE0841AD6808B2, 0x9D11FF978AAA9DD1), 285 | }, 286 | PointAffine { /* 14 */ 287 | x: GFp5::from_u64_reduce(0xF234CA90DC099C7D, 0x5F852BE145BA1D9C, 288 | 0x81216F4F65D6C3FB, 0x0D9EEE31A8A67E94, 0x98FDF4A20AFB5398), 289 | u: GFp5::from_u64_reduce(0x24347A34B17D37E6, 0x27BB083AA277AC09, 290 | 0x7DBAEF7B797E2CE5, 0xE9D42158A195E722, 0x00C334027C0D49B3), 291 | }, 292 | PointAffine { /* 15 */ 293 | x: GFp5::from_u64_reduce(0x655DF87F3AF6CC59, 0xAF94BD95765D3477, 294 | 0xA7045CE70F44E1FC, 0xC9BA067F552C2BBC, 0xE43C604D34512222), 295 | u: GFp5::from_u64_reduce(0xC27AD2F5D6671B2C, 0xB9378F2C40637BE7, 296 | 0xCA0C8A5255F697CE, 0x8BE0E122DC2E05A7, 0xB8CF62F86BF9A6FA), 297 | }, 298 | PointAffine { /* 16 */ 299 | x: GFp5::from_u64_reduce(0x1CC5BAA51DA2D312, 0xC0F23B1B0FE4ED2A, 300 | 0x5B88DE0A0B959A46, 0xB0B9A35ABED716C3, 0xC9536A8EB50C5D89), 301 | u: GFp5::from_u64_reduce(0x0015F3561509A9BC, 0x47FC4BC52D6BB76B, 302 | 0x1D1A0EEA38A66200, 0x324D88263AE0B8E2, 0xDBC3D405C7DA217B), 303 | }, 304 | ]; 305 | 306 | pub(crate) const G120: [PointAffine; 16] = [ 307 | PointAffine { /* 1 */ 308 | x: GFp5::from_u64_reduce(0x81DFC7A7A5475CBD, 0x56113BCFAC979210, 309 | 0xF33847958D1D8345, 0xFC3047B32E95A30B, 0x9FF367E17679CF4E), 310 | u: GFp5::from_u64_reduce(0xD590CF0CA8D4761E, 0xF1D3810D25C0B2C6, 311 | 0xD90967F9BFE553BF, 0xD0BC6D8C9E227371, 0x6E275038FA572267), 312 | }, 313 | PointAffine { /* 2 */ 314 | x: GFp5::from_u64_reduce(0x05E572BA66B15B39, 0xBBA9C5154EBEB103, 315 | 0xAE8E2A206C070CFB, 0xD6CCFF9306078AB9, 0x2B1F8D1112EFD844), 316 | u: GFp5::from_u64_reduce(0x01EFFE47EC31E510, 0xB511EE2B390B0EC8, 317 | 0xCA2F7CDA89514AD1, 0x1F1C5DB055753591, 0xDB6827DFB0F409F3), 318 | }, 319 | PointAffine { /* 3 */ 320 | x: GFp5::from_u64_reduce(0xC918CEFC9CD298A2, 0x7F9103ABCB08E989, 321 | 0x47D00AF024CAF0B5, 0xC9783B10BA49607E, 0xEDB25A9081BB9CF2), 322 | u: GFp5::from_u64_reduce(0x935A4020350E5BEB, 0xD670C08A7082F4A0, 323 | 0x0107AC6F82F699B7, 0x620D1C3B355A0565, 0x1A7BDE0AECF72F81), 324 | }, 325 | PointAffine { /* 4 */ 326 | x: GFp5::from_u64_reduce(0xC47C9CD50B313694, 0x9702D00B57F7CC7D, 327 | 0x88A0C30D160A23BA, 0x0446898927D7D998, 0x1F57A65F0AF07CC6), 328 | u: GFp5::from_u64_reduce(0x8D67DB1D9C30123B, 0x2DE54DC1702F4889, 329 | 0xD2DA0ED1E2F9B6F3, 0x15229A59D9A0F308, 0x8F6D90EA4A987A4A), 330 | }, 331 | PointAffine { /* 5 */ 332 | x: GFp5::from_u64_reduce(0x60FC681C2040DF2B, 0x5DDB0B69F69A2C78, 333 | 0xB04189024B566274, 0x971DD29D44E756A0, 0xCA1B53DDFAFBB17C), 334 | u: GFp5::from_u64_reduce(0xA83CDC13589012A5, 0xF9361D69CD5D929A, 335 | 0x130724E183727F72, 0x61D732B0B0C613F1, 0xC4B677CE6D45BB1D), 336 | }, 337 | PointAffine { /* 6 */ 338 | x: GFp5::from_u64_reduce(0x9A874D914401394F, 0x1B10689A9F8DBCD3, 339 | 0xDA1EDF2CBF452064, 0x2F3F0CA1DFB9D39C, 0x49935367CDE51D51), 340 | u: GFp5::from_u64_reduce(0x32502FEBF9FC4762, 0x648FC03B7084325A, 341 | 0x0C41A62661CC2BB8, 0x43DD5654E0EC867A, 0x5121D65E62AF2D83), 342 | }, 343 | PointAffine { /* 7 */ 344 | x: GFp5::from_u64_reduce(0xF6BCD91B9D229C4F, 0x1ED90B399CE1F26F, 345 | 0xC69BF32330251380, 0x54882B5C8B4B746B, 0xB7E5714D168BF813), 346 | u: GFp5::from_u64_reduce(0xBCADEEFA2B9B265D, 0x4A324D10DD8950B4, 347 | 0xDDAE64902599551E, 0xF3D542FA995AE725, 0x3E4C6EBD817C001D), 348 | }, 349 | PointAffine { /* 8 */ 350 | x: GFp5::from_u64_reduce(0xFAB7B11C9F5DA1D2, 0xF923CFBC1E20152D, 351 | 0x5FB28B0BEDFAAD83, 0x48DE620DBF3D5831, 0x4CF82E5AE1D1B588), 352 | u: GFp5::from_u64_reduce(0xBD1F6C5B2DF2FDCE, 0xE275428000B3CFB7, 353 | 0xF32B9605CA11F251, 0x2915E5380C54D3FB, 0x80157D2979DB3056), 354 | }, 355 | PointAffine { /* 9 */ 356 | x: GFp5::from_u64_reduce(0x11F8FD3613EB3394, 0x26FD38FDE73F91E2, 357 | 0xEA33D1E4655AF9EE, 0x05C24E7B239B8EAF, 0x7B46BB6A9E90B5C3), 358 | u: GFp5::from_u64_reduce(0xC376EC1F984898E3, 0x1432C1C994F0C08C, 359 | 0x6316DDCF376D7CF7, 0x2DDAA603E9B5BF50, 0x24518AFD5F7C2860), 360 | }, 361 | PointAffine { /* 10 */ 362 | x: GFp5::from_u64_reduce(0x5716E9A9A5082F96, 0x2F0E8BAD605D068A, 363 | 0xEF5D32FDA8D82584, 0xC42304AFBD7D871C, 0x36687E044DEE4634), 364 | u: GFp5::from_u64_reduce(0x69DDBCE5A4ED87F4, 0x526EC1AF1567153B, 365 | 0x7E5C060D48CE5C40, 0xBBFEC2C8FD487CC1, 0x8EA69A07F739EABC), 366 | }, 367 | PointAffine { /* 11 */ 368 | x: GFp5::from_u64_reduce(0xA16992D30A7AF7D9, 0xAF24506003383737, 369 | 0x011AE62187AA31D0, 0xF8B24E6FC064709E, 0x50DCB633172F23ED), 370 | u: GFp5::from_u64_reduce(0x25F89F2718EB46C2, 0xC7F267222A19A771, 371 | 0x906877203579DFBA, 0x28F5F954A16530B2, 0xEC51C1AD7FC50EA1), 372 | }, 373 | PointAffine { /* 12 */ 374 | x: GFp5::from_u64_reduce(0x8A9EFBFAD6FE2B7B, 0x05F489AA4CB43222, 375 | 0x6B0644743682456E, 0xCDFE708173216B70, 0x1F4602CE2EE4C17C), 376 | u: GFp5::from_u64_reduce(0xEE8F4D0979D37A3C, 0xBFA35BC84142F9DE, 377 | 0x4077ED5E3F53B5E1, 0x8FC7E38E062AB1F7, 0xB475144BB33472D7), 378 | }, 379 | PointAffine { /* 13 */ 380 | x: GFp5::from_u64_reduce(0x4B554EB298E6827B, 0x520957E0C44F2929, 381 | 0xA70073FF75531C87, 0xC3B0CBF9F188FC9E, 0x280D47FD2D499A8A), 382 | u: GFp5::from_u64_reduce(0xB35F4C19929B3CF0, 0x59A153F47D6D83DA, 383 | 0x771AFF712A96CBC2, 0x828189393AF72E26, 0x39D179AF302624EE), 384 | }, 385 | PointAffine { /* 14 */ 386 | x: GFp5::from_u64_reduce(0xC2F8E5C058865246, 0x8AB1F6998CA70ABC, 387 | 0xE6673CF650D2B916, 0x7A33C8A0D27F468B, 0x07900417775ED00B), 388 | u: GFp5::from_u64_reduce(0xFC5B21F6C152F2AA, 0xA064DE2B87EC6B74, 389 | 0x6C06C835AC6A1839, 0xA0C0EC16E089F39B, 0x612D331E37583569), 390 | }, 391 | PointAffine { /* 15 */ 392 | x: GFp5::from_u64_reduce(0x0B4C5AE4DFB02D59, 0x01928464B2E04ECE, 393 | 0x31509CBC754DF607, 0x4F6B3C63839468B7, 0xBBD9267B5B575941), 394 | u: GFp5::from_u64_reduce(0x94BE85F938B56FA7, 0x139794D63C1AA09D, 395 | 0x1DD665B970682C0C, 0xCC7E05810839AFBD, 0xBD5552D96F644DC1), 396 | }, 397 | PointAffine { /* 16 */ 398 | x: GFp5::from_u64_reduce(0xE4C0092E44A2CD56, 0x8640177369C89FB1, 399 | 0x6BB2EFB2D47BEF55, 0xC3DAF4226CB841EE, 0xC496CD80F48FC705), 400 | u: GFp5::from_u64_reduce(0xE0D4A4D054567841, 0xDAEECD5F3824B100, 401 | 0x7921F0AC2E9068A8, 0xC6FA0D80661C7931, 0x5F583DEB723D2502), 402 | }, 403 | ]; 404 | 405 | pub(crate) const G160: [PointAffine; 16] = [ 406 | PointAffine { /* 1 */ 407 | x: GFp5::from_u64_reduce(0x3830EE0B997A7060, 0x7497CABD07057DC0, 408 | 0x75927E746D00183E, 0x9C6DADCC11DEAEAC, 0x9F66177B8921CD13), 409 | u: GFp5::from_u64_reduce(0x8A86D127D7936BEF, 0xE5149E6285479398, 410 | 0x87F259B34E5BD418, 0xC9BEF27D34416090, 0xE8F755D40D6865BA), 411 | }, 412 | PointAffine { /* 2 */ 413 | x: GFp5::from_u64_reduce(0xE5319BBC868A2D7B, 0x2725C72D33F3817D, 414 | 0xC9E0DE1B121D2EDF, 0x6FF67E5D3EFD38A3, 0x4CFD98FA2FB85BF2), 415 | u: GFp5::from_u64_reduce(0xB696AED1DD180D3F, 0xC3659521AAD5ECA1, 416 | 0x32D71817413B6DC1, 0x7295CB868CCFA40C, 0x6F61DA7362EA8310), 417 | }, 418 | PointAffine { /* 3 */ 419 | x: GFp5::from_u64_reduce(0x63E82230DE1A29EE, 0x5A4EDC24826D47C8, 420 | 0x794F1E3CC2F9C121, 0x45D55698BCEE11DC, 0x146BD13724FBD24D), 421 | u: GFp5::from_u64_reduce(0xDFD1F5F4F742A5E5, 0xF51E15E97A61F13F, 422 | 0x4B4DD1444E59F27D, 0xB0118CDBF0436FCB, 0xA25FEEDA6DC43B5E), 423 | }, 424 | PointAffine { /* 4 */ 425 | x: GFp5::from_u64_reduce(0xD7ABE1664B48916D, 0x52F58A2EC038E24B, 426 | 0xF468611E6A5CD3D0, 0x19F2CEDB6FD320E2, 0x03A45C530E0E481E), 427 | u: GFp5::from_u64_reduce(0xE7058EDFFC45E01C, 0x229820847CA245FF, 428 | 0xB4571CE63E1FE7CD, 0xFAD16D9CCA297C82, 0xE0936814B7A3FAA2), 429 | }, 430 | PointAffine { /* 5 */ 431 | x: GFp5::from_u64_reduce(0xC88E81BE7CB8BE13, 0x253980DF689AA3B3, 432 | 0xDB0EE9B47B609458, 0x89937595BCF05ABF, 0xED186AB8BCA3CFA8), 433 | u: GFp5::from_u64_reduce(0xB4A0638B7F06669A, 0xFBBF589D6FCEDCBE, 434 | 0x473DDDC6DF96C85A, 0x0811C5874613BCED, 0x94C84D7D9D2209D2), 435 | }, 436 | PointAffine { /* 6 */ 437 | x: GFp5::from_u64_reduce(0x18191FA200FCF6AF, 0x2302A5F56CDD8D5E, 438 | 0x75D51CEE7508100A, 0x2A7CB1BBCC4B6E82, 0x68ED086B8EFE0952), 439 | u: GFp5::from_u64_reduce(0x238679B861A2B71E, 0x72E5947423E98442, 440 | 0x825E4FB253B3BEE7, 0xD59828C79D39170D, 0xD64BE0C2D3C4BDDF), 441 | }, 442 | PointAffine { /* 7 */ 443 | x: GFp5::from_u64_reduce(0x906BE97E5BC88259, 0xA9D29DEB5BFA6301, 444 | 0x5794A30DDA67C7AF, 0x391573041251E9DA, 0xE958FCA20E602C8E), 445 | u: GFp5::from_u64_reduce(0x2BF49C6D28B7CD14, 0x2ED045F27AC1AC99, 446 | 0xB7B07E480D6E4980, 0xDB7DED6782E1AEF3, 0x6F0C4FBE7C1C5FC0), 447 | }, 448 | PointAffine { /* 8 */ 449 | x: GFp5::from_u64_reduce(0xFC44982DA86BFA06, 0x64099395CF0D88B6, 450 | 0xCD1D2382A6C43A54, 0x1BB3F627B1B99008, 0xD0E9DDEA10328819), 451 | u: GFp5::from_u64_reduce(0xDA17D468F1DE5C01, 0x78EF4B4D0ED0BE22, 452 | 0xE11770286753C974, 0x0368BF12362BF72C, 0x404DEE08CFDDFD8A), 453 | }, 454 | PointAffine { /* 9 */ 455 | x: GFp5::from_u64_reduce(0x198E62045DB35A74, 0xFC033F301DB930CC, 456 | 0x628E7DBA48CDD07F, 0xF1C019DF9777F7C9, 0xDC4526D93675652D), 457 | u: GFp5::from_u64_reduce(0xCF5037DB2206A383, 0x147AA2B70FFE8CA7, 458 | 0xDEBE043528F23B85, 0xCF27F1AC633AA891, 0x9E1E0153D9C060A8), 459 | }, 460 | PointAffine { /* 10 */ 461 | x: GFp5::from_u64_reduce(0x98E0C0CCAB0764E4, 0x8F97708426132A48, 462 | 0xD5E74BDE6BEC4714, 0x349746192FA1CB98, 0x8F525A63D2A9223A), 463 | u: GFp5::from_u64_reduce(0x146AAB875512FE13, 0x3DC2670A500DE2B6, 464 | 0x1BBE14227A54E1FF, 0x19D792E2356253AA, 0x8882DD5B5969304D), 465 | }, 466 | PointAffine { /* 11 */ 467 | x: GFp5::from_u64_reduce(0x5D6C8679525E09F1, 0x870A9337817CAC33, 468 | 0xC882B79142B218C1, 0xA9C42826B65CE8A2, 0x477783199F3B842F), 469 | u: GFp5::from_u64_reduce(0xB8A2C0757677C713, 0x851ACDFFD11BB41A, 470 | 0x62B97CD3A35DC135, 0xA3B31A08DE1FBFAC, 0xD752D76988DDD12D), 471 | }, 472 | PointAffine { /* 12 */ 473 | x: GFp5::from_u64_reduce(0xE0E90C6ED39B654C, 0x67A39AF711058681, 474 | 0x0D3DAEF3081904F3, 0xD3C338BADA40006D, 0xB33F7393564EC4D1), 475 | u: GFp5::from_u64_reduce(0x987708704EB02D95, 0x0CC04AB0A501765E, 476 | 0x943216BE72A6DFD7, 0x7F51AD2A5D248721, 0x61CC888BEBCFFB80), 477 | }, 478 | PointAffine { /* 13 */ 479 | x: GFp5::from_u64_reduce(0x4CAA65A18B975968, 0xB495513833E42713, 480 | 0x78C113F1379958F1, 0x697CA2048D326DD5, 0xE963A1404D90CB8C), 481 | u: GFp5::from_u64_reduce(0xF04BA0466834991C, 0x7B912D4EB5F31927, 482 | 0x99C6E4E89612F6E6, 0xA91F4BC177C2176A, 0x94A885B9B08B2D5F), 483 | }, 484 | PointAffine { /* 14 */ 485 | x: GFp5::from_u64_reduce(0x56F0D795C32CBE87, 0x931F870DF82C1525, 486 | 0xCB6ABB5DDBC8E3A5, 0x3DBF89D464C2312F, 0xD48BF8E7B291816E), 487 | u: GFp5::from_u64_reduce(0x276C69A01726373F, 0x5107374917AB3272, 488 | 0x773DD2A5CC82B6F8, 0x991989E9601107EC, 0x944E8035A63D7EAF), 489 | }, 490 | PointAffine { /* 15 */ 491 | x: GFp5::from_u64_reduce(0xFF673761A7450B49, 0x4AE007FAF082E23F, 492 | 0x3C8C3E05EEB3F6A5, 0x35FF9C11CC2C3DED, 0x62BC066A6E5C6720), 493 | u: GFp5::from_u64_reduce(0x372C34D7004A8EFD, 0xEF289159A6DB7C6C, 494 | 0x29FA45E3FA32C323, 0xA6DE74555D6F4465, 0xE68B116A5AA1BF61), 495 | }, 496 | PointAffine { /* 16 */ 497 | x: GFp5::from_u64_reduce(0xE9EE4BD0ECE528FC, 0xCD31C6FDD181B556, 498 | 0x25E8F951B5C13F88, 0x4F1D865943228FB1, 0xE526F967A8BDCCE8), 499 | u: GFp5::from_u64_reduce(0xD8FD6271DED8E77A, 0x8675F320D3750CEB, 500 | 0xBD5FE217A8A07A18, 0xA40203C0B4FCCD19, 0x4AE32049567A9B8B), 501 | }, 502 | ]; 503 | 504 | pub(crate) const G200: [PointAffine; 16] = [ 505 | PointAffine { /* 1 */ 506 | x: GFp5::from_u64_reduce(0xE616A89F4A4C3DDC, 0x40965D57246A34FD, 507 | 0x1A16EB3EFDFB7930, 0x6371F59AB8442179, 0x8B287EF89765E37C), 508 | u: GFp5::from_u64_reduce(0xCB9DE30898259CD1, 0x70E50403C409DE58, 509 | 0x175767980E9CB777, 0x2AE405D1B885A713, 0x46883205F6461F73), 510 | }, 511 | PointAffine { /* 2 */ 512 | x: GFp5::from_u64_reduce(0xB383FC6C08553929, 0xBDF89C37968EF761, 513 | 0x7D1F44EE38C66D8B, 0xFAD5FEB55710565D, 0x8BB6AC3E11119EC2), 514 | u: GFp5::from_u64_reduce(0x1A3FF0E69015297B, 0x83118325E370E206, 515 | 0x95A5C6611F30EC5F, 0xD1B9AB1269DA3C74, 0x75D202969B61D2FF), 516 | }, 517 | PointAffine { /* 3 */ 518 | x: GFp5::from_u64_reduce(0x371CF61D1B6BEE4F, 0xA46896D9A0D6D340, 519 | 0x64A4EC7ED6F98B69, 0x27619FE45F8042C9, 0x07B2BBA45D8B4DA0), 520 | u: GFp5::from_u64_reduce(0x1FB1647224994736, 0x40EE9C769BD173F1, 521 | 0x62091AE7A020F159, 0x11F4F4AC42AAF6DB, 0x3935E9FAF93174AB), 522 | }, 523 | PointAffine { /* 4 */ 524 | x: GFp5::from_u64_reduce(0xF0C24C0829D03884, 0x77E44895D7601409, 525 | 0xDB85941EDF473DFA, 0xF91CDACADB1C6EA9, 0xD3D067DC0ECCD4C0), 526 | u: GFp5::from_u64_reduce(0x46DFFBED29FBD34F, 0x3794971420F3F342, 527 | 0xBB34444ABECC2B7A, 0x13FAB655F99C4328, 0x287805E5B39C7A54), 528 | }, 529 | PointAffine { /* 5 */ 530 | x: GFp5::from_u64_reduce(0x600FE70A8B05B252, 0x42D935D34D536CFF, 531 | 0xC72035E7C95B3354, 0xE57CC01BD27B29F6, 0x732714762A107AA3), 532 | u: GFp5::from_u64_reduce(0x3144661D70681FA1, 0x51E1542CFF2C0CE8, 533 | 0xDC665EB7401E02BC, 0x3E5EBA71BD571963, 0x09B22663205E1FBA), 534 | }, 535 | PointAffine { /* 6 */ 536 | x: GFp5::from_u64_reduce(0xE163D75C513E0CCC, 0x5518CABC28DDC4F7, 537 | 0xE9961D5370BC5B65, 0xEF742BAE495AC058, 0x9B395F6A4F9B6A26), 538 | u: GFp5::from_u64_reduce(0x3BA66FFA8EAD89EB, 0x63C5E48A245D7AA8, 539 | 0x82B6ACEFEA7E4D76, 0xC9A9359D54A4DD0F, 0x768ACA943DDECEEC), 540 | }, 541 | PointAffine { /* 7 */ 542 | x: GFp5::from_u64_reduce(0xD661DAE6D3DD9F89, 0x4C710B466E774311, 543 | 0xE4C95E255E5683A8, 0xB78137C990E22F80, 0x2BDA022583DAFB0A), 544 | u: GFp5::from_u64_reduce(0x4838E9014DEA3D70, 0xBAE8C676F3AFCA06, 545 | 0xEE93BE162C1AF743, 0xFD3883EA55C9ACF9, 0xD4B0C1DAF01C9D23), 546 | }, 547 | PointAffine { /* 8 */ 548 | x: GFp5::from_u64_reduce(0x93657B3895954257, 0xEDD022FFBE81D10E, 549 | 0x8142C0945C90101C, 0x82033657EB7353F8, 0x76BC6646BB3716A6), 550 | u: GFp5::from_u64_reduce(0x6A5E0A4DB04ED6F9, 0xE1429E1F0979A5DD, 551 | 0x5B747016E1ED1833, 0xCAE934C07525C37F, 0x5E3424ADE845E49B), 552 | }, 553 | PointAffine { /* 9 */ 554 | x: GFp5::from_u64_reduce(0x09FF77563A30B446, 0x07BF53470594DE2A, 555 | 0x4CE72C200F8D6E9B, 0xE52273BC7ED7CA35, 0x9033849A19000AF4), 556 | u: GFp5::from_u64_reduce(0xB933301371FF9291, 0xBB910D3069AE5106, 557 | 0x5E0D307FE9F0F779, 0xCD9A0EB95DDDCEF1, 0x19178A0F43AE2C1E), 558 | }, 559 | PointAffine { /* 10 */ 560 | x: GFp5::from_u64_reduce(0x7623C4BE201756E1, 0xBD2F0633AEC30A94, 561 | 0xD337C303193BB7AE, 0x572CFBFED1A8ACF2, 0xB01C34B6EA7E1EF3), 562 | u: GFp5::from_u64_reduce(0xD380C1FC9D68FF85, 0xBBB8B9F9CF5182A2, 563 | 0x0557D0E40BBA2FDF, 0xECB278AD146404F7, 0x87FFD86E47AF23AA), 564 | }, 565 | PointAffine { /* 11 */ 566 | x: GFp5::from_u64_reduce(0xE40EDFE66BA15A89, 0x0E9E3E2F33526D04, 567 | 0x4BA5C6033A38B887, 0x3842C01D32AB77CC, 0x28E5DE9E77245D22), 568 | u: GFp5::from_u64_reduce(0xF8CFA6BB96DD0801, 0xF133E8AD176BCA29, 569 | 0x9C1EAB583453E8F8, 0xBEC6ADFB1A8D8E97, 0xAE7635A4AF1C877C), 570 | }, 571 | PointAffine { /* 12 */ 572 | x: GFp5::from_u64_reduce(0xFF384ED70E2E92B1, 0xC1B07C818FE7707F, 573 | 0xCEB40739BD2E6AA8, 0x4A03A62921D5C51C, 0x8BD601A17D2F7CB4), 574 | u: GFp5::from_u64_reduce(0x727EF2E2653167B4, 0x4FD64C260F9A4FD8, 575 | 0x3297687634294C9C, 0xB307544D393DC724, 0xEA542C84CB669681), 576 | }, 577 | PointAffine { /* 13 */ 578 | x: GFp5::from_u64_reduce(0x156FC875BDCDFEBA, 0x092FCFF076042FFE, 579 | 0xFFAFCA9A76A95EB9, 0x2D0C797173C306F9, 0x485198EF86718F90), 580 | u: GFp5::from_u64_reduce(0x3DE83246EBA16461, 0x2FE484942CB74FA7, 581 | 0xEE8D2538886D8B97, 0xE7B3CFE2D407E1A4, 0x09E243332ADB265D), 582 | }, 583 | PointAffine { /* 14 */ 584 | x: GFp5::from_u64_reduce(0x57586A9E04A0DD6E, 0x0EB1CCC8CD18F018, 585 | 0x4A1D82A3B69C217D, 0x4635FB578FC490AF, 0x30F103C0D76E17B9), 586 | u: GFp5::from_u64_reduce(0x9AD7880E8116B808, 0xB4388460DC70697C, 587 | 0x3135215930D6569F, 0xA38FB7775B6F9168, 0x8C27888E47141129), 588 | }, 589 | PointAffine { /* 15 */ 590 | x: GFp5::from_u64_reduce(0xA36967535666430F, 0x0B10F73BE3D3B04B, 591 | 0x9C90A44FAD53D2D1, 0xD1542D9374A67FEF, 0x23F683062E9E65EB), 592 | u: GFp5::from_u64_reduce(0x98C6FBB81DC121C1, 0x4C2C310848E48D1B, 593 | 0xCF8082BCB5E7D69A, 0x9B495103371A673C, 0x4A5E8A2E23C4D340), 594 | }, 595 | PointAffine { /* 16 */ 596 | x: GFp5::from_u64_reduce(0x6AC4419C7277D1CA, 0x2C5C1F5E44B298D7, 597 | 0x18C662C232D514DD, 0x44D950568161E181, 0xE714CE4B73A7B072), 598 | u: GFp5::from_u64_reduce(0xA81D41AAAF4FA6CF, 0x6E1DE35FDAD17243, 599 | 0xC98288B163F43776, 0x130AB99648D7BD44, 0x9C417A9BAFAB5466), 600 | }, 601 | ]; 602 | 603 | pub(crate) const G240: [PointAffine; 16] = [ 604 | PointAffine { /* 1 */ 605 | x: GFp5::from_u64_reduce(0xA8A0EE7803A9262A, 0x3A9E64856DDF4568, 606 | 0xA8D15981C6B5E366, 0x8C50DFC96818C3F6, 0x67747DA91EA27AFA), 607 | u: GFp5::from_u64_reduce(0x721E3407EBD5F510, 0xCD45A5FB06B5D837, 608 | 0x5ADF593FEF90259D, 0xC3B0FFB9AA1C972A, 0x86EF37AC9F5CF331), 609 | }, 610 | PointAffine { /* 2 */ 611 | x: GFp5::from_u64_reduce(0x200E8B5C9C37E3AF, 0x921C4CD1D43B733E, 612 | 0x285F3CFA7B98BDEB, 0xC43D0057A09411A4, 0xCABD6E7DC2D3243A), 613 | u: GFp5::from_u64_reduce(0xB8C8962CB51C8455, 0x27E98000626D107C, 614 | 0x8D9E90EEA287F110, 0x2315D937D3B14704, 0xD855ED171240A04F), 615 | }, 616 | PointAffine { /* 3 */ 617 | x: GFp5::from_u64_reduce(0x8EBDF4139F2AC3BC, 0x5EBD2FDBAE6EE6DC, 618 | 0x329010A7F5F44D0B, 0x30EB8FFFB8A0547E, 0x8094D0CD6D63E13F), 619 | u: GFp5::from_u64_reduce(0xFFC1C1D1EDB1FD4D, 0xE2B3A119FC8E2D33, 620 | 0x140D049877E3268B, 0x0763801996111CC8, 0xE55EB94944D8FD3F), 621 | }, 622 | PointAffine { /* 4 */ 623 | x: GFp5::from_u64_reduce(0x4F2A667724E34E0C, 0x66C0D5F79375414F, 624 | 0x91398D556C18DE95, 0x56B37C05A0068FDA, 0x46FD0DA0D321A8D0), 625 | u: GFp5::from_u64_reduce(0xF3F7F65AADC697EE, 0x2498FBE550D9998D, 626 | 0x42D957D8D25DCC73, 0x0371646CB0DB7F11, 0x3EE41B5B478EFE53), 627 | }, 628 | PointAffine { /* 5 */ 629 | x: GFp5::from_u64_reduce(0x3DFFE1FB511B6C11, 0x07BFD5AEE108720C, 630 | 0x4140011508479DA9, 0xE725984D11458168, 0xCFD5C8AEBA6BEB4F), 631 | u: GFp5::from_u64_reduce(0x3F0B1B59FCFEB6F7, 0xF78EECBE0E1A6029, 632 | 0x358349B2F8B945C6, 0x33490E5BBC1FD56A, 0x932EE56AFE959D14), 633 | }, 634 | PointAffine { /* 6 */ 635 | x: GFp5::from_u64_reduce(0x853D24B74B77BE2D, 0x28D86D666FF156DC, 636 | 0x6824E07BDFD14641, 0x3236F2172775AFA1, 0x6EFB083EFC900F5B), 637 | u: GFp5::from_u64_reduce(0x11C1CA68D2B4998E, 0x4206817DB4E66EB6, 638 | 0xAC5EA5717A533864, 0xECB4C2DEE8BA26A1, 0xDEEB73F656D8AD27), 639 | }, 640 | PointAffine { /* 7 */ 641 | x: GFp5::from_u64_reduce(0x0089D625D4A275FE, 0x4AEE1C1C159E64A8, 642 | 0xCF02452BB6ABE083, 0x097802619A3CD9E8, 0xFC53D6AEDE0E9FE9), 643 | u: GFp5::from_u64_reduce(0x435279229BD3625C, 0x1889895FE07C89E2, 644 | 0xE056DDC0F402CE97, 0xE804E68342F959D9, 0x2F028095D09C7904), 645 | }, 646 | PointAffine { /* 8 */ 647 | x: GFp5::from_u64_reduce(0x1CD80828E1157548, 0x7AB3312B81CBD770, 648 | 0xA191B99E0F818F66, 0x36FFEE5A4112C1F2, 0xAD3F8AC8AD921E49), 649 | u: GFp5::from_u64_reduce(0xBF4F161F2614DCFB, 0x7587B98E8A95AAF7, 650 | 0x2C0EC2B7F5F7CBB8, 0x95E505D4A14AF60A, 0xB45D26196646C9AF), 651 | }, 652 | PointAffine { /* 9 */ 653 | x: GFp5::from_u64_reduce(0x8A9CE262C5329443, 0x07A3B8C865423F4F, 654 | 0xE018203AA3102625, 0x9F596F7B6817C8C1, 0xFCB769579909678B), 655 | u: GFp5::from_u64_reduce(0xEFD12FC70B32AAB2, 0x47630ED573839DAE, 656 | 0x8311D5C3758990F4, 0x20A8765C516F9BCF, 0x679A6D91FF5DD6D2), 657 | }, 658 | PointAffine { /* 10 */ 659 | x: GFp5::from_u64_reduce(0x5AF23895A8573E74, 0xF6AC9DA9076A8710, 660 | 0x7162587AE722EF71, 0xE4B3BCCE6873A42D, 0x7DA1F5CD972D59C1), 661 | u: GFp5::from_u64_reduce(0x461B13504D813650, 0x70FF153C0B3881BE, 662 | 0x5BE064C9652C1CF9, 0xD64C780542D5C6D8, 0xF783C2AD3E4E50B8), 663 | }, 664 | PointAffine { /* 11 */ 665 | x: GFp5::from_u64_reduce(0xB2AC7989C02502E9, 0x46F0A5085FD69389, 666 | 0x83AD6BE496C248D3, 0x1FE71326A8F0A166, 0xD75068C5D239ED41), 667 | u: GFp5::from_u64_reduce(0x89DE70D73D84658D, 0x437C5714FE4F8B2C, 668 | 0x63CD50470844C646, 0xAEB39B4DC0637ED3, 0xE96CE8D06024A136), 669 | }, 670 | PointAffine { /* 12 */ 671 | x: GFp5::from_u64_reduce(0x4C6B70FEF9176C74, 0x6DD1A052CEB84B64, 672 | 0xEC6B6236BC4CDB96, 0xA995D28DA35CCBBA, 0x413DA1C9356B35BB), 673 | u: GFp5::from_u64_reduce(0xD8E20D8054E422DD, 0x8A5C4188E9409B51, 674 | 0x3798827D3AAE1108, 0x9E75FFAC84DABFEA, 0x8455308253067827), 675 | }, 676 | PointAffine { /* 13 */ 677 | x: GFp5::from_u64_reduce(0x092BE4D5B779CE6E, 0x205D65D92C7BBE5A, 678 | 0x4410382623DCC479, 0xF426801189B81EEB, 0x18A49AEDBAD87946), 679 | u: GFp5::from_u64_reduce(0x72E31C8A73E2A5F2, 0x038D4FCB2D0D45F9, 680 | 0xFACE7BBC088C9465, 0x4D8A29C235CE48DF, 0x6DDE986C6F3DE573), 681 | }, 682 | PointAffine { /* 14 */ 683 | x: GFp5::from_u64_reduce(0xCE8281832197F5E7, 0x02DF944FE891E27E, 684 | 0x7C32B2AC7CA4DF96, 0xDDDE4642C673E511, 0x910884A15C6E9E90), 685 | u: GFp5::from_u64_reduce(0x927B31CAA1595D30, 0x9AB94754B9B1C1C8, 686 | 0xB0FFE7EF45AD9608, 0x5617F7967DC07DD7, 0x748B52FC2F538F06), 687 | }, 688 | PointAffine { /* 15 */ 689 | x: GFp5::from_u64_reduce(0x85987CE43845212C, 0x0E13745D09A067D9, 690 | 0xAE4D3C09C5D3B59C, 0x17DCCE2F3BC41E08, 0xA8DD0B19FE8DF324), 691 | u: GFp5::from_u64_reduce(0x8B52DBAC4316DF3C, 0xE6DD1B0B096BA49B, 692 | 0x45B50205A85D4930, 0xBCA014CCF49A96CF, 0x43EF77C96A70C64D), 693 | }, 694 | PointAffine { /* 16 */ 695 | x: GFp5::from_u64_reduce(0xD253F638008F64D9, 0x300911F4CF750079, 696 | 0xEBA4C0CCF84421F1, 0xD9A8EC0A382CC42A, 0xB2B4D66CAC8FDA35), 697 | u: GFp5::from_u64_reduce(0x8C725DFAE21259FB, 0xA93424C7A5FC6938, 698 | 0x97CE72FE5A59C0EF, 0xA8EE2E81BCA93CBC, 0x3B54AD70A6670624), 699 | }, 700 | ]; 701 | 702 | pub(crate) const G280: [PointAffine; 16] = [ 703 | PointAffine { /* 1 */ 704 | x: GFp5::from_u64_reduce(0xE101EFE0DD3D126E, 0xEE415579E7763701, 705 | 0x481DBDD5A30063D0, 0x9A4A5A2CE9DC78D0, 0x70DFF28028661446), 706 | u: GFp5::from_u64_reduce(0x9DE418BD4B624FA5, 0x3E080A3E987B0F4D, 707 | 0x7D171E8A81DAF782, 0x13B661FEF2D14550, 0xE193CBFC0EC5427A), 708 | }, 709 | PointAffine { /* 2 */ 710 | x: GFp5::from_u64_reduce(0x5DF5EC1ABF7F91F1, 0xED85BEA6EACB5E1C, 711 | 0x571DEA21506D257F, 0x81FECBEFEE649A35, 0x60C9A0D1FA6944FB), 712 | u: GFp5::from_u64_reduce(0x17B12A9CD0DA93BD, 0xE34F65A1C5FE95E5, 713 | 0x85402906EAB91693, 0xFF1BF5F49AE493A4, 0x8839E8685FAE3E48), 714 | }, 715 | PointAffine { /* 3 */ 716 | x: GFp5::from_u64_reduce(0x80FBEEF7CD2E13C8, 0xEDBC56CCE4B4BDB9, 717 | 0xF6D0C9C57A7D8082, 0x6DAF1CFC656E1455, 0xBA7E4182B917F1FD), 718 | u: GFp5::from_u64_reduce(0xCE0EFDA5F6B68E02, 0x35CCA934993EDDEA, 719 | 0xF41B71BDB24254F1, 0xB7F6C4594149EE3D, 0xE9A7895F548783E2), 720 | }, 721 | PointAffine { /* 4 */ 722 | x: GFp5::from_u64_reduce(0x59FE2AB81ABB02AE, 0xCBADFCCB2F04B57A, 723 | 0x0D1969F23952938D, 0x831C8C1258CAE189, 0xEB843136EA9A94FC), 724 | u: GFp5::from_u64_reduce(0x608228855343E93E, 0x78091B902F3A0E56, 725 | 0x27E3EC97B5F1D957, 0x5E3FCAA84D86532D, 0x40FD2108182D5785), 726 | }, 727 | PointAffine { /* 5 */ 728 | x: GFp5::from_u64_reduce(0xF7BEB6DBF433A526, 0x0B2A53C27E29826C, 729 | 0x88809F3B7ADFE4D6, 0x269D7073C55388BA, 0x99104832D8D4C84C), 730 | u: GFp5::from_u64_reduce(0xF10D0D94CD8A08E4, 0x9FE8F86BF58A0740, 731 | 0x922B67E3F366C40B, 0xD15574F1FD92B0CF, 0xAB3245EEA77D39A7), 732 | }, 733 | PointAffine { /* 6 */ 734 | x: GFp5::from_u64_reduce(0xF8CC69057F7C5225, 0x0DB771295EA9B3F1, 735 | 0xEDC9814F24289219, 0x33FB7A518409DA5A, 0xA7A4B6DEDF49F9B2), 736 | u: GFp5::from_u64_reduce(0xE82170DA6BE518E0, 0xE7B7459B42536CEB, 737 | 0x188951DE0336CD0E, 0x47EC99CA8DFD40B5, 0xB319A211D2EF9288), 738 | }, 739 | PointAffine { /* 7 */ 740 | x: GFp5::from_u64_reduce(0x48B43DB63C7204A7, 0x69EC0CFEBE4F89D8, 741 | 0xB700D394422584B0, 0x4A91AFFADC8C6C71, 0x42345336177B3C4C), 742 | u: GFp5::from_u64_reduce(0x0C92DF3450E3302A, 0x230917E124B9227E, 743 | 0x1846F380DFF4F6EC, 0x7498858A1257454F, 0xBFA3A9AF58CB8E48), 744 | }, 745 | PointAffine { /* 8 */ 746 | x: GFp5::from_u64_reduce(0x300FE3B1CBD6A543, 0x82B53FA07658D890, 747 | 0xE704A58CE42E61E5, 0x6FB145C2E7EB6299, 0x378DEF33925DEB86), 748 | u: GFp5::from_u64_reduce(0x07DAD4739750D586, 0x4E42EDC099CEE5A1, 749 | 0x30A64BA70DF477FC, 0x0B7E53D8154398E7, 0x27A84D1C4843D524), 750 | }, 751 | PointAffine { /* 9 */ 752 | x: GFp5::from_u64_reduce(0x92D2D5B7EF4EAB2A, 0x13CA27355FD36907, 753 | 0x3D9538F8C489F69F, 0x7FC99FC81C60EC16, 0x70E05CE274B2DDA7), 754 | u: GFp5::from_u64_reduce(0x4DD5F5E8AA5F8B6C, 0xCB929B26407014DC, 755 | 0x30F1214C5E00B284, 0xE0BB4238490F1785, 0xCD979B08C903ACCE), 756 | }, 757 | PointAffine { /* 10 */ 758 | x: GFp5::from_u64_reduce(0x5A7572DBFD2428DD, 0x8E7B9FAFCDFBED39, 759 | 0x62A60250AB74DDF1, 0xCB2E17DA9E0BE40F, 0x5BF6B50F358AD87F), 760 | u: GFp5::from_u64_reduce(0xC93002FECFBA924F, 0x20BDDD4FA0C3967D, 761 | 0x7AED8F011DA76FE7, 0xEDCAC7D07BFCD27D, 0x2926364889CE6286), 762 | }, 763 | PointAffine { /* 11 */ 764 | x: GFp5::from_u64_reduce(0x2CE1D8A8F8C936C9, 0x2CC208D366E2AC94, 765 | 0x6257C9ED583D473D, 0xB2B56863B06A3EDA, 0xA07329D48DB98857), 766 | u: GFp5::from_u64_reduce(0x27258DD37498FC8E, 0xE1E1DCDA4FF7613D, 767 | 0x755F5BF95A6AB233, 0x032742AC5790144F, 0x815E83A0EAAF5B08), 768 | }, 769 | PointAffine { /* 12 */ 770 | x: GFp5::from_u64_reduce(0x805FA56682DC7521, 0x1884FA5CFFB9706B, 771 | 0xBAD790C71F9FAA62, 0xB96900BBA4D86E22, 0x6F42C94A933A1D71), 772 | u: GFp5::from_u64_reduce(0x1F4BB93538BC8AD3, 0x84225EC5AEFE8090, 773 | 0x25C275174590A594, 0x9FEEA6C1E2A5F099, 0x19B8897561179347), 774 | }, 775 | PointAffine { /* 13 */ 776 | x: GFp5::from_u64_reduce(0x2955E592EB1EFE5A, 0x7A282B0798A33C54, 777 | 0x74BA891F8A832B8B, 0x7C6D6A557F1625FD, 0x019718B0DB5A0FB0), 778 | u: GFp5::from_u64_reduce(0x8E71B3C48DF30D11, 0x82B0B16111166A18, 779 | 0x5DA9B5BC441B0F5C, 0x52C893875D4FC42A, 0x4A94ADFBBCAB9093), 780 | }, 781 | PointAffine { /* 14 */ 782 | x: GFp5::from_u64_reduce(0x9C61B93A717AC45D, 0x2CBCA52F2D36F2B3, 783 | 0x4289E63D03B28D39, 0x82919F9E399E7D80, 0x8633A31B3179F23B), 784 | u: GFp5::from_u64_reduce(0x071F2E41D3CC3E52, 0x5CEF76B14B6F9011, 785 | 0xD12158D2928C481B, 0x76020B56EB95AFBE, 0x81F94088FBD89C53), 786 | }, 787 | PointAffine { /* 15 */ 788 | x: GFp5::from_u64_reduce(0x7A657D2637EBB4E5, 0x5D2ED7179B27BF7A, 789 | 0xA7C592D6837D8E67, 0xEDAEB459581E2034, 0xD89552C3F92ACA28), 790 | u: GFp5::from_u64_reduce(0xFFA6C86C78A12912, 0xCE9BD2556518E6AB, 791 | 0xE8E07519D7635B1A, 0x962EDA250276ACF0, 0x5C941DC8238F8EED), 792 | }, 793 | PointAffine { /* 16 */ 794 | x: GFp5::from_u64_reduce(0x33E179A5F02EA96F, 0x35486A003E0C192E, 795 | 0x8A17FDB3A32F31A9, 0xBCB9C322FE0B8DA8, 0x7162143807AA558C), 796 | u: GFp5::from_u64_reduce(0xF1848305499B4272, 0xAA60A5F08C4015F2, 797 | 0x01DFEB78A828F5BC, 0x5E8F88CD8A52F258, 0xDC499E276A245215), 798 | }, 799 | ]; 800 | -------------------------------------------------------------------------------- /rust/src/scalar.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, AddAssign, Neg, Sub, SubAssign, Mul, MulAssign}; 2 | 3 | /// A scalar (integer modulo the prime group order n). 4 | #[derive(Clone, Copy, Debug)] 5 | pub struct Scalar([u64; 5]); 6 | 7 | impl Scalar { 8 | 9 | // IMPLEMENTATION NOTES: 10 | // --------------------- 11 | // 12 | // Group order n is slightly below 2^319. We store values over five 13 | // 64-bit limbs. We use Montgomery multiplication to perform 14 | // computations; however, we keep the limbs in normal 15 | // (non-Montgomery) representation, so that operations that do not 16 | // require any multiplication of scalars, just encoding and 17 | // decoding, are fastest. 18 | 19 | pub const ZERO: Self = Self([0, 0, 0, 0, 0]); 20 | 21 | pub const ONE: Self = Self([1, 0, 0, 0, 0]); 22 | 23 | // The modulus itself, stored in a Scalar structure (which 24 | // contravenes to the rules of a Scalar; this constant MUST NOT leak 25 | // outside the API). 26 | const N: Self = Self([ 27 | 0xE80FD996948BFFE1, 28 | 0xE8885C39D724A09C, 29 | 0x7FFFFFE6CFB80639, 30 | 0x7FFFFFF100000016, 31 | 0x7FFFFFFD80000007, 32 | ]); 33 | 34 | // -1/N[0] mod 2^64 35 | const N0I: u64 = 0xD78BEF72057B7BDF; 36 | 37 | /* not used 38 | // 2^320 mod n. 39 | const R: Self = Self([ 40 | 0x2FE04CD2D6E8003E, 41 | 0x2EEF478C51B6BEC6, 42 | 0x00000032608FF38C, 43 | 0x0000001DFFFFFFD3, 44 | 0x00000004FFFFFFF1, 45 | ]); 46 | */ 47 | 48 | // 2^640 mod n. 49 | const R2: Self = Self([ 50 | 0xA01001DCE33DC739, 51 | 0x6C3228D33F62ACCF, 52 | 0xD1D796CC91CF8525, 53 | 0xAADFFF5D1574C1D8, 54 | 0x4ACA13B28CA251F5, 55 | ]); 56 | 57 | // 2^632 mod n. 58 | const T632: Self = Self([ 59 | 0x2B0266F317CA91B3, 60 | 0xEC1D26528E984773, 61 | 0x8651D7865E12DB94, 62 | 0xDA2ADFF5941574D0, 63 | 0x53CACA12110CA256, 64 | ]); 65 | 66 | // raw addition (no reduction) 67 | fn add_inner(self, a: Self) -> Self { 68 | let mut r = Self::ZERO; 69 | let mut c: u64 = 0; 70 | for i in 0..5 { 71 | let z = (self.0[i] as u128).wrapping_add(a.0[i] as u128) 72 | .wrapping_add(c as u128); 73 | r.0[i] = z as u64; 74 | c = (z >> 64) as u64; 75 | } 76 | // no extra carry, since inputs are supposed to fit on 319 bits. 77 | r 78 | } 79 | 80 | // raw subtraction (no reduction) 81 | // Final borrow is returned (0xFFFFFFFFFFFFFFFF if borrow, 0 otherwise). 82 | fn sub_inner(self, a: Self) -> (Self, u64) { 83 | let mut r = Self::ZERO; 84 | let mut c: u64 = 0; 85 | for i in 0..5 { 86 | let z = (self.0[i] as u128).wrapping_sub(a.0[i] as u128) 87 | .wrapping_sub(c as u128); 88 | r.0[i] = z as u64; 89 | c = ((z >> 64) as u64) & 1; 90 | } 91 | (r, c.wrapping_neg()) 92 | } 93 | 94 | /// If c == 0, return a0. 95 | /// If c == 0xFFFFFFFFFFFFFFFF, return a1. 96 | /// c MUST be equal to 0 or 0xFFFFFFFFFFFFFFFF. 97 | pub fn select(c: u64, a0: Self, a1: Self) -> Self { 98 | let mut r = Self::ZERO; 99 | for i in 0..5 { 100 | r.0[i] = a0.0[i] ^ (c & (a0.0[i] ^ a1.0[i])); 101 | } 102 | r 103 | } 104 | 105 | // Scalar addition. 106 | fn add(self, rhs: Self) -> Self { 107 | let r0 = self.add_inner(rhs); 108 | let (r1, c) = r0.sub_inner(Self::N); 109 | Self::select(c, r1, r0) 110 | } 111 | 112 | // Scalar subtraction. 113 | fn sub(self, rhs: Self) -> Self { 114 | let (r0, c) = self.sub_inner(rhs); 115 | let r1 = r0.add_inner(Self::N); 116 | Self::select(c, r0, r1) 117 | } 118 | 119 | // Scalar negation. 120 | fn neg(self) -> Self { 121 | Self::ZERO.sub(self) 122 | } 123 | 124 | // Montgomery multiplication. 125 | // Returns (self*rhs)/2^320 mod n. 126 | // 'self' MUST be less than n (the other operand can be up to 2^320-1). 127 | fn montymul(self, rhs: Self) -> Self { 128 | let mut r = Self::ZERO; 129 | for i in 0..5 { 130 | // Iteration i computes r <- (r + self*rhs_i + f*n)/2^64. 131 | // Factor f is at most 2^64-1 and set so that the division 132 | // is exact. 133 | // On input: 134 | // r <= 2^320 - 1 135 | // self <= n - 1 136 | // rhs_i <= 2^64 - 1 137 | // f <= 2^64 - 1 138 | // Therefore: 139 | // r + self*rhs_i + f*n <= 2^320-1 + (2^64 - 1) * (n - 1) 140 | // + (2^64 - 1) * n 141 | // < 2^384 142 | // Thus, the new r fits on 320 bits. 143 | let m = rhs.0[i]; 144 | let f = self.0[0].wrapping_mul(m).wrapping_add(r.0[0]) 145 | .wrapping_mul(Self::N0I); 146 | let mut cc1: u64 = 0; 147 | let mut cc2: u64 = 0; 148 | for j in 0..5 { 149 | let mut z = (self.0[j] as u128).wrapping_mul(m as u128) 150 | .wrapping_add(r.0[j] as u128).wrapping_add(cc1 as u128); 151 | cc1 = (z >> 64) as u64; 152 | z = (f as u128).wrapping_mul(Self::N.0[j] as u128) 153 | .wrapping_add((z as u64) as u128).wrapping_add(cc2 as u128); 154 | cc2 = (z >> 64) as u64; 155 | if j > 0 { 156 | r.0[j - 1] = z as u64; 157 | } 158 | } 159 | // No overflow here since the new r fits on 320 bits. 160 | r.0[4] = cc1.wrapping_add(cc2); 161 | } 162 | 163 | // We computed (self*rhs + ff*n) / 2^320, with: 164 | // self < n 165 | // rhs < 2^320 166 | // ff < 2^320 167 | // Thus, the value we obtained is lower than 2*n. Subtracting n 168 | // once (conditionally) is sufficient to achieve full reduction. 169 | let (r2, c) = r.sub_inner(Self::N); 170 | Self::select(c, r2, r) 171 | } 172 | 173 | fn mul(self, rhs: Self) -> Self { 174 | self.montymul(Self::R2).montymul(rhs) 175 | } 176 | 177 | /// Decode the provided byte slice into a scalar. The bytes are 178 | /// interpreted into an integer in little-endian unsigned convention. 179 | /// All slice bytes are read. Return value is (s, c): 180 | /// - If the decoded integer is lower than the group order, then that 181 | /// value is returned as s, and c == 0xFFFFFFFFFFFFFFFF. 182 | /// - Otherwise, s is set to Scalar::ZERO, and c == 0. 183 | pub fn decode(buf: &[u8]) -> (Self, u64) { 184 | let n = buf.len(); 185 | let mut r = Self::ZERO; 186 | let mut extra: u8 = 0; 187 | for i in 0..n { 188 | if i < 40 { 189 | r.0[i >> 3] |= (buf[i] as u64) 190 | .wrapping_shl(((i as u32) & 7) << 3); 191 | } else { 192 | extra |= buf[i]; 193 | } 194 | } 195 | 196 | // If input buffer is at most 39 bytes then the result is 197 | // necessarily in range; we can skip the reduction tests. 198 | if n <= 39 { 199 | return (r, 0xFFFFFFFFFFFFFFFF); 200 | } 201 | 202 | // Output is in the correct range if and only if extra == 0 and 203 | // the value is lower than n. 204 | let (_, mut c) = r.sub_inner(Self::N); 205 | c &= ((extra as u64).wrapping_add(0xFF) >> 8).wrapping_sub(1); 206 | for i in 0..5 { 207 | r.0[i] &= c; 208 | } 209 | (r, c) 210 | } 211 | 212 | /// Decode the provided byte slice into a scalar. The bytes are 213 | /// interpreted into an integer in little-endian unsigned convention. 214 | /// All slice bytes are read, and the value is REDUCED modulo n. This 215 | /// function never fails; it accepts arbitrary input values. 216 | pub fn decode_reduce(buf: &[u8]) -> Self { 217 | // We inject the value by chunks of 312 bits, in high-to-low 218 | // order. We multiply by 2^312 the intermediate result, which 219 | // is equivalent to performing a Montgomery multiplication 220 | // by 2^632 mod n. 221 | 222 | // If buffer length is at most 39 bytes, then the plain decode() 223 | // function works. 224 | let n = buf.len(); 225 | if n <= 39 { 226 | let (r, _) = Self::decode(buf); 227 | return r; 228 | } 229 | 230 | // We can now assume that we have at least 40 bytes of input. 231 | 232 | // Compute k as a multiple of 39 such that n-39 <= k < n. Since 233 | // n >= 40, this implies that k >= 1. We decode the top chunk 234 | // (which has length _at most_ 39 bytes) into acc. 235 | let mut k = ((n - 1) / 39) * 39; 236 | let (mut acc, _) = Self::decode(&buf[k..n]); 237 | while k > 0 { 238 | k -= 39; 239 | let (b, _) = Self::decode(&buf[k..k+39]); 240 | acc = acc.montymul(Self::T632).add(b); 241 | } 242 | acc 243 | } 244 | 245 | /// Encode this scalar over exactly 40 bytes. 246 | pub fn encode(self) -> [u8; 40] { 247 | let mut r = [0u8; 40]; 248 | for i in 0..5 { 249 | r[8*i..8*i+8].copy_from_slice(&self.0[i].to_le_bytes()); 250 | } 251 | r 252 | } 253 | 254 | // Recode a scalar into signed integers. For a window width of w 255 | // bits, returned integers are in the -(2^w-1) to +2^w range. The 256 | // provided slice is filled; if w*len(ss) >= 320, then the output 257 | // encodes the complete scalar value, and the top (last) signed 258 | // integer is nonnegative. 259 | // Window width MUST be between 2 and 10. 260 | pub(crate) fn recode_signed(self, ss: &mut [i32], w: i32) { 261 | Self::recode_signed_from_limbs(&self.0, ss, w); 262 | } 263 | 264 | fn recode_signed_from_limbs(limbs: &[u64], ss: &mut [i32], w: i32) { 265 | let mut acc: u64 = 0; 266 | let mut acc_len: i32 = 0; 267 | let mut j = 0; 268 | let mw = (1u32 << w) - 1; 269 | let hw = 1u32 << (w - 1); 270 | let mut cc: u32 = 0; 271 | for i in 0..ss.len() { 272 | // Get next w-bit chunk in bb. 273 | let mut bb: u32; 274 | if acc_len < w { 275 | if j < limbs.len() { 276 | let nl = limbs[j]; 277 | j += 1; 278 | bb = ((acc | (nl << acc_len)) as u32) & mw; 279 | acc = nl >> (w - acc_len); 280 | } else { 281 | bb = (acc as u32) & mw; 282 | acc = 0; 283 | } 284 | acc_len += 64 - w; 285 | } else { 286 | bb = (acc as u32) & mw; 287 | acc_len -= w; 288 | acc >>= w; 289 | } 290 | 291 | // If bb is greater than 2^(w-1), subtract 2^w and 292 | // propagate a carry. 293 | bb += cc; 294 | cc = hw.wrapping_sub(bb) >> 31; 295 | ss[i] = (bb as i32).wrapping_sub((cc << w) as i32); 296 | } 297 | } 298 | 299 | // Use Lagrange's algorithm to represent this scalar k as a 300 | // pair (v0, v1) such that k = v0/v1 mod n. 301 | // This function is NOT constant-time and should be used only on 302 | // a non-secret scalar (e.g. as part of signature verification). 303 | pub fn lagrange(self) -> (Signed161, Signed161) { 304 | // We use algorithm 4 from: https://eprint.iacr.org/2020/454 305 | 306 | // Nu <- n^2 307 | // Nv <- k^2 + 1 308 | // sp <- n*k 309 | let mut nu_buf = Signed640::from_nsquared(); 310 | let mut nv_buf = Signed640::from_mul_scalars(self, self); 311 | nv_buf.add1(); 312 | let (mut nu, mut nv) = (&mut nu_buf, &mut nv_buf); 313 | let mut sp = Signed640::from_mul_scalars(self, Self::N); 314 | 315 | // (u0, u1) <- (n, 0) 316 | // (v0, v1) <- (k, 1) 317 | let mut u0_buf = Signed161::from_scalar(Self::N); 318 | let mut u1_buf = Signed161::from_scalar(Self::ZERO); 319 | let mut v0_buf = Signed161::from_scalar(self); 320 | let mut v1_buf = Signed161::from_scalar(Self::ONE); 321 | let (mut u0, mut u1) = (&mut u0_buf, &mut u1_buf); 322 | let (mut v0, mut v1) = (&mut v0_buf, &mut v1_buf); 323 | 324 | // Main loop. 325 | loop { 326 | // if u is smaller than v, then swap them. 327 | if nu.lt_unsigned(nv) { 328 | let tn = nu; 329 | nu = nv; 330 | nv = tn; 331 | let (t0, t1) = (u0, u1); 332 | u0 = v0; 333 | u1 = v1; 334 | v0 = t0; 335 | v1 = t1; 336 | } 337 | 338 | // if len(Nv) <= 320, then we are finished. 339 | let vlen = nv.bitlength(); 340 | if vlen <= 320 { 341 | return (*v0, *v1); 342 | } 343 | 344 | // shift count s = max(0, len(p) - len(Nv)) 345 | let mut s = sp.bitlength() - vlen; 346 | if s < 0 { 347 | s = 0; 348 | } 349 | 350 | if sp.is_nonnegative() { 351 | u0.sub_shifted(v0, s); 352 | u1.sub_shifted(v1, s); 353 | nu.add_shifted(nv, s << 1); 354 | nu.sub_shifted(&sp, s + 1); 355 | sp.sub_shifted(nv, s); 356 | } else { 357 | u0.add_shifted(v0, s); 358 | u1.add_shifted(v1, s); 359 | nu.add_shifted(nv, s << 1); 360 | nu.add_shifted(&sp, s + 1); 361 | sp.add_shifted(nv, s); 362 | } 363 | } 364 | } 365 | 366 | /// Compare this scalar with zero. Returned value is 0xFFFFFFFFFFFFFFFF 367 | /// if this scalar is zero, or 0 otherwise. 368 | pub fn iszero(self) -> u64 { 369 | let x = self.0[0] | self.0[1] | self.0[2] | self.0[3] | self.0[4]; 370 | ((x | x.wrapping_neg()) >> 63).wrapping_sub(1) 371 | } 372 | 373 | /// Compare this scalar with another one. Returned value is 374 | /// 0xFFFFFFFFFFFFFFFF if they are equal, or 0 otherwise. 375 | /// Equality is defined modulo n. 376 | pub fn equals(self, rhs: Self) -> u64 { 377 | let x = (self.0[0] ^ rhs.0[0]) 378 | | (self.0[1] ^ rhs.0[1]) 379 | | (self.0[2] ^ rhs.0[2]) 380 | | (self.0[3] ^ rhs.0[3]) 381 | | (self.0[4] ^ rhs.0[4]); 382 | ((x | x.wrapping_neg()) >> 63).wrapping_sub(1) 383 | } 384 | } 385 | 386 | impl Add for Scalar { 387 | type Output = Scalar; 388 | 389 | fn add(self, other: Scalar) -> Scalar { 390 | Scalar::add(self, other) 391 | } 392 | } 393 | 394 | impl AddAssign for Scalar { 395 | fn add_assign(&mut self, other: Scalar) { 396 | *self = Scalar::add(*self, other) 397 | } 398 | } 399 | 400 | impl Sub for Scalar { 401 | type Output = Scalar; 402 | 403 | fn sub(self, other: Scalar) -> Scalar { 404 | Scalar::sub(self, other) 405 | } 406 | } 407 | 408 | impl SubAssign for Scalar { 409 | fn sub_assign(&mut self, other: Scalar) { 410 | *self = Scalar::sub(*self, other) 411 | } 412 | } 413 | 414 | impl Neg for Scalar { 415 | type Output = Scalar; 416 | 417 | fn neg(self) -> Scalar { 418 | Scalar::neg(self) 419 | } 420 | } 421 | 422 | impl Mul for Scalar { 423 | type Output = Scalar; 424 | 425 | fn mul(self, other: Scalar) -> Scalar { 426 | Scalar::mul(self, other) 427 | } 428 | } 429 | 430 | impl MulAssign for Scalar { 431 | fn mul_assign(&mut self, other: Scalar) { 432 | *self = Scalar::mul(*self, other) 433 | } 434 | } 435 | 436 | /// A custom 161-bit integer type; used for splitting a scalar into a 437 | /// fraction. Negative values use two's complement notation; the value 438 | /// is truncated to 161 bits (upper bits in the top limb are ignored). 439 | /// Elements are mutable containers. 440 | /// WARNING: everything in here is vartime; do not use on secret values. 441 | #[derive(Clone, Copy, Debug)] 442 | pub struct Signed161([u64; 3]); 443 | 444 | impl Signed161 { 445 | 446 | fn from_scalar(s: Scalar) -> Self { 447 | Self([s.0[0], s.0[1], s.0[2]]) 448 | } 449 | 450 | /// Convert that value into a scalar (integer modulo n). 451 | pub fn to_scalar_vartime(self) -> Scalar { 452 | let mut tmp = self.to_u192(); 453 | let neg = (tmp[2] >> 63) != 0; 454 | if neg { 455 | tmp[0] = (!tmp[0]).wrapping_add(1); 456 | let mut cc = tmp[0] == 0; 457 | tmp[1] = !tmp[1]; 458 | if cc { 459 | tmp[1] = tmp[1].wrapping_add(1); 460 | cc = tmp[1] == 0; 461 | } 462 | tmp[2] = !tmp[2]; 463 | if cc { 464 | tmp[2] = tmp[2].wrapping_add(1); 465 | } 466 | return -Scalar([tmp[0], tmp[1], tmp[2], 0, 0]); 467 | } else { 468 | return Scalar([tmp[0], tmp[1], tmp[2], 0, 0]); 469 | } 470 | } 471 | 472 | /// Export this value as a 192-bit integer (three 64-bit limbs, 473 | /// in little-endian order). 474 | pub fn to_u192(self) -> [u64; 3] { 475 | let mut x = self.0[2]; 476 | x &= 0x00000001FFFFFFFF; 477 | x |= (x >> 32).wrapping_neg() << 33; 478 | [self.0[0], self.0[1], x] 479 | } 480 | 481 | // Recode this integer into 33 signed digits for a 5-bit window. 482 | pub(crate) fn recode_signed_5(self) -> [i32; 33] { 483 | // We first sign-extend the value to 192 bits, then add 484 | // 2^160 to get a nonnegative value in the 0 to 2^161-1 485 | // range. We then recode that value; and finally we fix 486 | // the result by subtracting 1 from the top digit. 487 | let mut tmp = self.to_u192(); 488 | tmp[2] = tmp[2].wrapping_add(0x0000000100000000); 489 | let mut ss = [0i32; 33]; 490 | Scalar::recode_signed_from_limbs(&tmp, &mut ss, 5); 491 | ss[32] -= 1; 492 | ss 493 | } 494 | 495 | // Add v*2^s to this value. 496 | fn add_shifted(&mut self, v: &Signed161, s: i32) { 497 | if s == 0 { 498 | Self::add(self, &v.0[..]); 499 | } else if s < 64 { 500 | Self::add_shifted_small(self, &v.0[..], s); 501 | } else if s < 161 { 502 | Self::add_shifted_small(self, &v.0[((s >> 6) as usize)..], s & 63); 503 | } 504 | } 505 | 506 | fn add_shifted_small(&mut self, v: &[u64], s: i32) { 507 | let mut cc = 0u64; 508 | let j = 3 - v.len(); 509 | let mut vbits = 0u64; 510 | for i in j..3 { 511 | let vw = v[i - j]; 512 | let vws = vw.wrapping_shl(s as u32) | vbits; 513 | vbits = vw.wrapping_shr((64 - s) as u32); 514 | let z = (self.0[i] as u128) + (vws as u128) + (cc as u128); 515 | self.0[i] = z as u64; 516 | cc = (z >> 64) as u64; 517 | } 518 | } 519 | 520 | fn add(&mut self, v: &[u64]) { 521 | let mut cc = 0; 522 | let j = 3 - v.len(); 523 | for i in j..3 { 524 | let z = (self.0[i] as u128) + (v[i - j] as u128) + (cc as u128); 525 | self.0[i] = z as u64; 526 | cc = (z >> 64) as u64; 527 | } 528 | } 529 | 530 | // Subtract v*2^s from this value. 531 | fn sub_shifted(&mut self, v: &Signed161, s: i32) { 532 | if s == 0 { 533 | Self::sub(self, &v.0[..]); 534 | } else if s < 64 { 535 | Self::sub_shifted_small(self, &v.0[..], s); 536 | } else if s < 161 { 537 | Self::sub_shifted_small(self, &v.0[((s >> 6) as usize)..], s & 63); 538 | } 539 | } 540 | 541 | fn sub_shifted_small(&mut self, v: &[u64], s: i32) { 542 | let mut cc = 0u64; 543 | let j = 3 - v.len(); 544 | let mut vbits = 0u64; 545 | for i in j..3 { 546 | let vw = v[i - j]; 547 | let vws = vw.wrapping_shl(s as u32) | vbits; 548 | vbits = vw.wrapping_shr((64 - s) as u32); 549 | let z = (self.0[i] as u128).wrapping_sub(vws as u128) 550 | .wrapping_sub(cc as u128); 551 | self.0[i] = z as u64; 552 | cc = ((z >> 64) as u64) & 1; 553 | } 554 | } 555 | 556 | fn sub(&mut self, v: &[u64]) { 557 | let mut cc = 0; 558 | let j = 3 - v.len(); 559 | for i in j..3 { 560 | let z = (self.0[i] as u128).wrapping_sub(v[i - j] as u128) 561 | .wrapping_sub(cc as u128); 562 | self.0[i] = z as u64; 563 | cc = ((z >> 64) as u64) & 1; 564 | } 565 | } 566 | } 567 | 568 | // A custom 640-bit integer type (signed). 569 | // Elements are mutable containers. 570 | // WARNING: everything in here is vartime; do not use on secret values. 571 | #[derive(Clone, Copy, Debug)] 572 | struct Signed640([u64; 10]); 573 | 574 | impl Signed640 { 575 | 576 | // Obtain an instance containing n^2. 577 | fn from_nsquared() -> Self { 578 | Signed640([ 579 | 0x8E6B7A18061803C1, 580 | 0x0AD8BDEE1594E2CF, 581 | 0x17640E465F2598BC, 582 | 0x90465B4214B27B1C, 583 | 0xD308FECCB1878B88, 584 | 0x3CC55EB2EAC07502, 585 | 0x59F038FB784335CE, 586 | 0xBFFFFE954FB808EA, 587 | 0xBFFFFFCB80000099, 588 | 0x3FFFFFFD8000000D, 589 | ]) 590 | } 591 | 592 | // Obtain an instance containing a*b (both a and b are interpreted 593 | // as integers in the 0..n-1 range). 594 | fn from_mul_scalars(a: Scalar, b: Scalar) -> Self { 595 | let mut r = Signed640([0u64; 10]); 596 | for i in 0..5 { 597 | let aw = a.0[i]; 598 | let mut cc = 0u64; 599 | for j in 0..5 { 600 | let bw = b.0[j]; 601 | let z = ((aw as u128) * (bw as u128)) 602 | .wrapping_add(r.0[i + j] as u128) 603 | .wrapping_add(cc as u128); 604 | r.0[i + j] = z as u64; 605 | cc = (z >> 64) as u64; 606 | } 607 | r.0[i + 5] = cc; 608 | } 609 | r 610 | } 611 | 612 | // Add 1 to this instance. 613 | fn add1(&mut self) { 614 | for i in 0..10 { 615 | self.0[i] = self.0[i].wrapping_add(1); 616 | if self.0[i] != 0 { 617 | return; 618 | } 619 | } 620 | } 621 | 622 | fn is_nonnegative(&self) -> bool { 623 | (self.0[9] >> 63) == 0 624 | } 625 | 626 | fn lt_unsigned(&self, rhs: &Self) -> bool { 627 | for i in (0..10).rev() { 628 | let aw = self.0[i]; 629 | let bw = rhs.0[i]; 630 | if aw < bw { 631 | return true; 632 | } 633 | if aw > bw { 634 | return false; 635 | } 636 | } 637 | false 638 | } 639 | 640 | // Get the bit length of this value. The bit length is defined as the 641 | // minimal size of the binary representation in two's complement, 642 | // _excluding_ the sign bit (thus, -2^k has bit length k, whereas +2^k 643 | // has bit length k+1). 644 | fn bitlength(&self) -> i32 { 645 | let sm = (self.0[9] >> 63).wrapping_neg(); 646 | for i in (0..10).rev() { 647 | let w = self.0[i] ^ sm; 648 | if w != 0 { 649 | return ((i as i32) << 6) + Self::u64_bitlength(w); 650 | } 651 | } 652 | 0 653 | } 654 | 655 | fn u64_bitlength(w: u64) -> i32 { 656 | // We use here a portable algorithm; some architectures have 657 | // dedicated opcodes that could speed up this operation 658 | // greatly (e.g. lzcnt on recent x86). 659 | let mut x = w; 660 | let mut r = 0; 661 | if x > 0xFFFFFFFF { x >>= 32; r += 32; } 662 | if x > 0x0000FFFF { x >>= 16; r += 16; } 663 | if x > 0x000000FF { x >>= 8; r += 8; } 664 | if x > 0x0000000F { x >>= 4; r += 4; } 665 | if x > 0x00000003 { x >>= 2; r += 2; } 666 | r + (x as i32) - (((x + 1) >> 2) as i32) 667 | } 668 | 669 | // Add v*2^s to this instance. 670 | fn add_shifted(&mut self, v: &Signed640, s: i32) { 671 | if s == 0 { 672 | Self::add(self, &v.0[..]); 673 | } else if s < 64 { 674 | Self::add_shifted_small(self, &v.0[..], s); 675 | } else if s < 640 { 676 | Self::add_shifted_small(self, &v.0[((s >> 6) as usize)..], s & 63); 677 | } 678 | } 679 | 680 | fn add_shifted_small(&mut self, v: &[u64], s: i32) { 681 | let mut cc = 0u64; 682 | let j = 10 - v.len(); 683 | let mut vbits = 0u64; 684 | for i in j..10 { 685 | let vw = v[i - j]; 686 | let vws = vw.wrapping_shl(s as u32) | vbits; 687 | vbits = vw.wrapping_shr((64 - s) as u32); 688 | let z = (self.0[i] as u128) + (vws as u128) + (cc as u128); 689 | self.0[i] = z as u64; 690 | cc = (z >> 64) as u64; 691 | } 692 | } 693 | 694 | fn add(&mut self, v: &[u64]) { 695 | let mut cc = 0; 696 | let j = 10 - v.len(); 697 | for i in j..10 { 698 | let z = (self.0[i] as u128) + (v[i - j] as u128) + (cc as u128); 699 | self.0[i] = z as u64; 700 | cc = (z >> 64) as u64; 701 | } 702 | } 703 | 704 | // Subtract v*2^s from this instance. 705 | fn sub_shifted(&mut self, v: &Signed640, s: i32) { 706 | if s == 0 { 707 | Self::sub(self, &v.0[..]); 708 | } else if s < 64 { 709 | Self::sub_shifted_small(self, &v.0[..], s); 710 | } else { 711 | Self::sub_shifted_small(self, &v.0[((s >> 6) as usize)..], s & 63); 712 | } 713 | } 714 | 715 | fn sub_shifted_small(&mut self, v: &[u64], s: i32) { 716 | let mut cc = 0u64; 717 | let j = 10 - v.len(); 718 | let mut vbits = 0u64; 719 | for i in j..10 { 720 | let vw = v[i - j]; 721 | let vws = vw.wrapping_shl(s as u32) | vbits; 722 | vbits = vw.wrapping_shr((64 - s) as u32); 723 | let z = (self.0[i] as u128).wrapping_sub(vws as u128) 724 | .wrapping_sub(cc as u128); 725 | self.0[i] = z as u64; 726 | cc = ((z >> 64) as u64) & 1; 727 | } 728 | } 729 | 730 | fn sub(&mut self, v: &[u64]) { 731 | let mut cc = 0; 732 | let j = 10 - v.len(); 733 | for i in j..10 { 734 | let z = (self.0[i] as u128).wrapping_sub(v[i - j] as u128) 735 | .wrapping_sub(cc as u128); 736 | self.0[i] = z as u64; 737 | cc = ((z >> 64) as u64) & 1; 738 | } 739 | } 740 | } 741 | 742 | // ======================================================================== 743 | // Unit tests. 744 | 745 | #[cfg(test)] 746 | mod tests { 747 | use super::Scalar; 748 | use super::super::PRNG; 749 | 750 | #[test] 751 | fn scalar_ops() { 752 | let buf1: [u8; 50] = [ 753 | 0xE0, 0xFF, 0x8B, 0x94, 0x96, 0xD9, 0x0F, 0xE8, 754 | 0x9C, 0xA0, 0x24, 0xD7, 0x39, 0x5C, 0x88, 0xE8, 755 | 0x39, 0x06, 0xB8, 0xCF, 0xE6, 0xFF, 0xFF, 0x7F, 756 | 0x16, 0x00, 0x00, 0x00, 0xF1, 0xFF, 0xFF, 0x7F, 757 | 0x07, 0x00, 0x00, 0x80, 0xFD, 0xFF, 0xFF, 0x7F, 758 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 759 | 0x00, 0x00, 760 | ]; 761 | let buf2: [u8; 50] = [ 762 | 0xE1, 0xFF, 0x8B, 0x94, 0x96, 0xD9, 0x0F, 0xE8, 763 | 0x9C, 0xA0, 0x24, 0xD7, 0x39, 0x5C, 0x88, 0xE8, 764 | 0x39, 0x06, 0xB8, 0xCF, 0xE6, 0xFF, 0xFF, 0x7F, 765 | 0x16, 0x00, 0x00, 0x00, 0xF1, 0xFF, 0xFF, 0x7F, 766 | 0x07, 0x00, 0x00, 0x80, 0xFD, 0xFF, 0xFF, 0x7F, 767 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 768 | 0x00, 0x00, 769 | ]; 770 | let buf3: [u8; 50] = [ 771 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 772 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 773 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 774 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 775 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 776 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 777 | 0x00, 0x00, 778 | ]; 779 | for i in 0..51 { 780 | let (s1, c1) = Scalar::decode(&buf1[..i]); 781 | let (s2, c2) = Scalar::decode(&buf2[..i]); 782 | let (s3, c3) = Scalar::decode(&buf3[..i]); 783 | assert!(c1 == 0xFFFFFFFFFFFFFFFF); 784 | if i <= 40 { 785 | assert!(s1.encode()[..i] == buf1[..i]); 786 | } else { 787 | assert!(s1.encode()[..] == buf1[..40]); 788 | } 789 | if i <= 39 { 790 | assert!(c2 == 0xFFFFFFFFFFFFFFFF); 791 | assert!(s2.encode()[..i] == buf2[..i]); 792 | } else { 793 | assert!(c2 == 0); 794 | } 795 | if i <= 47 { 796 | assert!(c3 == 0xFFFFFFFFFFFFFFFF); 797 | if i <= 40 { 798 | assert!(s3.encode()[..i] == buf3[..i]); 799 | } else { 800 | assert!(s3.encode()[..] == buf3[..40]); 801 | } 802 | } else { 803 | assert!(c3 == 0); 804 | } 805 | } 806 | 807 | // buf4 = a randomly chosen 512-bit integer 808 | let buf4: [u8; 64] = [ 809 | 0xB5, 0xDD, 0x28, 0xB8, 0xD2, 0x9B, 0x6F, 0xF8, 810 | 0x15, 0x65, 0x3F, 0x89, 0xDB, 0x7B, 0xA9, 0xDE, 811 | 0x33, 0x7D, 0xA8, 0x27, 0x82, 0x26, 0xB4, 0xD6, 812 | 0x9E, 0x1F, 0xFA, 0x97, 0x3D, 0x9E, 0x01, 0x9C, 813 | 0x77, 0xC9, 0x63, 0x5C, 0xB8, 0x34, 0xD8, 0x1A, 814 | 0x4D, 0xCB, 0x03, 0x48, 0x62, 0xCD, 0xEE, 0xC9, 815 | 0x8E, 0xC8, 0xC9, 0xA7, 0xB3, 0x6E, 0xDA, 0xCE, 816 | 0x18, 0x75, 0x1B, 0xDD, 0x4F, 0x94, 0x67, 0xB5, 817 | ]; 818 | // buf5 = buf4 mod n 819 | let buf5: [u8; 40] = [ 820 | 0x89, 0x01, 0x7A, 0x52, 0xBD, 0xDF, 0x45, 0x60, 821 | 0xCE, 0x5B, 0xBA, 0xE5, 0x5D, 0x25, 0x96, 0x5A, 822 | 0x0A, 0x4F, 0x0A, 0x27, 0x1A, 0x7A, 0xE8, 0x1D, 823 | 0x7D, 0xBF, 0xE3, 0xE3, 0xFA, 0x5E, 0x17, 0xE0, 824 | 0x44, 0xD9, 0xA5, 0x37, 0x9B, 0xF8, 0x38, 0x74, 825 | ]; 826 | let s4 = Scalar::decode_reduce(&buf4[..]); 827 | assert!(s4.encode() == buf5); 828 | let (s5, c5) = Scalar::decode(&buf5[..]); 829 | assert!(c5 == 0xFFFFFFFFFFFFFFFF); 830 | assert!(s5.encode() == buf5); 831 | 832 | // buf6 = (buf4^256) mod n 833 | let buf6: [u8; 40] = [ 834 | 0x27, 0x7E, 0x2C, 0xAB, 0x6D, 0xAD, 0x8D, 0xA0, 835 | 0x15, 0x44, 0x02, 0x0F, 0xFA, 0xD5, 0x4F, 0x15, 836 | 0xBF, 0x6D, 0x1D, 0x76, 0x22, 0x73, 0xCD, 0xDA, 837 | 0x23, 0xFE, 0x5A, 0xED, 0xCA, 0x75, 0xD7, 0x04, 838 | 0x05, 0x66, 0x87, 0x3D, 0x37, 0x5B, 0x24, 0x13, 839 | ]; 840 | let mut s6 = s4; 841 | for _ in 0..8 { 842 | s6 *= s6; 843 | } 844 | assert!(s6.encode() == buf6); 845 | 846 | // buf6 recoded in signed integers, w = 4 847 | let ref4: [i32; 80] = [ 848 | 7, 2, -2, 8, -4, 3, -5, -5, -2, 7, -3, -5, -2, -7, 1, -6, 849 | 6, 1, 4, 4, 2, 0, -1, 1, -6, 0, 6, -3, 0, 5, 5, 1, 850 | -1, -4, -2, 7, -3, 2, 6, 7, 2, 2, 3, 7, -3, -3, -5, -2, 851 | 4, 2, -2, 0, -5, 6, -3, -1, -5, -3, 6, 7, 7, -3, 5, 0, 852 | 5, 0, 6, 6, 7, 8, -3, 4, 7, 3, -5, 6, 4, 2, 3, 1, 853 | ]; 854 | // buf6 recoded in signed integers, w = 5 855 | let ref5: [i32; 64] = [ 856 | 7, -15, 0, -7, -13, -10, -9, 14, 13, 13, 3, 1, 857 | -6, 11, 16, 8, 2, -8, 4, -12, 0, 11, -1, 10, 858 | -11, -7, 16, -5, -9, 15, -8, 15, 2, -7, -3, -5, 859 | 13, 13, 15, 4, -2, -8, -9, -5, 15, 5, -9, 15, 860 | -9, 7, 1, 10, 0, -13, -2, -15, -2, -6, 14, -10, 861 | 6, -14, 13, 2, 862 | ]; 863 | 864 | let mut ss4 = [0i32; 80]; 865 | s6.recode_signed(&mut ss4[..], 4); 866 | assert!(ss4 == ref4); 867 | let mut ss5 = [0i32; 64]; 868 | s6.recode_signed(&mut ss5[..], 5); 869 | assert!(ss5 == ref5); 870 | } 871 | 872 | #[test] 873 | fn lagrange() { 874 | let mut prng = PRNG(0); 875 | for _ in 0..100 { 876 | let mut sbuf = [0u8; 48]; 877 | prng.next(&mut sbuf); 878 | let s = Scalar::decode_reduce(&mut sbuf); 879 | let (v0, v1) = s.lagrange(); 880 | let c0 = v0.to_scalar_vartime(); 881 | let c1 = v1.to_scalar_vartime(); 882 | assert!((c1 * s - c0).iszero() == 0xFFFFFFFFFFFFFFFF); 883 | } 884 | } 885 | } 886 | --------------------------------------------------------------------------------