Session.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #include "Session.h"
  2. #include "MercuryManager.h"
  3. #include "Logger.h"
  4. using random_bytes_engine = std::independent_bits_engine<std::default_random_engine, CHAR_BIT, uint8_t>;
  5. Session::Session()
  6. {
  7. this->clientHello = {};
  8. this->apResponse = {};
  9. this->authRequest = {};
  10. this->clientResPlaintext = {};
  11. // Generates the public and priv key
  12. this->crypto = std::make_unique<Crypto>();
  13. this->shanConn = std::make_shared<ShannonConnection>();
  14. }
  15. Session::~Session()
  16. {
  17. pb_release(ClientHello_fields, &clientHello);
  18. pb_release(APResponseMessage_fields, &apResponse);
  19. pb_release(ClientResponsePlaintext_fields, &clientResPlaintext);
  20. }
  21. void Session::connect(std::unique_ptr<PlainConnection> connection)
  22. {
  23. this->conn = std::move(connection);
  24. auto helloPacket = this->sendClientHelloRequest();
  25. this->processAPHelloResponse(helloPacket);
  26. }
  27. void Session::connectWithRandomAp()
  28. {
  29. auto apResolver = std::make_unique<ApResolve>();
  30. this->conn = std::make_unique<PlainConnection>();
  31. auto apAddr = apResolver->fetchFirstApAddress();
  32. CSPOT_LOG(debug, "Connecting with AP <%s>", apAddr.c_str());
  33. this->conn->connectToAp(apAddr);
  34. auto helloPacket = this->sendClientHelloRequest();
  35. CSPOT_LOG(debug, "Sending APHello packet...");
  36. this->processAPHelloResponse(helloPacket);
  37. }
  38. std::vector<uint8_t> Session::authenticate(std::shared_ptr<LoginBlob> blob)
  39. {
  40. // save auth blob for reconnection purposes
  41. authBlob = blob;
  42. // prepare authentication request proto
  43. pbPutString(blob->username, authRequest.login_credentials.username);
  44. std::copy(blob->authData.begin(), blob->authData.end(), authRequest.login_credentials.auth_data.bytes);
  45. authRequest.login_credentials.auth_data.size = blob->authData.size();
  46. authRequest.login_credentials.typ = (AuthenticationType) blob->authType;
  47. authRequest.system_info.cpu_family = CpuFamily_CPU_UNKNOWN;
  48. authRequest.system_info.os = Os_OS_UNKNOWN;
  49. auto infoStr = std::string(informationString);
  50. pbPutString(infoStr, authRequest.system_info.system_information_string);
  51. auto deviceIdStr = std::string(deviceId);
  52. pbPutString(deviceId, authRequest.system_info.device_id);
  53. auto versionStr = std::string(versionString);
  54. pbPutString(versionStr, authRequest.version_string);
  55. authRequest.has_version_string = true;
  56. auto data = pbEncode(ClientResponseEncrypted_fields, &authRequest);
  57. // Send login request
  58. this->shanConn->sendPacket(LOGIN_REQUEST_COMMAND, data);
  59. auto packet = this->shanConn->recvPacket();
  60. switch (packet->command)
  61. {
  62. case AUTH_SUCCESSFUL_COMMAND:
  63. {
  64. CSPOT_LOG(debug, "Authorization successful");
  65. // @TODO store the reusable credentials
  66. // PBWrapper<APWelcome> welcomePacket(packet->data)
  67. return std::vector<uint8_t>({0x1}); // TODO: return actual reusable credentaials to be stored somewhere
  68. break;
  69. }
  70. case AUTH_DECLINED_COMMAND:
  71. {
  72. CSPOT_LOG(error, "Authorization declined");
  73. break;
  74. }
  75. default:
  76. CSPOT_LOG(error, "Unknown auth fail code %d", packet->command);
  77. }
  78. return std::vector<uint8_t>(0);
  79. }
  80. void Session::processAPHelloResponse(std::vector<uint8_t> &helloPacket)
  81. {
  82. CSPOT_LOG(debug, "Processing AP hello response...");
  83. auto data = this->conn->recvPacket();
  84. CSPOT_LOG(debug, "Received AP hello response");
  85. // Decode the response
  86. auto skipSize = std::vector<uint8_t>(data.begin() + 4, data.end());
  87. pb_release(APResponseMessage_fields, &apResponse);
  88. pbDecode(apResponse, APResponseMessage_fields, skipSize);
  89. auto diffieKey = std::vector<uint8_t>(apResponse.challenge.login_crypto_challenge.diffie_hellman.gs, apResponse.challenge.login_crypto_challenge.diffie_hellman.gs + 96);
  90. // Compute the diffie hellman shared key based on the response
  91. auto sharedKey = this->crypto->dhCalculateShared(diffieKey);
  92. // Init client packet + Init server packets are required for the hmac challenge
  93. data.insert(data.begin(), helloPacket.begin(), helloPacket.end());
  94. // Solve the hmac challenge
  95. auto resultData = std::vector<uint8_t>(0);
  96. for (int x = 1; x < 6; x++)
  97. {
  98. auto challengeVector = std::vector<uint8_t>(1);
  99. challengeVector[0] = x;
  100. challengeVector.insert(challengeVector.begin(), data.begin(), data.end());
  101. auto digest = crypto->sha1HMAC(sharedKey, challengeVector);
  102. resultData.insert(resultData.end(), digest.begin(), digest.end());
  103. }
  104. auto lastVec = std::vector<uint8_t>(resultData.begin(), resultData.begin() + 0x14);
  105. // Digest generated!
  106. auto digest = crypto->sha1HMAC(lastVec, data);
  107. clientResPlaintext.login_crypto_response.has_diffie_hellman = true;
  108. std::copy(digest.begin(),
  109. digest.end(),
  110. clientResPlaintext.login_crypto_response.diffie_hellman.hmac);
  111. auto resultPacket = pbEncode(ClientResponsePlaintext_fields, &clientResPlaintext);
  112. auto emptyPrefix = std::vector<uint8_t>(0);
  113. this->conn->sendPrefixPacket(emptyPrefix, resultPacket);
  114. // Get send and receive keys
  115. auto sendKey = std::vector<uint8_t>(resultData.begin() + 0x14, resultData.begin() + 0x34);
  116. auto recvKey = std::vector<uint8_t>(resultData.begin() + 0x34, resultData.begin() + 0x54);
  117. CSPOT_LOG(debug, "Received shannon keys");
  118. // Init shanno-encrypted connection
  119. this->shanConn->wrapConnection(this->conn, sendKey, recvKey);
  120. }
  121. void Session::close() {
  122. this->conn->closeSocket();
  123. }
  124. std::vector<uint8_t> Session::sendClientHelloRequest()
  125. {
  126. // Prepare protobuf message
  127. this->crypto->dhInit();
  128. // Copy the public key into diffiehellman hello packet
  129. std::copy(this->crypto->publicKey.begin(),
  130. this->crypto->publicKey.end(),
  131. clientHello.login_crypto_hello.diffie_hellman.gc);
  132. clientHello.login_crypto_hello.diffie_hellman.server_keys_known = 1;
  133. clientHello.build_info.product = Product_PRODUCT_CLIENT;
  134. clientHello.build_info.platform = Platform2_PLATFORM_LINUX_X86;
  135. clientHello.build_info.version = SPOTIFY_VERSION;
  136. clientHello.feature_set.autoupdate2 = true;
  137. clientHello.cryptosuites_supported[0] = Cryptosuite_CRYPTO_SUITE_SHANNON;
  138. clientHello.padding[0] = 0x1E;
  139. clientHello.has_feature_set = true;
  140. clientHello.login_crypto_hello.has_diffie_hellman = true;
  141. clientHello.has_padding = true;
  142. clientHello.has_feature_set = true;
  143. // Generate the random nonce
  144. auto nonce = crypto->generateVectorWithRandomData(16);
  145. std::copy(nonce.begin(), nonce.end(), clientHello.client_nonce);
  146. auto vecData = pbEncode(ClientHello_fields, &clientHello);
  147. auto prefix = std::vector<uint8_t>({0x00, 0x04});
  148. return this->conn->sendPrefixPacket(prefix, vecData);
  149. }