2
0

ZeroconfAuthenticator.cpp 4.8 KB

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