ecc_p224_curve.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. /**
  2. * secp224r1 point arithmetic, in process and with no platform provider.
  3. * Windows offers this curve through CNG, Wine does not, so the agreement carries its own math.
  4. * Field values live in the Montgomery domain between the entry points.
  5. */
  6. #include "ecc_p224_curve.h"
  7. #include <algorithm>
  8. namespace sunrise::middleware::crypto::ecc::curve {
  9. namespace {
  10. /** Bits in one field word. */
  11. constexpr unsigned kWordBits = 32;
  12. /** Bytes in one field word. */
  13. constexpr std::size_t kWordBytes = 4;
  14. /** Bits in one byte of the wire form. */
  15. constexpr unsigned kByteBits = 8;
  16. /** Every word of a value the mask selects. */
  17. constexpr std::uint32_t kAllBits = 0xFFFFFFFFU;
  18. /** p = 2^224 - 2^96 + 1, the secp224r1 field prime. */
  19. constexpr Field kPrime{
  20. 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
  21. /** p - 2, the exponent that inverts a field element by Fermat's theorem. */
  22. constexpr Field kPrimeMinusTwo{
  23. 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
  24. /** n, the order of the generator. A private key must be below it. */
  25. constexpr Field kOrder{
  26. 0x5C5C2A3D, 0x13DD2945, 0xE0B8F03E, 0xFFFF16A2, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
  27. /** b of the curve equation y^2 = x^3 - 3x + b. */
  28. constexpr Field kCoefficientB{
  29. 0x2355FFB4, 0x270B3943, 0xD7BFD8BA, 0x5044B0B7, 0xF5413256, 0x0C04B3AB, 0xB4050A85};
  30. /** x of the generator. */
  31. constexpr Field kGeneratorX{
  32. 0x115C1D21, 0x343280D6, 0x56C21122, 0x4A03C1D3, 0x321390B9, 0x6BB4BF7F, 0xB70E0CBD};
  33. /** y of the generator. */
  34. constexpr Field kGeneratorY{
  35. 0x85007E34, 0x44D58199, 0x5A074764, 0xCD4375A0, 0x4C22DFE6, 0xB5F723FB, 0xBD376388};
  36. /** R mod p, which is the value one takes in the Montgomery domain. */
  37. constexpr Field kMontgomeryOne{
  38. 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000};
  39. /** R squared mod p, the multiplier that moves a plain value into the Montgomery domain. */
  40. constexpr Field kMontgomeryR2{
  41. 0x00000001, 0x00000000, 0x00000000, 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000};
  42. /** -p^-1 mod 2^32, the word each reduction step multiplies by. p ends in 1, so this is all bits. */
  43. constexpr std::uint32_t kMontgomeryFactor = 0xFFFFFFFFU;
  44. /** One point in Jacobian coordinates, where x = X/Z^2 and y = Y/Z^3. Z of zero is infinity. */
  45. struct Jacobian {
  46. Field x{};
  47. Field y{};
  48. Field z{};
  49. };
  50. /**
  51. * Adds two values as plain words.
  52. * @param augend Value added to.
  53. * @param addend Value added.
  54. * @param sum Receives the low 224 bits.
  55. * @return The carry out of the top word.
  56. */
  57. [[nodiscard]] std::uint32_t
  58. add_words(const Field& augend, const Field& addend, Field& sum) noexcept {
  59. std::uint64_t carry = 0;
  60. for (std::size_t index = 0; index < kWords; ++index) {
  61. const std::uint64_t total =
  62. static_cast<std::uint64_t>(augend[index]) + addend[index] + carry;
  63. sum[index] = static_cast<std::uint32_t>(total);
  64. carry = total >> kWordBits;
  65. }
  66. return static_cast<std::uint32_t>(carry);
  67. }
  68. /**
  69. * Subtracts one value from another as plain words.
  70. * @param minuend Value subtracted from.
  71. * @param subtrahend Value subtracted.
  72. * @param difference Receives the low 224 bits.
  73. * @return One when the subtraction borrowed, which means the minuend is the smaller value.
  74. */
  75. [[nodiscard]] std::uint32_t
  76. subtract_words(const Field& minuend, const Field& subtrahend, Field& difference) noexcept {
  77. std::uint64_t borrow = 0;
  78. for (std::size_t index = 0; index < kWords; ++index) {
  79. const std::uint64_t total =
  80. static_cast<std::uint64_t>(minuend[index]) - subtrahend[index] - borrow;
  81. difference[index] = static_cast<std::uint32_t>(total);
  82. borrow = (total >> kWordBits) & 1U;
  83. }
  84. return static_cast<std::uint32_t>(borrow);
  85. }
  86. /** @return True when every word is zero. */
  87. [[nodiscard]] bool is_zero(const Field& value) noexcept {
  88. std::uint32_t bits = 0;
  89. for (const std::uint32_t word : value) {
  90. bits |= word;
  91. }
  92. return bits == 0;
  93. }
  94. /** @return True when left is below right. */
  95. [[nodiscard]] bool less_than(const Field& left, const Field& right) noexcept {
  96. Field ignored{};
  97. return subtract_words(left, right, ignored) != 0;
  98. }
  99. /**
  100. * Picks one of two values without branching on the choice.
  101. * @param take True to take the first value.
  102. * @param first Value taken when the choice holds.
  103. * @param second Value taken otherwise.
  104. * @param output Receives the picked value.
  105. */
  106. void select(bool take, const Field& first, const Field& second, Field& output) noexcept {
  107. const std::uint32_t mask = take ? kAllBits : 0U;
  108. for (std::size_t index = 0; index < kWords; ++index) {
  109. output[index] = (first[index] & mask) | (second[index] & ~mask);
  110. }
  111. }
  112. /**
  113. * Multiplies in the Montgomery domain and reduces in the same pass.
  114. * @param multiplicand First factor, below p.
  115. * @param multiplier Second factor, below p.
  116. * @param product Receives multiplicand * multiplier * R^-1 mod p. May alias either factor.
  117. */
  118. void montgomery_multiply(const Field& multiplicand,
  119. const Field& multiplier,
  120. Field& product) noexcept {
  121. // Two words above the field hold the running carries the reduction consumes.
  122. std::array<std::uint32_t, kWords + 2> accumulator{};
  123. for (std::size_t step = 0; step < kWords; ++step) {
  124. std::uint64_t carry = 0;
  125. for (std::size_t index = 0; index < kWords; ++index) {
  126. const std::uint64_t sum =
  127. static_cast<std::uint64_t>(accumulator[index])
  128. + static_cast<std::uint64_t>(multiplicand[index]) * multiplier[step] + carry;
  129. accumulator[index] = static_cast<std::uint32_t>(sum);
  130. carry = sum >> kWordBits;
  131. }
  132. std::uint64_t top = static_cast<std::uint64_t>(accumulator[kWords]) + carry;
  133. accumulator[kWords] = static_cast<std::uint32_t>(top);
  134. accumulator[kWords + 1] = static_cast<std::uint32_t>(top >> kWordBits);
  135. // Clearing the low word by a multiple of p is what divides the result by R.
  136. const auto factor = static_cast<std::uint32_t>(static_cast<std::uint64_t>(accumulator[0])
  137. * kMontgomeryFactor);
  138. carry = (static_cast<std::uint64_t>(accumulator[0])
  139. + static_cast<std::uint64_t>(factor) * kPrime[0])
  140. >> kWordBits;
  141. for (std::size_t index = 1; index < kWords; ++index) {
  142. const std::uint64_t sum = static_cast<std::uint64_t>(accumulator[index])
  143. + static_cast<std::uint64_t>(factor) * kPrime[index] + carry;
  144. accumulator[index - 1] = static_cast<std::uint32_t>(sum);
  145. carry = sum >> kWordBits;
  146. }
  147. top = static_cast<std::uint64_t>(accumulator[kWords]) + carry;
  148. accumulator[kWords - 1] = static_cast<std::uint32_t>(top);
  149. accumulator[kWords] =
  150. accumulator[kWords + 1] + static_cast<std::uint32_t>(top >> kWordBits);
  151. }
  152. Field result{};
  153. std::copy_n(accumulator.begin(), kWords, result.begin());
  154. Field reduced{};
  155. const std::uint32_t borrow = subtract_words(result, kPrime, reduced);
  156. // The result is below 2p, so the one conditional subtraction always finishes it.
  157. select(accumulator[kWords] != 0 || borrow == 0, reduced, result, product);
  158. }
  159. /** Adds two field elements mod p. The sum may alias either input. */
  160. void field_add(const Field& augend, const Field& addend, Field& sum) noexcept {
  161. Field total{};
  162. const std::uint32_t carry = add_words(augend, addend, total);
  163. Field reduced{};
  164. const std::uint32_t borrow = subtract_words(total, kPrime, reduced);
  165. select(carry != 0 || borrow == 0, reduced, total, sum);
  166. }
  167. /** Subtracts one field element from another mod p. The difference may alias either input. */
  168. void field_subtract(const Field& minuend, const Field& subtrahend, Field& difference) noexcept {
  169. Field total{};
  170. const std::uint32_t borrow = subtract_words(minuend, subtrahend, total);
  171. Field wrapped{};
  172. (void)add_words(total, kPrime, wrapped);
  173. select(borrow != 0, wrapped, total, difference);
  174. }
  175. /** Moves a plain value into the Montgomery domain. */
  176. void to_montgomery(const Field& value, Field& output) noexcept {
  177. montgomery_multiply(value, kMontgomeryR2, output);
  178. }
  179. /** Moves a Montgomery value back to its plain form. */
  180. void from_montgomery(const Field& value, Field& output) noexcept {
  181. Field one{};
  182. one[0] = 1;
  183. montgomery_multiply(value, one, output);
  184. }
  185. /**
  186. * Inverts a field element by raising it to p - 2.
  187. * @param value Montgomery value, not zero.
  188. * @param output Receives the Montgomery inverse.
  189. */
  190. void montgomery_inverse(const Field& value, Field& output) noexcept {
  191. Field result = kMontgomeryOne;
  192. for (std::size_t index = kWords; index-- > 0;) {
  193. for (unsigned bit = kWordBits; bit-- > 0;) {
  194. montgomery_multiply(result, result, result);
  195. if (((kPrimeMinusTwo[index] >> bit) & 1U) != 0) {
  196. montgomery_multiply(result, value, result);
  197. }
  198. }
  199. }
  200. output = result;
  201. }
  202. /**
  203. * Doubles one Jacobian point, using that the curve has a of -3.
  204. * @param point Point to double, infinity allowed.
  205. * @param output Receives the doubled point. May alias the input.
  206. */
  207. void jacobian_double(const Jacobian& point, Jacobian& output) noexcept {
  208. Field delta{};
  209. Field gamma{};
  210. Field beta{};
  211. Field alpha{};
  212. Field first{};
  213. Field second{};
  214. Field third{};
  215. montgomery_multiply(point.z, point.z, delta);
  216. montgomery_multiply(point.y, point.y, gamma);
  217. montgomery_multiply(point.x, gamma, beta);
  218. field_subtract(point.x, delta, first);
  219. field_add(point.x, delta, second);
  220. montgomery_multiply(first, second, third);
  221. field_add(third, third, alpha);
  222. field_add(alpha, third, alpha);
  223. Jacobian result{};
  224. montgomery_multiply(alpha, alpha, first);
  225. field_add(beta, beta, second);
  226. field_add(second, second, second);
  227. field_add(second, second, third);
  228. field_subtract(first, third, result.x);
  229. field_add(point.y, point.z, first);
  230. montgomery_multiply(first, first, first);
  231. field_subtract(first, gamma, first);
  232. field_subtract(first, delta, result.z);
  233. field_subtract(second, result.x, first);
  234. montgomery_multiply(alpha, first, first);
  235. montgomery_multiply(gamma, gamma, third);
  236. field_add(third, third, third);
  237. field_add(third, third, third);
  238. field_add(third, third, third);
  239. field_subtract(first, third, result.y);
  240. output = result;
  241. }
  242. /**
  243. * Adds two Jacobian points.
  244. * @param left First point, infinity allowed.
  245. * @param right Second point, infinity allowed.
  246. * @param output Receives the sum. May alias either input.
  247. */
  248. void jacobian_add(const Jacobian& left, const Jacobian& right, Jacobian& output) noexcept {
  249. if (is_zero(left.z)) {
  250. output = right;
  251. return;
  252. }
  253. if (is_zero(right.z)) {
  254. output = left;
  255. return;
  256. }
  257. Field leftSquare{};
  258. Field rightSquare{};
  259. Field leftScaled{};
  260. Field rightScaled{};
  261. Field leftLine{};
  262. Field rightLine{};
  263. Field first{};
  264. Field second{};
  265. montgomery_multiply(left.z, left.z, leftSquare);
  266. montgomery_multiply(right.z, right.z, rightSquare);
  267. montgomery_multiply(left.x, rightSquare, leftScaled);
  268. montgomery_multiply(right.x, leftSquare, rightScaled);
  269. montgomery_multiply(right.z, rightSquare, first);
  270. montgomery_multiply(left.y, first, leftLine);
  271. montgomery_multiply(left.z, leftSquare, second);
  272. montgomery_multiply(right.y, second, rightLine);
  273. Field difference{};
  274. Field slope{};
  275. field_subtract(rightScaled, leftScaled, difference);
  276. field_subtract(rightLine, leftLine, slope);
  277. if (is_zero(difference)) {
  278. // Equal points need the doubling formula; opposite points sum to infinity.
  279. if (is_zero(slope)) {
  280. jacobian_double(left, output);
  281. } else {
  282. output = {};
  283. }
  284. return;
  285. }
  286. field_add(slope, slope, slope);
  287. Field square{};
  288. Field cube{};
  289. Field scaled{};
  290. field_add(difference, difference, square);
  291. montgomery_multiply(square, square, square);
  292. montgomery_multiply(difference, square, cube);
  293. montgomery_multiply(leftScaled, square, scaled);
  294. Jacobian result{};
  295. montgomery_multiply(slope, slope, first);
  296. field_subtract(first, cube, first);
  297. field_add(scaled, scaled, second);
  298. field_subtract(first, second, result.x);
  299. field_subtract(scaled, result.x, first);
  300. montgomery_multiply(slope, first, first);
  301. montgomery_multiply(leftLine, cube, second);
  302. field_add(second, second, second);
  303. field_subtract(first, second, result.y);
  304. field_add(left.z, right.z, first);
  305. montgomery_multiply(first, first, first);
  306. field_subtract(first, leftSquare, first);
  307. field_subtract(first, rightSquare, first);
  308. montgomery_multiply(first, difference, result.z);
  309. output = result;
  310. }
  311. /**
  312. * Converts a Jacobian point to affine coordinates.
  313. * @param point Point with a non-zero z.
  314. * @param output Receives the plain affine coordinates.
  315. */
  316. void to_affine(const Jacobian& point, Point& output) noexcept {
  317. Field inverse{};
  318. Field square{};
  319. Field cube{};
  320. Field value{};
  321. montgomery_inverse(point.z, inverse);
  322. montgomery_multiply(inverse, inverse, square);
  323. montgomery_multiply(square, inverse, cube);
  324. montgomery_multiply(point.x, square, value);
  325. from_montgomery(value, output.x);
  326. montgomery_multiply(point.y, cube, value);
  327. from_montgomery(value, output.y);
  328. }
  329. } // namespace
  330. /** @return The curve generator, which both sides multiply. */
  331. Point generator() noexcept {
  332. return Point{kGeneratorX, kGeneratorY};
  333. }
  334. /** Checks that a point may be multiplied. */
  335. bool on_curve(const Point& point) noexcept {
  336. if (!less_than(point.x, kPrime) || !less_than(point.y, kPrime)) {
  337. return false;
  338. }
  339. Field x{};
  340. Field y{};
  341. to_montgomery(point.x, x);
  342. to_montgomery(point.y, y);
  343. Field left{};
  344. Field right{};
  345. Field term{};
  346. montgomery_multiply(y, y, left);
  347. montgomery_multiply(x, x, right);
  348. montgomery_multiply(right, x, right);
  349. field_add(x, x, term);
  350. field_add(term, x, term);
  351. field_subtract(right, term, right);
  352. to_montgomery(kCoefficientB, term);
  353. field_add(right, term, right);
  354. return left == right;
  355. }
  356. /** Checks one private key. */
  357. bool valid_scalar(const Field& scalar) noexcept {
  358. return !is_zero(scalar) && less_than(scalar, kOrder);
  359. }
  360. /** Multiplies a point by a scalar. */
  361. bool multiply(const Field& scalar, const Point& point, Point& output) noexcept {
  362. Jacobian base{};
  363. to_montgomery(point.x, base.x);
  364. to_montgomery(point.y, base.y);
  365. base.z = kMontgomeryOne;
  366. // Both branches of every bit run, so the scalar does not steer the work that is done.
  367. Jacobian accumulator{};
  368. for (std::size_t index = kWords; index-- > 0;) {
  369. for (unsigned bit = kWordBits; bit-- > 0;) {
  370. Jacobian doubled{};
  371. jacobian_double(accumulator, doubled);
  372. Jacobian summed{};
  373. jacobian_add(doubled, base, summed);
  374. const bool take = ((scalar[index] >> bit) & 1U) != 0;
  375. select(take, summed.x, doubled.x, accumulator.x);
  376. select(take, summed.y, doubled.y, accumulator.y);
  377. select(take, summed.z, doubled.z, accumulator.z);
  378. }
  379. }
  380. if (is_zero(accumulator.z)) {
  381. return false;
  382. }
  383. to_affine(accumulator, output);
  384. return true;
  385. }
  386. /** Reads a wire value into a field element. */
  387. void load(std::span<const std::byte> bytes, Field& output) noexcept {
  388. output = {};
  389. if (bytes.size() != kWords * kWordBytes) {
  390. return;
  391. }
  392. for (std::size_t index = 0; index < kWords; ++index) {
  393. const std::size_t offset = (kWords - 1 - index) * kWordBytes;
  394. std::uint32_t word = 0;
  395. for (std::size_t step = 0; step < kWordBytes; ++step) {
  396. word = (word << kByteBits) | std::to_integer<std::uint32_t>(bytes[offset + step]);
  397. }
  398. output[index] = word;
  399. }
  400. }
  401. /** Writes a field element in its wire form. */
  402. void store(const Field& value, std::span<std::byte> bytes) noexcept {
  403. if (bytes.size() != kWords * kWordBytes) {
  404. return;
  405. }
  406. for (std::size_t index = 0; index < kWords; ++index) {
  407. const std::size_t offset = (kWords - 1 - index) * kWordBytes;
  408. for (std::size_t step = 0; step < kWordBytes; ++step) {
  409. const auto shift = static_cast<unsigned>((kWordBytes - 1 - step) * kByteBits);
  410. bytes[offset + step] = static_cast<std::byte>((value[index] >> shift) & 0xFFU);
  411. }
  412. }
  413. }
  414. } // namespace sunrise::middleware::crypto::ecc::curve