AuthChallenges.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. #include "AuthChallenges.h"
  2. #include <algorithm> // for copy
  3. #include <climits> // for CHAR_BIT
  4. #include <random> // for default_random_engine, independent_bits_en...
  5. #include "NanoPBHelper.h" // for pbPutString, pbEncode, pbDecode
  6. #include "pb.h" // for pb_byte_t
  7. #include "pb_decode.h" // for pb_release
  8. using namespace cspot;
  9. using random_bytes_engine =
  10. std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t>;
  11. AuthChallenges::AuthChallenges() {
  12. this->crypto = std::make_unique<Crypto>();
  13. this->clientHello = {};
  14. this->apResponse = {};
  15. this->authRequest = {};
  16. this->clientResPlaintext = {};
  17. }
  18. AuthChallenges::~AuthChallenges() {
  19. // Destruct the protobufs
  20. pb_release(ClientHello_fields, &clientHello);
  21. pb_release(APResponseMessage_fields, &apResponse);
  22. pb_release(ClientResponsePlaintext_fields, &clientResPlaintext);
  23. pb_release(ClientResponseEncrypted_fields, &authRequest);
  24. }
  25. std::vector<uint8_t> AuthChallenges::prepareAuthPacket(
  26. std::vector<uint8_t>& authData, int authType, const std::string& deviceId,
  27. const std::string& username) {
  28. // prepare authentication request proto
  29. pbPutString(username, authRequest.login_credentials.username);
  30. std::copy(authData.begin(), authData.end(),
  31. authRequest.login_credentials.auth_data.bytes);
  32. authRequest.login_credentials.auth_data.size = authData.size();
  33. authRequest.login_credentials.typ = (AuthenticationType)authType;
  34. authRequest.system_info.cpu_family = CpuFamily_CPU_UNKNOWN;
  35. authRequest.system_info.os = Os_OS_UNKNOWN;
  36. auto infoStr = std::string("cspot-player");
  37. pbPutString(infoStr, authRequest.system_info.system_information_string);
  38. pbPutString(deviceId, authRequest.system_info.device_id);
  39. auto versionStr = std::string("cspot-1.1");
  40. pbPutString(versionStr, authRequest.version_string);
  41. authRequest.has_version_string = true;
  42. return pbEncode(ClientResponseEncrypted_fields, &authRequest);
  43. }
  44. std::vector<uint8_t> AuthChallenges::solveApHello(
  45. std::vector<uint8_t>& helloPacket, std::vector<uint8_t>& data) {
  46. // Decode the response
  47. auto skipSize = std::vector<uint8_t>(data.begin() + 4, data.end());
  48. pb_release(APResponseMessage_fields, &apResponse);
  49. pbDecode(apResponse, APResponseMessage_fields, skipSize);
  50. auto diffieKey = std::vector<uint8_t>(
  51. apResponse.challenge.login_crypto_challenge.diffie_hellman.gs,
  52. apResponse.challenge.login_crypto_challenge.diffie_hellman.gs + 96);
  53. // Compute the diffie hellman shared key based on the response
  54. auto sharedKey = this->crypto->dhCalculateShared(diffieKey);
  55. // Init client packet + Init server packets are required for the hmac challenge
  56. data.insert(data.begin(), helloPacket.begin(), helloPacket.end());
  57. // Solve the hmac challenge
  58. auto resultData = std::vector<uint8_t>(0);
  59. for (int x = 1; x < 6; x++) {
  60. auto challengeVector = std::vector<uint8_t>(1);
  61. challengeVector[0] = x;
  62. challengeVector.insert(challengeVector.begin(), data.begin(), data.end());
  63. auto digest = crypto->sha1HMAC(sharedKey, challengeVector);
  64. resultData.insert(resultData.end(), digest.begin(), digest.end());
  65. }
  66. auto lastVec =
  67. std::vector<uint8_t>(resultData.begin(), resultData.begin() + 0x14);
  68. // Digest generated!
  69. auto digest = crypto->sha1HMAC(lastVec, data);
  70. clientResPlaintext.login_crypto_response.has_diffie_hellman = true;
  71. std::copy(digest.begin(), digest.end(),
  72. clientResPlaintext.login_crypto_response.diffie_hellman.hmac);
  73. // Get send and receive keys
  74. this->shanSendKey = std::vector<uint8_t>(resultData.begin() + 0x14,
  75. resultData.begin() + 0x34);
  76. this->shanRecvKey = std::vector<uint8_t>(resultData.begin() + 0x34,
  77. resultData.begin() + 0x54);
  78. return pbEncode(ClientResponsePlaintext_fields, &clientResPlaintext);
  79. }
  80. std::vector<uint8_t> AuthChallenges::prepareClientHello() {
  81. // Prepare protobuf message
  82. this->crypto->dhInit();
  83. // Copy the public key into diffiehellman hello packet
  84. std::copy(this->crypto->publicKey.begin(), this->crypto->publicKey.end(),
  85. clientHello.login_crypto_hello.diffie_hellman.gc);
  86. clientHello.login_crypto_hello.diffie_hellman.server_keys_known = 1;
  87. clientHello.build_info.product = Product_PRODUCT_CLIENT;
  88. clientHello.build_info.platform = Platform2_PLATFORM_LINUX_X86;
  89. clientHello.build_info.version = SPOTIFY_VERSION;
  90. clientHello.feature_set.autoupdate2 = true;
  91. clientHello.cryptosuites_supported[0] = Cryptosuite_CRYPTO_SUITE_SHANNON;
  92. clientHello.padding[0] = 0x1E;
  93. clientHello.has_feature_set = true;
  94. clientHello.login_crypto_hello.has_diffie_hellman = true;
  95. clientHello.has_padding = true;
  96. clientHello.has_feature_set = true;
  97. // Generate the random nonce
  98. auto nonce = crypto->generateVectorWithRandomData(16);
  99. std::copy(nonce.begin(), nonce.end(), clientHello.client_nonce);
  100. return pbEncode(ClientHello_fields, &clientHello);
  101. }