srp_exchange.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #include "srp_exchange.h"
  2. #include <Windows.h>
  3. #include "../../crypto/random_bytes.h"
  4. namespace sunrise::middleware::gameplay::association::srp {
  5. namespace {
  6. namespace modular = crypto::modular;
  7. /** Private exponents are 32 bytes, the width the remote peer also generates. */
  8. constexpr std::size_t kPrivateSize = 32;
  9. /** Generator of the group below. It is fixed at 2 by the group's definition. */
  10. constexpr std::uint64_t kGenerator = 2;
  11. /** Multiplier that scales the verifier into the public value. SRP fixes it at 3. */
  12. constexpr std::uint64_t kMultiplier = 3;
  13. /**
  14. * The published 1024-bit SRP group modulus, least significant limb first.
  15. * Both endpoints are fixed to this group. It is a public parameter and carries no secret.
  16. */
  17. constexpr modular::Number kGroupModulus{
  18. 0x9FC61D2FC0EB06E3ULL,
  19. 0xFD5138FE8376435BULL,
  20. 0x2FD4CBF4976EAA9AULL,
  21. 0x68EDBC3C05726CC0ULL,
  22. 0xC529F566660E57ECULL,
  23. 0x82559B297BCF1885ULL,
  24. 0xCE8EF4AD69B15D49ULL,
  25. 0x5DC7D7B46154D6B6ULL,
  26. 0x8E495C1D6089DAD1ULL,
  27. 0xE0D5D8E250B98BE4ULL,
  28. 0x383B4813D692C6E0ULL,
  29. 0xD674DF7496EA81D3ULL,
  30. 0x9EA2314C9C256576ULL,
  31. 0x6072618775FF3C0BULL,
  32. 0x9C33F80AFA8FC5E8ULL,
  33. 0xEEAF0AB9ADB38DD6ULL,
  34. };
  35. /**
  36. * Checks one exchanged public value against the Sunrise range policy.
  37. * @param value Imported integer.
  38. * @param modulus Prepared group.
  39. * @return True when the value is nonzero and below the modulus.
  40. */
  41. [[nodiscard]] bool in_range(const modular::Number& value,
  42. const modular::Modulus& modulus) noexcept {
  43. return !modular::is_zero(value) && modular::less_than(value, modulus.value);
  44. }
  45. /**
  46. * Computes the scrambling parameter from both public values.
  47. * @param clientPublic Offered public value in wire form.
  48. * @param serverPublic Answered public value in wire form.
  49. * @param output Receives the parameter as a reduced integer.
  50. * @return True when the hash succeeded.
  51. */
  52. [[nodiscard]] bool scramble(const Integer& clientPublic,
  53. const Integer& serverPublic,
  54. modular::Number& output) noexcept {
  55. crypto::sha256::Digest digest{};
  56. if (!crypto::sha256::hash_pair(clientPublic, serverPublic, digest)) {
  57. return false;
  58. }
  59. // The digest is shorter than one integer, so it enters the arithmetic left zero padded.
  60. std::array<std::byte, kIntegerSize> padded{};
  61. for (std::size_t index = 0; index < digest.size(); ++index) {
  62. padded[kIntegerSize - digest.size() + index] = digest[index];
  63. }
  64. modular::import_big_endian(padded, output);
  65. return true;
  66. }
  67. } // namespace
  68. /** Runs the server half of the exchange. */
  69. bool derive(Exchange& exchange) noexcept {
  70. exchange.complete = false;
  71. // Preparing the group is far cheaper than one exponentiation, so it stays call local.
  72. modular::Modulus modulus{};
  73. if (!modular::prepare(kGroupModulus, modulus)) {
  74. return false;
  75. }
  76. modular::Number verifier{};
  77. modular::Number clientPublic{};
  78. modular::import_big_endian(exchange.verifier, verifier);
  79. modular::import_big_endian(exchange.clientPublic, clientPublic);
  80. if (!in_range(verifier, modulus) || !in_range(clientPublic, modulus)) {
  81. return false;
  82. }
  83. std::array<std::byte, kPrivateSize> privateBytes{};
  84. if (!crypto::random::fill(privateBytes)) {
  85. return false;
  86. }
  87. std::array<std::byte, kIntegerSize> paddedPrivate{};
  88. for (std::size_t index = 0; index < privateBytes.size(); ++index) {
  89. paddedPrivate[kIntegerSize - privateBytes.size() + index] = privateBytes[index];
  90. }
  91. modular::Number exponent{};
  92. modular::import_big_endian(paddedPrivate, exponent);
  93. SecureZeroMemory(privateBytes.data(), privateBytes.size());
  94. SecureZeroMemory(paddedPrivate.data(), paddedPrivate.size());
  95. modular::Number generator{};
  96. modular::set_small(generator, kGenerator);
  97. modular::Number multiplier{};
  98. modular::set_small(multiplier, kMultiplier);
  99. modular::Number serverPublic{};
  100. modular::power(modulus, generator, exponent, serverPublic);
  101. modular::Number scaled{};
  102. modular::multiply(modulus, multiplier, verifier, scaled);
  103. modular::add(modulus, scaled, serverPublic, serverPublic);
  104. modular::export_big_endian(serverPublic, exchange.serverPublic);
  105. modular::Number parameter{};
  106. bool derived = scramble(exchange.clientPublic, exchange.serverPublic, parameter);
  107. if (derived) {
  108. modular::Number shared{};
  109. modular::power(modulus, verifier, parameter, shared);
  110. modular::multiply(modulus, clientPublic, shared, shared);
  111. modular::power(modulus, shared, exponent, shared);
  112. std::array<std::byte, kIntegerSize> sharedBytes{};
  113. modular::export_big_endian(shared, sharedBytes);
  114. derived = crypto::sha256::hash(sharedBytes, exchange.derivedKey);
  115. SecureZeroMemory(sharedBytes.data(), sharedBytes.size());
  116. SecureZeroMemory(shared.data(), shared.size() * sizeof(std::uint64_t));
  117. }
  118. SecureZeroMemory(exponent.data(), exponent.size() * sizeof(std::uint64_t));
  119. exchange.complete = derived;
  120. return derived;
  121. }
  122. } // namespace sunrise::middleware::gameplay::association::srp