blob: 87b74174b4a1330c2b9c933dbf2f4d9940e94521 [file] [log] [blame]
Pete Bentley0c61efe2019-08-13 09:32:23 +01001/********************************************************************************************
2* SIDH: an efficient supersingular isogeny cryptography library
3*
4* Abstract: supersingular isogeny key encapsulation (SIKE) protocol
5*********************************************************************************************/
6
7#include <assert.h>
8#include <stdint.h>
9#include <string.h>
10#include <openssl/bn.h>
11#include <openssl/base.h>
12#include <openssl/rand.h>
13#include <openssl/mem.h>
14#include <openssl/sha.h>
15
16#include "utils.h"
17#include "isogeny.h"
18#include "fpx.h"
19
20extern const struct params_t sike_params;
21
22// SIDH_JINV_BYTESZ is a number of bytes used for encoding j-invariant.
23#define SIDH_JINV_BYTESZ 110U
24// SIDH_PRV_A_BITSZ is a number of bits of SIDH private key (2-isogeny)
25#define SIDH_PRV_A_BITSZ 216U
26// SIDH_PRV_A_BITSZ is a number of bits of SIDH private key (3-isogeny)
27#define SIDH_PRV_B_BITSZ 217U
28// MAX_INT_POINTS_ALICE is a number of points used in 2-isogeny tree computation
29#define MAX_INT_POINTS_ALICE 7U
30// MAX_INT_POINTS_ALICE is a number of points used in 3-isogeny tree computation
31#define MAX_INT_POINTS_BOB 8U
32
33// Swap points.
34// If option = 0 then P <- P and Q <- Q, else if option = 0xFF...FF then P <- Q and Q <- P
35#if !defined(OPENSSL_X86_64) || defined(OPENSSL_NO_ASM)
36static void sike_cswap(point_proj_t P, point_proj_t Q, const crypto_word_t option)
37{
38 crypto_word_t temp;
39 for (size_t i = 0; i < NWORDS_FIELD; i++) {
40 temp = option & (P->X->c0[i] ^ Q->X->c0[i]);
41 P->X->c0[i] = temp ^ P->X->c0[i];
42 Q->X->c0[i] = temp ^ Q->X->c0[i];
43 temp = option & (P->Z->c0[i] ^ Q->Z->c0[i]);
44 P->Z->c0[i] = temp ^ P->Z->c0[i];
45 Q->Z->c0[i] = temp ^ Q->Z->c0[i];
46 temp = option & (P->X->c1[i] ^ Q->X->c1[i]);
47 P->X->c1[i] = temp ^ P->X->c1[i];
48 Q->X->c1[i] = temp ^ Q->X->c1[i];
49 temp = option & (P->Z->c1[i] ^ Q->Z->c1[i]);
50 P->Z->c1[i] = temp ^ P->Z->c1[i];
51 Q->Z->c1[i] = temp ^ Q->Z->c1[i];
52 }
53}
54#endif
55
56// Swap points.
57// If option = 0 then P <- P and Q <- Q, else if option = 0xFF...FF then P <- Q and Q <- P
58static inline void sike_fp2cswap(point_proj_t P, point_proj_t Q, const crypto_word_t option)
59{
60#if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM)
61 sike_cswap_asm(P, Q, option);
62#else
63 sike_cswap(P, Q, option);
64#endif
65}
66
67static void ladder3Pt(
68 const f2elm_t xP, const f2elm_t xQ, const f2elm_t xPQ, const uint8_t* m,
69 int is_A, point_proj_t R, const f2elm_t A) {
70 point_proj_t R0 = POINT_PROJ_INIT, R2 = POINT_PROJ_INIT;
71 f2elm_t A24 = F2ELM_INIT;
72 crypto_word_t mask;
73 int bit, swap, prevbit = 0;
74
75 const size_t nbits = is_A?SIDH_PRV_A_BITSZ:SIDH_PRV_B_BITSZ;
76
77 // Initializing constant
78 sike_fpcopy(sike_params.mont_one, A24[0].c0);
79 sike_fp2add(A24, A24, A24);
80 sike_fp2add(A, A24, A24);
81 sike_fp2div2(A24, A24);
82 sike_fp2div2(A24, A24); // A24 = (A+2)/4
83
84 // Initializing points
85 sike_fp2copy(xQ, R0->X);
86 sike_fpcopy(sike_params.mont_one, R0->Z[0].c0);
87 sike_fp2copy(xPQ, R2->X);
88 sike_fpcopy(sike_params.mont_one, R2->Z[0].c0);
89 sike_fp2copy(xP, R->X);
90 sike_fpcopy(sike_params.mont_one, R->Z[0].c0);
91 memset(R->Z->c1, 0, sizeof(R->Z->c1));
92
93 // Main loop
94 for (size_t i = 0; i < nbits; i++) {
95 bit = (m[i >> 3] >> (i & 7)) & 1;
96 swap = bit ^ prevbit;
97 prevbit = bit;
98 mask = 0 - (crypto_word_t)swap;
99
100 sike_fp2cswap(R, R2, mask);
101 sike_xDBLADD(R0, R2, R->X, A24);
102 sike_fp2mul_mont(R2->X, R->Z, R2->X);
103 }
104
105 mask = 0 - (crypto_word_t)prevbit;
106 sike_fp2cswap(R, R2, mask);
107}
108
109// Initialization of basis points
110static inline void sike_init_basis(const crypto_word_t *gen, f2elm_t XP, f2elm_t XQ, f2elm_t XR) {
111 sike_fpcopy(gen, XP->c0);
112 sike_fpcopy(gen + NWORDS_FIELD, XP->c1);
113 sike_fpcopy(gen + 2*NWORDS_FIELD, XQ->c0);
114 sike_fpcopy(gen + 3*NWORDS_FIELD, XQ->c1);
115 sike_fpcopy(gen + 4*NWORDS_FIELD, XR->c0);
116 sike_fpcopy(gen + 5*NWORDS_FIELD, XR->c1);
117}
118
119// Conversion of GF(p^2) element from Montgomery to standard representation.
120static inline void sike_fp2_encode(const f2elm_t x, uint8_t *enc) {
121 f2elm_t t;
122 sike_from_fp2mont(x, t);
123
124 // convert to bytes in little endian form
125 for (size_t i=0; i<FIELD_BYTESZ; i++) {
126 enc[i+ 0] = (t[0].c0[i/LSZ] >> (8*(i%LSZ))) & 0xFF;
127 enc[i+FIELD_BYTESZ] = (t[0].c1[i/LSZ] >> (8*(i%LSZ))) & 0xFF;
128 }
129}
130
131// Parse byte sequence back into GF(p^2) element, and conversion to Montgomery representation.
132// Elements over GF(p503) are encoded in 63 octets in little endian format
133// (i.e., the least significant octet is located in the lowest memory address).
134static inline void fp2_decode(const uint8_t *enc, f2elm_t t) {
135 memset(t[0].c0, 0, sizeof(t[0].c0));
136 memset(t[0].c1, 0, sizeof(t[0].c1));
137 // convert bytes in little endian form to f2elm_t
138 for (size_t i = 0; i < FIELD_BYTESZ; i++) {
139 t[0].c0[i/LSZ] |= ((crypto_word_t)enc[i+ 0]) << (8*(i%LSZ));
140 t[0].c1[i/LSZ] |= ((crypto_word_t)enc[i+FIELD_BYTESZ]) << (8*(i%LSZ));
141 }
142 sike_to_fp2mont(t, t);
143}
144
145// Alice's ephemeral public key generation
146// Input: a private key prA in the range [0, 2^250 - 1], stored in 32 bytes.
147// Output: the public key pkA consisting of 3 GF(p503^2) elements encoded in 378 bytes.
148static void gen_iso_A(const uint8_t* skA, uint8_t* pkA)
149{
150 point_proj_t R, pts[MAX_INT_POINTS_ALICE];
151 point_proj_t phiP = POINT_PROJ_INIT;
152 point_proj_t phiQ = POINT_PROJ_INIT;
153 point_proj_t phiR = POINT_PROJ_INIT;
154 f2elm_t XPA, XQA, XRA, coeff[3];
155 f2elm_t A24plus = F2ELM_INIT;
156 f2elm_t C24 = F2ELM_INIT;
157 f2elm_t A = F2ELM_INIT;
158 unsigned int m, index = 0, pts_index[MAX_INT_POINTS_ALICE], npts = 0, ii = 0;
159
160 // Initialize basis points
161 sike_init_basis(sike_params.A_gen, XPA, XQA, XRA);
162 sike_init_basis(sike_params.B_gen, phiP->X, phiQ->X, phiR->X);
163 sike_fpcopy(sike_params.mont_one, (phiP->Z)->c0);
164 sike_fpcopy(sike_params.mont_one, (phiQ->Z)->c0);
165 sike_fpcopy(sike_params.mont_one, (phiR->Z)->c0);
166
167 // Initialize constants: A24plus = A+2C, C24 = 4C, where A=6, C=1
168 sike_fpcopy(sike_params.mont_one, A24plus->c0);
169 sike_fp2add(A24plus, A24plus, A24plus);
170 sike_fp2add(A24plus, A24plus, C24);
171 sike_fp2add(A24plus, C24, A);
172 sike_fp2add(C24, C24, A24plus);
173
174 // Retrieve kernel point
175 ladder3Pt(XPA, XQA, XRA, skA, 1, R, A);
176
177 // Traverse tree
178 index = 0;
179 for (size_t row = 1; row < A_max; row++) {
180 while (index < A_max-row) {
181 sike_fp2copy(R->X, pts[npts]->X);
182 sike_fp2copy(R->Z, pts[npts]->Z);
183 pts_index[npts++] = index;
184 m = sike_params.A_strat[ii++];
185 sike_xDBLe(R, R, A24plus, C24, (2*m));
186 index += m;
187 }
188 sike_get_4_isog(R, A24plus, C24, coeff);
189
190 for (size_t i = 0; i < npts; i++) {
191 sike_eval_4_isog(pts[i], coeff);
192 }
193 sike_eval_4_isog(phiP, coeff);
194 sike_eval_4_isog(phiQ, coeff);
195 sike_eval_4_isog(phiR, coeff);
196
197 sike_fp2copy(pts[npts-1]->X, R->X);
198 sike_fp2copy(pts[npts-1]->Z, R->Z);
199 index = pts_index[npts-1];
200 npts -= 1;
201 }
202
203 sike_get_4_isog(R, A24plus, C24, coeff);
204 sike_eval_4_isog(phiP, coeff);
205 sike_eval_4_isog(phiQ, coeff);
206 sike_eval_4_isog(phiR, coeff);
207
208 sike_inv_3_way(phiP->Z, phiQ->Z, phiR->Z);
209 sike_fp2mul_mont(phiP->X, phiP->Z, phiP->X);
210 sike_fp2mul_mont(phiQ->X, phiQ->Z, phiQ->X);
211 sike_fp2mul_mont(phiR->X, phiR->Z, phiR->X);
212
213 // Format public key
214 sike_fp2_encode(phiP->X, pkA);
215 sike_fp2_encode(phiQ->X, pkA + SIDH_JINV_BYTESZ);
216 sike_fp2_encode(phiR->X, pkA + 2*SIDH_JINV_BYTESZ);
217}
218
219// Bob's ephemeral key-pair generation
220// It produces a private key skB and computes the public key pkB.
221// The private key is an integer in the range [0, 2^Floor(Log(2,3^159)) - 1], stored in 32 bytes.
222// The public key consists of 3 GF(p503^2) elements encoded in 378 bytes.
223static void gen_iso_B(const uint8_t* skB, uint8_t* pkB)
224{
225 point_proj_t R, pts[MAX_INT_POINTS_BOB];
226 point_proj_t phiP = POINT_PROJ_INIT;
227 point_proj_t phiQ = POINT_PROJ_INIT;
228 point_proj_t phiR = POINT_PROJ_INIT;
229 f2elm_t XPB, XQB, XRB, coeff[3];
230 f2elm_t A24plus = F2ELM_INIT;
231 f2elm_t A24minus = F2ELM_INIT;
232 f2elm_t A = F2ELM_INIT;
233 unsigned int m, index = 0, pts_index[MAX_INT_POINTS_BOB], npts = 0, ii = 0;
234
235 // Initialize basis points
236 sike_init_basis(sike_params.B_gen, XPB, XQB, XRB);
237 sike_init_basis(sike_params.A_gen, phiP->X, phiQ->X, phiR->X);
238 sike_fpcopy(sike_params.mont_one, (phiP->Z)->c0);
239 sike_fpcopy(sike_params.mont_one, (phiQ->Z)->c0);
240 sike_fpcopy(sike_params.mont_one, (phiR->Z)->c0);
241
242 // Initialize constants: A24minus = A-2C, A24plus = A+2C, where A=6, C=1
243 sike_fpcopy(sike_params.mont_one, A24plus->c0);
244 sike_fp2add(A24plus, A24plus, A24plus);
245 sike_fp2add(A24plus, A24plus, A24minus);
246 sike_fp2add(A24plus, A24minus, A);
247 sike_fp2add(A24minus, A24minus, A24plus);
248
249 // Retrieve kernel point
250 ladder3Pt(XPB, XQB, XRB, skB, 0, R, A);
251
252 // Traverse tree
253 index = 0;
254 for (size_t row = 1; row < B_max; row++) {
255 while (index < B_max-row) {
256 sike_fp2copy(R->X, pts[npts]->X);
257 sike_fp2copy(R->Z, pts[npts]->Z);
258 pts_index[npts++] = index;
259 m = sike_params.B_strat[ii++];
260 sike_xTPLe(R, R, A24minus, A24plus, m);
261 index += m;
262 }
263 sike_get_3_isog(R, A24minus, A24plus, coeff);
264
265 for (size_t i = 0; i < npts; i++) {
266 sike_eval_3_isog(pts[i], coeff);
267 }
268 sike_eval_3_isog(phiP, coeff);
269 sike_eval_3_isog(phiQ, coeff);
270 sike_eval_3_isog(phiR, coeff);
271
272 sike_fp2copy(pts[npts-1]->X, R->X);
273 sike_fp2copy(pts[npts-1]->Z, R->Z);
274 index = pts_index[npts-1];
275 npts -= 1;
276 }
277
278 sike_get_3_isog(R, A24minus, A24plus, coeff);
279 sike_eval_3_isog(phiP, coeff);
280 sike_eval_3_isog(phiQ, coeff);
281 sike_eval_3_isog(phiR, coeff);
282
283 sike_inv_3_way(phiP->Z, phiQ->Z, phiR->Z);
284 sike_fp2mul_mont(phiP->X, phiP->Z, phiP->X);
285 sike_fp2mul_mont(phiQ->X, phiQ->Z, phiQ->X);
286 sike_fp2mul_mont(phiR->X, phiR->Z, phiR->X);
287
288 // Format public key
289 sike_fp2_encode(phiP->X, pkB);
290 sike_fp2_encode(phiQ->X, pkB + SIDH_JINV_BYTESZ);
291 sike_fp2_encode(phiR->X, pkB + 2*SIDH_JINV_BYTESZ);
292}
293
294// Alice's ephemeral shared secret computation
295// It produces a shared secret key ssA using her secret key skA and Bob's public key pkB
296// Inputs: Alice's skA is an integer in the range [0, 2^250 - 1], stored in 32 bytes.
297// Bob's pkB consists of 3 GF(p503^2) elements encoded in 378 bytes.
298// Output: a shared secret ssA that consists of one element in GF(p503^2) encoded in 126 bytes.
299static void ex_iso_A(const uint8_t* skA, const uint8_t* pkB, uint8_t* ssA)
300{
301 point_proj_t R, pts[MAX_INT_POINTS_ALICE];
302 f2elm_t coeff[3], PKB[3], jinv;
303 f2elm_t A24plus = F2ELM_INIT;
304 f2elm_t C24 = F2ELM_INIT;
305 f2elm_t A = F2ELM_INIT;
306 unsigned int m, index = 0, pts_index[MAX_INT_POINTS_ALICE], npts = 0, ii = 0;
307
308 // Initialize images of Bob's basis
309 fp2_decode(pkB, PKB[0]);
310 fp2_decode(pkB + SIDH_JINV_BYTESZ, PKB[1]);
311 fp2_decode(pkB + 2*SIDH_JINV_BYTESZ, PKB[2]);
312
313 // Initialize constants
314 sike_get_A(PKB[0], PKB[1], PKB[2], A);
315 sike_fpadd(sike_params.mont_one, sike_params.mont_one, C24->c0);
316 sike_fp2add(A, C24, A24plus);
317 sike_fpadd(C24->c0, C24->c0, C24->c0);
318
319 // Retrieve kernel point
320 ladder3Pt(PKB[0], PKB[1], PKB[2], skA, 1, R, A);
321
322 // Traverse tree
323 index = 0;
324 for (size_t row = 1; row < A_max; row++) {
325 while (index < A_max-row) {
326 sike_fp2copy(R->X, pts[npts]->X);
327 sike_fp2copy(R->Z, pts[npts]->Z);
328 pts_index[npts++] = index;
329 m = sike_params.A_strat[ii++];
330 sike_xDBLe(R, R, A24plus, C24, (2*m));
331 index += m;
332 }
333 sike_get_4_isog(R, A24plus, C24, coeff);
334
335 for (size_t i = 0; i < npts; i++) {
336 sike_eval_4_isog(pts[i], coeff);
337 }
338
339 sike_fp2copy(pts[npts-1]->X, R->X);
340 sike_fp2copy(pts[npts-1]->Z, R->Z);
341 index = pts_index[npts-1];
342 npts -= 1;
343 }
344
345 sike_get_4_isog(R, A24plus, C24, coeff);
346 sike_fp2add(A24plus, A24plus, A24plus);
347 sike_fp2sub(A24plus, C24, A24plus);
348 sike_fp2add(A24plus, A24plus, A24plus);
349 sike_j_inv(A24plus, C24, jinv);
350 sike_fp2_encode(jinv, ssA);
351}
352
353// Bob's ephemeral shared secret computation
354// It produces a shared secret key ssB using his secret key skB and Alice's public key pkA
355// Inputs: Bob's skB is an integer in the range [0, 2^Floor(Log(2,3^159)) - 1], stored in 32 bytes.
356// Alice's pkA consists of 3 GF(p503^2) elements encoded in 378 bytes.
357// Output: a shared secret ssB that consists of one element in GF(p503^2) encoded in 126 bytes.
358static void ex_iso_B(const uint8_t* skB, const uint8_t* pkA, uint8_t* ssB)
359{
360 point_proj_t R, pts[MAX_INT_POINTS_BOB];
361 f2elm_t coeff[3], PKB[3], jinv;
362 f2elm_t A24plus = F2ELM_INIT;
363 f2elm_t A24minus = F2ELM_INIT;
364 f2elm_t A = F2ELM_INIT;
365 unsigned int m, index = 0, pts_index[MAX_INT_POINTS_BOB], npts = 0, ii = 0;
366
367 // Initialize images of Alice's basis
368 fp2_decode(pkA, PKB[0]);
369 fp2_decode(pkA + SIDH_JINV_BYTESZ, PKB[1]);
370 fp2_decode(pkA + 2*SIDH_JINV_BYTESZ, PKB[2]);
371
372 // Initialize constants
373 sike_get_A(PKB[0], PKB[1], PKB[2], A);
374 sike_fpadd(sike_params.mont_one, sike_params.mont_one, A24minus->c0);
375 sike_fp2add(A, A24minus, A24plus);
376 sike_fp2sub(A, A24minus, A24minus);
377
378 // Retrieve kernel point
379 ladder3Pt(PKB[0], PKB[1], PKB[2], skB, 0, R, A);
380
381 // Traverse tree
382 index = 0;
383 for (size_t row = 1; row < B_max; row++) {
384 while (index < B_max-row) {
385 sike_fp2copy(R->X, pts[npts]->X);
386 sike_fp2copy(R->Z, pts[npts]->Z);
387 pts_index[npts++] = index;
388 m = sike_params.B_strat[ii++];
389 sike_xTPLe(R, R, A24minus, A24plus, m);
390 index += m;
391 }
392 sike_get_3_isog(R, A24minus, A24plus, coeff);
393
394 for (size_t i = 0; i < npts; i++) {
395 sike_eval_3_isog(pts[i], coeff);
396 }
397
398 sike_fp2copy(pts[npts-1]->X, R->X);
399 sike_fp2copy(pts[npts-1]->Z, R->Z);
400 index = pts_index[npts-1];
401 npts -= 1;
402 }
403
404 sike_get_3_isog(R, A24minus, A24plus, coeff);
405 sike_fp2add(A24plus, A24minus, A);
406 sike_fp2add(A, A, A);
407 sike_fp2sub(A24plus, A24minus, A24plus);
408 sike_j_inv(A, A24plus, jinv);
409 sike_fp2_encode(jinv, ssB);
410}
411
412int SIKE_keypair(uint8_t out_priv[SIKE_PRV_BYTESZ],
413 uint8_t out_pub[SIKE_PUB_BYTESZ]) {
414 int ret = 0;
415
416 // Calculate private key for Alice. Needs to be in range [0, 2^0xFA - 1] and <
417 // 253 bits
418 BIGNUM *bn_sidh_prv = BN_new();
419 if (!bn_sidh_prv ||
420 !BN_rand(bn_sidh_prv, SIDH_PRV_B_BITSZ, BN_RAND_TOP_ONE,
421 BN_RAND_BOTTOM_ANY) ||
422 !BN_bn2le_padded(out_priv, BITS_TO_BYTES(SIDH_PRV_B_BITSZ),
423 bn_sidh_prv)) {
424 goto end;
425 }
426
427 gen_iso_B(out_priv, out_pub);
428 ret = 1;
429
430end:
431 BN_free(bn_sidh_prv);
432 return ret;
433}
434
435void SIKE_encaps(uint8_t out_shared_key[SIKE_SS_BYTESZ],
436 uint8_t out_ciphertext[SIKE_CT_BYTESZ],
437 const uint8_t pub_key[SIKE_PUB_BYTESZ]) {
438 // Secret buffer is reused by the function to store some ephemeral
439 // secret data. It's size must be maximum of SHA256_CBLOCK,
440 // SIKE_MSG_BYTESZ and SIDH_PRV_A_BITSZ in bytes.
441 uint8_t secret[SHA256_CBLOCK];
442 uint8_t j[SIDH_JINV_BYTESZ];
443 uint8_t temp[SIKE_MSG_BYTESZ + SIKE_CT_BYTESZ];
444 SHA256_CTX ctx;
445
446 // Generate secret key for A
447 // secret key A = SHA256({0,1}^n || pub_key)) mod SIDH_PRV_A_BITSZ
448 RAND_bytes(temp, SIKE_MSG_BYTESZ);
449
450 SHA256_Init(&ctx);
451 SHA256_Update(&ctx, temp, SIKE_MSG_BYTESZ);
452 SHA256_Update(&ctx, pub_key, SIKE_PUB_BYTESZ);
453 SHA256_Final(secret, &ctx);
454
455 // Generate public key for A - first part of the ciphertext
456 gen_iso_A(secret, out_ciphertext);
457
458 // Generate c1:
459 // h = SHA256(j-invariant)
460 // c1 = h ^ m
461 ex_iso_A(secret, pub_key, j);
462 SHA256_Init(&ctx);
463 SHA256_Update(&ctx, j, sizeof(j));
464 SHA256_Final(secret, &ctx);
465
466 // c1 = h ^ m
467 uint8_t *c1 = &out_ciphertext[SIKE_PUB_BYTESZ];
468 for (size_t i = 0; i < SIKE_MSG_BYTESZ; i++) {
469 c1[i] = temp[i] ^ secret[i];
470 }
471
472 SHA256_Init(&ctx);
473 SHA256_Update(&ctx, temp, SIKE_MSG_BYTESZ);
474 SHA256_Update(&ctx, out_ciphertext, SIKE_CT_BYTESZ);
475 SHA256_Final(secret, &ctx);
476 // Generate shared secret out_shared_key = SHA256(m||out_ciphertext)
477 memcpy(out_shared_key, secret, SIKE_SS_BYTESZ);
478}
479
480void SIKE_decaps(uint8_t out_shared_key[SIKE_SS_BYTESZ],
481 const uint8_t ciphertext[SIKE_CT_BYTESZ],
482 const uint8_t pub_key[SIKE_PUB_BYTESZ],
483 const uint8_t priv_key[SIKE_PRV_BYTESZ]) {
484 // Secret buffer is reused by the function to store some ephemeral
485 // secret data. It's size must be maximum of SHA256_CBLOCK,
486 // SIKE_MSG_BYTESZ and SIDH_PRV_A_BITSZ in bytes.
487 uint8_t secret[SHA256_CBLOCK];
488 uint8_t j[SIDH_JINV_BYTESZ];
489 uint8_t c0[SIKE_PUB_BYTESZ];
490 uint8_t temp[SIKE_MSG_BYTESZ];
491 uint8_t shared_nok[SIKE_MSG_BYTESZ];
492 SHA256_CTX ctx;
493
494 // This is OK as we are only using ephemeral keys in BoringSSL
495 RAND_bytes(shared_nok, SIKE_MSG_BYTESZ);
496
497 // Recover m
498 // Let ciphertext = c0 || c1 - both have fixed sizes
499 // m = F(j-invariant(c0, priv_key)) ^ c1
500 ex_iso_B(priv_key, ciphertext, j);
501
502 SHA256_Init(&ctx);
503 SHA256_Update(&ctx, j, sizeof(j));
504 SHA256_Final(secret, &ctx);
505
506 const uint8_t *c1 = &ciphertext[sizeof(c0)];
507 for (size_t i = 0; i < SIKE_MSG_BYTESZ; i++) {
508 temp[i] = c1[i] ^ secret[i];
509 }
510
511 SHA256_Init(&ctx);
512 SHA256_Update(&ctx, temp, SIKE_MSG_BYTESZ);
513 SHA256_Update(&ctx, pub_key, SIKE_PUB_BYTESZ);
514 SHA256_Final(secret, &ctx);
515
516 // Recover c0 = public key A
517 gen_iso_A(secret, c0);
518 crypto_word_t ok = constant_time_is_zero_w(
519 CRYPTO_memcmp(c0, ciphertext, SIKE_PUB_BYTESZ));
520 for (size_t i = 0; i < SIKE_MSG_BYTESZ; i++) {
521 temp[i] = constant_time_select_8(ok, temp[i], shared_nok[i]);
522 }
523
524 SHA256_Init(&ctx);
525 SHA256_Update(&ctx, temp, SIKE_MSG_BYTESZ);
526 SHA256_Update(&ctx, ciphertext, SIKE_CT_BYTESZ);
527 SHA256_Final(secret, &ctx);
528
529 // Generate shared secret out_shared_key = SHA256(m||ciphertext)
530 memcpy(out_shared_key, secret, SIKE_SS_BYTESZ);
531}