12 | * Reviewed/commented by Bloody Rookie (nemproject@gmx.de) 13 | */ 14 | public final class Ed25519LittleEndianEncoding { 15 | /** 16 | * Encodes a given field element in its 32 byte representation. This is done in two steps: 17 | *
21 | * The idea for the modulo $p$ reduction algorithm is as follows: 22 | *
23 | *29 | * Then $q = [2^{-255} * (h + 19 * 2^{-25} * h_9 + 1/2)]$ where $[x] = floor(x)$. 30 | *
31 | *33 | * We begin with some very raw estimation for the bounds of some expressions: 34 | *
35 | * $$ 36 | * \begin{equation} 37 | * |h| \lt 2^{230} * 2^{30} = 2^{260} \Rightarrow |r + q * p| \lt 2^{260} \Rightarrow |q| \lt 2^{10}. \\ 38 | * \Rightarrow -1/4 \le a := 19^2 * 2^{-255} * q \lt 1/4. \\ 39 | * |h - 2^{230} * h_9| = |h_0 + \dots + 2^{204} * h_8| \lt 2^{204} * 2^{30} = 2^{234}. \\ 40 | * \Rightarrow -1/4 \le b := 19 * 2^{-255} * (h - 2^{230} * h_9) \lt 1/4 41 | * \end{equation} 42 | * $$ 43 | *
44 | * Therefore $0 \lt 1/2 - a - b \lt 1$. 45 | *
46 | * Set $x := r + 19 * 2^{-255} * r + 1/2 - a - b$. Then: 47 | *
48 | * $$ 49 | * 0 \le x \lt 255 - 20 + 19 + 1 = 2^{255} \\ 50 | * \Rightarrow 0 \le 2^{-255} * x \lt 1. 51 | * $$ 52 | *
53 | * Since $q$ is an integer we have 54 | *
55 | * $$ 56 | * [q + 2^{-255} * x] = q \quad (1) 57 | * $$ 58 | *
59 | * Have a closer look at $x$: 60 | *
61 | * $$ 62 | * \begin{align} 63 | * x &= h - q * (2^{255} - 19) + 19 * 2^{-255} * (h - q * (2^{255} - 19)) + 1/2 - 19^2 * 2^{-255} * q - 19 * 2^{-255} * (h - 2^{230} * h_9) \\ 64 | * &= h - q * 2^{255} + 19 * q + 19 * 2^{-255} * h - 19 * q + 19^2 * 2^{-255} * q + 1/2 - 19^2 * 2^{-255} * q - 19 * 2^{-255} * h + 19 * 2^{-25} * h_9 \\ 65 | * &= h + 19 * 2^{-25} * h_9 + 1/2 - q^{255}. 66 | * \end{align} 67 | * $$ 68 | *
69 | * Inserting the expression for $x$ into $(1)$ we get the desired expression for $q$. 70 | */ 71 | public static byte[] encode(FieldElement x) { 72 | int[] h = ((Ed25519FieldElement)x).t; 73 | int h0 = h[0]; 74 | int h1 = h[1]; 75 | int h2 = h[2]; 76 | int h3 = h[3]; 77 | int h4 = h[4]; 78 | int h5 = h[5]; 79 | int h6 = h[6]; 80 | int h7 = h[7]; 81 | int h8 = h[8]; 82 | int h9 = h[9]; 83 | int q; 84 | int carry0; 85 | int carry1; 86 | int carry2; 87 | int carry3; 88 | int carry4; 89 | int carry5; 90 | int carry6; 91 | int carry7; 92 | int carry8; 93 | int carry9; 94 | 95 | // Step 1: 96 | // Calculate q 97 | q = (19 * h9 + (1 << 24)) >> 25; 98 | q = (h0 + q) >> 26; 99 | q = (h1 + q) >> 25; 100 | q = (h2 + q) >> 26; 101 | q = (h3 + q) >> 25; 102 | q = (h4 + q) >> 26; 103 | q = (h5 + q) >> 25; 104 | q = (h6 + q) >> 26; 105 | q = (h7 + q) >> 25; 106 | q = (h8 + q) >> 26; 107 | q = (h9 + q) >> 25; 108 | 109 | // r = h - q * p = h - 2^255 * q + 19 * q 110 | // First add 19 * q then discard the bit 255 111 | h0 += 19 * q; 112 | 113 | carry0 = h0 >> 26; h1 += carry0; h0 -= carry0 << 26; 114 | carry1 = h1 >> 25; h2 += carry1; h1 -= carry1 << 25; 115 | carry2 = h2 >> 26; h3 += carry2; h2 -= carry2 << 26; 116 | carry3 = h3 >> 25; h4 += carry3; h3 -= carry3 << 25; 117 | carry4 = h4 >> 26; h5 += carry4; h4 -= carry4 << 26; 118 | carry5 = h5 >> 25; h6 += carry5; h5 -= carry5 << 25; 119 | carry6 = h6 >> 26; h7 += carry6; h6 -= carry6 << 26; 120 | carry7 = h7 >> 25; h8 += carry7; h7 -= carry7 << 25; 121 | carry8 = h8 >> 26; h9 += carry8; h8 -= carry8 << 26; 122 | carry9 = h9 >> 25; h9 -= carry9 << 25; 123 | 124 | // Step 2 (straight forward conversion): 125 | byte[] s = new byte[32]; 126 | s[0] = (byte) h0; 127 | s[1] = (byte) (h0 >> 8); 128 | s[2] = (byte) (h0 >> 16); 129 | s[3] = (byte) ((h0 >> 24) | (h1 << 2)); 130 | s[4] = (byte) (h1 >> 6); 131 | s[5] = (byte) (h1 >> 14); 132 | s[6] = (byte) ((h1 >> 22) | (h2 << 3)); 133 | s[7] = (byte) (h2 >> 5); 134 | s[8] = (byte) (h2 >> 13); 135 | s[9] = (byte) ((h2 >> 21) | (h3 << 5)); 136 | s[10] = (byte) (h3 >> 3); 137 | s[11] = (byte) (h3 >> 11); 138 | s[12] = (byte) ((h3 >> 19) | (h4 << 6)); 139 | s[13] = (byte) (h4 >> 2); 140 | s[14] = (byte) (h4 >> 10); 141 | s[15] = (byte) (h4 >> 18); 142 | s[16] = (byte) h5; 143 | s[17] = (byte) (h5 >> 8); 144 | s[18] = (byte) (h5 >> 16); 145 | s[19] = (byte) ((h5 >> 24) | (h6 << 1)); 146 | s[20] = (byte) (h6 >> 7); 147 | s[21] = (byte) (h6 >> 15); 148 | s[22] = (byte) ((h6 >> 23) | (h7 << 3)); 149 | s[23] = (byte) (h7 >> 5); 150 | s[24] = (byte) (h7 >> 13); 151 | s[25] = (byte) ((h7 >> 21) | (h8 << 4)); 152 | s[26] = (byte) (h8 >> 4); 153 | s[27] = (byte) (h8 >> 12); 154 | s[28] = (byte) ((h8 >> 20) | (h9 << 6)); 155 | s[29] = (byte) (h9 >> 2); 156 | s[30] = (byte) (h9 >> 10); 157 | s[31] = (byte) (h9 >> 18); 158 | return s; 159 | } 160 | 161 | static int load_3(byte[] in, int offset) { 162 | int result = in[offset++] & 0xff; 163 | result |= (in[offset++] & 0xff) << 8; 164 | result |= (in[offset] & 0xff) << 16; 165 | return result; 166 | } 167 | 168 | static long load_4(byte[] in, int offset) { 169 | int result = in[offset++] & 0xff; 170 | result |= (in[offset++] & 0xff) << 8; 171 | result |= (in[offset++] & 0xff) << 16; 172 | result |= in[offset] << 24; 173 | return ((long)result) & 0xffffffffL; 174 | } 175 | 176 | /** 177 | * Decodes a given field element in its 10 byte $2^{25.5}$ representation. 178 | * 179 | * @param in The 32 byte representation. 180 | * @return The field element in its $2^{25.5}$ bit representation. 181 | */ 182 | public static FieldElement decode(Ed25519Field f, byte[] in) { 183 | long h0 = load_4(in, 0); 184 | long h1 = load_3(in, 4) << 6; 185 | long h2 = load_3(in, 7) << 5; 186 | long h3 = load_3(in, 10) << 3; 187 | long h4 = load_3(in, 13) << 2; 188 | long h5 = load_4(in, 16); 189 | long h6 = load_3(in, 20) << 7; 190 | long h7 = load_3(in, 23) << 5; 191 | long h8 = load_3(in, 26) << 4; 192 | long h9 = (load_3(in, 29) & 0x7FFFFF) << 2; 193 | long carry0; 194 | long carry1; 195 | long carry2; 196 | long carry3; 197 | long carry4; 198 | long carry5; 199 | long carry6; 200 | long carry7; 201 | long carry8; 202 | long carry9; 203 | 204 | // Remember: 2^255 congruent 19 modulo p 205 | carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25; 206 | carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25; 207 | carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25; 208 | carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25; 209 | carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25; 210 | 211 | carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26; 212 | carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26; 213 | carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26; 214 | carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26; 215 | carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26; 216 | 217 | int[] h = new int[10]; 218 | h[0] = (int) h0; 219 | h[1] = (int) h1; 220 | h[2] = (int) h2; 221 | h[3] = (int) h3; 222 | h[4] = (int) h4; 223 | h[5] = (int) h5; 224 | h[6] = (int) h6; 225 | h[7] = (int) h7; 226 | h[8] = (int) h8; 227 | h[9] = (int) h9; 228 | return new Ed25519FieldElement(f, h); 229 | } 230 | 231 | /** 232 | * Is the FieldElement negative in this encoding? 233 | *
234 | * Return true if $x$ is in $\{1,3,5,\dots,q-2\}$
235 | * Return false if $x$ is in $\{0,2,4,\dots,q-1\}$
236 | *
237 | * Preconditions: 238 | *
37 | * This is in-place operation.
38 | *
39 | * @param s 256^0*s[0] + 256^1*s[1] + ... + 256^63*s[63]
40 | */
41 | public static void reduce(final byte[] s) {
42 | if (s.length != 64) {
43 | throw new IllegalArgumentException("The input must be 64 bytes.");
44 | }
45 |
46 | long s0 = 2097151 & load_3(s, 0);
47 | long s1 = 2097151 & (load_4(s, 2) >> 5);
48 | long s2 = 2097151 & (load_3(s, 5) >> 2);
49 | long s3 = 2097151 & (load_4(s, 7) >> 7);
50 | long s4 = 2097151 & (load_4(s, 10) >> 4);
51 | long s5 = 2097151 & (load_3(s, 13) >> 1);
52 | long s6 = 2097151 & (load_4(s, 15) >> 6);
53 | long s7 = 2097151 & (load_3(s, 18) >> 3);
54 | long s8 = 2097151 & load_3(s, 21);
55 | long s9 = 2097151 & (load_4(s, 23) >> 5);
56 | long s10 = 2097151 & (load_3(s, 26) >> 2);
57 | long s11 = 2097151 & (load_4(s, 28) >> 7);
58 | long s12 = 2097151 & (load_4(s, 31) >> 4);
59 | long s13 = 2097151 & (load_3(s, 34) >> 1);
60 | long s14 = 2097151 & (load_4(s, 36) >> 6);
61 | long s15 = 2097151 & (load_3(s, 39) >> 3);
62 | long s16 = 2097151 & load_3(s, 42);
63 | long s17 = 2097151 & (load_4(s, 44) >> 5);
64 | long s18 = 2097151 & (load_3(s, 47) >> 2);
65 | long s19 = 2097151 & (load_4(s, 49) >> 7);
66 | long s20 = 2097151 & (load_4(s, 52) >> 4);
67 | long s21 = 2097151 & (load_3(s, 55) >> 1);
68 | long s22 = 2097151 & (load_4(s, 57) >> 6);
69 | long s23 = (load_4(s, 60) >> 3);
70 | long carry0;
71 | long carry1;
72 | long carry2;
73 | long carry3;
74 | long carry4;
75 | long carry5;
76 | long carry6;
77 | long carry7;
78 | long carry8;
79 | long carry9;
80 | long carry10;
81 | long carry11;
82 | long carry12;
83 | long carry13;
84 | long carry14;
85 | long carry15;
86 | long carry16;
87 |
88 | s11 += s23 * 666643;
89 | s12 += s23 * 470296;
90 | s13 += s23 * 654183;
91 | s14 -= s23 * 997805;
92 | s15 += s23 * 136657;
93 | s16 -= s23 * 683901;
94 | s23 = 0;
95 |
96 | s10 += s22 * 666643;
97 | s11 += s22 * 470296;
98 | s12 += s22 * 654183;
99 | s13 -= s22 * 997805;
100 | s14 += s22 * 136657;
101 | s15 -= s22 * 683901;
102 | s22 = 0;
103 |
104 | s9 += s21 * 666643;
105 | s10 += s21 * 470296;
106 | s11 += s21 * 654183;
107 | s12 -= s21 * 997805;
108 | s13 += s21 * 136657;
109 | s14 -= s21 * 683901;
110 | s21 = 0;
111 |
112 | s8 += s20 * 666643;
113 | s9 += s20 * 470296;
114 | s10 += s20 * 654183;
115 | s11 -= s20 * 997805;
116 | s12 += s20 * 136657;
117 | s13 -= s20 * 683901;
118 | s20 = 0;
119 |
120 | s7 += s19 * 666643;
121 | s8 += s19 * 470296;
122 | s9 += s19 * 654183;
123 | s10 -= s19 * 997805;
124 | s11 += s19 * 136657;
125 | s12 -= s19 * 683901;
126 | s19 = 0;
127 |
128 | s6 += s18 * 666643;
129 | s7 += s18 * 470296;
130 | s8 += s18 * 654183;
131 | s9 -= s18 * 997805;
132 | s10 += s18 * 136657;
133 | s11 -= s18 * 683901;
134 | s18 = 0;
135 |
136 | carry6 = (s6 + (1 << 20)) >> 21;
137 | s7 += carry6;
138 | s6 -= int64_lshift21(carry6);
139 | carry8 = (s8 + (1 << 20)) >> 21;
140 | s9 += carry8;
141 | s8 -= int64_lshift21(carry8);
142 | carry10 = (s10 + (1 << 20)) >> 21;
143 | s11 += carry10;
144 | s10 -= int64_lshift21(carry10);
145 | carry12 = (s12 + (1 << 20)) >> 21;
146 | s13 += carry12;
147 | s12 -= int64_lshift21(carry12);
148 | carry14 = (s14 + (1 << 20)) >> 21;
149 | s15 += carry14;
150 | s14 -= int64_lshift21(carry14);
151 | carry16 = (s16 + (1 << 20)) >> 21;
152 | s17 += carry16;
153 | s16 -= int64_lshift21(carry16);
154 |
155 | carry7 = (s7 + (1 << 20)) >> 21;
156 | s8 += carry7;
157 | s7 -= int64_lshift21(carry7);
158 | carry9 = (s9 + (1 << 20)) >> 21;
159 | s10 += carry9;
160 | s9 -= int64_lshift21(carry9);
161 | carry11 = (s11 + (1 << 20)) >> 21;
162 | s12 += carry11;
163 | s11 -= int64_lshift21(carry11);
164 | carry13 = (s13 + (1 << 20)) >> 21;
165 | s14 += carry13;
166 | s13 -= int64_lshift21(carry13);
167 | carry15 = (s15 + (1 << 20)) >> 21;
168 | s16 += carry15;
169 | s15 -= int64_lshift21(carry15);
170 |
171 | s5 += s17 * 666643;
172 | s6 += s17 * 470296;
173 | s7 += s17 * 654183;
174 | s8 -= s17 * 997805;
175 | s9 += s17 * 136657;
176 | s10 -= s17 * 683901;
177 | s17 = 0;
178 |
179 | s4 += s16 * 666643;
180 | s5 += s16 * 470296;
181 | s6 += s16 * 654183;
182 | s7 -= s16 * 997805;
183 | s8 += s16 * 136657;
184 | s9 -= s16 * 683901;
185 | s16 = 0;
186 |
187 | s3 += s15 * 666643;
188 | s4 += s15 * 470296;
189 | s5 += s15 * 654183;
190 | s6 -= s15 * 997805;
191 | s7 += s15 * 136657;
192 | s8 -= s15 * 683901;
193 | s15 = 0;
194 |
195 | s2 += s14 * 666643;
196 | s3 += s14 * 470296;
197 | s4 += s14 * 654183;
198 | s5 -= s14 * 997805;
199 | s6 += s14 * 136657;
200 | s7 -= s14 * 683901;
201 | s14 = 0;
202 |
203 | s1 += s13 * 666643;
204 | s2 += s13 * 470296;
205 | s3 += s13 * 654183;
206 | s4 -= s13 * 997805;
207 | s5 += s13 * 136657;
208 | s6 -= s13 * 683901;
209 | s13 = 0;
210 |
211 | s0 += s12 * 666643;
212 | s1 += s12 * 470296;
213 | s2 += s12 * 654183;
214 | s3 -= s12 * 997805;
215 | s4 += s12 * 136657;
216 | s5 -= s12 * 683901;
217 | s12 = 0;
218 |
219 | carry0 = (s0 + (1 << 20)) >> 21;
220 | s1 += carry0;
221 | s0 -= int64_lshift21(carry0);
222 | carry2 = (s2 + (1 << 20)) >> 21;
223 | s3 += carry2;
224 | s2 -= int64_lshift21(carry2);
225 | carry4 = (s4 + (1 << 20)) >> 21;
226 | s5 += carry4;
227 | s4 -= int64_lshift21(carry4);
228 | carry6 = (s6 + (1 << 20)) >> 21;
229 | s7 += carry6;
230 | s6 -= int64_lshift21(carry6);
231 | carry8 = (s8 + (1 << 20)) >> 21;
232 | s9 += carry8;
233 | s8 -= int64_lshift21(carry8);
234 | carry10 = (s10 + (1 << 20)) >> 21;
235 | s11 += carry10;
236 | s10 -= int64_lshift21(carry10);
237 |
238 | carry1 = (s1 + (1 << 20)) >> 21;
239 | s2 += carry1;
240 | s1 -= int64_lshift21(carry1);
241 | carry3 = (s3 + (1 << 20)) >> 21;
242 | s4 += carry3;
243 | s3 -= int64_lshift21(carry3);
244 | carry5 = (s5 + (1 << 20)) >> 21;
245 | s6 += carry5;
246 | s5 -= int64_lshift21(carry5);
247 | carry7 = (s7 + (1 << 20)) >> 21;
248 | s8 += carry7;
249 | s7 -= int64_lshift21(carry7);
250 | carry9 = (s9 + (1 << 20)) >> 21;
251 | s10 += carry9;
252 | s9 -= int64_lshift21(carry9);
253 | carry11 = (s11 + (1 << 20)) >> 21;
254 | s12 += carry11;
255 | s11 -= int64_lshift21(carry11);
256 |
257 | s0 += s12 * 666643;
258 | s1 += s12 * 470296;
259 | s2 += s12 * 654183;
260 | s3 -= s12 * 997805;
261 | s4 += s12 * 136657;
262 | s5 -= s12 * 683901;
263 | s12 = 0;
264 |
265 | carry0 = s0 >> 21;
266 | s1 += carry0;
267 | s0 -= int64_lshift21(carry0);
268 | carry1 = s1 >> 21;
269 | s2 += carry1;
270 | s1 -= int64_lshift21(carry1);
271 | carry2 = s2 >> 21;
272 | s3 += carry2;
273 | s2 -= int64_lshift21(carry2);
274 | carry3 = s3 >> 21;
275 | s4 += carry3;
276 | s3 -= int64_lshift21(carry3);
277 | carry4 = s4 >> 21;
278 | s5 += carry4;
279 | s4 -= int64_lshift21(carry4);
280 | carry5 = s5 >> 21;
281 | s6 += carry5;
282 | s5 -= int64_lshift21(carry5);
283 | carry6 = s6 >> 21;
284 | s7 += carry6;
285 | s6 -= int64_lshift21(carry6);
286 | carry7 = s7 >> 21;
287 | s8 += carry7;
288 | s7 -= int64_lshift21(carry7);
289 | carry8 = s8 >> 21;
290 | s9 += carry8;
291 | s8 -= int64_lshift21(carry8);
292 | carry9 = s9 >> 21;
293 | s10 += carry9;
294 | s9 -= int64_lshift21(carry9);
295 | carry10 = s10 >> 21;
296 | s11 += carry10;
297 | s10 -= int64_lshift21(carry10);
298 | carry11 = s11 >> 21;
299 | s12 += carry11;
300 | s11 -= int64_lshift21(carry11);
301 |
302 | s0 += s12 * 666643;
303 | s1 += s12 * 470296;
304 | s2 += s12 * 654183;
305 | s3 -= s12 * 997805;
306 | s4 += s12 * 136657;
307 | s5 -= s12 * 683901;
308 | s12 = 0;
309 |
310 | carry0 = s0 >> 21;
311 | s1 += carry0;
312 | s0 -= int64_lshift21(carry0);
313 | carry1 = s1 >> 21;
314 | s2 += carry1;
315 | s1 -= int64_lshift21(carry1);
316 | carry2 = s2 >> 21;
317 | s3 += carry2;
318 | s2 -= int64_lshift21(carry2);
319 | carry3 = s3 >> 21;
320 | s4 += carry3;
321 | s3 -= int64_lshift21(carry3);
322 | carry4 = s4 >> 21;
323 | s5 += carry4;
324 | s4 -= int64_lshift21(carry4);
325 | carry5 = s5 >> 21;
326 | s6 += carry5;
327 | s5 -= int64_lshift21(carry5);
328 | carry6 = s6 >> 21;
329 | s7 += carry6;
330 | s6 -= int64_lshift21(carry6);
331 | carry7 = s7 >> 21;
332 | s8 += carry7;
333 | s7 -= int64_lshift21(carry7);
334 | carry8 = s8 >> 21;
335 | s9 += carry8;
336 | s8 -= int64_lshift21(carry8);
337 | carry9 = s9 >> 21;
338 | s10 += carry9;
339 | s9 -= int64_lshift21(carry9);
340 | carry10 = s10 >> 21;
341 | s11 += carry10;
342 | s10 -= int64_lshift21(carry10);
343 |
344 | s[0] = (byte) (s0 >> 0);
345 | s[1] = (byte) (s0 >> 8);
346 | s[2] = (byte) ((s0 >> 16) | (s1 << 5));
347 | s[3] = (byte) (s1 >> 3);
348 | s[4] = (byte) (s1 >> 11);
349 | s[5] = (byte) ((s1 >> 19) | (s2 << 2));
350 | s[6] = (byte) (s2 >> 6);
351 | s[7] = (byte) ((s2 >> 14) | (s3 << 7));
352 | s[8] = (byte) (s3 >> 1);
353 | s[9] = (byte) (s3 >> 9);
354 | s[10] = (byte) ((s3 >> 17) | (s4 << 4));
355 | s[11] = (byte) (s4 >> 4);
356 | s[12] = (byte) (s4 >> 12);
357 | s[13] = (byte) ((s4 >> 20) | (s5 << 1));
358 | s[14] = (byte) (s5 >> 7);
359 | s[15] = (byte) ((s5 >> 15) | (s6 << 6));
360 | s[16] = (byte) (s6 >> 2);
361 | s[17] = (byte) (s6 >> 10);
362 | s[18] = (byte) ((s6 >> 18) | (s7 << 3));
363 | s[19] = (byte) (s7 >> 5);
364 | s[20] = (byte) (s7 >> 13);
365 | s[21] = (byte) (s8 >> 0);
366 | s[22] = (byte) (s8 >> 8);
367 | s[23] = (byte) ((s8 >> 16) | (s9 << 5));
368 | s[24] = (byte) (s9 >> 3);
369 | s[25] = (byte) (s9 >> 11);
370 | s[26] = (byte) ((s9 >> 19) | (s10 << 2));
371 | s[27] = (byte) (s10 >> 6);
372 | s[28] = (byte) ((s10 >> 14) | (s11 << 7));
373 | s[29] = (byte) (s11 >> 1);
374 | s[30] = (byte) (s11 >> 9);
375 | s[31] = (byte) (s11 >> 17);
376 | }
377 | }
378 |
--------------------------------------------------------------------------------
/java/src/test/java/io/github/muntashirakon/crypto/spake2/Spake25519Test.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 Muntashir Al-Islam
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 |
7 | package io.github.muntashirakon.crypto.spake2;
8 |
9 | import static org.junit.Assert.assertArrayEquals;
10 | import static org.junit.Assert.assertEquals;
11 | import static org.junit.Assert.assertFalse;
12 | import static org.junit.Assert.assertTrue;
13 |
14 | import org.junit.Test;
15 |
16 | import java.math.BigInteger;
17 | import java.nio.charset.StandardCharsets;
18 | import java.util.Arrays;
19 |
20 | import io.github.muntashirakon.crypto.ed25519.Curve;
21 | import io.github.muntashirakon.crypto.ed25519.Ed25519;
22 | import io.github.muntashirakon.crypto.ed25519.Ed25519CurveParameterSpec;
23 | import io.github.muntashirakon.crypto.ed25519.Ed25519Field;
24 | import io.github.muntashirakon.crypto.ed25519.FieldElement;
25 | import io.github.muntashirakon.crypto.ed25519.GroupElement;
26 | import io.github.muntashirakon.crypto.ed25519.Utils;
27 | import io.github.muntashirakon.crypto.x25519.x25519Scalar;
28 |
29 | public class Spake25519Test {
30 | private static final byte[] B_EIGHT = Utils.hexToBytes("0800000000000000000000000000000000000000000000000000000000000000");
31 |
32 | // Based on http://ed25519.cr.yp.to/python/ed25519.py
33 | private static GroupElement ed25519Edwards(GroupElement P, GroupElement Q) {
34 | Curve curve = P.getCurve();
35 | Ed25519Field field = curve.getField();
36 | FieldElement x1 = P.getX();
37 | FieldElement y1 = P.getY();
38 | FieldElement x2 = Q.getX();
39 | FieldElement y2 = Q.getY();
40 | FieldElement dx1x2y1y2 = curve.getD().multiply(x1).multiply(x2).multiply(y1).multiply(y2);
41 | FieldElement x3 = x1.multiply(y2).add(x2.multiply(y1)).multiply(dx1x2y1y2.addOne().invert()); // (x1*y2+x2*y1) * inv(1+d*x1*x2*y1*y2)
42 | FieldElement y3 = y1.multiply(y2).add(x1.multiply(x2)).multiply(field.ONE.subtract(dx1x2y1y2).invert()); // (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2)
43 | return GroupElement.p3(curve, x3, y3, field.ZERO, field.ZERO);
44 | }
45 |
46 | private static GroupElement ed25519ScalarMultiply(GroupElement P, BigInteger e) {
47 | GroupElement Q = P.getCurve().getZero(GroupElement.Representation.P3);
48 | Q = ed25519Edwards(Q, Q);
49 | Q = ed25519Edwards(Q, P);
50 |
51 | int len = e.bitLength() - 2;
52 | for (int c = len; c >= 0; --c) {
53 | Q = ed25519Edwards(Q, Q);
54 | if (e.testBit(c)) Q = ed25519Edwards(Q, P);
55 | }
56 | return Q;
57 | }
58 |
59 | static GroupElement[] precomputeTable(String seed) {
60 | GroupElement[] t = new GroupElement[15];
61 | byte[] seedBytes = seed.getBytes(StandardCharsets.UTF_8);
62 | byte[] v = Spake2Context.getHash("SHA-256", seedBytes);
63 | Ed25519CurveParameterSpec spec = Ed25519.getSpec();
64 | GroupElement P = spec.getCurve().createPoint(v, true);
65 | Curve curve = P.getCurve();
66 | for (int i = 1; i < 16; ++i) {
67 | // (i >>> 3 & 1) * (1 << 192)
68 | BigInteger t1 = BigInteger.valueOf((i >>> 3 & 1)).multiply(BigInteger.ONE.shiftLeft(192));
69 | // (i >>> 2 & 1) * (1 << 128)
70 | BigInteger t2 = BigInteger.valueOf((i >>> 2 & 1)).multiply(BigInteger.ONE.shiftLeft(128));
71 | // (i >>> 1 & 1) * (1 << 64)
72 | BigInteger t3 = BigInteger.valueOf((i >>> 1 & 1)).multiply(BigInteger.ONE.shiftLeft(64));
73 | // (i & 1)
74 | BigInteger t4 = BigInteger.ZERO.add(BigInteger.valueOf(i & 1));
75 | // k is the sum of all the above
76 | BigInteger k = BigInteger.ZERO.add(t1).add(t2).add(t3).add(t4);
77 |
78 | GroupElement ge = ed25519ScalarMultiply(P, k);
79 | FieldElement x = ge.getX();
80 | FieldElement y = ge.getY();
81 |
82 | FieldElement ypx = y.add(x);
83 | FieldElement ymx = y.subtract(x);
84 | FieldElement xy2d = x.multiply(y).multiply(curve.get2D());
85 |
86 | t[i - 1] = GroupElement.precomp(curve, ypx, ymx, xy2d);
87 | }
88 | return t;
89 | }
90 |
91 | private static byte[] printPrecompTable(GroupElement[] groupElements, String name) {
92 | byte[] table = new byte[groupElements.length * 3 * 32];
93 | for (int i = 0; i < groupElements.length; ++i) {
94 | System.arraycopy(groupElements[i].getX().toByteArray(), 0, table, i * 96, 32);
95 | System.arraycopy(groupElements[i].getY().toByteArray(), 0, table, i * 96 + 32, 32);
96 | System.arraycopy(groupElements[i].getZ().toByteArray(), 0, table, i * 96 + 64, 32);
97 | }
98 | System.out.printf(" private static final int[] %s = new int[] {", name);
99 | for (int i = 0; i < table.length; ++i) {
100 | if (i % 15 == 0) System.out.printf("%n ");
101 | System.out.printf(" 0x%02X,", table[i]);
102 | }
103 | System.out.println("\n };");
104 | return table;
105 | }
106 |
107 | private static void printCTable(byte[] table, String name) {
108 | System.out.printf("static const uint8_t %s[%d] = {", name, table.length);
109 | for (int i = 0; i < table.length; ++i) {
110 | if (i % 12 == 0) System.out.printf("%n ");
111 | System.out.printf(" 0x%02X,", table[i]);
112 | }
113 | System.out.println("\n};");
114 | }
115 |
116 | public void printPasswords() {
117 | printCTable(Utils.hexToBytes("47f6c458e5f062db8427d2d9bb20c954a76d6943959756a18d11d45e1ad190f980a86d185a93ca1d3025c5febe3aac4045b34a39b1f511385ca97fc4332137f3"), "kAlicePrivKey");
118 | printCTable(Utils.hexToBytes("a6bf9f9bf7819e0ded8c2dd82a1aa38acb2f8a6403429cff33d64ea9c40439d5fd7029811a5f5a8f7c89c8b44ac0b421f6b24ca2ba18d2069995831730cd8c5a"), "kBobPrivKey");
119 | }
120 |
121 | @Test
122 | public void testX25519ScReduce() {
123 | byte[] expected = Utils.hexToBytes("00f4f4563dba61b24551c122bbbb630855b1b5ed3f0a619792a0d9fd2a2cfb3880a86d185a93ca1d3025c5febe3aac4045b34a39b1f511385ca97fc4332137f3");
124 | byte[] privateKey = Utils.hexToBytes("47f6c458e5f062db8427d2d9bb20c954a76d6943959756a18d11d45e1ad190f980a86d185a93ca1d3025c5febe3aac4045b34a39b1f511385ca97fc4332137f3");
125 | x25519Scalar.reduce(privateKey);
126 | Spake2Context.leftShift3(privateKey);
127 | assertEquals(Utils.bytesToHex(expected), Utils.bytesToHex(privateKey));
128 | }
129 |
130 | @Test
131 | public void testAllOnesIfEquals() {
132 | assertEquals(Spake2Context.isEqual(1, 1), 0xffff_ffff);
133 | assertEquals(Spake2Context.isEqual(1, 0), 0x0);
134 | }
135 |
136 | @Test
137 | public void scalarTestCmov() {
138 | Spake2Context.Scalar scalar = new Spake2Context.Scalar(Utils.hexToBytes(
139 | "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010"));
140 | Spake2Context.Scalar zero = new Spake2Context.Scalar();
141 | Spake2Context.Scalar tmpScalar = new Spake2Context.Scalar();
142 | tmpScalar.copy(scalar);
143 | tmpScalar.cmov(zero, 0);
144 | assertEquals("edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010",
145 | Utils.bytesToHex(tmpScalar.getBytes()));
146 | tmpScalar.copy(scalar);
147 | tmpScalar.cmov(zero, 1);
148 | assertEquals("ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010",
149 | Utils.bytesToHex(tmpScalar.getBytes()));
150 | tmpScalar.copy(scalar);
151 | tmpScalar.cmov(zero, 5);
152 | assertEquals("e8d3f55c1a631258d29cf7a2daf9de1400000000000000000000000000000010",
153 | Utils.bytesToHex(tmpScalar.getBytes()));
154 | tmpScalar.copy(scalar);
155 | tmpScalar.cmov(zero, 0x11);
156 | assertEquals("ecd3f55c0a631258c69cf7a2cef9de1400000000000000000000000000000010",
157 | Utils.bytesToHex(tmpScalar.getBytes()));
158 | tmpScalar.copy(scalar);
159 | tmpScalar.cmov(zero, 0x31);
160 | assertEquals("ccd3f55c0a631258c69cf7a2cef9de1400000000000000000000000000000010",
161 | Utils.bytesToHex(tmpScalar.getBytes()));
162 | tmpScalar.copy(scalar);
163 | tmpScalar.cmov(zero, 0x71);
164 | assertEquals("8cd3f55c0a631258869cf7a28ef9de1400000000000000000000000000000010",
165 | Utils.bytesToHex(tmpScalar.getBytes()));
166 | tmpScalar.copy(scalar);
167 | tmpScalar.cmov(zero, 0xf9);
168 | assertEquals("04d3f55c02631258069cf7a206f9de1400000000000000000000000000000010",
169 | Utils.bytesToHex(tmpScalar.getBytes()));
170 | }
171 |
172 | @Test
173 | public void scalarTestCmov2() {
174 | Spake2Context.Scalar scalar = new Spake2Context.Scalar(Utils.hexToBytes(
175 | "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010"));
176 | Spake2Context.Scalar base = new Spake2Context.Scalar();
177 | base.cmov(scalar, 0);
178 | assertEquals("0000000000000000000000000000000000000000000000000000000000000000",
179 | Utils.bytesToHex(base.getBytes()));
180 | base.cmov(scalar, 1);
181 | assertEquals("0100000000000000000000000000000000000000000000000000000000000000",
182 | Utils.bytesToHex(base.getBytes()));
183 | base.cmov(scalar, 5);
184 | assertEquals("0500000000000000040000000400000000000000000000000000000000000000",
185 | Utils.bytesToHex(base.getBytes()));
186 | base.cmov(scalar, 0x11);
187 | assertEquals("0500000010000000140000001400000000000000000000000000000000000000",
188 | Utils.bytesToHex(base.getBytes()));
189 | base.cmov(scalar, 0x31);
190 | assertEquals("2500000010000000140000001400000000000000000000000000000000000000",
191 | Utils.bytesToHex(base.getBytes()));
192 | base.cmov(scalar, 0x71);
193 | assertEquals("6500000010000000540000005400000000000000000000000000000000000000",
194 | Utils.bytesToHex(base.getBytes()));
195 | base.cmov(scalar, 0xf9);
196 | assertEquals("ed00000018000000d4000000dc00000000000000000000000000000000000000",
197 | Utils.bytesToHex(base.getBytes()));
198 | }
199 |
200 | @Test
201 | public void scalarTestDbl() {
202 | Spake2Context.Scalar scalar = new Spake2Context.Scalar(Utils.hexToBytes(
203 | "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010"));
204 | Spake2Context.Scalar eight = new Spake2Context.Scalar(B_EIGHT);
205 | scalar.dbl();
206 | assertEquals("daa7ebb934c624b0ac39ef45bdf3bd2900000000000000000000000000000020",
207 | Utils.bytesToHex(scalar.getBytes()));
208 | eight.dbl();
209 | assertEquals("1000000000000000000000000000000000000000000000000000000000000000",
210 | Utils.bytesToHex(eight.getBytes()));
211 | }
212 |
213 | @Test
214 | public void scalarTestAdd() {
215 | Spake2Context.Scalar scalar = new Spake2Context.Scalar(Utils.hexToBytes(
216 | "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010"));
217 | Spake2Context.Scalar eight = new Spake2Context.Scalar(B_EIGHT);
218 | eight.add(scalar);
219 | assertEquals("f5d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010",
220 | Utils.bytesToHex(eight.getBytes()));
221 | scalar.add(scalar);
222 | assertEquals("daa7ebb934c624b0ac39ef45bdf3bd2900000000000000000000000000000020",
223 | Utils.bytesToHex(scalar.getBytes()));
224 | }
225 |
226 | @Test
227 | public void checkIfGeneratedValuesAreSameForN() {
228 | GroupElement[] ge = precomputeTable("edwards25519 point generation seed (N)");
229 | assertArrayEquals(ge, Spake2Context.SPAKE_N_SMALL_PRECOMP);
230 | }
231 |
232 | @Test
233 | public void checkIfGeneratedValuesAreSameForM() {
234 | GroupElement[] ge = precomputeTable("edwards25519 point generation seed (M)");
235 | assertArrayEquals(ge, Spake2Context.SPAKE_M_SMALL_PRECOMP);
236 | }
237 |
238 | @Test
239 | public void spake2() {
240 | for (int i = 0; i < 20; i++) {
241 | System.out.println("========");
242 | SPAKE2Run spake2 = new SPAKE2Run();
243 | assertTrue(spake2.run());
244 | assertTrue(spake2.keyMatches());
245 | }
246 | }
247 |
248 | @Test
249 | public void oldAlice() {
250 | for (int i = 0; i < 20; i++) {
251 | System.out.println("========");
252 | SPAKE2Run spake2 = new SPAKE2Run();
253 | spake2.aliceDisablePasswordScalarHack = true;
254 | assertTrue(spake2.run());
255 | assertTrue(spake2.keyMatches());
256 | }
257 | }
258 |
259 | @Test
260 | public void oldBob() {
261 | for (int i = 0; i < 20; i++) {
262 | System.out.println("========");
263 | SPAKE2Run spake2 = new SPAKE2Run();
264 | spake2.bobDisablePasswordScalarHack = true;
265 | assertTrue(spake2.run());
266 | assertTrue(spake2.keyMatches());
267 | }
268 | }
269 |
270 | @Test
271 | public void wrongPassword() {
272 | SPAKE2Run spake2 = new SPAKE2Run();
273 | spake2.bobPassword = "wrong password".getBytes(StandardCharsets.UTF_8);
274 | assertTrue(spake2.run());
275 | assertFalse(spake2.keyMatches());
276 | }
277 |
278 | @Test
279 | public void wrongNames() {
280 | SPAKE2Run spake2 = new SPAKE2Run();
281 | spake2.aliceNames.second = "charlie";
282 | spake2.bobNames.second = "charlie";
283 | assertTrue(spake2.run());
284 | assertFalse(spake2.keyMatches());
285 | }
286 |
287 | @Test
288 | public void corruptMessages() {
289 | for (int i = 0; i < 8 * Spake2Context.MAX_MSG_SIZE; i++) {
290 | SPAKE2Run spake2 = new SPAKE2Run();
291 | spake2.aliceCorruptMsgBit = i;
292 | assertFalse(spake2.run() && spake2.keyMatches());
293 | }
294 | }
295 |
296 | // Based on https://android.googlesource.com/platform/external/boringssl/+/f9e0b0e17fabac35627f18f94a8954c3857784ac/src/crypto/curve25519/spake25519_test.cc
297 | private static class SPAKE2Run {
298 | private final Pair
14 | * An element $t$, entries $t[0] \dots t[9]$, represents the integer
15 | * $t[0]+2^{26} t[1]+2^{51} t[2]+2^{77} t[3]+2^{102} t[4]+\dots+2^{230} t[9]$.
16 | * Bounds on each $t[i]$ vary depending on context.
17 | *
18 | * Reviewed/commented by Bloody Rookie (nemproject@gmx.de)
19 | */
20 | public class Ed25519FieldElement extends FieldElement {
21 | /**
22 | * Variable is package private for encoding.
23 | */
24 | protected final int[] t;
25 |
26 | /**
27 | * Creates a field element.
28 | *
29 | * @param f The underlying field, must be the finite field with $p = 2^{255} - 19$ elements
30 | * @param t The $2^{25.5}$ bit representation of the field element.
31 | */
32 | public Ed25519FieldElement(Ed25519Field f, int[] t) {
33 | super(f);
34 | if (t.length != 10)
35 | throw new IllegalArgumentException("Invalid radix-2^51 representation");
36 | this.t = t;
37 | }
38 |
39 | private static final byte[] ZERO = new byte[32];
40 |
41 | /**
42 | * Gets a value indicating whether the field element is non-zero.
43 | *
44 | * @return 1 if it is non-zero, 0 otherwise.
45 | */
46 | public boolean isNonZero() {
47 | final byte[] s = toByteArray();
48 | return Utils.equal(s, ZERO) == 0;
49 | }
50 |
51 | /**
52 | * $h = f + g$
53 | *
54 | * TODO-CR BR: $h$ is allocated via new, probably not a good idea. Do we need the copying into temp variables if we do that?
55 | *
56 | * Preconditions:
57 | *
61 | * Postconditions:
62 | *
81 | * Can overlap $h$ with $f$ or $g$.
82 | *
83 | * TODO-CR BR: See above.
84 | *
85 | * Preconditions:
86 | *
90 | * Postconditions:
91 | *
110 | * TODO-CR BR: see above.
111 | *
112 | * Preconditions:
113 | *
116 | * Postconditions:
117 | *
134 | * Can overlap $h$ with $f$ or $g$.
135 | *
136 | * Preconditions:
137 | *
143 | * Postconditions:
144 | *
148 | * Notes on implementation strategy:
149 | *
150 | * Using schoolbook multiplication. Karatsuba would save a little in some
151 | * cost models.
152 | *
153 | * Most multiplications by 2 and 19 are 32-bit precomputations; cheaper than
154 | * 64-bit postcomputations.
155 | *
156 | * There is one remaining multiplication by 19 in the carry chain; one *19
157 | * precomputation can be merged into this, but the resulting data flow is
158 | * considerably less clean.
159 | *
160 | * There are 12 carries below. 10 of them are 2-way parallelizable and
161 | * vectorizable. Can get away with 11 carries, but then data flow is much
162 | * deeper.
163 | *
164 | * With tighter constraints on inputs can squeeze carries into int32.
165 | *
166 | * @param val The field element to multiply.
167 | * @return The (reasonably reduced) field element this * val.
168 | */
169 | public FieldElement multiply(FieldElement val) {
170 | int[] g = ((Ed25519FieldElement)val).t;
171 | long x1;
172 | long x2;
173 | long x3;
174 | long x4;
175 | long x5;
176 | long x6;
177 | long x7;
178 | long x8;
179 | long x9;
180 | long x10;
181 | long x11;
182 | long x12;
183 | long x13;
184 | long x14;
185 | long x15;
186 | long x16;
187 | long x17;
188 | long x18;
189 | long x19;
190 | long x20;
191 | long x21;
192 | long x22;
193 | long x23;
194 | long x24;
195 | long x25;
196 | long x26;
197 | long x27;
198 | long x28;
199 | long x29;
200 | long x30;
201 | long x31;
202 | long x32;
203 | long x33;
204 | long x34;
205 | long x35;
206 | long x36;
207 | long x37;
208 | long x38;
209 | long x39;
210 | long x40;
211 | long x41;
212 | long x42;
213 | long x43;
214 | long x44;
215 | long x45;
216 | long x46;
217 | long x47;
218 | long x48;
219 | long x49;
220 | long x50;
221 | long x51;
222 | long x52;
223 | long x53;
224 | long x54;
225 | long x55;
226 | long x56;
227 | long x57;
228 | long x58;
229 | long x59;
230 | long x60;
231 | long x61;
232 | long x62;
233 | long x63;
234 | long x64;
235 | long x65;
236 | long x66;
237 | long x67;
238 | long x68;
239 | long x69;
240 | long x70;
241 | long x71;
242 | long x72;
243 | long x73;
244 | long x74;
245 | long x75;
246 | long x76;
247 | long x77;
248 | long x78;
249 | long x79;
250 | long x80;
251 | long x81;
252 | long x82;
253 | long x83;
254 | long x84;
255 | long x85;
256 | long x86;
257 | long x87;
258 | long x88;
259 | long x89;
260 | long x90;
261 | long x91;
262 | long x92;
263 | long x93;
264 | long x94;
265 | long x95;
266 | long x96;
267 | long x97;
268 | long x98;
269 | long x99;
270 | long x100;
271 | long x101;
272 | long x102;
273 | int x103;
274 | long x104;
275 | long x105;
276 | long x106;
277 | long x107;
278 | long x108;
279 | long x109;
280 | long x110;
281 | long x111;
282 | long x112;
283 | long x113;
284 | long x114;
285 | int x115;
286 | long x116;
287 | long x117;
288 | int x118;
289 | long x119;
290 | long x120;
291 | int x121;
292 | long x122;
293 | long x123;
294 | int x124;
295 | long x125;
296 | long x126;
297 | int x127;
298 | long x128;
299 | long x129;
300 | int x130;
301 | long x131;
302 | long x132;
303 | int x133;
304 | long x134;
305 | long x135;
306 | int x136;
307 | long x137;
308 | long x138;
309 | int x139;
310 | long x140;
311 | long x141;
312 | int x142;
313 | int x143;
314 | int x144;
315 | byte x145;
316 | int x146;
317 | int x147;
318 | x1 = ((long)(t[9]) * ((g[9]) * (byte) 0x26));
319 | x2 = ((long)(t[9]) * ((g[8]) * (byte) 0x13));
320 | x3 = ((long)(t[9]) * ((g[7]) * (byte) 0x26));
321 | x4 = ((long)(t[9]) * ((g[6]) * (byte) 0x13));
322 | x5 = ((long)(t[9]) * ((g[5]) * (byte) 0x26));
323 | x6 = ((long)(t[9]) * ((g[4]) * (byte) 0x13));
324 | x7 = ((long)(t[9]) * ((g[3]) * (byte) 0x26));
325 | x8 = ((long)(t[9]) * ((g[2]) * (byte) 0x13));
326 | x9 = ((long)(t[9]) * ((g[1]) * (byte) 0x26));
327 | x10 = ((long)(t[8]) * ((g[9]) * (byte) 0x13));
328 | x11 = ((long)(t[8]) * ((g[8]) * (byte) 0x13));
329 | x12 = ((long)(t[8]) * ((g[7]) * (byte) 0x13));
330 | x13 = ((long)(t[8]) * ((g[6]) * (byte) 0x13));
331 | x14 = ((long)(t[8]) * ((g[5]) * (byte) 0x13));
332 | x15 = ((long)(t[8]) * ((g[4]) * (byte) 0x13));
333 | x16 = ((long)(t[8]) * ((g[3]) * (byte) 0x13));
334 | x17 = ((long)(t[8]) * ((g[2]) * (byte) 0x13));
335 | x18 = ((long)(t[7]) * ((g[9]) * (byte) 0x26));
336 | x19 = ((long)(t[7]) * ((g[8]) * (byte) 0x13));
337 | x20 = ((long)(t[7]) * ((g[7]) * (byte) 0x26));
338 | x21 = ((long)(t[7]) * ((g[6]) * (byte) 0x13));
339 | x22 = ((long)(t[7]) * ((g[5]) * (byte) 0x26));
340 | x23 = ((long)(t[7]) * ((g[4]) * (byte) 0x13));
341 | x24 = ((long)(t[7]) * ((g[3]) * (byte) 0x26));
342 | x25 = ((long)(t[6]) * ((g[9]) * (byte) 0x13));
343 | x26 = ((long)(t[6]) * ((g[8]) * (byte) 0x13));
344 | x27 = ((long)(t[6]) * ((g[7]) * (byte) 0x13));
345 | x28 = ((long)(t[6]) * ((g[6]) * (byte) 0x13));
346 | x29 = ((long)(t[6]) * ((g[5]) * (byte) 0x13));
347 | x30 = ((long)(t[6]) * ((g[4]) * (byte) 0x13));
348 | x31 = ((long)(t[5]) * ((g[9]) * (byte) 0x26));
349 | x32 = ((long)(t[5]) * ((g[8]) * (byte) 0x13));
350 | x33 = ((long)(t[5]) * ((g[7]) * (byte) 0x26));
351 | x34 = ((long)(t[5]) * ((g[6]) * (byte) 0x13));
352 | x35 = ((long)(t[5]) * ((g[5]) * (byte) 0x26));
353 | x36 = ((long)(t[4]) * ((g[9]) * (byte) 0x13));
354 | x37 = ((long)(t[4]) * ((g[8]) * (byte) 0x13));
355 | x38 = ((long)(t[4]) * ((g[7]) * (byte) 0x13));
356 | x39 = ((long)(t[4]) * ((g[6]) * (byte) 0x13));
357 | x40 = ((long)(t[3]) * ((g[9]) * (byte) 0x26));
358 | x41 = ((long)(t[3]) * ((g[8]) * (byte) 0x13));
359 | x42 = ((long)(t[3]) * ((g[7]) * (byte) 0x26));
360 | x43 = ((long)(t[2]) * ((g[9]) * (byte) 0x13));
361 | x44 = ((long)(t[2]) * ((g[8]) * (byte) 0x13));
362 | x45 = ((long)(t[1]) * ((g[9]) * (byte) 0x26));
363 | x46 = ((long)(t[9]) * (g[0]));
364 | x47 = ((long)(t[8]) * (g[1]));
365 | x48 = ((long)(t[8]) * (g[0]));
366 | x49 = ((long)(t[7]) * (g[2]));
367 | x50 = ((long)(t[7]) * ((g[1]) * 0x2));
368 | x51 = ((long)(t[7]) * (g[0]));
369 | x52 = ((long)(t[6]) * (g[3]));
370 | x53 = ((long)(t[6]) * (g[2]));
371 | x54 = ((long)(t[6]) * (g[1]));
372 | x55 = ((long)(t[6]) * (g[0]));
373 | x56 = ((long)(t[5]) * (g[4]));
374 | x57 = ((long)(t[5]) * ((g[3]) * 0x2));
375 | x58 = ((long)(t[5]) * (g[2]));
376 | x59 = ((long)(t[5]) * ((g[1]) * 0x2));
377 | x60 = ((long)(t[5]) * (g[0]));
378 | x61 = ((long)(t[4]) * (g[5]));
379 | x62 = ((long)(t[4]) * (g[4]));
380 | x63 = ((long)(t[4]) * (g[3]));
381 | x64 = ((long)(t[4]) * (g[2]));
382 | x65 = ((long)(t[4]) * (g[1]));
383 | x66 = ((long)(t[4]) * (g[0]));
384 | x67 = ((long)(t[3]) * (g[6]));
385 | x68 = ((long)(t[3]) * ((g[5]) * 0x2));
386 | x69 = ((long)(t[3]) * (g[4]));
387 | x70 = ((long)(t[3]) * ((g[3]) * 0x2));
388 | x71 = ((long)(t[3]) * (g[2]));
389 | x72 = ((long)(t[3]) * ((g[1]) * 0x2));
390 | x73 = ((long)(t[3]) * (g[0]));
391 | x74 = ((long)(t[2]) * (g[7]));
392 | x75 = ((long)(t[2]) * (g[6]));
393 | x76 = ((long)(t[2]) * (g[5]));
394 | x77 = ((long)(t[2]) * (g[4]));
395 | x78 = ((long)(t[2]) * (g[3]));
396 | x79 = ((long)(t[2]) * (g[2]));
397 | x80 = ((long)(t[2]) * (g[1]));
398 | x81 = ((long)(t[2]) * (g[0]));
399 | x82 = ((long)(t[1]) * (g[8]));
400 | x83 = ((long)(t[1]) * ((g[7]) * 0x2));
401 | x84 = ((long)(t[1]) * (g[6]));
402 | x85 = ((long)(t[1]) * ((g[5]) * 0x2));
403 | x86 = ((long)(t[1]) * (g[4]));
404 | x87 = ((long)(t[1]) * ((g[3]) * 0x2));
405 | x88 = ((long)(t[1]) * (g[2]));
406 | x89 = ((long)(t[1]) * ((g[1]) * 0x2));
407 | x90 = ((long)(t[1]) * (g[0]));
408 | x91 = ((long)(t[0]) * (g[9]));
409 | x92 = ((long)(t[0]) * (g[8]));
410 | x93 = ((long)(t[0]) * (g[7]));
411 | x94 = ((long)(t[0]) * (g[6]));
412 | x95 = ((long)(t[0]) * (g[5]));
413 | x96 = ((long)(t[0]) * (g[4]));
414 | x97 = ((long)(t[0]) * (g[3]));
415 | x98 = ((long)(t[0]) * (g[2]));
416 | x99 = ((long)(t[0]) * (g[1]));
417 | x100 = ((long)(t[0]) * (g[0]));
418 | x101 = (x100 + (x45 + (x44 + (x42 + (x39 + (x35 + (x30 + (x24 + (x17 + x9)))))))));
419 | x102 = (x101 >> 26);
420 | x103 = (int)(x101 & 0x3ffffff);
421 | x104 = (x91 + (x82 + (x74 + (x67 + (x61 + (x56 + (x52 + (x49 + (x47 + x46)))))))));
422 | x105 = (x92 + (x83 + (x75 + (x68 + (x62 + (x57 + (x53 + (x50 + (x48 + x1)))))))));
423 | x106 = (x93 + (x84 + (x76 + (x69 + (x63 + (x58 + (x54 + (x51 + (x10 + x2)))))))));
424 | x107 = (x94 + (x85 + (x77 + (x70 + (x64 + (x59 + (x55 + (x18 + (x11 + x3)))))))));
425 | x108 = (x95 + (x86 + (x78 + (x71 + (x65 + (x60 + (x25 + (x19 + (x12 + x4)))))))));
426 | x109 = (x96 + (x87 + (x79 + (x72 + (x66 + (x31 + (x26 + (x20 + (x13 + x5)))))))));
427 | x110 = (x97 + (x88 + (x80 + (x73 + (x36 + (x32 + (x27 + (x21 + (x14 + x6)))))))));
428 | x111 = (x98 + (x89 + (x81 + (x40 + (x37 + (x33 + (x28 + (x22 + (x15 + x7)))))))));
429 | x112 = (x99 + (x90 + (x43 + (x41 + (x38 + (x34 + (x29 + (x23 + (x16 + x8)))))))));
430 | x113 = (x102 + x112);
431 | x114 = (x113 >> 25);
432 | x115 = (int)(x113 & 0x1ffffff);
433 | x116 = (x114 + x111);
434 | x117 = (x116 >> 26);
435 | x118 = (int)(x116 & 0x3ffffff);
436 | x119 = (x117 + x110);
437 | x120 = (x119 >> 25);
438 | x121 = (int)(x119 & 0x1ffffff);
439 | x122 = (x120 + x109);
440 | x123 = (x122 >> 26);
441 | x124 = (int)(x122 & 0x3ffffff);
442 | x125 = (x123 + x108);
443 | x126 = (x125 >> 25);
444 | x127 = (int)(x125 & 0x1ffffff);
445 | x128 = (x126 + x107);
446 | x129 = (x128 >> 26);
447 | x130 = (int)(x128 & 0x3ffffff);
448 | x131 = (x129 + x106);
449 | x132 = (x131 >> 25);
450 | x133 = (int)(x131 & 0x1ffffff);
451 | x134 = (x132 + x105);
452 | x135 = (x134 >> 26);
453 | x136 = (int)(x134 & 0x3ffffff);
454 | x137 = (x135 + x104);
455 | x138 = (x137 >> 25);
456 | x139 = (int)(x137 & 0x1ffffff);
457 | x140 = (x138 * (byte) 0x13);
458 | x141 = (x103 + x140);
459 | x142 = (int)(x141 >> 26);
460 | x143 = (int)(x141 & 0x3ffffff);
461 | x144 = (x142 + x115);
462 | x145 = (byte)(x144 >> 25);
463 | x146 = (x144 & 0x1ffffff);
464 | x147 = (x145 + x118);
465 | int[] out1 = new int[10];
466 | out1[0] = x143;
467 | out1[1] = x146;
468 | out1[2] = x147;
469 | out1[3] = x121;
470 | out1[4] = x124;
471 | out1[5] = x127;
472 | out1[6] = x130;
473 | out1[7] = x133;
474 | out1[8] = x136;
475 | out1[9] = x139;
476 | return new Ed25519FieldElement(f, out1);
477 | }
478 |
479 | /**
480 | * $h = f * f$
481 | *
482 | * Can overlap $h$ with $f$.
483 | *
484 | * Preconditions:
485 | *
488 | * Postconditions:
489 | *
492 | * See {@link #multiply(FieldElement)} for discussion
493 | * of implementation strategy.
494 | *
495 | * @return The (reasonably reduced) square of this field element.
496 | */
497 | public FieldElement square() {
498 | int f0 = t[0];
499 | int f1 = t[1];
500 | int f2 = t[2];
501 | int f3 = t[3];
502 | int f4 = t[4];
503 | int f5 = t[5];
504 | int f6 = t[6];
505 | int f7 = t[7];
506 | int f8 = t[8];
507 | int f9 = t[9];
508 | int f0_2 = 2 * f0;
509 | int f1_2 = 2 * f1;
510 | int f2_2 = 2 * f2;
511 | int f3_2 = 2 * f3;
512 | int f4_2 = 2 * f4;
513 | int f5_2 = 2 * f5;
514 | int f6_2 = 2 * f6;
515 | int f7_2 = 2 * f7;
516 | int f5_38 = 38 * f5; /* 1.959375*2^30 */
517 | int f6_19 = 19 * f6; /* 1.959375*2^30 */
518 | int f7_38 = 38 * f7; /* 1.959375*2^30 */
519 | int f8_19 = 19 * f8; /* 1.959375*2^30 */
520 | int f9_38 = 38 * f9; /* 1.959375*2^30 */
521 | long f0f0 = f0 * (long) f0;
522 | long f0f1_2 = f0_2 * (long) f1;
523 | long f0f2_2 = f0_2 * (long) f2;
524 | long f0f3_2 = f0_2 * (long) f3;
525 | long f0f4_2 = f0_2 * (long) f4;
526 | long f0f5_2 = f0_2 * (long) f5;
527 | long f0f6_2 = f0_2 * (long) f6;
528 | long f0f7_2 = f0_2 * (long) f7;
529 | long f0f8_2 = f0_2 * (long) f8;
530 | long f0f9_2 = f0_2 * (long) f9;
531 | long f1f1_2 = f1_2 * (long) f1;
532 | long f1f2_2 = f1_2 * (long) f2;
533 | long f1f3_4 = f1_2 * (long) f3_2;
534 | long f1f4_2 = f1_2 * (long) f4;
535 | long f1f5_4 = f1_2 * (long) f5_2;
536 | long f1f6_2 = f1_2 * (long) f6;
537 | long f1f7_4 = f1_2 * (long) f7_2;
538 | long f1f8_2 = f1_2 * (long) f8;
539 | long f1f9_76 = f1_2 * (long) f9_38;
540 | long f2f2 = f2 * (long) f2;
541 | long f2f3_2 = f2_2 * (long) f3;
542 | long f2f4_2 = f2_2 * (long) f4;
543 | long f2f5_2 = f2_2 * (long) f5;
544 | long f2f6_2 = f2_2 * (long) f6;
545 | long f2f7_2 = f2_2 * (long) f7;
546 | long f2f8_38 = f2_2 * (long) f8_19;
547 | long f2f9_38 = f2 * (long) f9_38;
548 | long f3f3_2 = f3_2 * (long) f3;
549 | long f3f4_2 = f3_2 * (long) f4;
550 | long f3f5_4 = f3_2 * (long) f5_2;
551 | long f3f6_2 = f3_2 * (long) f6;
552 | long f3f7_76 = f3_2 * (long) f7_38;
553 | long f3f8_38 = f3_2 * (long) f8_19;
554 | long f3f9_76 = f3_2 * (long) f9_38;
555 | long f4f4 = f4 * (long) f4;
556 | long f4f5_2 = f4_2 * (long) f5;
557 | long f4f6_38 = f4_2 * (long) f6_19;
558 | long f4f7_38 = f4 * (long) f7_38;
559 | long f4f8_38 = f4_2 * (long) f8_19;
560 | long f4f9_38 = f4 * (long) f9_38;
561 | long f5f5_38 = f5 * (long) f5_38;
562 | long f5f6_38 = f5_2 * (long) f6_19;
563 | long f5f7_76 = f5_2 * (long) f7_38;
564 | long f5f8_38 = f5_2 * (long) f8_19;
565 | long f5f9_76 = f5_2 * (long) f9_38;
566 | long f6f6_19 = f6 * (long) f6_19;
567 | long f6f7_38 = f6 * (long) f7_38;
568 | long f6f8_38 = f6_2 * (long) f8_19;
569 | long f6f9_38 = f6 * (long) f9_38;
570 | long f7f7_38 = f7 * (long) f7_38;
571 | long f7f8_38 = f7_2 * (long) f8_19;
572 | long f7f9_76 = f7_2 * (long) f9_38;
573 | long f8f8_19 = f8 * (long) f8_19;
574 | long f8f9_38 = f8 * (long) f9_38;
575 | long f9f9_38 = f9 * (long) f9_38;
576 |
577 | /**
578 | * Same procedure as in multiply, but this time we have a higher symmetry leading to less summands.
579 | * e.g. f1f9_76 really stands for f1 * 2^26 * f9 * 2^230 + f9 * 2^230 + f1 * 2^26 congruent 2 * 2 * 19 * f1 * f9 2^0 modulo p.
580 | */
581 | long h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38;
582 | long h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38;
583 | long h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19;
584 | long h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38;
585 | long h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38;
586 | long h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38;
587 | long h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19;
588 | long h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38;
589 | long h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38;
590 | long h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2;
591 | long carry0;
592 | long carry1;
593 | long carry2;
594 | long carry3;
595 | long carry4;
596 | long carry5;
597 | long carry6;
598 | long carry7;
599 | long carry8;
600 | long carry9;
601 |
602 | carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
603 | carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
604 |
605 | carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
606 | carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
607 |
608 | carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
609 | carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
610 |
611 | carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
612 | carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
613 |
614 | carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
615 | carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
616 |
617 | carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
618 |
619 | carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
620 |
621 | int[] h = new int[10];
622 | h[0] = (int) h0;
623 | h[1] = (int) h1;
624 | h[2] = (int) h2;
625 | h[3] = (int) h3;
626 | h[4] = (int) h4;
627 | h[5] = (int) h5;
628 | h[6] = (int) h6;
629 | h[7] = (int) h7;
630 | h[8] = (int) h8;
631 | h[9] = (int) h9;
632 | return new Ed25519FieldElement(f, h);
633 | }
634 |
635 | /**
636 | * $h = 2 * f * f$
637 | *
638 | * Can overlap $h$ with $f$.
639 | *
640 | * Preconditions:
641 | *
644 | * Postconditions:
645 | *
648 | * See {@link #multiply(FieldElement)} for discussion
649 | * of implementation strategy.
650 | *
651 | * @return The (reasonably reduced) square of this field element times 2.
652 | */
653 | public FieldElement squareAndDouble() {
654 | int f0 = t[0];
655 | int f1 = t[1];
656 | int f2 = t[2];
657 | int f3 = t[3];
658 | int f4 = t[4];
659 | int f5 = t[5];
660 | int f6 = t[6];
661 | int f7 = t[7];
662 | int f8 = t[8];
663 | int f9 = t[9];
664 | int f0_2 = 2 * f0;
665 | int f1_2 = 2 * f1;
666 | int f2_2 = 2 * f2;
667 | int f3_2 = 2 * f3;
668 | int f4_2 = 2 * f4;
669 | int f5_2 = 2 * f5;
670 | int f6_2 = 2 * f6;
671 | int f7_2 = 2 * f7;
672 | int f5_38 = 38 * f5; /* 1.959375*2^30 */
673 | int f6_19 = 19 * f6; /* 1.959375*2^30 */
674 | int f7_38 = 38 * f7; /* 1.959375*2^30 */
675 | int f8_19 = 19 * f8; /* 1.959375*2^30 */
676 | int f9_38 = 38 * f9; /* 1.959375*2^30 */
677 | long f0f0 = f0 * (long) f0;
678 | long f0f1_2 = f0_2 * (long) f1;
679 | long f0f2_2 = f0_2 * (long) f2;
680 | long f0f3_2 = f0_2 * (long) f3;
681 | long f0f4_2 = f0_2 * (long) f4;
682 | long f0f5_2 = f0_2 * (long) f5;
683 | long f0f6_2 = f0_2 * (long) f6;
684 | long f0f7_2 = f0_2 * (long) f7;
685 | long f0f8_2 = f0_2 * (long) f8;
686 | long f0f9_2 = f0_2 * (long) f9;
687 | long f1f1_2 = f1_2 * (long) f1;
688 | long f1f2_2 = f1_2 * (long) f2;
689 | long f1f3_4 = f1_2 * (long) f3_2;
690 | long f1f4_2 = f1_2 * (long) f4;
691 | long f1f5_4 = f1_2 * (long) f5_2;
692 | long f1f6_2 = f1_2 * (long) f6;
693 | long f1f7_4 = f1_2 * (long) f7_2;
694 | long f1f8_2 = f1_2 * (long) f8;
695 | long f1f9_76 = f1_2 * (long) f9_38;
696 | long f2f2 = f2 * (long) f2;
697 | long f2f3_2 = f2_2 * (long) f3;
698 | long f2f4_2 = f2_2 * (long) f4;
699 | long f2f5_2 = f2_2 * (long) f5;
700 | long f2f6_2 = f2_2 * (long) f6;
701 | long f2f7_2 = f2_2 * (long) f7;
702 | long f2f8_38 = f2_2 * (long) f8_19;
703 | long f2f9_38 = f2 * (long) f9_38;
704 | long f3f3_2 = f3_2 * (long) f3;
705 | long f3f4_2 = f3_2 * (long) f4;
706 | long f3f5_4 = f3_2 * (long) f5_2;
707 | long f3f6_2 = f3_2 * (long) f6;
708 | long f3f7_76 = f3_2 * (long) f7_38;
709 | long f3f8_38 = f3_2 * (long) f8_19;
710 | long f3f9_76 = f3_2 * (long) f9_38;
711 | long f4f4 = f4 * (long) f4;
712 | long f4f5_2 = f4_2 * (long) f5;
713 | long f4f6_38 = f4_2 * (long) f6_19;
714 | long f4f7_38 = f4 * (long) f7_38;
715 | long f4f8_38 = f4_2 * (long) f8_19;
716 | long f4f9_38 = f4 * (long) f9_38;
717 | long f5f5_38 = f5 * (long) f5_38;
718 | long f5f6_38 = f5_2 * (long) f6_19;
719 | long f5f7_76 = f5_2 * (long) f7_38;
720 | long f5f8_38 = f5_2 * (long) f8_19;
721 | long f5f9_76 = f5_2 * (long) f9_38;
722 | long f6f6_19 = f6 * (long) f6_19;
723 | long f6f7_38 = f6 * (long) f7_38;
724 | long f6f8_38 = f6_2 * (long) f8_19;
725 | long f6f9_38 = f6 * (long) f9_38;
726 | long f7f7_38 = f7 * (long) f7_38;
727 | long f7f8_38 = f7_2 * (long) f8_19;
728 | long f7f9_76 = f7_2 * (long) f9_38;
729 | long f8f8_19 = f8 * (long) f8_19;
730 | long f8f9_38 = f8 * (long) f9_38;
731 | long f9f9_38 = f9 * (long) f9_38;
732 | long h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38;
733 | long h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38;
734 | long h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19;
735 | long h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38;
736 | long h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38;
737 | long h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38;
738 | long h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19;
739 | long h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38;
740 | long h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38;
741 | long h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2;
742 | long carry0;
743 | long carry1;
744 | long carry2;
745 | long carry3;
746 | long carry4;
747 | long carry5;
748 | long carry6;
749 | long carry7;
750 | long carry8;
751 | long carry9;
752 |
753 | h0 += h0;
754 | h1 += h1;
755 | h2 += h2;
756 | h3 += h3;
757 | h4 += h4;
758 | h5 += h5;
759 | h6 += h6;
760 | h7 += h7;
761 | h8 += h8;
762 | h9 += h9;
763 |
764 | carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
765 | carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
766 |
767 | carry1 = (h1 + (long) (1<<24)) >> 25; h2 += carry1; h1 -= carry1 << 25;
768 | carry5 = (h5 + (long) (1<<24)) >> 25; h6 += carry5; h5 -= carry5 << 25;
769 |
770 | carry2 = (h2 + (long) (1<<25)) >> 26; h3 += carry2; h2 -= carry2 << 26;
771 | carry6 = (h6 + (long) (1<<25)) >> 26; h7 += carry6; h6 -= carry6 << 26;
772 |
773 | carry3 = (h3 + (long) (1<<24)) >> 25; h4 += carry3; h3 -= carry3 << 25;
774 | carry7 = (h7 + (long) (1<<24)) >> 25; h8 += carry7; h7 -= carry7 << 25;
775 |
776 | carry4 = (h4 + (long) (1<<25)) >> 26; h5 += carry4; h4 -= carry4 << 26;
777 | carry8 = (h8 + (long) (1<<25)) >> 26; h9 += carry8; h8 -= carry8 << 26;
778 |
779 | carry9 = (h9 + (long) (1<<24)) >> 25; h0 += carry9 * 19; h9 -= carry9 << 25;
780 |
781 | carry0 = (h0 + (long) (1<<25)) >> 26; h1 += carry0; h0 -= carry0 << 26;
782 |
783 | int[] h = new int[10];
784 | h[0] = (int) h0;
785 | h[1] = (int) h1;
786 | h[2] = (int) h2;
787 | h[3] = (int) h3;
788 | h[4] = (int) h4;
789 | h[5] = (int) h5;
790 | h[6] = (int) h6;
791 | h[7] = (int) h7;
792 | h[8] = (int) h8;
793 | h[9] = (int) h9;
794 | return new Ed25519FieldElement(f, h);
795 | }
796 |
797 | /**
798 | * Invert this field element.
799 | *
800 | * The inverse is found via Fermat's little theorem:
922 | * TODO-CR BR: I think it makes sense to have a sqrt function.
923 | *
924 | * @return This field element to the power of $(2^{252} - 3)$.
925 | */
926 | public FieldElement pow22523() {
927 | FieldElement t0, t1, t2;
928 |
929 | // 2 == 2 * 1
930 | t0 = square();
931 |
932 | // 4 == 2 * 2
933 | t1 = t0.square();
934 |
935 | // 8 == 2 * 4
936 | t1 = t1.square();
937 |
938 | // z9 = z1*z8
939 | t1 = multiply(t1);
940 |
941 | // 11 == 9 + 2
942 | t0 = t0.multiply(t1);
943 |
944 | // 22 == 2 * 11
945 | t0 = t0.square();
946 |
947 | // 31 == 22 + 9
948 | t0 = t1.multiply(t0);
949 |
950 | // 2^6 - 2^1
951 | t1 = t0.square();
952 |
953 | // 2^10 - 2^5
954 | for (int i = 1; i < 5; ++i) {
955 | t1 = t1.square();
956 | }
957 |
958 | // 2^10 - 2^0
959 | t0 = t1.multiply(t0);
960 |
961 | // 2^11 - 2^1
962 | t1 = t0.square();
963 |
964 | // 2^20 - 2^10
965 | for (int i = 1; i < 10; ++i) {
966 | t1 = t1.square();
967 | }
968 |
969 | // 2^20 - 2^0
970 | t1 = t1.multiply(t0);
971 |
972 | // 2^21 - 2^1
973 | t2 = t1.square();
974 |
975 | // 2^40 - 2^20
976 | for (int i = 1; i < 20; ++i) {
977 | t2 = t2.square();
978 | }
979 |
980 | // 2^40 - 2^0
981 | t1 = t2.multiply(t1);
982 |
983 | // 2^41 - 2^1
984 | t1 = t1.square();
985 |
986 | // 2^50 - 2^10
987 | for (int i = 1; i < 10; ++i) {
988 | t1 = t1.square();
989 | }
990 |
991 | // 2^50 - 2^0
992 | t0 = t1.multiply(t0);
993 |
994 | // 2^51 - 2^1
995 | t1 = t0.square();
996 |
997 | // 2^100 - 2^50
998 | for (int i = 1; i < 50; ++i) {
999 | t1 = t1.square();
1000 | }
1001 |
1002 | // 2^100 - 2^0
1003 | t1 = t1.multiply(t0);
1004 |
1005 | // 2^101 - 2^1
1006 | t2 = t1.square();
1007 |
1008 | // 2^200 - 2^100
1009 | for (int i = 1; i < 100; ++i) {
1010 | t2 = t2.square();
1011 | }
1012 |
1013 | // 2^200 - 2^0
1014 | t1 = t2.multiply(t1);
1015 |
1016 | // 2^201 - 2^1
1017 | t1 = t1.square();
1018 |
1019 | // 2^250 - 2^50
1020 | for (int i = 1; i < 50; ++i) {
1021 | t1 = t1.square();
1022 | }
1023 |
1024 | // 2^250 - 2^0
1025 | t0 = t1.multiply(t0);
1026 |
1027 | // 2^251 - 2^1
1028 | t0 = t0.square();
1029 |
1030 | // 2^252 - 2^2
1031 | t0 = t0.square();
1032 |
1033 | // 2^252 - 3
1034 | return multiply(t0);
1035 | }
1036 |
1037 | /**
1038 | * Constant-time conditional move. Well, actually it is a conditional copy.
1039 | * Logic is inspired by the SUPERCOP implementation at:
1040 | * https://github.com/floodyberry/supercop/blob/master/crypto_sign/ed25519/ref10/fe_cmov.c
1041 | *
1042 | * @param val the other field element.
1043 | * @param b must be 0 or 1, otherwise results are undefined.
1044 | * @return a copy of this if $b == 0$, or a copy of val if $b == 1$.
1045 | */
1046 | @Override
1047 | public FieldElement cmov(FieldElement val, int b) {
1048 | Ed25519FieldElement that = (Ed25519FieldElement) val;
1049 | b = -b;
1050 | int[] result = new int[10];
1051 | for (int i = 0; i < 10; i++) {
1052 | result[i] = this.t[i];
1053 | int x = this.t[i] ^ that.t[i];
1054 | x &= b;
1055 | result[i] ^= x;
1056 | }
1057 | return new Ed25519FieldElement(this.f, result);
1058 | }
1059 |
1060 | @Override
1061 | public FieldElement carry() {
1062 | int x1;
1063 | int x2;
1064 | int x3;
1065 | int x4;
1066 | int x5;
1067 | int x6;
1068 | int x7;
1069 | int x8;
1070 | int x9;
1071 | int x10;
1072 | int x11;
1073 | int x12;
1074 | int x13;
1075 | int x14;
1076 | int x15;
1077 | int x16;
1078 | int x17;
1079 | int x18;
1080 | int x19;
1081 | int x20;
1082 | int x21;
1083 | int x22;
1084 | x1 = (this.t[0]);
1085 | x2 = ((x1 >>> 26) + (this.t[1]));
1086 | x3 = ((x2 >>> 25) + (this.t[2]));
1087 | x4 = ((x3 >>> 26) + (this.t[3]));
1088 | x5 = ((x4 >>> 25) + (this.t[4]));
1089 | x6 = ((x5 >>> 26) + (this.t[5]));
1090 | x7 = ((x6 >>> 25) + (this.t[6]));
1091 | x8 = ((x7 >>> 26) + (this.t[7]));
1092 | x9 = ((x8 >>> 25) + (this.t[8]));
1093 | x10 = ((x9 >>> 26) + (this.t[9]));
1094 | x11 = ((x1 & 0x3ffffff) + ((x10 >> 25) * (byte) 0x13));
1095 | x12 = ((byte)(x11 >>> 26) + (x2 & 0x1ffffff));
1096 | x13 = (x11 & 0x3ffffff);
1097 | x14 = (x12 & 0x1ffffff);
1098 | x15 = ((byte)(x12 >>> 25) + (x3 & 0x3ffffff));
1099 | x16 = (x4 & 0x1ffffff);
1100 | x17 = (x5 & 0x3ffffff);
1101 | x18 = (x6 & 0x1ffffff);
1102 | x19 = (x7 & 0x3ffffff);
1103 | x20 = (x8 & 0x1ffffff);
1104 | x21 = (x9 & 0x3ffffff);
1105 | x22 = (x10 & 0x1ffffff);
1106 | int[] out1 = new int[10];
1107 | out1[0] = x13;
1108 | out1[1] = x14;
1109 | out1[2] = x15;
1110 | out1[3] = x16;
1111 | out1[4] = x17;
1112 | out1[5] = x18;
1113 | out1[6] = x19;
1114 | out1[7] = x20;
1115 | out1[8] = x21;
1116 | out1[9] = x22;
1117 | return new Ed25519FieldElement(this.f, out1);
1118 | }
1119 |
1120 | @Override
1121 | public int hashCode() {
1122 | return Arrays.hashCode(t);
1123 | }
1124 |
1125 | @Override
1126 | public boolean equals(Object obj) {
1127 | if (!(obj instanceof Ed25519FieldElement))
1128 | return false;
1129 | Ed25519FieldElement fe = (Ed25519FieldElement) obj;
1130 | return 1==Utils.equal(toByteArray(), fe.toByteArray());
1131 | }
1132 |
1133 | @Override
1134 | public String toString() {
1135 | // return "[Ed25519FieldElement val="+Utils.bytesToHex(toByteArray())+"]";
1136 | StringBuilder sb = new StringBuilder();
1137 | for (int i : t) sb.append(i & 0xFFFF_FFFFL).append(" ");
1138 | return sb.toString();
1139 | }
1140 | }
1141 |
--------------------------------------------------------------------------------
/java/src/main/java/io/github/muntashirakon/crypto/ed25519/GroupElement.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 Muntashir Al-Islam
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 |
7 | package io.github.muntashirakon.crypto.ed25519;
8 |
9 | import java.io.Serializable;
10 | import java.util.Arrays;
11 |
12 | /**
13 | * A point $(x,y)$ on an EdDSA curve.
14 | *
15 | * Reviewed/commented by Bloody Rookie (nemproject@gmx.de)
16 | *
17 | * Literature:
200 | * Variable is package private only so that tests run.
201 | */
202 | final GroupElement[][] precmp;
203 |
204 | /**
205 | * Precomputed table for {@link #doubleScalarMultiplyVariableTime(GroupElement, byte[], byte[])},
206 | * filled if necessary.
207 | *
208 | * Variable is package private only so that tests run.
209 | */
210 | final GroupElement[] dblPrecmp;
211 |
212 | /**
213 | * Creates a group element for a curve, without any pre-computation.
214 | *
215 | * @param curve The curve.
216 | * @param repr The representation used to represent the group element.
217 | * @param X The $X$ coordinate.
218 | * @param Y The $Y$ coordinate.
219 | * @param Z The $Z$ coordinate.
220 | * @param T The $T$ coordinate.
221 | */
222 | public GroupElement(
223 | final Curve curve,
224 | final Representation repr,
225 | final FieldElement X,
226 | final FieldElement Y,
227 | final FieldElement Z,
228 | final FieldElement T) {
229 | this(curve, repr, X, Y, Z, T, false);
230 | }
231 |
232 | /**
233 | * Creates a group element for a curve, with optional pre-computation.
234 | *
235 | * @param curve The curve.
236 | * @param repr The representation used to represent the group element.
237 | * @param X The $X$ coordinate.
238 | * @param Y The $Y$ coordinate.
239 | * @param Z The $Z$ coordinate.
240 | * @param T The $T$ coordinate.
241 | * @param precomputeDouble If true, populate dblPrecmp, else set to null.
242 | */
243 | public GroupElement(
244 | final Curve curve,
245 | final Representation repr,
246 | final FieldElement X,
247 | final FieldElement Y,
248 | final FieldElement Z,
249 | final FieldElement T,
250 | final boolean precomputeDouble) {
251 | this(curve, repr, X, Y, Z, T, false, precomputeDouble);
252 | }
253 |
254 | public GroupElement(
255 | final Curve curve,
256 | final Representation repr,
257 | final FieldElement X,
258 | final FieldElement Y,
259 | final FieldElement Z,
260 | final FieldElement T,
261 | final boolean precomputeSingle, final boolean precomputeDouble) {
262 | this.curve = curve;
263 | this.repr = repr;
264 | this.X = X;
265 | this.Y = Y;
266 | this.Z = Z;
267 | this.T = T;
268 | this.precmp = precomputeSingle ? precomputeSingle() : null;
269 | this.dblPrecmp = precomputeDouble ? precomputeDouble() : null;
270 | }
271 |
272 | /**
273 | * Creates a group element for a curve from a given encoded point. No pre-computation.
274 | *
275 | * A point $(x,y)$ is encoded by storing $y$ in bit 0 to bit 254 and the sign of $x$ in bit 255.
276 | * $x$ is recovered in the following way:
277 | *
295 | * A point $(x,y)$ is encoded by storing $y$ in bit 0 to bit 254 and the sign of $x$ in bit 255.
296 | * $x$ is recovered in the following way:
297 | *
480 | * Supported conversions:
481 | *
590 | * $r$ in $P \times P$ representation:
591 | *
592 | * $r = ((X' : Z'), (Y' : T'))$ where
593 | *
599 | * $r$ converted from $P \times P$ to $P^2$ representation:
600 | *
601 | * $r = (X'' : Y'' : Z'')$ where
602 | *
607 | * Formula for the $P^2$ representation is in agreement with the formula given in [4] page 12 (with $a = -1$)
608 | * up to a common factor -1 which does not matter:
609 | *
610 | * $$
611 | * B = (X + Y)^2; C = X^2; D = Y^2; E = -C = -X^2; F := E + D = Y^2 - X^2; H = Z^2; J = F − 2 * H; \\
612 | * X3 = (B − C − D) · J = X' * (-T'); \\
613 | * Y3 = F · (E − D) = Z' * (-Y'); \\
614 | * Z3 = F · J = Z' * (-T').
615 | * $$
616 | *
617 | * @return The P1P1 representation
618 | */
619 | public GroupElement dbl() {
620 | switch (this.repr) {
621 | case P2:
622 | case P3: // Ignore T for P3 representation
623 | FieldElement XX, YY, B, A, AA, Yn, Zn;
624 | XX = this.X.square();
625 | YY = this.Y.square();
626 | B = this.Z.squareAndDouble();
627 | A = this.X.add(this.Y);
628 | AA = A.square();
629 | Yn = YY.add(XX);
630 | Zn = YY.subtract(XX);
631 | return p1p1(this.curve, AA.subtract(Yn), Yn, Zn, B.subtract(Zn));
632 | default:
633 | throw new UnsupportedOperationException();
634 | }
635 | }
636 |
637 | /**
638 | * GroupElement addition using the twisted Edwards addition law with
639 | * extended coordinates (Hisil2008).
640 | *
641 | * this must be in $P^3$ representation and $q$ in PRECOMP representation.
642 | * $r = p + q$ where $p = this = (X1 : Y1 : Z1 : T1), q = (q.X, q.Y, q.Z) = (Y2/Z2 + X2/Z2, Y2/Z2 - X2/Z2, 2 * d * X2/Z2 * Y2/Z2)$
643 | *
644 | * $r$ in $P \times P$ representation:
645 | *
646 | * $r = ((X' : Z'), (Y' : T'))$ where
647 | *
653 | * Setting $A = (Y1 - X1) * (Y2 - X2), B = (Y1 + X1) * (Y2 + X2), C = 2 * d * T1 * T2, D = 2 * Z1 * Z2$ we get
654 | *
660 | * $r$ converted from $P \times P$ to $P^2$ representation:
661 | *
662 | * $r = (X'' : Y'' : Z'' : T'')$ where
663 | *
669 | * TODO-CR BR: Formula for the $P^2$ representation is not in agreement with the formula given in [2] page 6
702 | * this must be in $P^3$ representation and $q$ in PRECOMP representation.
703 | * $r = p - q$ where $p = this = (X1 : Y1 : Z1 : T1), q = (q.X, q.Y, q.Z) = (Y2/Z2 + X2/Z2, Y2/Z2 - X2/Z2, 2 * d * X2/Z2 * Y2/Z2)$
704 | *
705 | * Negating $q$ means negating the value of $X2$ and $T2$ (the latter is irrelevant here).
706 | * The formula is in accordance to {@link #madd the above addition}.
707 | *
708 | * @param q the PRECOMP representation of the GroupElement to subtract.
709 | * @return the P1P1 representation of the result.
710 | */
711 | public GroupElement msub(GroupElement q) {
712 | if (this.repr != Representation.P3)
713 | throw new UnsupportedOperationException();
714 | if (q.repr != Representation.PRECOMP)
715 | throw new IllegalArgumentException();
716 |
717 | FieldElement YpX, YmX, A, B, C, D;
718 | YpX = this.Y.add(this.X);
719 | YmX = this.Y.subtract(this.X);
720 | A = YpX.multiply(q.Y); // q->y-x
721 | B = YmX.multiply(q.X); // q->y+x
722 | C = q.Z.multiply(this.T); // q->2dxy
723 | D = this.Z.add(this.Z);
724 | return p1p1(this.curve, A.subtract(B), A.add(B), D.subtract(C), D.add(C));
725 | }
726 |
727 | /**
728 | * GroupElement addition using the twisted Edwards addition law with
729 | * extended coordinates (Hisil2008).
730 | *
731 | * this must be in $P^3$ representation and $q$ in CACHED representation.
732 | * $r = p + q$ where $p = this = (X1 : Y1 : Z1 : T1), q = (q.X, q.Y, q.Z, q.T) = (Y2 + X2, Y2 - X2, Z2, 2 * d * T2)$
733 | *
734 | * $r$ in $P \times P$ representation:
735 | *
741 | * Setting $A = (Y1 - X1) * (Y2 - X2), B = (Y1 + X1) * (Y2 + X2), C = 2 * d * T1 * T2, D = 2 * Z1 * Z2$ we get
742 | *
748 | * Same result as in {@link #madd} (up to a common factor which does not matter).
749 | *
750 | * @param q the CACHED representation of the GroupElement to add.
751 | * @return the P1P1 representation of the result.
752 | */
753 | public GroupElement add(GroupElement q) {
754 | if (this.repr != Representation.P3)
755 | throw new UnsupportedOperationException();
756 | if (q.repr != Representation.CACHED)
757 | throw new IllegalArgumentException();
758 |
759 | FieldElement YpX, YmX, A, B, C, ZZ, D;
760 | YpX = this.Y.add(this.X);
761 | YmX = this.Y.subtract(this.X);
762 | A = YpX.multiply(q.X); // q->Y+X
763 | B = YmX.multiply(q.Y); // q->Y-X
764 | C = q.T.multiply(this.T); // q->2dT
765 | ZZ = this.Z.multiply(q.Z);
766 | D = ZZ.add(ZZ);
767 | return p1p1(this.curve, A.subtract(B), A.add(B), D.add(C), D.subtract(C));
768 | }
769 |
770 | /**
771 | * GroupElement subtraction using the twisted Edwards addition law with
772 | * extended coordinates (Hisil2008).
773 | *
774 | * $r = p - q$
775 | *
776 | * Negating $q$ means negating the value of the coordinate $X2$ and $T2$.
777 | * The formula is in accordance to {@link #add the above addition}.
778 | *
779 | * @param q the PRECOMP representation of the GroupElement to subtract.
780 | * @return the P1P1 representation of the result.
781 | */
782 | public GroupElement sub(GroupElement q) {
783 | if (this.repr != Representation.P3)
784 | throw new UnsupportedOperationException();
785 | if (q.repr != Representation.CACHED)
786 | throw new IllegalArgumentException();
787 |
788 | FieldElement YpX, YmX, trZ, trY, trT, trX, D;
789 | YpX = Y.add(X);
790 | YmX = Y.subtract(X);
791 | trZ = YpX.multiply(q.Y); // q->Y-X
792 | trY = YmX.multiply(q.X); // q->Y+X
793 | trT = q.T.multiply(T); // q->2dT
794 | trX = Z.multiply(q.Z);
795 | D = trX.add(trX);
796 | System.out.println("===> " + D.toString());
797 | return p1p1(curve, trZ.subtract(trY), trZ.add(trY), D.subtract(trT), D.add(trT));
798 | }
799 |
800 | /**
801 | * Negates this group element by subtracting it from the neutral group element.
802 | *
803 | * TODO-CR BR: why not simply negate the coordinates $X$ and $T$?
804 | *
805 | * @return The negative of this group element.
806 | */
807 | public GroupElement negate() {
808 | if (this.repr != Representation.P3)
809 | throw new UnsupportedOperationException();
810 | return this.curve.getZero(Representation.P3).sub(toCached()).toP3PrecomputeDouble();
811 | }
812 |
813 | @Override
814 | public int hashCode() {
815 | return Arrays.hashCode(this.toByteArray());
816 | }
817 |
818 | @Override
819 | public boolean equals(Object obj) {
820 | if (obj == this)
821 | return true;
822 | if (!(obj instanceof GroupElement))
823 | return false;
824 | GroupElement ge = (GroupElement) obj;
825 | if (!this.repr.equals(ge.repr)) {
826 | try {
827 | ge = ge.toRep(this.repr);
828 | } catch (RuntimeException e) {
829 | return false;
830 | }
831 | }
832 | switch (this.repr) {
833 | case P2:
834 | case P3:
835 | // Try easy way first
836 | if (this.Z.equals(ge.Z))
837 | return this.X.equals(ge.X) && this.Y.equals(ge.Y);
838 | // X1/Z1 = X2/Z2 --> X1*Z2 = X2*Z1
839 | final FieldElement x1 = this.X.multiply(ge.Z);
840 | final FieldElement y1 = this.Y.multiply(ge.Z);
841 | final FieldElement x2 = ge.X.multiply(this.Z);
842 | final FieldElement y2 = ge.Y.multiply(this.Z);
843 | return x1.equals(x2) && y1.equals(y2);
844 | case P1P1:
845 | return toP2().equals(ge);
846 | case PRECOMP:
847 | // Compare directly, PRECOMP is derived directly from x and y
848 | return this.X.equals(ge.X) && this.Y.equals(ge.Y) && this.Z.equals(ge.Z);
849 | case CACHED:
850 | // Try easy way first
851 | if (this.Z.equals(ge.Z))
852 | return this.X.equals(ge.X) && this.Y.equals(ge.Y) && this.T.equals(ge.T);
853 | // (Y+X)/Z = y+x etc.
854 | final FieldElement x3 = this.X.multiply(ge.Z);
855 | final FieldElement y3 = this.Y.multiply(ge.Z);
856 | final FieldElement t3 = this.T.multiply(ge.Z);
857 | final FieldElement x4 = ge.X.multiply(this.Z);
858 | final FieldElement y4 = ge.Y.multiply(this.Z);
859 | final FieldElement t4 = ge.T.multiply(this.Z);
860 | return x3.equals(x4) && y3.equals(y4) && t3.equals(t4);
861 | default:
862 | return false;
863 | }
864 | }
865 |
866 | /**
867 | * Convert a to radix 16.
868 | *
869 | * Method is package private only so that tests run.
870 | *
871 | * @param a $= a[0]+256*a[1]+...+256^{31} a[31]$
872 | * @return 64 bytes, each between -8 and 7
873 | */
874 | static byte[] toRadix16(final byte[] a) {
875 | final byte[] e = new byte[64];
876 | int i;
877 | // Radix 16 notation
878 | for (i = 0; i < 32; i++) {
879 | e[2*i+0] = (byte) (a[i] & 15);
880 | e[2*i+1] = (byte) ((a[i] >> 4) & 15);
881 | }
882 | /* each e[i] is between 0 and 15 */
883 | /* e[63] is between 0 and 7 */
884 | int carry = 0;
885 | for (i = 0; i < 63; i++) {
886 | e[i] += carry;
887 | carry = e[i] + 8;
888 | carry >>= 4;
889 | e[i] -= carry << 4;
890 | }
891 | e[63] += carry;
892 | /* each e[i] is between -8 and 7 */
893 | return e;
894 | }
895 |
896 | /**
897 | * Constant-time conditional move.
898 | *
899 | * Replaces this with $u$ if $b == 1$.
902 | * Method is package private only so that tests run.
903 | *
904 | * @param u The group element to return if $b == 1$.
905 | * @param b in $\{0, 1\}$
906 | * @return $u$ if $b == 1$; this if $b == 0$. Results undefined if $b$ is not in $\{0, 1\}$.
907 | */
908 | public GroupElement cmov(final GroupElement u, final int b) {
909 | return precomp(curve, X.cmov(u.X, b), Y.cmov(u.Y, b), Z.cmov(u.Z, b));
910 | }
911 |
912 | /**
913 | * Look up $16^i r_i B$ in the precomputed table.
914 | *
915 | * No secret array indices, no secret branching.
916 | * Constant time.
917 | *
918 | * Must have previously precomputed.
919 | *
920 | * Method is package private only so that tests run.
921 | *
922 | * @param pos $= i/2$ for $i$ in $\{0, 2, 4,..., 62\}$
923 | * @param b $= r_i$
924 | * @return the GroupElement
925 | */
926 | GroupElement select(final int pos, final int b) {
927 | // Is r_i negative?
928 | final int bnegative = Utils.negative(b);
929 | // |r_i|
930 | final int babs = b - (((-bnegative) & b) << 1);
931 |
932 | // 16^i |r_i| B
933 | final GroupElement t = this.curve.getZero(Representation.PRECOMP)
934 | .cmov(this.precmp[pos][0], Utils.equal(babs, 1))
935 | .cmov(this.precmp[pos][1], Utils.equal(babs, 2))
936 | .cmov(this.precmp[pos][2], Utils.equal(babs, 3))
937 | .cmov(this.precmp[pos][3], Utils.equal(babs, 4))
938 | .cmov(this.precmp[pos][4], Utils.equal(babs, 5))
939 | .cmov(this.precmp[pos][5], Utils.equal(babs, 6))
940 | .cmov(this.precmp[pos][6], Utils.equal(babs, 7))
941 | .cmov(this.precmp[pos][7], Utils.equal(babs, 8));
942 | // -16^i |r_i| B
943 | final GroupElement tminus = precomp(curve, t.Y, t.X, t.Z.negate());
944 | // 16^i r_i B
945 | return t.cmov(tminus, bnegative);
946 | }
947 |
948 | /**
949 | * $h = a * B$ where $a = a[0]+256*a[1]+\dots+256^{31} a[31]$ and
950 | * $B$ is this point. If its lookup table has not been precomputed, it
951 | * will be at the start of the method (and cached for later calls).
952 | * Constant time.
953 | *
954 | * Preconditions: (TODO: Check this applies here)
955 | * $a[31] \le 127$
956 | * @param a $= a[0]+256*a[1]+\dots+256^{31} a[31]$
957 | * @return the GroupElement
958 | */
959 | public GroupElement scalarMultiply(final byte[] a) {
960 | GroupElement t;
961 | int i;
962 |
963 | final byte[] e = toRadix16(a);
964 |
965 | GroupElement h = this.curve.getZero(Representation.P3);
966 | for (i = 1; i < 64; i += 2) {
967 | t = select(i/2, e[i]);
968 | h = h.madd(t).toP3();
969 | }
970 |
971 | h = h.dbl().toP2().dbl().toP2().dbl().toP2().dbl().toP3();
972 |
973 | for (i = 0; i < 64; i += 2) {
974 | t = select(i/2, e[i]);
975 | h = h.madd(t).toP3();
976 | }
977 |
978 | return h;
979 | }
980 |
981 | /**
982 | * Calculates a sliding-windows base 2 representation for a given value $a$.
983 | * To learn more about it see [6] page 8.
984 | *
985 | * Output: $r$ which satisfies
986 | * $a = r0 * 2^0 + r1 * 2^1 + \dots + r255 * 2^{255}$ with $ri$ in $\{-15, -13, -11, -9, -7, -5, -3, -1, 0, 1, 3, 5, 7, 9, 11, 13, 15\}$
987 | *
988 | * Method is package private only so that tests run.
989 | *
990 | * @param a $= a[0]+256*a[1]+\dots+256^{31} a[31]$.
991 | * @return The byte array $r$ in the above described form.
992 | */
993 | static byte[] slide(final byte[] a) {
994 | byte[] r = new byte[256];
995 |
996 | // Put each bit of 'a' into a separate byte, 0 or 1
997 | for (int i = 0; i < 256; ++i) {
998 | r[i] = (byte) (1 & (a[i >> 3] >> (i & 7)));
999 | }
1000 |
1001 | // Note: r[i] will always be odd.
1002 | for (int i = 0; i < 256; ++i) {
1003 | if (r[i] != 0) {
1004 | for (int b = 1; b <= 6 && i + b < 256; ++b) {
1005 | // Accumulate bits if possible
1006 | if (r[i + b] != 0) {
1007 | if (r[i] + (r[i + b] << b) <= 15) {
1008 | r[i] += r[i + b] << b;
1009 | r[i + b] = 0;
1010 | } else if (r[i] - (r[i + b] << b) >= -15) {
1011 | r[i] -= r[i + b] << b;
1012 | for (int k = i + b; k < 256; ++k) {
1013 | if (r[k] == 0) {
1014 | r[k] = 1;
1015 | break;
1016 | }
1017 | r[k] = 0;
1018 | }
1019 | } else
1020 | break;
1021 | }
1022 | }
1023 | }
1024 | }
1025 |
1026 | return r;
1027 | }
1028 |
1029 | /**
1030 | * $r = a * A + b * B$ where $a = a[0]+256*a[1]+\dots+256^{31} a[31]$,
1031 | * $b = b[0]+256*b[1]+\dots+256^{31} b[31]$ and $B$ is this point.
1032 | *
1033 | * $A$ must have been previously precomputed.
1034 | *
1035 | * @param A in P3 representation.
1036 | * @param a $= a[0]+256*a[1]+\dots+256^{31} a[31]$
1037 | * @param b $= b[0]+256*b[1]+\dots+256^{31} b[31]$
1038 | * @return the GroupElement
1039 | */
1040 | public GroupElement doubleScalarMultiplyVariableTime(final GroupElement A, final byte[] a, final byte[] b) {
1041 | // TODO-CR BR: A check that this is the base point is needed.
1042 | final byte[] aslide = slide(a);
1043 | final byte[] bslide = slide(b);
1044 |
1045 | GroupElement r = this.curve.getZero(Representation.P2);
1046 |
1047 | int i;
1048 | for (i = 255; i >= 0; --i) {
1049 | if (aslide[i] != 0 || bslide[i] != 0) break;
1050 | }
1051 |
1052 | for (; i >= 0; --i) {
1053 | GroupElement t = r.dbl();
1054 |
1055 | if (aslide[i] > 0) {
1056 | t = t.toP3().madd(A.dblPrecmp[aslide[i]/2]);
1057 | } else if(aslide[i] < 0) {
1058 | t = t.toP3().msub(A.dblPrecmp[(-aslide[i])/2]);
1059 | }
1060 |
1061 | if (bslide[i] > 0) {
1062 | t = t.toP3().madd(this.dblPrecmp[bslide[i]/2]);
1063 | } else if(bslide[i] < 0) {
1064 | t = t.toP3().msub(this.dblPrecmp[(-bslide[i])/2]);
1065 | }
1066 |
1067 | r = t.toP2();
1068 | }
1069 |
1070 | return r;
1071 | }
1072 |
1073 | /**
1074 | * Verify that a point is on its curve.
1075 | * @return true if the point lies on its curve.
1076 | */
1077 | public boolean isOnCurve() {
1078 | return isOnCurve(curve);
1079 | }
1080 |
1081 | /**
1082 | * Verify that a point is on the curve.
1083 | * @param curve The curve to check.
1084 | * @return true if the point lies on the curve.
1085 | */
1086 | public boolean isOnCurve(Curve curve) {
1087 | switch (repr) {
1088 | case P2:
1089 | case P3:
1090 | FieldElement recip = Z.invert();
1091 | FieldElement x = X.multiply(recip);
1092 | FieldElement y = Y.multiply(recip);
1093 | FieldElement xx = x.square();
1094 | FieldElement yy = y.square();
1095 | FieldElement dxxyy = curve.getD().multiply(xx).multiply(yy);
1096 | return curve.getField().ONE.add(dxxyy).add(xx).equals(yy);
1097 |
1098 | default:
1099 | return toP2().isOnCurve(curve);
1100 | }
1101 | }
1102 |
1103 | @Override
1104 | public String toString() {
1105 | return "[GroupElement\nX="+X+"\nY="+Y+"\nZ="+Z+"\nT="+T+"\n]";
1106 | }
1107 | }
1108 |
--------------------------------------------------------------------------------
{
364 | private S first;
365 | private T second;
366 |
367 | public Pair(S first, T second) {
368 | this.first = first;
369 | this.second = second;
370 | }
371 | }
372 | }
--------------------------------------------------------------------------------
/java/src/main/java/io/github/muntashirakon/crypto/spake2/Spake2Context.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2021 Muntashir Al-Islam
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 |
7 | package io.github.muntashirakon.crypto.spake2;
8 |
9 | import java.security.MessageDigest;
10 | import java.security.NoSuchAlgorithmException;
11 | import java.security.SecureRandom;
12 | import java.util.Arrays;
13 |
14 | import javax.security.auth.Destroyable;
15 |
16 | import io.github.muntashirakon.crypto.ed25519.Curve;
17 | import io.github.muntashirakon.crypto.ed25519.Ed25519;
18 | import io.github.muntashirakon.crypto.ed25519.Ed25519CurveParameterSpec;
19 | import io.github.muntashirakon.crypto.ed25519.Ed25519Field;
20 | import io.github.muntashirakon.crypto.ed25519.FieldElement;
21 | import io.github.muntashirakon.crypto.ed25519.GroupElement;
22 | import io.github.muntashirakon.crypto.ed25519.Utils;
23 | import io.github.muntashirakon.crypto.x25519.x25519Scalar;
24 |
25 | @SuppressWarnings("unused")
26 | public class Spake2Context implements Destroyable {
27 | /**
28 | * Maximum message size in bytes
29 | */
30 | public static final int MAX_MSG_SIZE = 32;
31 | /**
32 | * Maximum key size in bytes
33 | */
34 | public static final int MAX_KEY_SIZE = 64;
35 |
36 | private static byte[] intToByteArray(int[] ints) {
37 | byte[] bytes = new byte[ints.length];
38 | for (int i = 0; i < bytes.length; ++i) {
39 | bytes[i] = (byte) ints[i];
40 | }
41 | return bytes;
42 | }
43 |
44 | // Package private for testing purposes
45 | static GroupElement[] getGEFromTable(Curve curve, int[] intTable) {
46 | byte[] table = intToByteArray(intTable);
47 | Ed25519Field f = curve.getField();
48 | GroupElement[] ge = new GroupElement[table.length / (3 * 32)];
49 | byte[] bytes = new byte[32];
50 | for (int i = 0; i < ge.length; ++i) {
51 | System.arraycopy(table, i * 96, bytes, 0, 32);
52 | FieldElement ypx = f.fromByteArray(bytes);
53 | System.arraycopy(table, i * 96 + 32, bytes, 0, 32);
54 | FieldElement ymx = f.fromByteArray(bytes);
55 | System.arraycopy(table, i * 96 + 64, bytes, 0, 32);
56 | FieldElement xy2d = f.fromByteArray(bytes);
57 | ge[i] = GroupElement.precomp(curve, ypx, ymx, xy2d);
58 | }
59 | return ge;
60 | }
61 |
62 | // https://datatracker.ietf.org/doc/html/draft-ietf-kitten-krb-spake-preauth-01#appendix-B
63 | private static final String SEED_N = "edwards25519 point generation seed (N)";
64 | private static final String SEED_M = "edwards25519 point generation seed (M)";
65 |
66 | private static final int[] PRECOMP_TABLE_N = new int[]{
67 | 0x43, 0xFE, 0xA4, 0xBE, 0x26, 0x95, 0xFF, 0x8A, 0xDD, 0xD3, 0x71, 0x28, 0x63, 0x86, 0x31,
68 | 0xB0, 0x32, 0x02, 0xA5, 0xD1, 0x95, 0x80, 0x6A, 0xCF, 0xFB, 0x4C, 0x07, 0xFC, 0xB6, 0x3B,
69 | 0x24, 0x67, 0xF0, 0xC7, 0x1A, 0x57, 0x9F, 0x66, 0x1D, 0x6A, 0x55, 0x97, 0x8B, 0xC1, 0x05,
70 | 0x07, 0xB3, 0x70, 0x47, 0x78, 0x14, 0xAA, 0x77, 0xDD, 0x66, 0x4B, 0xE6, 0x17, 0x2D, 0x27,
71 | 0x1B, 0x3B, 0x6A, 0x0A, 0x81, 0x39, 0x04, 0x13, 0x80, 0x6B, 0x54, 0x7E, 0x26, 0xF3, 0xB0,
72 | 0xA2, 0xCA, 0x96, 0x32, 0x8E, 0x98, 0x44, 0x30, 0xFF, 0x8C, 0x2D, 0x67, 0x77, 0x16, 0xF1,
73 | 0xE9, 0x96, 0x8B, 0x33, 0x06, 0x61, 0xFB, 0x78, 0x6B, 0xFB, 0xD8, 0x00, 0x66, 0x8B, 0x17,
74 | 0x5A, 0x26, 0x2D, 0x71, 0xCD, 0x03, 0xA0, 0x14, 0x93, 0xB3, 0xFF, 0xED, 0x68, 0x29, 0xE4,
75 | 0x84, 0x37, 0x81, 0xBB, 0xCD, 0x06, 0x30, 0x40, 0xC2, 0x95, 0xBB, 0xCC, 0xFB, 0x1F, 0x88,
76 | 0x00, 0xA9, 0xC5, 0x3D, 0x15, 0x41, 0x2B, 0x19, 0x00, 0x46, 0x1A, 0xFB, 0x25, 0xE4, 0x8D,
77 | 0x87, 0x0C, 0xEF, 0x3F, 0xC9, 0xBB, 0xCD, 0x2F, 0xDE, 0x78, 0x7F, 0xA8, 0x5C, 0xA8, 0xF3,
78 | 0x4D, 0x18, 0xE9, 0xF4, 0x80, 0x8C, 0x6C, 0x1F, 0x5B, 0xFF, 0xFD, 0x33, 0xDF, 0x6E, 0xFC,
79 | 0xE1, 0xA2, 0x1E, 0xF2, 0xC1, 0xA8, 0x2E, 0x60, 0x50, 0x2B, 0x1C, 0x7B, 0x5C, 0x00, 0xCC,
80 | 0x18, 0x35, 0x4C, 0x36, 0xF8, 0x19, 0xC9, 0x07, 0xF4, 0x99, 0xB9, 0x88, 0xEF, 0x58, 0x71,
81 | 0x33, 0xCB, 0x98, 0x9C, 0xC6, 0xF3, 0x5E, 0x21, 0x68, 0xAC, 0x8C, 0xA0, 0x2A, 0x0A, 0xAA,
82 | 0xB1, 0x9C, 0xDE, 0x59, 0x43, 0xAB, 0x7B, 0x1A, 0x9E, 0x62, 0x8A, 0xFF, 0xA5, 0x5D, 0x6E,
83 | 0x77, 0xC9, 0x73, 0xED, 0x77, 0x08, 0x00, 0xFC, 0xC1, 0x21, 0xE7, 0x5D, 0xFE, 0x48, 0xD4,
84 | 0x3C, 0x8D, 0x18, 0x5C, 0xE6, 0xCB, 0x41, 0xC7, 0x61, 0x03, 0x23, 0x53, 0x96, 0x08, 0x26,
85 | 0xC3, 0x7C, 0x0B, 0x8E, 0x55, 0xC4, 0xF9, 0x21, 0x44, 0x72, 0x56, 0xBE, 0xA3, 0xF0, 0x37,
86 | 0xF1, 0x25, 0x54, 0xA6, 0x3B, 0xB1, 0x4D, 0xF9, 0x8B, 0xE4, 0x1F, 0x77, 0x28, 0x39, 0x9C,
87 | 0x83, 0x9B, 0xC3, 0xE4, 0xAA, 0x46, 0x8F, 0x14, 0x14, 0x6B, 0x9E, 0x8C, 0x9C, 0xF6, 0x3D,
88 | 0x9D, 0xDC, 0x5C, 0xC3, 0x61, 0x64, 0x15, 0xAE, 0xD1, 0x2A, 0x2E, 0x88, 0xF5, 0x8F, 0x0C,
89 | 0xB5, 0x50, 0x39, 0x29, 0x51, 0x6A, 0xCE, 0x29, 0x7C, 0x99, 0x42, 0xF3, 0xCE, 0xCF, 0x83,
90 | 0x29, 0x27, 0xD6, 0x8B, 0x20, 0x27, 0x79, 0x62, 0xF4, 0x71, 0xD1, 0xBA, 0x68, 0x8C, 0x31,
91 | 0x32, 0xAC, 0xE6, 0x16, 0xF7, 0x8F, 0x18, 0x70, 0x75, 0x11, 0x81, 0xDA, 0x82, 0xD1, 0x69,
92 | 0xC8, 0x2E, 0xB2, 0xBE, 0x0E, 0x5B, 0x73, 0xAB, 0x2A, 0x37, 0x6E, 0x2C, 0x1D, 0x69, 0xEA,
93 | 0x9D, 0x0D, 0x92, 0x96, 0xC9, 0xDD, 0x14, 0xEF, 0x0D, 0x2B, 0x96, 0xD0, 0xCF, 0x43, 0xC8,
94 | 0x48, 0xDD, 0x69, 0x78, 0x2F, 0x6B, 0xAD, 0x5C, 0xEB, 0x38, 0x6E, 0xC3, 0x1D, 0xC9, 0xB2,
95 | 0xE4, 0xAC, 0x7F, 0x88, 0x9E, 0x83, 0x5E, 0xB1, 0xC8, 0x4C, 0x7E, 0x13, 0x8E, 0x92, 0x95,
96 | 0x51, 0x8E, 0x3D, 0x91, 0x92, 0x0D, 0xFC, 0xB6, 0x64, 0x82, 0xC8, 0xAD, 0x0A, 0xE5, 0x52,
97 | 0xB5, 0x33, 0x3F, 0x52, 0x77, 0x9E, 0x1F, 0xEA, 0x93, 0x83, 0xBD, 0x38, 0xB5, 0x4F, 0xD1,
98 | 0x9E, 0x56, 0xDA, 0x9E, 0x6B, 0x6A, 0xDA, 0x00, 0xC5, 0x1B, 0xEC, 0x7B, 0xE2, 0x8B, 0x78,
99 | 0x86, 0x61, 0x66, 0x29, 0xDE, 0x54, 0xA9, 0xCF, 0xC8, 0x59, 0x66, 0xCF, 0xB5, 0xA2, 0x61,
100 | 0x99, 0x81, 0x6D, 0xD1, 0xAE, 0xAD, 0xE3, 0xC6, 0x09, 0x84, 0x5D, 0xF0, 0x82, 0xB7, 0x05,
101 | 0x34, 0x39, 0x67, 0x75, 0x75, 0x92, 0x90, 0x39, 0xB7, 0x10, 0x78, 0x43, 0xB0, 0xB6, 0x78,
102 | 0xD9, 0x0B, 0xF6, 0x4C, 0xE8, 0xF2, 0x8B, 0x0D, 0x69, 0xC4, 0x5D, 0x40, 0xDD, 0xBC, 0xC5,
103 | 0x82, 0x35, 0x30, 0x6E, 0xA9, 0x4F, 0x2E, 0x5F, 0x7A, 0x08, 0x9E, 0xFE, 0x44, 0xAE, 0xB9,
104 | 0x81, 0x13, 0x0C, 0x75, 0x77, 0xE5, 0x48, 0x54, 0x38, 0x8F, 0x8F, 0x87, 0x9B, 0xA8, 0xEF,
105 | 0x62, 0x60, 0x56, 0x90, 0x9F, 0x08, 0x6A, 0x5D, 0xF5, 0xF5, 0xE3, 0x6E, 0xAA, 0xC8, 0x34,
106 | 0x1B, 0xBF, 0xFD, 0x65, 0xCD, 0x61, 0x93, 0xCF, 0x6E, 0x2C, 0x97, 0x22, 0x7A, 0x23, 0x91,
107 | 0x02, 0xF6, 0x1E, 0x3E, 0x21, 0xC6, 0xEB, 0x66, 0xCC, 0x2D, 0x29, 0x6F, 0x41, 0x9D, 0xFF,
108 | 0x50, 0x06, 0x49, 0x90, 0xB1, 0x58, 0x91, 0x3C, 0x23, 0xD6, 0xC3, 0xCC, 0x19, 0xE0, 0x43,
109 | 0xC1, 0xBA, 0xEA, 0xAD, 0xED, 0x04, 0x3D, 0xF0, 0x2E, 0x6E, 0xEF, 0xF2, 0xD2, 0x28, 0xA5,
110 | 0xE3, 0x13, 0xCB, 0xBE, 0xEE, 0xE6, 0xC4, 0x23, 0xE0, 0xEA, 0xAF, 0xB3, 0xEF, 0x87, 0x83,
111 | 0x51, 0xC2, 0x5A, 0xCC, 0xC5, 0x38, 0xA5, 0xA1, 0xE5, 0xF5, 0x0A, 0x77, 0x46, 0xF1, 0xA0,
112 | 0x6A, 0x27, 0xFC, 0xB8, 0xA1, 0x37, 0x62, 0x21, 0x47, 0xD1, 0xCE, 0x24, 0xB6, 0x84, 0x3E,
113 | 0x73, 0xFA, 0x11, 0x10, 0x43, 0xB7, 0x1E, 0x4E, 0x4E, 0xB3, 0xF7, 0xE7, 0x15, 0x1F, 0xD7,
114 | 0xE9, 0x37, 0x90, 0x76, 0x65, 0xDA, 0xA3, 0xB7, 0x7A, 0x66, 0x90, 0x47, 0x9F, 0xCF, 0xAB,
115 | 0x9D, 0x64, 0xE6, 0x75, 0xAB, 0xEE, 0xBF, 0x07, 0xF0, 0xAC, 0xE3, 0x50, 0xBC, 0x78, 0x86,
116 | 0x25, 0x41, 0x26, 0x27, 0x53, 0xE4, 0xF8, 0x12, 0xE6, 0x89, 0x7B, 0x19, 0x7E, 0xC3, 0x1C,
117 | 0x39, 0x91, 0x04, 0xF6, 0xC3, 0x70, 0x28, 0xE3, 0xA6, 0x18, 0xC8, 0xB9, 0xB4, 0xF0, 0x6A,
118 | 0x88, 0xD6, 0x6A, 0xA8, 0xFE, 0x56, 0x63, 0xF4, 0x23, 0xEB, 0x2E, 0x62, 0x21, 0x54, 0x16,
119 | 0xD4, 0xFC, 0x72, 0xB7, 0xBF, 0x10, 0x13, 0xE3, 0x04, 0x81, 0x1B, 0x4B, 0xF6, 0x2A, 0x1A,
120 | 0x02, 0xE2, 0xD6, 0x59, 0x3F, 0x1D, 0x33, 0xCA, 0xC5, 0xBE, 0x85, 0x93, 0xDA, 0xB4, 0xA4,
121 | 0xB9, 0x77, 0x5E, 0x37, 0xB4, 0x82, 0x1E, 0x2B, 0x14, 0x03, 0xB3, 0x06, 0x9E, 0x99, 0x46,
122 | 0xB0, 0x2F, 0x52, 0xE7, 0x03, 0x76, 0x5C, 0x19, 0xC0, 0xF7, 0x94, 0xC5, 0x97, 0x1B, 0xE3,
123 | 0xF4, 0xA0, 0x2D, 0xE5, 0x11, 0xA2, 0x9A, 0x5C, 0xFA, 0x2A, 0x55, 0x00, 0x9F, 0xC6, 0xAA,
124 | 0x69, 0xDD, 0x38, 0xBB, 0x28, 0x78, 0x24, 0xB0, 0x18, 0x16, 0xD6, 0x38, 0x65, 0x2F, 0xEE,
125 | 0x6A, 0xC2, 0x7A, 0x56, 0xA7, 0x7B, 0xE7, 0xEE, 0xC0, 0xC7, 0x5D, 0x0A, 0x43, 0x42, 0x03,
126 | 0x59, 0x54, 0x4B, 0x1B, 0x2C, 0xDD, 0xCA, 0x2C, 0x4B, 0x9C, 0x45, 0xD6, 0xDC, 0x51, 0xF0,
127 | 0xF7, 0xE0, 0x1B, 0x4B, 0xD0, 0xFF, 0xF5, 0x1A, 0x05, 0xC3, 0xBE, 0xC0, 0xFE, 0x17, 0x7D,
128 | 0x39, 0xD2, 0x8B, 0x67, 0x9F, 0xD8, 0x7B, 0xF7, 0x7E, 0x64, 0x68, 0x05, 0x21, 0xF4, 0x22,
129 | 0x72, 0x9D, 0xE5, 0xE0, 0x15, 0xB9, 0xA7, 0x80, 0xD9, 0x39, 0x7A, 0xAD, 0xCA, 0x6C, 0x07,
130 | 0x9A, 0x8F, 0xFD, 0x97, 0xB7, 0x50, 0x04, 0x69, 0xD8, 0x01, 0xDD, 0x77, 0x18, 0xA6, 0x60,
131 | 0x1E, 0x86, 0x88, 0x2D, 0x2D, 0xCA, 0xA9, 0xC6, 0xB6, 0x49, 0xAD, 0x15, 0x84, 0xEA, 0xAD,
132 | 0x3F, 0x0A, 0xF8, 0xCD, 0xD1, 0x4B, 0xFD, 0xA8, 0x71, 0xCB, 0xA7, 0xB5, 0xD1, 0xBC, 0x51,
133 | 0x4B, 0x01, 0x09, 0xD3, 0x36, 0xD2, 0x0A, 0xCF, 0x82, 0x72, 0x94, 0xAB, 0xEF, 0x95, 0x6F,
134 | 0x46, 0x34, 0xD8, 0xA5, 0x32, 0x27, 0xFD, 0x5B, 0xEC, 0x21, 0xA5, 0x7C, 0x21, 0xAB, 0xFE,
135 | 0xFC, 0xA6, 0x60, 0x2B, 0x05, 0x17, 0x84, 0xFE, 0xFA, 0x65, 0x21, 0x87, 0x5D, 0xC0, 0x8E,
136 | 0xC2, 0x01, 0xE5, 0x9E, 0xE5, 0x1D, 0x6D, 0x4A, 0xF0, 0x09, 0x4B, 0x0D, 0xE9, 0x9D, 0xA6,
137 | 0xA4, 0xCB, 0x56, 0xFB, 0xCC, 0x55, 0x84, 0x0E, 0x20, 0x6E, 0x2F, 0x5F, 0x7C, 0x49, 0xCA,
138 | 0x46, 0xF6, 0x16, 0x15, 0x7D, 0xE7, 0xF9, 0x73, 0xDD, 0x86, 0xFD, 0xA5, 0x01, 0xE5, 0x68,
139 | 0x5F, 0x0B, 0xA0, 0xBE, 0x38, 0xBF, 0xF5, 0x00, 0x95, 0xE4, 0xF7, 0xC7, 0xA3, 0xE8, 0x8B,
140 | 0x8B, 0xBE, 0xCA, 0x83, 0x9D, 0x66, 0xA7, 0x42, 0x15, 0xBD, 0x6A, 0x74, 0x81, 0xA0, 0xBA,
141 | 0xC4, 0xD4, 0xF0, 0x83, 0xE6, 0x24, 0x7E, 0x2C, 0x12, 0x57, 0x2E, 0x2E, 0x9B, 0xDB, 0xCA,
142 | 0x04, 0x38, 0x9A, 0x49, 0xAB, 0x82, 0x7D, 0x23, 0xB4, 0xC1, 0x1E, 0xCC, 0xC6, 0x55, 0x37,
143 | 0x41, 0x4B, 0x0C, 0xE5, 0xB5, 0x7D, 0xDE, 0xAB, 0xF5, 0xA9, 0xDF, 0x16, 0x0D, 0xE4, 0x9A,
144 | 0x0B, 0xA4, 0x6D, 0x14, 0x3E, 0x55, 0xB3, 0xE7, 0x67, 0x25, 0x8E, 0x31, 0xAF, 0x35, 0x81,
145 | 0x50, 0x3E, 0x5F, 0xAD, 0xAC, 0x76, 0x12, 0x01, 0x3B, 0xBC, 0x94, 0x21, 0xE4, 0x46, 0x5F,
146 | 0x15, 0x51, 0x84, 0x9F, 0x06, 0x1E, 0x79, 0xC8, 0xB8, 0xF8, 0x45, 0x53, 0x5D, 0xC1, 0x60,
147 | 0x01, 0xA2, 0xDF, 0xE5, 0x69, 0x87, 0x21, 0x19, 0x45, 0xB4, 0x53, 0x91, 0x69, 0x05, 0x4C,
148 | 0x3E, 0x45, 0x93, 0x97, 0x2F, 0xE1, 0x24, 0xD0, 0xD6, 0xB3, 0x12, 0xBE, 0x5B, 0xC6, 0x63,
149 | 0x11, 0xD0, 0x23, 0xF9, 0x51, 0x9B, 0xD2, 0xFA, 0xF1, 0x7B, 0x02, 0x71, 0x5E, 0xA3, 0x3C,
150 | 0xF7, 0x71, 0x41, 0xD6, 0x72, 0x90, 0x25, 0x85, 0x82, 0x75, 0x17, 0xF5, 0x31, 0x9B, 0xC8,
151 | 0x60, 0xEE, 0xAD, 0xB4, 0xF6, 0x81, 0x75, 0x95, 0xDE, 0x7D, 0xB3, 0x14, 0x82, 0x9A, 0x69,
152 | 0x21, 0x42, 0x11, 0x01, 0x13, 0x1F, 0xEC, 0xD9, 0x93, 0xA9, 0x6B, 0x07, 0xB4, 0xCE, 0xE8,
153 | 0xEE, 0xF6, 0x16, 0xB0, 0xCF, 0x71, 0xA8, 0x68, 0x9E, 0xDD, 0xD6, 0x0C, 0xA0, 0x4D, 0xFE,
154 | 0xEA, 0x95, 0xAA, 0x3D, 0x3B, 0x58, 0x74, 0xAC, 0x76, 0xA7, 0x2E, 0x66, 0x42, 0x94, 0xB0,
155 | 0xAD, 0x45, 0x12, 0xFD, 0x26, 0x6C, 0x2A, 0xFE, 0xC1, 0x98, 0x5B, 0xFB, 0x31, 0x88, 0xE1,
156 | 0xD2, 0xF1, 0x3B, 0x56, 0xB3, 0x49, 0x97, 0x34, 0x61, 0xD3, 0xE9, 0x52, 0x06, 0x10, 0xAB,
157 | 0x40, 0xC0, 0xC7, 0x72, 0x4D, 0xB8, 0xFF, 0x68, 0x3B, 0x54, 0x72, 0x4C, 0xFE, 0xBA, 0x01,
158 | 0x0C, 0x77, 0xBF, 0x94, 0x5D, 0xC8, 0x70, 0x82, 0x79, 0x70, 0x01, 0x2C, 0xFE, 0x52, 0x44,
159 | 0xA2, 0x0E, 0x59, 0xFA, 0x57, 0x39, 0x48, 0xE6, 0xC1, 0xC3, 0xC1, 0x44, 0x13, 0xD2, 0xC9,
160 | 0x7F, 0xE3, 0x80, 0x91, 0x4E, 0x4F, 0x5A, 0x20, 0x9F, 0x1F, 0x70, 0x43, 0x61, 0x6B, 0x0B,
161 | 0x08, 0x7B, 0xB0, 0x83, 0x18, 0xA7, 0x61, 0x46, 0x21, 0x78, 0xD1, 0x9A, 0xC6, 0xC3, 0xE6,
162 | 0x59, 0xDF, 0x0A, 0x4D, 0xE2, 0x35, 0x31, 0x9F, 0x3A, 0x08, 0x86, 0xE8, 0x08, 0xC4, 0x60,
163 | };
164 |
165 | private static final int[] PRECOMP_TABLE_M = new int[]{
166 | 0x22, 0x81, 0xE2, 0x10, 0x8E, 0xCF, 0xC8, 0xEE, 0x61, 0xC5, 0xAF, 0x20, 0x39, 0x8B, 0x9D,
167 | 0xC8, 0xC6, 0xCD, 0x8A, 0x1B, 0x61, 0x7A, 0xCA, 0x71, 0xE0, 0x6B, 0x02, 0x20, 0x31, 0x67,
168 | 0x41, 0x74, 0x7F, 0x33, 0x1B, 0x86, 0x5E, 0xEC, 0xEA, 0x6C, 0x0B, 0x00, 0x2B, 0x44, 0xED,
169 | 0xAC, 0x38, 0xF0, 0xDB, 0x74, 0x3C, 0x74, 0xE5, 0x14, 0x54, 0x35, 0xBE, 0x33, 0x87, 0x16,
170 | 0xDF, 0x75, 0x62, 0x69, 0x9F, 0x80, 0x69, 0x41, 0x2F, 0x5B, 0xF3, 0x5A, 0xD8, 0x22, 0x94,
171 | 0x18, 0x7B, 0x00, 0xDF, 0x75, 0x27, 0x4C, 0x8F, 0x7D, 0x9F, 0x7B, 0x66, 0x37, 0xC2, 0x5D,
172 | 0x3E, 0xA8, 0xE0, 0x53, 0x1E, 0x09, 0x61, 0x35, 0x5B, 0xA6, 0x9C, 0x02, 0x4F, 0x6B, 0x8A,
173 | 0x7C, 0x82, 0x9A, 0x78, 0x81, 0xB4, 0xB0, 0xB5, 0xFD, 0x1F, 0x19, 0x57, 0x5F, 0xF0, 0x47,
174 | 0x0D, 0x8A, 0x50, 0x3C, 0xCF, 0xDF, 0x86, 0x62, 0x48, 0x23, 0xFC, 0x24, 0xD9, 0xA7, 0x12,
175 | 0x00, 0xB4, 0x5E, 0x7C, 0xDA, 0x08, 0x8A, 0x68, 0x37, 0x2C, 0x81, 0x06, 0x07, 0x42, 0x41,
176 | 0x44, 0x94, 0x7E, 0x4D, 0x83, 0xC0, 0x62, 0xA5, 0x87, 0x58, 0x9E, 0xD3, 0x1F, 0x9E, 0x5F,
177 | 0xC3, 0xF1, 0x0B, 0xD6, 0x87, 0xAE, 0xDC, 0x62, 0x6F, 0x8E, 0x8E, 0x9E, 0x09, 0x19, 0xE4,
178 | 0x8A, 0x8D, 0x66, 0xA9, 0x96, 0x4D, 0x4E, 0x21, 0xEF, 0x6E, 0x2E, 0x66, 0xAB, 0x3F, 0xDE,
179 | 0xCD, 0x85, 0x32, 0x31, 0x3E, 0x4C, 0x48, 0x30, 0xFC, 0x80, 0x7A, 0xCC, 0xD9, 0x5B, 0x6C,
180 | 0xC8, 0xB1, 0x5B, 0xA4, 0x8C, 0x1F, 0x0A, 0xB7, 0xEF, 0x5A, 0x24, 0x20, 0x58, 0x67, 0xE9,
181 | 0x65, 0x8B, 0x79, 0x2E, 0xD0, 0xAC, 0xCC, 0x55, 0x71, 0x53, 0xEA, 0x69, 0x06, 0x2F, 0x88,
182 | 0xC6, 0x38, 0x08, 0x7B, 0x2F, 0xC2, 0x40, 0xE3, 0x6B, 0xEF, 0x22, 0x1E, 0xAE, 0x58, 0x81,
183 | 0x6B, 0x0E, 0xF2, 0xA7, 0xC5, 0xA2, 0x11, 0x5B, 0x59, 0x4B, 0x71, 0x08, 0x80, 0x0E, 0x04,
184 | 0x65, 0x20, 0x4C, 0x61, 0xB4, 0x2F, 0x54, 0xF2, 0xED, 0xE6, 0x19, 0x0A, 0x1F, 0x84, 0xE2,
185 | 0x87, 0x69, 0x39, 0x75, 0xDE, 0x04, 0x6C, 0x10, 0x8D, 0x7E, 0x73, 0xD5, 0xF2, 0x85, 0x86,
186 | 0x48, 0xBB, 0xC5, 0x1B, 0x8C, 0xE4, 0x1F, 0xD5, 0x97, 0x35, 0x5F, 0x18, 0x94, 0xAD, 0xA3,
187 | 0x50, 0x65, 0xDC, 0x7D, 0x33, 0x9C, 0x3A, 0xE6, 0xA4, 0x6A, 0x83, 0xD3, 0x21, 0x09, 0x88,
188 | 0xB8, 0x78, 0x36, 0xBF, 0xE9, 0xBA, 0xA6, 0xB7, 0x5F, 0xBF, 0xFA, 0x71, 0x94, 0xC5, 0x50,
189 | 0x49, 0x13, 0x98, 0x6F, 0xA1, 0xBB, 0x41, 0xAE, 0x41, 0xF2, 0xD0, 0x11, 0x33, 0x87, 0xC1,
190 | 0xF6, 0xB2, 0x9F, 0x33, 0xC8, 0x30, 0x90, 0x4D, 0x9E, 0x67, 0xED, 0x6A, 0xEC, 0xAE, 0x44,
191 | 0xD2, 0x48, 0x22, 0x77, 0x3B, 0xE6, 0xF4, 0x6D, 0x71, 0xF7, 0x4A, 0x88, 0x9A, 0x49, 0x96,
192 | 0x29, 0xFC, 0x29, 0x8B, 0xF7, 0x2D, 0xD7, 0xC9, 0x6D, 0xB1, 0xDC, 0xEA, 0x26, 0xF5, 0x23,
193 | 0x1C, 0x61, 0xD5, 0xFD, 0xE2, 0x23, 0xAC, 0xC4, 0xD4, 0x17, 0x0B, 0xF7, 0x11, 0x07, 0x08,
194 | 0x7D, 0x9E, 0xD3, 0xC7, 0x3F, 0xAA, 0x2C, 0x90, 0xFC, 0xDF, 0xAC, 0xC5, 0x83, 0x69, 0xC3,
195 | 0xD1, 0xA7, 0x37, 0x01, 0xC4, 0x62, 0x16, 0xB7, 0xBE, 0x98, 0x36, 0x9F, 0x32, 0x56, 0x24,
196 | 0xBE, 0x34, 0x45, 0x25, 0xCE, 0x51, 0xF2, 0x41, 0x19, 0x17, 0x46, 0xDE, 0x9D, 0x0F, 0x63,
197 | 0xA8, 0xE9, 0x33, 0xE2, 0xE9, 0x75, 0x00, 0x6E, 0x31, 0xFE, 0x4D, 0x5C, 0x0F, 0x43, 0x6C,
198 | 0xA7, 0x5E, 0xE3, 0xF0, 0xB5, 0x1E, 0xC6, 0xE6, 0x4E, 0xD3, 0x43, 0x5B, 0xC5, 0x1D, 0xA7,
199 | 0x30, 0x03, 0xF0, 0x64, 0x9C, 0x1D, 0xEC, 0xA1, 0xED, 0xA1, 0x5C, 0x5F, 0x75, 0x2C, 0x63,
200 | 0x9F, 0x00, 0x11, 0xB4, 0xB6, 0xA5, 0xFE, 0xDF, 0x9A, 0xA0, 0xFE, 0x66, 0xD3, 0x15, 0x42,
201 | 0xB5, 0xF7, 0xFC, 0xE0, 0xD4, 0x17, 0x20, 0x89, 0xB2, 0x96, 0xF9, 0x4A, 0x9C, 0xD4, 0x68,
202 | 0x15, 0x86, 0xD5, 0x21, 0x4C, 0xBE, 0xE5, 0x8C, 0xFC, 0xA2, 0x91, 0x69, 0xF4, 0xC0, 0x51,
203 | 0xF8, 0xB9, 0x40, 0x64, 0x91, 0x86, 0xBA, 0xEE, 0x1F, 0xB6, 0x26, 0x19, 0xB6, 0xEE, 0xA0,
204 | 0x8B, 0x7A, 0x4C, 0x45, 0x1A, 0x02, 0x65, 0x57, 0x63, 0xF8, 0x85, 0xAA, 0xBF, 0x2E, 0x04,
205 | 0x26, 0x74, 0xB2, 0x53, 0x56, 0x00, 0x38, 0x1D, 0xF7, 0xCE, 0x7C, 0x78, 0x2C, 0x7F, 0xDB,
206 | 0xF0, 0x76, 0x5C, 0x83, 0x2E, 0xDB, 0x8B, 0x3C, 0x16, 0xB7, 0xC7, 0x89, 0x76, 0x67, 0x12,
207 | 0x1F, 0x2B, 0x09, 0x9C, 0x99, 0x80, 0x14, 0x77, 0xAF, 0x37, 0x7A, 0xE8, 0x4D, 0x52, 0x8A,
208 | 0xE1, 0x60, 0xD4, 0x3B, 0xA8, 0x6C, 0x43, 0x44, 0xD8, 0x63, 0x76, 0xFC, 0x37, 0xAA, 0x13,
209 | 0x26, 0xB8, 0x5A, 0x66, 0x58, 0x09, 0xE4, 0xC2, 0x04, 0x03, 0xCD, 0x11, 0xBA, 0x0C, 0xE4,
210 | 0x16, 0x80, 0x2A, 0xE9, 0xF1, 0x4D, 0xD7, 0x0B, 0x4E, 0xF6, 0xC2, 0x35, 0x12, 0xC3, 0xAD,
211 | 0xC3, 0xFE, 0x50, 0x01, 0x19, 0x80, 0x1A, 0x9E, 0xC3, 0x3E, 0xEB, 0xD8, 0x19, 0xA2, 0x1D,
212 | 0x29, 0xCA, 0xEA, 0xC8, 0xBB, 0xD9, 0xDD, 0xAC, 0x00, 0xC4, 0xE8, 0x57, 0x56, 0x0A, 0x7D,
213 | 0x55, 0x9D, 0x1F, 0xCE, 0xBB, 0xC4, 0x38, 0x28, 0xD7, 0xE1, 0x06, 0x11, 0xD3, 0x24, 0x06,
214 | 0x70, 0x46, 0xEC, 0x48, 0xC9, 0x7D, 0x06, 0x41, 0x7C, 0x3D, 0x89, 0x1E, 0xD1, 0x98, 0xAF,
215 | 0x12, 0x0E, 0x57, 0x09, 0x17, 0xA6, 0x09, 0xA2, 0xFA, 0xAE, 0x8A, 0x03, 0x60, 0xA5, 0x4E,
216 | 0x13, 0x1B, 0x22, 0xC3, 0xD5, 0x61, 0xDA, 0x03, 0xA2, 0x04, 0x71, 0xD1, 0xE6, 0x82, 0xB1,
217 | 0x9E, 0x95, 0x39, 0x34, 0x1E, 0x0D, 0x6F, 0x2D, 0x2E, 0x69, 0x3A, 0x86, 0x89, 0x5C, 0x81,
218 | 0xF7, 0xBE, 0xDA, 0xB1, 0xC6, 0xB8, 0x5F, 0x0B, 0x48, 0xF6, 0x02, 0x67, 0xD4, 0x78, 0x81,
219 | 0x59, 0x62, 0xCA, 0xC7, 0x0C, 0x56, 0x2C, 0x18, 0x18, 0xA6, 0x7E, 0x46, 0xDB, 0xBA, 0xAC,
220 | 0x79, 0x9E, 0x6D, 0x40, 0xFF, 0x29, 0x26, 0x50, 0x90, 0x7A, 0x69, 0x40, 0xE2, 0xDA, 0xD1,
221 | 0x0F, 0x68, 0xC9, 0xD0, 0xEF, 0x93, 0x17, 0x6F, 0xDB, 0x45, 0x13, 0x26, 0x8D, 0x61, 0x62,
222 | 0x50, 0x2A, 0x7B, 0x84, 0x17, 0xF3, 0x98, 0x52, 0x14, 0x82, 0xDF, 0xC3, 0x4F, 0xA5, 0x62,
223 | 0x82, 0x3F, 0xA2, 0x0B, 0x6C, 0x5A, 0x0C, 0x86, 0x4F, 0x3B, 0x07, 0x08, 0xCE, 0xFA, 0x2A,
224 | 0x7B, 0x2A, 0x63, 0x64, 0xAD, 0x3A, 0xB6, 0x5E, 0xE4, 0xC9, 0xAC, 0x0E, 0xF9, 0x14, 0x51,
225 | 0x21, 0xE1, 0x72, 0x1E, 0x23, 0x1F, 0xCF, 0x62, 0xFB, 0xF6, 0x62, 0x82, 0x78, 0x2A, 0x2D,
226 | 0xF5, 0x88, 0x02, 0xDE, 0x67, 0x63, 0xC2, 0x63, 0x7B, 0xA0, 0xF2, 0xF3, 0x07, 0x98, 0xF5,
227 | 0x23, 0xEF, 0x96, 0x02, 0xCF, 0x13, 0xB6, 0xA2, 0xB1, 0xC3, 0xE3, 0xC2, 0x55, 0x72, 0xC0,
228 | 0x03, 0x74, 0x4D, 0xD3, 0xF6, 0xF4, 0x5D, 0x1F, 0x60, 0x13, 0xD2, 0x06, 0x77, 0x6B, 0x84,
229 | 0x14, 0x10, 0xA7, 0xEF, 0x54, 0x66, 0x73, 0xFA, 0xF4, 0x5B, 0xF1, 0x1E, 0xE3, 0x0C, 0x73,
230 | 0x0E, 0xB3, 0xB7, 0xAC, 0xC3, 0x9D, 0xDC, 0x9B, 0x30, 0xE4, 0x45, 0x4A, 0xA3, 0xF7, 0xE1,
231 | 0xC5, 0xCC, 0x54, 0x55, 0x54, 0xFB, 0x3E, 0xD4, 0x73, 0xEB, 0xBC, 0xAD, 0xC9, 0xA4, 0xEB,
232 | 0xC0, 0x37, 0xF9, 0xED, 0x6B, 0x51, 0x6E, 0x19, 0x25, 0xC4, 0xAD, 0xE6, 0x26, 0xF7, 0x5B,
233 | 0x41, 0x2F, 0xAA, 0xD0, 0x12, 0xFC, 0x30, 0xAB, 0xCA, 0xB6, 0x4E, 0xDC, 0x62, 0x9F, 0x49,
234 | 0x9E, 0xC2, 0x28, 0x4D, 0x8C, 0x7C, 0xEB, 0xA0, 0xD3, 0xC7, 0x17, 0xE8, 0x6B, 0x1F, 0xB0,
235 | 0x11, 0x09, 0xD0, 0x08, 0x80, 0x22, 0xC9, 0x88, 0xC6, 0xD2, 0x1D, 0xD1, 0x69, 0xD8, 0x6B,
236 | 0x67, 0x80, 0x29, 0x69, 0x46, 0x42, 0x91, 0x7F, 0x9F, 0x9F, 0x3A, 0x81, 0x94, 0x5F, 0xE3,
237 | 0x7D, 0xEE, 0xF2, 0xAF, 0xB1, 0xAC, 0x9C, 0x7B, 0x21, 0x3A, 0x71, 0x03, 0xCF, 0x74, 0x1C,
238 | 0x61, 0xB8, 0x71, 0xB0, 0xE7, 0xDE, 0xD9, 0x2D, 0xE4, 0xB6, 0xE0, 0xF5, 0x0F, 0x45, 0x1F,
239 | 0x3A, 0x51, 0x6B, 0x6C, 0x06, 0x8E, 0x69, 0x6A, 0x93, 0x50, 0x27, 0x68, 0x9C, 0x54, 0x02,
240 | 0x0E, 0x41, 0xC8, 0xE3, 0xF1, 0x36, 0x26, 0x60, 0x35, 0x75, 0x0F, 0xAF, 0x2E, 0x9F, 0x7E,
241 | 0x4E, 0x4B, 0x6D, 0x07, 0x90, 0x23, 0x7C, 0xC7, 0x4F, 0x46, 0xC3, 0x1B, 0xA7, 0xA8, 0x52,
242 | 0x8B, 0x47, 0xC3, 0x37, 0x35, 0xD3, 0x60, 0xF3, 0x83, 0x29, 0x41, 0x63, 0xF1, 0xB5, 0xB4,
243 | 0xDF, 0x69, 0x4A, 0x3D, 0xC8, 0xDF, 0xD9, 0x27, 0xD8, 0xA9, 0xA2, 0x72, 0xBB, 0xE4, 0xE6,
244 | 0x3E, 0x18, 0x9F, 0x5E, 0x21, 0x8A, 0xC4, 0x0E, 0xB8, 0x14, 0x9F, 0x62, 0x3F, 0x61, 0x43,
245 | 0x76, 0x53, 0xBB, 0xB2, 0x70, 0x25, 0x34, 0x0F, 0xEC, 0x7F, 0x6B, 0xD9, 0x7D, 0x50, 0xFF,
246 | 0x23, 0x7D, 0xF6, 0x18, 0x9D, 0x4A, 0xD5, 0x19, 0xFF, 0x1B, 0x7F, 0xFB, 0xF4, 0x96, 0x53,
247 | 0x27, 0xCC, 0x7B, 0x91, 0x63, 0xE8, 0xB6, 0x46, 0xCA, 0x47, 0x99, 0xCD, 0x41, 0x49, 0x9A,
248 | 0xA4, 0xF9, 0x50, 0x11, 0x72, 0xC1, 0x59, 0x3E, 0x09, 0xB6, 0xA7, 0x1E, 0xF6, 0xEF, 0xCB,
249 | 0x79, 0xA5, 0x0D, 0xAB, 0x38, 0x9D, 0xF2, 0x30, 0x74, 0xE1, 0x41, 0xE5, 0x46, 0x34, 0x77,
250 | 0x3A, 0x92, 0x6D, 0xD2, 0x09, 0xDB, 0xDD, 0xF7, 0x0D, 0xBB, 0x83, 0x24, 0x8A, 0xFB, 0x5B,
251 | 0x53, 0xFD, 0x00, 0xED, 0x5A, 0xC3, 0xFD, 0x3E, 0x5E, 0xA7, 0x7E, 0x1E, 0x2A, 0x86, 0xD0,
252 | 0x06, 0x9A, 0x06, 0x7E, 0xB2, 0xDB, 0x8B, 0xD6, 0x0E, 0xCC, 0x57, 0x67, 0x5B, 0x48, 0xF6,
253 | 0x5F, 0x27, 0x5C, 0xBD, 0xEB, 0x75, 0x2B, 0x7C, 0x38, 0x74, 0x1A, 0xE2, 0x5E, 0x2C, 0x3C,
254 | 0x8A, 0xCC, 0x06, 0x4A, 0xD5, 0x88, 0xE2, 0x71, 0x05, 0x14, 0xE2, 0xC5, 0x6C, 0xF7, 0xC6,
255 | 0xCF, 0x20, 0xD0, 0xB9, 0x4C, 0x65, 0x72, 0x23, 0x29, 0x6C, 0xF6, 0xC7, 0x2C, 0xC5, 0xAA,
256 | 0xF6, 0xE9, 0xF1, 0xF9, 0xF3, 0x3C, 0xA2, 0x3A, 0xD3, 0x2B, 0x2A, 0x71, 0x15, 0x16, 0xFD,
257 | 0xAD, 0xFC, 0xE6, 0xF2, 0xBD, 0xA8, 0xC9, 0x97, 0xE8, 0x21, 0x1A, 0x73, 0x3A, 0xEE, 0x48,
258 | 0x71, 0xB7, 0x23, 0x16, 0xE6, 0x4A, 0xD0, 0xAD, 0x3E, 0x76, 0x5A, 0x87, 0x08, 0x18, 0xDD,
259 | 0xCC, 0x4F, 0x3F, 0x06, 0x6A, 0x71, 0x0D, 0x28, 0xC6, 0x90, 0xA3, 0x51, 0x72, 0xA7, 0xDD,
260 | 0x1D, 0x10, 0xAC, 0xF6, 0x4B, 0xE2, 0xAF, 0x57, 0xFF, 0xBD, 0x93, 0xA8, 0x87, 0xD9, 0x75,
261 | 0xE1, 0x30, 0x95, 0xE4, 0x2A, 0x47, 0xBE, 0xA7, 0x76, 0x8C, 0x0F, 0xF2, 0x0E, 0x62, 0x60,
262 | };
263 |
264 | static final GroupElement[] SPAKE_N_SMALL_PRECOMP;
265 | static final GroupElement[] SPAKE_M_SMALL_PRECOMP;
266 |
267 | static {
268 | Ed25519CurveParameterSpec spec = Ed25519.getSpec();
269 | SPAKE_N_SMALL_PRECOMP = getGEFromTable(spec.getCurve(), PRECOMP_TABLE_N);
270 | SPAKE_M_SMALL_PRECOMP = getGEFromTable(spec.getCurve(), PRECOMP_TABLE_M);
271 | }
272 |
273 | private final byte[] mMyName;
274 | private final byte[] mTheirName;
275 | private final Spake2Role mMyRole;
276 | private final byte[] mPrivateKey = new byte[32];
277 | private final byte[] mMyMsg = new byte[32];
278 | private final byte[] mPasswordScalar = new byte[32];
279 | private final byte[] mPasswordHash = new byte[64];
280 | private final Ed25519CurveParameterSpec mCurveSpec;
281 |
282 | private State mState;
283 | private boolean mDisablePasswordScalarHack;
284 | private boolean mIsDestroyed = false;
285 |
286 | public Spake2Context(Spake2Role myRole,
287 | final byte[] myName,
288 | final byte[] theirName) {
289 | mMyRole = myRole;
290 | mMyName = new byte[myName.length];
291 | mTheirName = new byte[theirName.length];
292 | mState = State.Init;
293 |
294 | System.arraycopy(myName, 0, mMyName, 0, myName.length);
295 | System.arraycopy(theirName, 0, mTheirName, 0, theirName.length);
296 |
297 | mCurveSpec = Ed25519.getSpec();
298 | }
299 |
300 | public void setDisablePasswordScalarHack(boolean disablePasswordScalarHack) {
301 | mDisablePasswordScalarHack = disablePasswordScalarHack;
302 | }
303 |
304 | public boolean isDisablePasswordScalarHack() {
305 | return mDisablePasswordScalarHack;
306 | }
307 |
308 | public Spake2Role getMyRole() {
309 | return mMyRole;
310 | }
311 |
312 | public byte[] getMyMsg() {
313 | return mMyMsg;
314 | }
315 |
316 | public byte[] getMyName() {
317 | return mMyName;
318 | }
319 |
320 | public byte[] getTheirName() {
321 | return mTheirName;
322 | }
323 |
324 | @Override
325 | public boolean isDestroyed() {
326 | return mIsDestroyed;
327 | }
328 |
329 | @Override
330 | public void destroy() {
331 | mIsDestroyed = true;
332 | Arrays.fill(mMyName, (byte) 0);
333 | Arrays.fill(mTheirName, (byte) 0);
334 | Arrays.fill(mPrivateKey, (byte) 0);
335 | Arrays.fill(mMyMsg, (byte) 0);
336 | Arrays.fill(mPasswordScalar, (byte) 0);
337 | Arrays.fill(mPasswordHash, (byte) 0);
338 | }
339 |
340 | /**
341 | * @param password Shared password.
342 | * @return A message of size {@link #MAX_MSG_SIZE}.
343 | * @throws IllegalArgumentException If SHA-512 is unavailable for some reason.
344 | * @throws IllegalStateException If the message has already been generated.
345 | */
346 | public byte[] generateMessage(final byte[] password) throws IllegalArgumentException, IllegalStateException {
347 | byte[] privateKey = new byte[64];
348 | new SecureRandom().nextBytes(privateKey);
349 | System.out.printf("PVKEY(%s): %s%n", mMyRole, Utils.bytesToHex(privateKey));
350 | return generateMessage(password, privateKey);
351 | }
352 |
353 | // Package private method for testing purposes
354 | byte[] generateMessage(final byte[] password, byte[] privateKey) throws IllegalArgumentException, IllegalStateException {
355 | if (mIsDestroyed) {
356 | throw new IllegalStateException("The context was destroyed.");
357 | }
358 | if (mState != State.Init) {
359 | throw new IllegalStateException("Invalid state: " + mState);
360 | }
361 |
362 | x25519Scalar.reduce(privateKey);
363 | // Multiply by the cofactor (eight) so that we'll clear it when operating on
364 | // the peer's point later in the protocol.
365 | leftShift3(privateKey);
366 | System.arraycopy(privateKey, 0, mPrivateKey, 0, mPrivateKey.length);
367 |
368 | final GroupElement P = mCurveSpec.getB().scalarMultiply(mPrivateKey);
369 |
370 | byte[] passwordTmp = getHash("SHA-512", password); // 64 byte
371 | System.arraycopy(passwordTmp, 0, mPasswordHash, 0, mPasswordHash.length);
372 | x25519Scalar.reduce(passwordTmp);
373 |
374 | /**
375 | * Due to a copy-paste error, the call to {@link #leftShift3(byte[])} was omitted after reducing it, just above.
376 | * This meant that {@link #mPasswordScalar} was not a multiple of eight to clear the cofactor and thus three bits
377 | * of the password hash would leak. In order to fix this in a unilateral way, points of small order are added to
378 | * the mask point such as that it is in the prime-order subgroup. Since the ephemeral scalar is a multiple of
379 | * eight, these points will cancel out when calculating the shared secret.
380 | *
381 | * Adding points of small order is the same as adding multiples of the prime order to the password scalar. Since
382 | * that's faster, this what is done below. {@link #l} is a large prime, thus, odd, thus the LSB is one. So,
383 | * adding it will flip the LSB. Adding twice, it will flip the next bit, and so on for all the bottom three bits.
384 | */
385 | Scalar passwordScalar = new Scalar(passwordTmp);
386 |
387 | /**
388 | * passwordScalar is the result of scalar reducing and thus is, at most, (l-1). In the following, we may add
389 | * (l + 2×l + 4×l) for a max value of (8×l - 1). That is, < 2^256, as required.
390 | */
391 |
392 | if (!mDisablePasswordScalarHack) {
393 | Scalar order = new Scalar(l);
394 | Scalar tmp = new Scalar();
395 | tmp.cmov(order, isEqual(passwordScalar.getByte(0) & 1, 1));
396 | passwordScalar.add(tmp);
397 | order.dbl();
398 |
399 | tmp.reset();
400 | tmp.cmov(order, isEqual(passwordScalar.getByte(0) & 2, 2));
401 | passwordScalar.add(tmp);
402 | order.dbl();
403 |
404 | tmp.reset();
405 | tmp.cmov(order, isEqual(passwordScalar.getByte(0) & 4, 4));
406 | passwordScalar.add(tmp);
407 |
408 | assert ((passwordScalar.getByte(0) & 7) == 0);
409 | }
410 |
411 | System.arraycopy(passwordScalar.getBytes(), 0, mPasswordScalar, 0, mPasswordScalar.length);
412 |
413 | // mask = h(password) *
58 | *
63 | *
65 | *
66 | * @param val The field element to add.
67 | * @return The field element this + val.
68 | */
69 | public FieldElement add(FieldElement val) {
70 | int[] g = ((Ed25519FieldElement)val).t;
71 | int[] h = new int[10];
72 | for (int i = 0; i < 10; i++) {
73 | h[i] = t[i] + g[i];
74 | }
75 | return new Ed25519FieldElement(f, h);
76 | }
77 |
78 | /**
79 | * $h = f - g$
80 | *
87 | *
92 | *
94 | *
95 | * @param val The field element to subtract.
96 | * @return The field element this - val.
97 | **/
98 | public FieldElement subtract(FieldElement val) {
99 | int[] g = ((Ed25519FieldElement)val).t;
100 | int[] h = new int[10];
101 | for (int i = 0; i < 10; i++) {
102 | h[i] = t[i] - g[i];
103 | }
104 | return new Ed25519FieldElement(f, h);
105 | }
106 |
107 | /**
108 | * $h = -f$
109 | *
114 | *
118 | *
120 | *
121 | * @return The field element (-1) * this.
122 | */
123 | public FieldElement negate() {
124 | int[] h = new int[10];
125 | for (int i = 0; i < 10; i++) {
126 | h[i] = - t[i];
127 | }
128 | return new Ed25519FieldElement(f, h);
129 | }
130 |
131 | /**
132 | * $h = f * g$
133 | *
138 | *
145 | *
486 | *
490 | *
642 | *
646 | *
801 | * $a^p \cong a \mod p$ and therefore $a^{(p-2)} \cong a^{-1} \mod p$
802 | *
803 | * @return The inverse of this field element.
804 | */
805 | public FieldElement invert() {
806 | FieldElement t0, t1, t2, t3;
807 |
808 | // 2 == 2 * 1
809 | t0 = square();
810 |
811 | // 4 == 2 * 2
812 | t1 = t0.square();
813 |
814 | // 8 == 2 * 4
815 | t1 = t1.square();
816 |
817 | // 9 == 8 + 1
818 | t1 = multiply(t1);
819 |
820 | // 11 == 9 + 2
821 | t0 = t0.multiply(t1);
822 |
823 | // 22 == 2 * 11
824 | t2 = t0.square();
825 |
826 | // 31 == 22 + 9
827 | t1 = t1.multiply(t2);
828 |
829 | // 2^6 - 2^1
830 | t2 = t1.square();
831 |
832 | // 2^10 - 2^5
833 | for (int i = 1; i < 5; ++i) {
834 | t2 = t2.square();
835 | }
836 |
837 | // 2^10 - 2^0
838 | t1 = t2.multiply(t1);
839 |
840 | // 2^11 - 2^1
841 | t2 = t1.square();
842 |
843 | // 2^20 - 2^10
844 | for (int i = 1; i < 10; ++i) {
845 | t2 = t2.square();
846 | }
847 |
848 | // 2^20 - 2^0
849 | t2 = t2.multiply(t1);
850 |
851 | // 2^21 - 2^1
852 | t3 = t2.square();
853 |
854 | // 2^40 - 2^20
855 | for (int i = 1; i < 20; ++i) {
856 | t3 = t3.square();
857 | }
858 |
859 | // 2^40 - 2^0
860 | t2 = t3.multiply(t2);
861 |
862 | // 2^41 - 2^1
863 | t2 = t2.square();
864 |
865 | // 2^50 - 2^10
866 | for (int i = 1; i < 10; ++i) {
867 | t2 = t2.square();
868 | }
869 |
870 | // 2^50 - 2^0
871 | t1 = t2.multiply(t1);
872 |
873 | // 2^51 - 2^1
874 | t2 = t1.square();
875 |
876 | // 2^100 - 2^50
877 | for (int i = 1; i < 50; ++i) {
878 | t2 = t2.square();
879 | }
880 |
881 | // 2^100 - 2^0
882 | t2 = t2.multiply(t1);
883 |
884 | // 2^101 - 2^1
885 | t3 = t2.square();
886 |
887 | // 2^200 - 2^100
888 | for (int i = 1; i < 100; ++i) {
889 | t3 = t3.square();
890 | }
891 |
892 | // 2^200 - 2^0
893 | t2 = t3.multiply(t2);
894 |
895 | // 2^201 - 2^1
896 | t2 = t2.square();
897 |
898 | // 2^250 - 2^50
899 | for (int i = 1; i < 50; ++i) {
900 | t2 = t2.square();
901 | }
902 |
903 | // 2^250 - 2^0
904 | t1 = t2.multiply(t1);
905 |
906 | // 2^251 - 2^1
907 | t1 = t1.square();
908 |
909 | // 2^255 - 2^5
910 | for (int i = 1; i < 5; ++i) {
911 | t1 = t1.square();
912 | }
913 |
914 | // 2^255 - 21
915 | return t1.multiply(t0);
916 | }
917 |
918 | /**
919 | * Gets this field element to the power of $(2^{252} - 3)$.
920 | * This is a helper function for calculating the square root.
921 | *
18 | * [1] Daniel J. Bernstein, Niels Duif, Tanja Lange, Peter Schwabe and Bo-Yin Yang : High-speed high-security signatures
19 | * [2] Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, Ed Dawson: Twisted Edwards Curves Revisited
20 | * [3] Daniel J. Bernsteina, Tanja Lange: A complete set of addition laws for incomplete Edwards curves
21 | * [4] Daniel J. Bernstein, Peter Birkner, Marc Joye, Tanja Lange and Christiane Peters: Twisted Edwards Curves
22 | * [5] Christiane Pascale Peters: Curves, Codes, and Cryptography (PhD thesis)
23 | * [6] Daniel J. Bernstein, Peter Birkner, Tanja Lange and Christiane Peters: Optimizing double-base elliptic-curve single-scalar multiplication
24 | */
25 | public class GroupElement implements Serializable {
26 | private static final long serialVersionUID = 2395879087349587L;
27 |
28 | /**
29 | * Available representations for a group element.
30 | *
31 | *
38 | */
39 | public enum Representation {
40 | /** Projective ($P^2$): $(X:Y:Z)$ satisfying $x=X/Z, y=Y/Z$ */
41 | P2,
42 | /** Extended ($P^3$): $(X:Y:Z:T)$ satisfying $x=X/Z, y=Y/Z, XY=ZT$ */
43 | P3,
44 | /** Can only be requested. Results in P3 representation but also populates dblPrecmp. */
45 | P3PrecomputedDouble,
46 | /** Completed ($P \times P$): $((X:Z),(Y:T))$ satisfying $x=X/Z, y=Y/T$ */
47 | P1P1,
48 | /** Precomputed (Duif): $(y+x,y-x,2dxy)$ */
49 | PRECOMP,
50 | /** Cached: $(Y+X,Y-X,Z,2dT)$ */
51 | CACHED
52 | }
53 |
54 | /**
55 | * Creates a new group element in P2 representation.
56 | *
57 | * @param curve The curve.
58 | * @param X The $X$ coordinate.
59 | * @param Y The $Y$ coordinate.
60 | * @param Z The $Z$ coordinate.
61 | * @return The group element in P2 representation.
62 | */
63 | public static GroupElement p2(
64 | final Curve curve,
65 | final FieldElement X,
66 | final FieldElement Y,
67 | final FieldElement Z) {
68 | return new GroupElement(curve, Representation.P2, X, Y, Z, null);
69 | }
70 |
71 | /**
72 | * Creates a new group element in P3 representation, without pre-computation.
73 | *
74 | * @param curve The curve.
75 | * @param X The $X$ coordinate.
76 | * @param Y The $Y$ coordinate.
77 | * @param Z The $Z$ coordinate.
78 | * @param T The $T$ coordinate.
79 | * @return The group element in P3 representation.
80 | */
81 | public static GroupElement p3(
82 | final Curve curve,
83 | final FieldElement X,
84 | final FieldElement Y,
85 | final FieldElement Z,
86 | final FieldElement T) {
87 | return p3(curve, X, Y, Z, T, false);
88 | }
89 |
90 | /**
91 | * Creates a new group element in P3 representation, potentially with pre-computation.
92 | *
93 | * @param curve The curve.
94 | * @param X The $X$ coordinate.
95 | * @param Y The $Y$ coordinate.
96 | * @param Z The $Z$ coordinate.
97 | * @param T The $T$ coordinate.
98 | * @param precomputeDoubleOnly If true, populate dblPrecmp, else set to null.
99 | * @return The group element in P3 representation.
100 | */
101 | public static GroupElement p3(
102 | final Curve curve,
103 | final FieldElement X,
104 | final FieldElement Y,
105 | final FieldElement Z,
106 | final FieldElement T,
107 | final boolean precomputeDoubleOnly) {
108 | return new GroupElement(curve, Representation.P3, X, Y, Z, T, precomputeDoubleOnly);
109 | }
110 |
111 | /**
112 | * Creates a new group element in P1P1 representation.
113 | *
114 | * @param curve The curve.
115 | * @param X The $X$ coordinate.
116 | * @param Y The $Y$ coordinate.
117 | * @param Z The $Z$ coordinate.
118 | * @param T The $T$ coordinate.
119 | * @return The group element in P1P1 representation.
120 | */
121 | public static GroupElement p1p1(
122 | final Curve curve,
123 | final FieldElement X,
124 | final FieldElement Y,
125 | final FieldElement Z,
126 | final FieldElement T) {
127 | return new GroupElement(curve, Representation.P1P1, X, Y, Z, T);
128 | }
129 |
130 | /**
131 | * Creates a new group element in PRECOMP representation.
132 | *
133 | * @param curve The curve.
134 | * @param ypx The $y + x$ value.
135 | * @param ymx The $y - x$ value.
136 | * @param xy2d The $2 * d * x * y$ value.
137 | * @return The group element in PRECOMP representation.
138 | */
139 | public static GroupElement precomp(
140 | final Curve curve,
141 | final FieldElement ypx,
142 | final FieldElement ymx,
143 | final FieldElement xy2d) {
144 | return new GroupElement(curve, Representation.PRECOMP, ypx, ymx, xy2d, null);
145 | }
146 |
147 | /**
148 | * Creates a new group element in CACHED representation.
149 | *
150 | * @param curve The curve.
151 | * @param YpX The $Y + X$ value.
152 | * @param YmX The $Y - X$ value.
153 | * @param Z The $Z$ coordinate.
154 | * @param T2d The $2 * d * T$ value.
155 | * @return The group element in CACHED representation.
156 | */
157 | public static GroupElement cached(
158 | final Curve curve,
159 | final FieldElement YpX,
160 | final FieldElement YmX,
161 | final FieldElement Z,
162 | final FieldElement T2d) {
163 | return new GroupElement(curve, Representation.CACHED, YpX, YmX, Z, T2d);
164 | }
165 |
166 | /**
167 | * Variable is package private only so that tests run.
168 | */
169 | final Curve curve;
170 |
171 | /**
172 | * Variable is package private only so that tests run.
173 | */
174 | final Representation repr;
175 |
176 | /**
177 | * Variable is package private only so that tests run.
178 | */
179 | final FieldElement X;
180 |
181 | /**
182 | * Variable is package private only so that tests run.
183 | */
184 | final FieldElement Y;
185 |
186 | /**
187 | * Variable is package private only so that tests run.
188 | */
189 | final FieldElement Z;
190 |
191 | /**
192 | * Variable is package private only so that tests run.
193 | */
194 | final FieldElement T;
195 |
196 | /**
197 | * Precomputed table for {@link #scalarMultiply(byte[])},
198 | * filled if necessary.
199 | *
278 | *
284 | *
285 | * @param curve The curve.
286 | * @param s The encoded point.
287 | */
288 | public GroupElement(final Curve curve, final byte[] s) {
289 | this(curve, s, false);
290 | }
291 |
292 | /**
293 | * Creates a group element for a curve from a given encoded point. With optional pre-computation.
294 | *
298 | *
304 | *
305 | * @param curve The curve.
306 | * @param s The encoded point.
307 | * @param precomputeSingleAndDouble If true, populate both precmp and dblPrecmp, else set both to null.
308 | */
309 | public GroupElement(final Curve curve, final byte[] s, boolean precomputeSingleAndDouble) {
310 | FieldElement x, y, yy, u, v, v3, vxx, check;
311 | y = curve.getField().fromByteArray(s);
312 | yy = y.square();
313 |
314 | // u = y^2-1
315 | u = yy.subtractOne();
316 |
317 | // v = dy^2+1
318 | v = yy.multiply(curve.getD()).addOne();
319 |
320 | // v3 = v^3
321 | v3 = v.square().multiply(v);
322 |
323 | // x = (v3^2)vu, aka x = uv^7
324 | x = v3.square().multiply(v).multiply(u);
325 |
326 | // x = (uv^7)^((q-5)/8)
327 | x = x.pow22523();
328 |
329 | // x = uv^3(uv^7)^((q-5)/8)
330 | x = v3.multiply(u).multiply(x);
331 |
332 | vxx = x.square().multiply(v);
333 | check = vxx.subtract(u); // vx^2-u
334 | if (check.isNonZero()) {
335 | check = vxx.add(u); // vx^2+u
336 |
337 | if (check.isNonZero())
338 | throw new IllegalArgumentException("not a valid GroupElement");
339 | x = x.multiply(curve.getI());
340 | }
341 |
342 | if ((x.isNegative() ? 1 : 0) != Utils.bit(s, curve.getField().getb()-1)) {
343 | x = x.negate();
344 | }
345 |
346 | this.curve = curve;
347 | this.repr = Representation.P3;
348 | this.X = x;
349 | this.Y = y;
350 | this.Z = curve.getField().ONE;
351 | this.T = this.X.multiply(this.Y);
352 | if(precomputeSingleAndDouble) {
353 | precmp = precomputeSingle();
354 | dblPrecmp = precomputeDouble();
355 | } else {
356 | precmp = null;
357 | dblPrecmp = null;
358 | }
359 | }
360 |
361 | /**
362 | * Gets the curve of the group element.
363 | *
364 | * @return The curve.
365 | */
366 | public Curve getCurve() {
367 | return this.curve;
368 | }
369 |
370 | /**
371 | * Gets the representation of the group element.
372 | *
373 | * @return The representation.
374 | */
375 | public Representation getRepresentation() {
376 | return this.repr;
377 | }
378 |
379 | /**
380 | * Gets the $X$ value of the group element.
381 | * This is for most representation the projective $X$ coordinate.
382 | *
383 | * @return The $X$ value.
384 | */
385 | public FieldElement getX() {
386 | return this.X;
387 | }
388 |
389 | /**
390 | * Gets the $Y$ value of the group element.
391 | * This is for most representation the projective $Y$ coordinate.
392 | *
393 | * @return The $Y$ value.
394 | */
395 | public FieldElement getY() {
396 | return this.Y;
397 | }
398 |
399 | /**
400 | * Gets the $Z$ value of the group element.
401 | * This is for most representation the projective $Z$ coordinate.
402 | *
403 | * @return The $Z$ value.
404 | */
405 | public FieldElement getZ() {
406 | return this.Z;
407 | }
408 |
409 | /**
410 | * Gets the $T$ value of the group element.
411 | * This is for most representation the projective $T$ coordinate.
412 | *
413 | * @return The $T$ value.
414 | */
415 | public FieldElement getT() {
416 | return this.T;
417 | }
418 |
419 | /**
420 | * Converts the group element to an encoded point on the curve.
421 | *
422 | * @return The encoded point as byte array.
423 | */
424 | public byte[] toByteArray() {
425 | switch (this.repr) {
426 | case P2:
427 | case P3:
428 | FieldElement recip = Z.invert();
429 | FieldElement x = X.multiply(recip);
430 | FieldElement y = Y.multiply(recip);
431 | byte[] s = y.toByteArray();
432 | s[s.length-1] |= (x.isNegative() ? (byte) 0x80 : 0);
433 | return s;
434 | default:
435 | return toP2().toByteArray();
436 | }
437 | }
438 |
439 | /**
440 | * Converts the group element to the P2 representation.
441 | *
442 | * @return The group element in the P2 representation.
443 | */
444 | public GroupElement toP2() {
445 | return toRep(Representation.P2);
446 | }
447 |
448 | /**
449 | * Converts the group element to the P3 representation.
450 | *
451 | * @return The group element in the P3 representation.
452 | */
453 | public GroupElement toP3() {
454 | return toRep(Representation.P3);
455 | }
456 |
457 | /**
458 | * Converts the group element to the P3 representation, with dblPrecmp populated.
459 | *
460 | * @return The group element in the P3 representation.
461 | */
462 | public GroupElement toP3PrecomputeDouble() {
463 | return toRep(Representation.P3PrecomputedDouble);
464 | }
465 |
466 | /**
467 | * Converts the group element to the CACHED representation.
468 | *
469 | * @return The group element in the CACHED representation.
470 | */
471 | public GroupElement toCached() {
472 | return toRep(Representation.CACHED);
473 | }
474 |
475 | /**
476 | * Convert a GroupElement from one Representation to another.
477 | * TODO-CR: Add additional conversion?
478 | * $r = p$
479 | *
482 | *
594 | *
603 | *
648 | *
655 | *
664 | *
670 | * TODO-CR BR: (the common factor $1/Z2^2$ does not matter):
671 | * $$
672 | * E = B - A, F = D - C, G = D + C, H = B + A \\
673 | * X3 = E * F = (B - A) * (D - C); \\
674 | * Y3 = G * H = (D + C) * (B + A); \\
675 | * Z3 = F * G = (D - C) * (D + C); \\
676 | * T3 = E * H = (B - A) * (B + A);
677 | * $$
678 | *
679 | * @param q the PRECOMP representation of the GroupElement to add.
680 | * @return the P1P1 representation of the result.
681 | */
682 | public GroupElement madd(GroupElement q) {
683 | if (this.repr != Representation.P3)
684 | throw new UnsupportedOperationException();
685 | if (q.repr != Representation.PRECOMP)
686 | throw new IllegalArgumentException();
687 |
688 | FieldElement YpX, YmX, A, B, C, D;
689 | YpX = this.Y.add(this.X);
690 | YmX = this.Y.subtract(this.X);
691 | A = YpX.multiply(q.X); // q->y+x
692 | B = YmX.multiply(q.Y); // q->y-x
693 | C = q.Z.multiply(this.T); // q->2dxy
694 | D = this.Z.add(this.Z);
695 | return p1p1(this.curve, A.subtract(B), A.add(B), D.add(C), D.subtract(C));
696 | }
697 |
698 | /**
699 | * GroupElement subtraction using the twisted Edwards addition law with
700 | * extended coordinates (Hisil2008).
701 | *
736 | *
743 | *
900 | * Replaces this with this if $b == 0$.
901 | *