ZeroconfAuthenticator.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #include "ZeroconfAuthenticator.h"
  2. #include "JSONObject.h"
  3. #include <sstream>
  4. #include <sys/select.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include "Logger.h"
  8. #include "ConfigJSON.h"
  9. ZeroconfAuthenticator::ZeroconfAuthenticator(authCallback callback, std::shared_ptr<bell::BaseHTTPServer> httpServer) {
  10. this->gotBlobCallback = callback;
  11. srand((unsigned int)time(NULL));
  12. this->crypto = std::make_unique<Crypto>();
  13. this->crypto->dhInit();
  14. this->server = httpServer;
  15. }
  16. void ZeroconfAuthenticator::registerHandlers() {
  17. // Make it discoverable for spoti clients
  18. registerZeroconf();
  19. auto getInfoHandler = [this](bell::HTTPRequest& request) {
  20. CSPOT_LOG(info, "Got request for info");
  21. bell::HTTPResponse response = {
  22. .connectionFd = request.connection,
  23. .status = 200,
  24. .body = this->buildJsonInfo(),
  25. .contentType = "application/json",
  26. };
  27. server->respond(response);
  28. };
  29. auto addUserHandler = [this](bell::HTTPRequest& request) {
  30. BELL_LOG(info, "http", "Got request for adding user");
  31. bell::JSONObject obj;
  32. obj["status"] = 101;
  33. obj["spotifyError"] = 0;
  34. obj["statusString"] = "ERROR-OK";
  35. bell::HTTPResponse response = {
  36. .connectionFd = request.connection,
  37. .status = 200,
  38. .body = obj.toString(),
  39. .contentType = "application/json",
  40. };
  41. server->respond(response);
  42. auto correctBlob = this->getParameterFromUrlEncoded(request.body, "blob");
  43. this->handleAddUser(request.queryParams);
  44. };
  45. BELL_LOG(info, "cspot", "Zeroconf registering handlers");
  46. this->server->registerHandler(bell::RequestType::GET, "/spotify_info", getInfoHandler);
  47. this->server->registerHandler(bell::RequestType::POST, "/spotify_info", addUserHandler);
  48. }
  49. void ZeroconfAuthenticator::registerZeroconf()
  50. {
  51. const char* service = "_spotify-connect._tcp";
  52. #ifdef ESP_PLATFORM
  53. mdns_txt_item_t serviceTxtData[3] = {
  54. {"VERSION", "1.0"},
  55. {"CPath", "/spotify_info"},
  56. {"Stack", "SP"} };
  57. mdns_service_add("cspot", "_spotify-connect", "_tcp", this->server->serverPort, serviceTxtData, 3);
  58. #else
  59. DNSServiceRef ref = NULL;
  60. TXTRecordRef txtRecord;
  61. TXTRecordCreate(&txtRecord, 0, NULL);
  62. TXTRecordSetValue(&txtRecord, "VERSION", 3, "1.0");
  63. TXTRecordSetValue(&txtRecord, "CPath", 13, "/spotify_info");
  64. TXTRecordSetValue(&txtRecord, "Stack", 2, "SP");
  65. DNSServiceRegister(&ref, 0, 0, (char*)informationString, service, NULL, NULL, htons(this->server->serverPort), TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), NULL, NULL);
  66. TXTRecordDeallocate(&txtRecord);
  67. #endif
  68. }
  69. std::string ZeroconfAuthenticator::getParameterFromUrlEncoded(std::string data, std::string param)
  70. {
  71. auto startStr = data.substr(data.find("&" + param + "=") + param.size() + 2, data.size());
  72. return urlDecode(startStr.substr(0, startStr.find("&")));
  73. }
  74. void ZeroconfAuthenticator::handleAddUser(std::map<std::string, std::string>& queryData)
  75. {
  76. // Get all urlencoded params
  77. auto username = queryData["userName"];
  78. auto blobString = queryData["blob"];
  79. auto clientKeyString = queryData["clientKey"];
  80. auto deviceName = queryData["deviceName"];
  81. // client key and bytes are urlencoded
  82. auto clientKeyBytes = crypto->base64Decode(clientKeyString);
  83. auto blobBytes = crypto->base64Decode(blobString);
  84. // Generated secret based on earlier generated DH
  85. auto secretKey = crypto->dhCalculateShared(clientKeyBytes);
  86. auto loginBlob = std::make_shared<LoginBlob>();
  87. std::string deviceIdStr = deviceId;
  88. loginBlob->loadZeroconf(blobBytes, secretKey, deviceIdStr, username);
  89. gotBlobCallback(loginBlob);
  90. }
  91. std::string ZeroconfAuthenticator::buildJsonInfo()
  92. {
  93. // Encode publicKey into base64
  94. auto encodedKey = crypto->base64Encode(crypto->publicKey);
  95. bell::JSONObject obj;
  96. obj["status"] = 101;
  97. obj["statusString"] = "OK";
  98. obj["version"] = protocolVersion;
  99. obj["spotifyError"] = 0;
  100. obj["libraryVersion"] = swVersion;
  101. obj["accountReq"] = "PREMIUM";
  102. obj["brandDisplayName"] = brandName;
  103. obj["modelDisplayName"] = configMan->deviceName.c_str();
  104. obj["voiceSupport"] = "NO";
  105. obj["availability"] = "";
  106. obj["productID"] = 0;
  107. obj["tokenType"] = "default";
  108. obj["groupStatus"] = "NONE";
  109. obj["resolverVersion"] = "0";
  110. obj["scope"] = "streaming,client-authorization-universal";
  111. obj["activeUser"] = "";
  112. obj["deviceID"] = deviceId;
  113. obj["remoteName"] = configMan->deviceName.c_str();
  114. obj["publicKey"] = encodedKey;
  115. obj["deviceType"] = "SPEAKER";
  116. return obj.toString();
  117. }