SECPxxxr1.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. /*
  2. * Copyright (c) 2023, Michiel Visser <opensource@webmichiel.nl>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/ByteBuffer.h>
  8. #include <AK/Endian.h>
  9. #include <AK/MemoryStream.h>
  10. #include <AK/Random.h>
  11. #include <AK/StdLibExtras.h>
  12. #include <AK/StringView.h>
  13. #include <AK/UFixedBigInt.h>
  14. #include <AK/UFixedBigIntDivision.h>
  15. #include <LibCrypto/ASN1/DER.h>
  16. #include <LibCrypto/Curves/EllipticCurve.h>
  17. namespace Crypto::Curves {
  18. struct SECPxxxr1CurveParameters {
  19. StringView prime;
  20. StringView a;
  21. StringView b;
  22. StringView order;
  23. StringView generator_point;
  24. };
  25. template<size_t bit_size, SECPxxxr1CurveParameters const& CURVE_PARAMETERS>
  26. class SECPxxxr1 : public EllipticCurve {
  27. private:
  28. using StorageType = AK::UFixedBigInt<bit_size>;
  29. using StorageTypeX2 = AK::UFixedBigInt<bit_size * 2>;
  30. struct JacobianPoint {
  31. StorageType x;
  32. StorageType y;
  33. StorageType z;
  34. };
  35. // Curve parameters
  36. static constexpr size_t KEY_BIT_SIZE = bit_size;
  37. static constexpr size_t KEY_BYTE_SIZE = KEY_BIT_SIZE / 8;
  38. static constexpr size_t POINT_BYTE_SIZE = 1 + 2 * KEY_BYTE_SIZE;
  39. static constexpr StorageType make_unsigned_fixed_big_int_from_string(StringView str)
  40. {
  41. StorageType result { 0 };
  42. for (auto c : str) {
  43. if (c == '_')
  44. continue;
  45. result <<= 4;
  46. result |= parse_ascii_hex_digit(c);
  47. }
  48. return result;
  49. }
  50. static constexpr StorageType PRIME = make_unsigned_fixed_big_int_from_string(CURVE_PARAMETERS.prime);
  51. static constexpr StorageType A = make_unsigned_fixed_big_int_from_string(CURVE_PARAMETERS.a);
  52. static constexpr StorageType B = make_unsigned_fixed_big_int_from_string(CURVE_PARAMETERS.b);
  53. static constexpr StorageType ORDER = make_unsigned_fixed_big_int_from_string(CURVE_PARAMETERS.order);
  54. static constexpr Array<u8, POINT_BYTE_SIZE> make_generator_point_bytes(StringView generator_point)
  55. {
  56. Array<u8, POINT_BYTE_SIZE> buf_array { 0 };
  57. auto it = generator_point.begin();
  58. for (size_t i = 0; i < POINT_BYTE_SIZE; i++) {
  59. if (it == CURVE_PARAMETERS.generator_point.end())
  60. break;
  61. while (*it == '_') {
  62. it++;
  63. }
  64. buf_array[i] = parse_ascii_hex_digit(*it) * 16;
  65. it++;
  66. if (it == CURVE_PARAMETERS.generator_point.end())
  67. break;
  68. buf_array[i] += parse_ascii_hex_digit(*it);
  69. it++;
  70. }
  71. return buf_array;
  72. }
  73. static constexpr Array<u8, POINT_BYTE_SIZE> GENERATOR_POINT = make_generator_point_bytes(CURVE_PARAMETERS.generator_point);
  74. // Check that the generator point starts with 0x04
  75. static_assert(GENERATOR_POINT[0] == 0x04);
  76. static constexpr StorageType calculate_modular_inverse_mod_r(StorageType value)
  77. {
  78. // Calculate the modular multiplicative inverse of value mod 2^bit_size using the extended euclidean algorithm
  79. using StorageTypeP1 = AK::UFixedBigInt<bit_size + 1>;
  80. StorageTypeP1 old_r = value;
  81. StorageTypeP1 r = static_cast<StorageTypeP1>(1u) << KEY_BIT_SIZE;
  82. StorageTypeP1 old_s = 1u;
  83. StorageTypeP1 s = 0u;
  84. while (!r.is_zero_constant_time()) {
  85. StorageTypeP1 r_save = r;
  86. StorageTypeP1 quotient = old_r.div_mod(r, r);
  87. old_r = r_save;
  88. StorageTypeP1 s_save = s;
  89. s = old_s - quotient * s;
  90. old_s = s_save;
  91. }
  92. return static_cast<StorageType>(old_s);
  93. }
  94. static constexpr StorageType calculate_r2_mod(StorageType modulus)
  95. {
  96. // Calculate the value of R^2 mod modulus, where R = 2^bit_size
  97. using StorageTypeX2P1 = AK::UFixedBigInt<bit_size * 2 + 1>;
  98. StorageTypeX2P1 r2 = static_cast<StorageTypeX2P1>(1u) << (2 * KEY_BIT_SIZE);
  99. return r2 % modulus;
  100. }
  101. // Verify that A = -3 mod p, which is required for some optimizations
  102. static_assert(A == PRIME - 3);
  103. // Precomputed helper values for reduction and Montgomery multiplication
  104. static constexpr StorageType REDUCE_PRIME = StorageType { 0 } - PRIME;
  105. static constexpr StorageType REDUCE_ORDER = StorageType { 0 } - ORDER;
  106. static constexpr StorageType PRIME_INVERSE_MOD_R = StorageType { 0 } - calculate_modular_inverse_mod_r(PRIME);
  107. static constexpr StorageType ORDER_INVERSE_MOD_R = StorageType { 0 } - calculate_modular_inverse_mod_r(ORDER);
  108. static constexpr StorageType R2_MOD_PRIME = calculate_r2_mod(PRIME);
  109. static constexpr StorageType R2_MOD_ORDER = calculate_r2_mod(ORDER);
  110. public:
  111. size_t key_size() override { return POINT_BYTE_SIZE; }
  112. ErrorOr<ByteBuffer> generate_private_key() override
  113. {
  114. auto buffer = TRY(ByteBuffer::create_uninitialized(KEY_BYTE_SIZE));
  115. fill_with_random(buffer);
  116. return buffer;
  117. }
  118. ErrorOr<ByteBuffer> generate_public_key(ReadonlyBytes a) override
  119. {
  120. return compute_coordinate(a, GENERATOR_POINT);
  121. }
  122. ErrorOr<ByteBuffer> compute_coordinate(ReadonlyBytes scalar_bytes, ReadonlyBytes point_bytes) override
  123. {
  124. AK::FixedMemoryStream scalar_stream { scalar_bytes };
  125. AK::FixedMemoryStream point_stream { point_bytes };
  126. StorageType scalar = TRY(scalar_stream.read_value<BigEndian<StorageType>>());
  127. JacobianPoint point = TRY(read_uncompressed_point(point_stream));
  128. JacobianPoint result = TRY(compute_coordinate_internal(scalar, point));
  129. // Export the values into an output buffer
  130. auto buf = TRY(ByteBuffer::create_uninitialized(POINT_BYTE_SIZE));
  131. AK::FixedMemoryStream buf_stream { buf.bytes() };
  132. TRY(buf_stream.write_value<u8>(0x04));
  133. TRY(buf_stream.write_value<BigEndian<StorageType>>(result.x));
  134. TRY(buf_stream.write_value<BigEndian<StorageType>>(result.y));
  135. return buf;
  136. }
  137. ErrorOr<ByteBuffer> derive_premaster_key(ReadonlyBytes shared_point) override
  138. {
  139. VERIFY(shared_point.size() == POINT_BYTE_SIZE);
  140. VERIFY(shared_point[0] == 0x04);
  141. ByteBuffer premaster_key = TRY(ByteBuffer::create_uninitialized(KEY_BYTE_SIZE));
  142. premaster_key.overwrite(0, shared_point.data() + 1, KEY_BYTE_SIZE);
  143. return premaster_key;
  144. }
  145. ErrorOr<bool> verify(ReadonlyBytes hash, ReadonlyBytes pubkey, ReadonlyBytes signature)
  146. {
  147. Crypto::ASN1::Decoder asn1_decoder(signature);
  148. TRY(asn1_decoder.enter());
  149. auto r_bigint = TRY(asn1_decoder.read<Crypto::UnsignedBigInteger>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::Integer));
  150. auto s_bigint = TRY(asn1_decoder.read<Crypto::UnsignedBigInteger>(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::Integer));
  151. StorageType r = 0u;
  152. StorageType s = 0u;
  153. for (size_t i = 0; i < (KEY_BIT_SIZE / 32); i++) {
  154. StorageType rr = r_bigint.words()[i];
  155. StorageType ss = s_bigint.words()[i];
  156. r |= (rr << (i * 32));
  157. s |= (ss << (i * 32));
  158. }
  159. // z is the hash
  160. StorageType z = 0u;
  161. for (uint8_t byte : hash) {
  162. z <<= 8;
  163. z |= byte;
  164. }
  165. AK::FixedMemoryStream pubkey_stream { pubkey };
  166. JacobianPoint pubkey_point = TRY(read_uncompressed_point(pubkey_stream));
  167. StorageType r_mo = to_montgomery_order(r);
  168. StorageType s_mo = to_montgomery_order(s);
  169. StorageType z_mo = to_montgomery_order(z);
  170. StorageType s_inv = modular_inverse_order(s_mo);
  171. StorageType u1 = modular_multiply_order(z_mo, s_inv);
  172. StorageType u2 = modular_multiply_order(r_mo, s_inv);
  173. u1 = from_montgomery_order(u1);
  174. u2 = from_montgomery_order(u2);
  175. JacobianPoint point1 = TRY(generate_public_key_internal(u1));
  176. JacobianPoint point2 = TRY(compute_coordinate_internal(u2, pubkey_point));
  177. // Convert the input point into Montgomery form
  178. point1.x = to_montgomery(point1.x);
  179. point1.y = to_montgomery(point1.y);
  180. point1.z = to_montgomery(point1.z);
  181. VERIFY(is_point_on_curve(point1));
  182. // Convert the input point into Montgomery form
  183. point2.x = to_montgomery(point2.x);
  184. point2.y = to_montgomery(point2.y);
  185. point2.z = to_montgomery(point2.z);
  186. VERIFY(is_point_on_curve(point2));
  187. JacobianPoint result = point_add(point1, point2);
  188. // Convert from Jacobian coordinates back to Affine coordinates
  189. convert_jacobian_to_affine(result);
  190. // Make sure the resulting point is on the curve
  191. VERIFY(is_point_on_curve(result));
  192. // Convert the result back from Montgomery form
  193. result.x = from_montgomery(result.x);
  194. result.y = from_montgomery(result.y);
  195. // Final modular reduction on the coordinates
  196. result.x = modular_reduce(result.x);
  197. result.y = modular_reduce(result.y);
  198. return r.is_equal_to_constant_time(result.x);
  199. }
  200. private:
  201. ErrorOr<JacobianPoint> generate_public_key_internal(StorageType a)
  202. {
  203. AK::FixedMemoryStream generator_point_stream { GENERATOR_POINT };
  204. JacobianPoint point = TRY(read_uncompressed_point(generator_point_stream));
  205. return compute_coordinate_internal(a, point);
  206. }
  207. ErrorOr<JacobianPoint> compute_coordinate_internal(StorageType scalar, JacobianPoint point)
  208. {
  209. // FIXME: This will slightly bias the distribution of client secrets
  210. scalar = modular_reduce_order(scalar);
  211. if (scalar.is_zero_constant_time())
  212. return Error::from_string_literal("SECPxxxr1: scalar is zero");
  213. // Convert the input point into Montgomery form
  214. point.x = to_montgomery(point.x);
  215. point.y = to_montgomery(point.y);
  216. point.z = to_montgomery(point.z);
  217. // Check that the point is on the curve
  218. if (!is_point_on_curve(point))
  219. return Error::from_string_literal("SECPxxxr1: point is not on the curve");
  220. JacobianPoint result { 0, 0, 0 };
  221. JacobianPoint temp_result { 0, 0, 0 };
  222. // Calculate the scalar times point multiplication in constant time
  223. for (size_t i = 0; i < KEY_BIT_SIZE; i++) {
  224. temp_result = point_add(result, point);
  225. auto condition = (scalar & 1u) == 1u;
  226. result.x = select(result.x, temp_result.x, condition);
  227. result.y = select(result.y, temp_result.y, condition);
  228. result.z = select(result.z, temp_result.z, condition);
  229. point = point_double(point);
  230. scalar >>= 1u;
  231. }
  232. // Convert from Jacobian coordinates back to Affine coordinates
  233. convert_jacobian_to_affine(result);
  234. // Make sure the resulting point is on the curve
  235. VERIFY(is_point_on_curve(result));
  236. // Convert the result back from Montgomery form
  237. result.x = from_montgomery(result.x);
  238. result.y = from_montgomery(result.y);
  239. result.z = from_montgomery(result.z);
  240. // Final modular reduction on the coordinates
  241. result.x = modular_reduce(result.x);
  242. result.y = modular_reduce(result.y);
  243. result.z = modular_reduce(result.z);
  244. return result;
  245. }
  246. static ErrorOr<JacobianPoint> read_uncompressed_point(Stream& stream)
  247. {
  248. // Make sure the point is uncompressed
  249. if (TRY(stream.read_value<u8>()) != 0x04)
  250. return Error::from_string_literal("SECPxxxr1: point is not uncompressed format");
  251. JacobianPoint point {
  252. TRY(stream.read_value<BigEndian<StorageType>>()),
  253. TRY(stream.read_value<BigEndian<StorageType>>()),
  254. 1u,
  255. };
  256. return point;
  257. }
  258. constexpr StorageType select(StorageType const& left, StorageType const& right, bool condition)
  259. {
  260. // If condition = 0 return left else right
  261. StorageType mask = static_cast<StorageType>(condition) - 1;
  262. AK::taint_for_optimizer(mask);
  263. return (left & mask) | (right & ~mask);
  264. }
  265. constexpr StorageType modular_reduce(StorageType const& value)
  266. {
  267. // Add -prime % 2^KEY_BIT_SIZE
  268. bool carry = false;
  269. StorageType other = value.addc(REDUCE_PRIME, carry);
  270. // Check for overflow
  271. return select(value, other, carry);
  272. }
  273. constexpr StorageType modular_reduce_order(StorageType const& value)
  274. {
  275. // Add -order % 2^KEY_BIT_SIZE
  276. bool carry = false;
  277. StorageType other = value.addc(REDUCE_ORDER, carry);
  278. // Check for overflow
  279. return select(value, other, carry);
  280. }
  281. constexpr StorageType modular_add(StorageType const& left, StorageType const& right, bool carry_in = false)
  282. {
  283. bool carry = carry_in;
  284. StorageType output = left.addc(right, carry);
  285. // If there is a carry, subtract p by adding 2^KEY_BIT_SIZE - p
  286. StorageType addend = select(0u, REDUCE_PRIME, carry);
  287. carry = false;
  288. output = output.addc(addend, carry);
  289. // If there is still a carry, subtract p by adding 2^KEY_BIT_SIZE - p
  290. addend = select(0u, REDUCE_PRIME, carry);
  291. return output + addend;
  292. }
  293. constexpr StorageType modular_sub(StorageType const& left, StorageType const& right)
  294. {
  295. bool borrow = false;
  296. StorageType output = left.subc(right, borrow);
  297. // If there is a borrow, add p by subtracting 2^KEY_BIT_SIZE - p
  298. StorageType sub = select(0u, REDUCE_PRIME, borrow);
  299. borrow = false;
  300. output = output.subc(sub, borrow);
  301. // If there is still a borrow, add p by subtracting 2^KEY_BIT_SIZE - p
  302. sub = select(0u, REDUCE_PRIME, borrow);
  303. return output - sub;
  304. }
  305. constexpr StorageType modular_multiply(StorageType const& left, StorageType const& right)
  306. {
  307. // Modular multiplication using the Montgomery method: https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
  308. // This requires that the inputs to this function are in Montgomery form.
  309. // T = left * right
  310. StorageTypeX2 mult = left.wide_multiply(right);
  311. StorageType mult_mod_r = static_cast<StorageType>(mult);
  312. // m = ((T mod R) * curve_p')
  313. StorageType m = mult_mod_r * PRIME_INVERSE_MOD_R;
  314. // mp = (m mod R) * curve_p
  315. StorageTypeX2 mp = m.wide_multiply(PRIME);
  316. // t = (T + mp)
  317. bool carry = false;
  318. mult_mod_r.addc(static_cast<StorageType>(mp), carry);
  319. // output = t / R
  320. StorageType mult_high = static_cast<StorageType>(mult >> KEY_BIT_SIZE);
  321. StorageType mp_high = static_cast<StorageType>(mp >> KEY_BIT_SIZE);
  322. return modular_add(mult_high, mp_high, carry);
  323. }
  324. constexpr StorageType modular_square(StorageType const& value)
  325. {
  326. return modular_multiply(value, value);
  327. }
  328. constexpr StorageType to_montgomery(StorageType const& value)
  329. {
  330. return modular_multiply(value, R2_MOD_PRIME);
  331. }
  332. constexpr StorageType from_montgomery(StorageType const& value)
  333. {
  334. return modular_multiply(value, 1u);
  335. }
  336. constexpr StorageType modular_inverse(StorageType const& value)
  337. {
  338. // Modular inverse modulo the curve prime can be computed using Fermat's little theorem: a^(p-2) mod p = a^-1 mod p.
  339. // Calculating a^(p-2) mod p can be done using the square-and-multiply exponentiation method, as p-2 is constant.
  340. StorageType base = value;
  341. StorageType result = to_montgomery(1u);
  342. StorageType prime_minus_2 = PRIME - 2u;
  343. for (size_t i = 0; i < KEY_BIT_SIZE; i++) {
  344. if ((prime_minus_2 & 1u) == 1u) {
  345. result = modular_multiply(result, base);
  346. }
  347. base = modular_square(base);
  348. prime_minus_2 >>= 1u;
  349. }
  350. return result;
  351. }
  352. constexpr StorageType modular_add_order(StorageType const& left, StorageType const& right, bool carry_in = false)
  353. {
  354. bool carry = carry_in;
  355. StorageType output = left.addc(right, carry);
  356. // If there is a carry, subtract n by adding 2^KEY_BIT_SIZE - n
  357. StorageType addend = select(0u, REDUCE_ORDER, carry);
  358. carry = false;
  359. output = output.addc(addend, carry);
  360. // If there is still a carry, subtract n by adding 2^KEY_BIT_SIZE - n
  361. addend = select(0u, REDUCE_ORDER, carry);
  362. return output + addend;
  363. }
  364. constexpr StorageType modular_multiply_order(StorageType const& left, StorageType const& right)
  365. {
  366. // Modular multiplication using the Montgomery method: https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
  367. // This requires that the inputs to this function are in Montgomery form.
  368. // T = left * right
  369. StorageTypeX2 mult = left.wide_multiply(right);
  370. StorageType mult_mod_r = static_cast<StorageType>(mult);
  371. // m = ((T mod R) * curve_n')
  372. StorageType m = mult_mod_r * ORDER_INVERSE_MOD_R;
  373. // mp = (m mod R) * curve_n
  374. StorageTypeX2 mp = m.wide_multiply(ORDER);
  375. // t = (T + mp)
  376. bool carry = false;
  377. mult_mod_r.addc(static_cast<StorageType>(mp), carry);
  378. // output = t / R
  379. StorageType mult_high = static_cast<StorageType>(mult >> KEY_BIT_SIZE);
  380. StorageType mp_high = static_cast<StorageType>(mp >> KEY_BIT_SIZE);
  381. return modular_add_order(mult_high, mp_high, carry);
  382. }
  383. constexpr StorageType modular_square_order(StorageType const& value)
  384. {
  385. return modular_multiply_order(value, value);
  386. }
  387. constexpr StorageType to_montgomery_order(StorageType const& value)
  388. {
  389. return modular_multiply_order(value, R2_MOD_ORDER);
  390. }
  391. constexpr StorageType from_montgomery_order(StorageType const& value)
  392. {
  393. return modular_multiply_order(value, 1u);
  394. }
  395. constexpr StorageType modular_inverse_order(StorageType const& value)
  396. {
  397. // Modular inverse modulo the curve order can be computed using Fermat's little theorem: a^(n-2) mod n = a^-1 mod n.
  398. // Calculating a^(n-2) mod n can be done using the square-and-multiply exponentiation method, as n-2 is constant.
  399. StorageType base = value;
  400. StorageType result = to_montgomery_order(1u);
  401. StorageType order_minus_2 = ORDER - 2u;
  402. for (size_t i = 0; i < KEY_BIT_SIZE; i++) {
  403. if ((order_minus_2 & 1u) == 1u) {
  404. result = modular_multiply_order(result, base);
  405. }
  406. base = modular_square_order(base);
  407. order_minus_2 >>= 1u;
  408. }
  409. return result;
  410. }
  411. JacobianPoint point_double(JacobianPoint const& point)
  412. {
  413. // Based on "Point Doubling" from http://point-at-infinity.org/ecc/Prime_Curve_Jacobian_Coordinates.html
  414. // if (Y == 0)
  415. // return POINT_AT_INFINITY
  416. if (point.y.is_zero_constant_time()) {
  417. VERIFY_NOT_REACHED();
  418. }
  419. StorageType temp;
  420. // Y2 = Y^2
  421. StorageType y2 = modular_square(point.y);
  422. // S = 4*X*Y2
  423. StorageType s = modular_multiply(point.x, y2);
  424. s = modular_add(s, s);
  425. s = modular_add(s, s);
  426. // M = 3*X^2 + a*Z^4 = 3*(X + Z^2)*(X - Z^2)
  427. // This specific equation from https://github.com/earlephilhower/bearssl-esp8266/blob/6105635531027f5b298aa656d44be2289b2d434f/src/ec/ec_p256_m64.c#L811-L816
  428. // This simplification only works because a = -3 mod p
  429. temp = modular_square(point.z);
  430. StorageType m = modular_add(point.x, temp);
  431. temp = modular_sub(point.x, temp);
  432. m = modular_multiply(m, temp);
  433. temp = modular_add(m, m);
  434. m = modular_add(m, temp);
  435. // X' = M^2 - 2*S
  436. StorageType xp = modular_square(m);
  437. xp = modular_sub(xp, s);
  438. xp = modular_sub(xp, s);
  439. // Y' = M*(S - X') - 8*Y2^2
  440. StorageType yp = modular_sub(s, xp);
  441. yp = modular_multiply(yp, m);
  442. temp = modular_square(y2);
  443. temp = modular_add(temp, temp);
  444. temp = modular_add(temp, temp);
  445. temp = modular_add(temp, temp);
  446. yp = modular_sub(yp, temp);
  447. // Z' = 2*Y*Z
  448. StorageType zp = modular_multiply(point.y, point.z);
  449. zp = modular_add(zp, zp);
  450. // return (X', Y', Z')
  451. return JacobianPoint { xp, yp, zp };
  452. }
  453. JacobianPoint point_add(JacobianPoint const& point_a, JacobianPoint const& point_b)
  454. {
  455. // Based on "Point Addition" from http://point-at-infinity.org/ecc/Prime_Curve_Jacobian_Coordinates.html
  456. if (point_a.x.is_zero_constant_time() && point_a.y.is_zero_constant_time() && point_a.z.is_zero_constant_time()) {
  457. return point_b;
  458. }
  459. StorageType temp;
  460. temp = modular_square(point_b.z);
  461. // U1 = X1*Z2^2
  462. StorageType u1 = modular_multiply(point_a.x, temp);
  463. // S1 = Y1*Z2^3
  464. StorageType s1 = modular_multiply(point_a.y, temp);
  465. s1 = modular_multiply(s1, point_b.z);
  466. temp = modular_square(point_a.z);
  467. // U2 = X2*Z1^2
  468. StorageType u2 = modular_multiply(point_b.x, temp);
  469. // S2 = Y2*Z1^3
  470. StorageType s2 = modular_multiply(point_b.y, temp);
  471. s2 = modular_multiply(s2, point_a.z);
  472. // if (U1 == U2)
  473. // if (S1 != S2)
  474. // return POINT_AT_INFINITY
  475. // else
  476. // return POINT_DOUBLE(X1, Y1, Z1)
  477. if (u1.is_equal_to_constant_time(u2)) {
  478. if (s1.is_equal_to_constant_time(s2)) {
  479. return point_double(point_a);
  480. } else {
  481. VERIFY_NOT_REACHED();
  482. }
  483. }
  484. // H = U2 - U1
  485. StorageType h = modular_sub(u2, u1);
  486. StorageType h2 = modular_square(h);
  487. StorageType h3 = modular_multiply(h2, h);
  488. // R = S2 - S1
  489. StorageType r = modular_sub(s2, s1);
  490. // X3 = R^2 - H^3 - 2*U1*H^2
  491. StorageType x3 = modular_square(r);
  492. x3 = modular_sub(x3, h3);
  493. temp = modular_multiply(u1, h2);
  494. temp = modular_add(temp, temp);
  495. x3 = modular_sub(x3, temp);
  496. // Y3 = R*(U1*H^2 - X3) - S1*H^3
  497. StorageType y3 = modular_multiply(u1, h2);
  498. y3 = modular_sub(y3, x3);
  499. y3 = modular_multiply(y3, r);
  500. temp = modular_multiply(s1, h3);
  501. y3 = modular_sub(y3, temp);
  502. // Z3 = H*Z1*Z2
  503. StorageType z3 = modular_multiply(h, point_a.z);
  504. z3 = modular_multiply(z3, point_b.z);
  505. // return (X3, Y3, Z3)
  506. return JacobianPoint { x3, y3, z3 };
  507. }
  508. void convert_jacobian_to_affine(JacobianPoint& point)
  509. {
  510. StorageType temp;
  511. // X' = X/Z^2
  512. temp = modular_square(point.z);
  513. temp = modular_inverse(temp);
  514. point.x = modular_multiply(point.x, temp);
  515. // Y' = Y/Z^3
  516. temp = modular_square(point.z);
  517. temp = modular_multiply(temp, point.z);
  518. temp = modular_inverse(temp);
  519. point.y = modular_multiply(point.y, temp);
  520. // Z' = 1
  521. point.z = to_montgomery(1u);
  522. }
  523. bool is_point_on_curve(JacobianPoint const& point)
  524. {
  525. // This check requires the point to be in Montgomery form, with Z=1
  526. StorageType temp, temp2;
  527. // Calulcate Y^2 - X^3 - a*X - b = Y^2 - X^3 + 3*X - b
  528. temp = modular_square(point.y);
  529. temp2 = modular_square(point.x);
  530. temp2 = modular_multiply(temp2, point.x);
  531. temp = modular_sub(temp, temp2);
  532. temp = modular_add(temp, point.x);
  533. temp = modular_add(temp, point.x);
  534. temp = modular_add(temp, point.x);
  535. temp = modular_sub(temp, to_montgomery(B));
  536. temp = modular_reduce(temp);
  537. return temp.is_zero_constant_time() && point.z.is_equal_to_constant_time(to_montgomery(1u));
  538. }
  539. };
  540. // SECP256r1 curve
  541. static constexpr SECPxxxr1CurveParameters SECP256r1_CURVE_PARAMETERS {
  542. .prime = "FFFFFFFF_00000001_00000000_00000000_00000000_FFFFFFFF_FFFFFFFF_FFFFFFFF"sv,
  543. .a = "FFFFFFFF_00000001_00000000_00000000_00000000_FFFFFFFF_FFFFFFFF_FFFFFFFC"sv,
  544. .b = "5AC635D8_AA3A93E7_B3EBBD55_769886BC_651D06B0_CC53B0F6_3BCE3C3E_27D2604B"sv,
  545. .order = "FFFFFFFF_00000000_FFFFFFFF_FFFFFFFF_BCE6FAAD_A7179E84_F3B9CAC2_FC632551"sv,
  546. .generator_point = "04_6B17D1F2_E12C4247_F8BCE6E5_63A440F2_77037D81_2DEB33A0_F4A13945_D898C296_4FE342E2_FE1A7F9B_8EE7EB4A_7C0F9E16_2BCE3357_6B315ECE_CBB64068_37BF51F5"sv,
  547. };
  548. using SECP256r1 = SECPxxxr1<256, SECP256r1_CURVE_PARAMETERS>;
  549. // SECP384r1 curve
  550. static constexpr SECPxxxr1CurveParameters SECP384r1_CURVE_PARAMETERS {
  551. .prime = "FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_00000000_00000000_FFFFFFFF"sv,
  552. .a = "FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFFFF_00000000_00000000_FFFFFFFC"sv,
  553. .b = "B3312FA7_E23EE7E4_988E056B_E3F82D19_181D9C6E_FE814112_0314088F_5013875A_C656398D_8A2ED19D_2A85C8ED_D3EC2AEF"sv,
  554. .order = "FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_C7634D81_F4372DDF_581A0DB2_48B0A77A_ECEC196A_CCC52973"sv,
  555. .generator_point = "04_AA87CA22_BE8B0537_8EB1C71E_F320AD74_6E1D3B62_8BA79B98_59F741E0_82542A38_5502F25D_BF55296C_3A545E38_72760AB7_3617DE4A_96262C6F_5D9E98BF_9292DC29_F8F41DBD_289A147C_E9DA3113_B5F0B8C0_0A60B1CE_1D7E819D_7A431D7C_90EA0E5F"sv,
  556. };
  557. using SECP384r1 = SECPxxxr1<384, SECP384r1_CURVE_PARAMETERS>;
  558. }