LCOV - code coverage report
Current view: top level - source/ps - ModIo.h (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 2 8 25.0 %
Date: 2023-01-19 00:18:29 Functions: 3 9 33.3 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  *
       3             :  * Permission is hereby granted, free of charge, to any person obtaining
       4             :  * a copy of this software and associated documentation files (the
       5             :  * "Software"), to deal in the Software without restriction, including
       6             :  * without limitation the rights to use, copy, modify, merge, publish,
       7             :  * distribute, sublicense, and/or sell copies of the Software, and to
       8             :  * permit persons to whom the Software is furnished to do so, subject to
       9             :  * the following conditions:
      10             :  *
      11             :  * The above copyright notice and this permission notice shall be included
      12             :  * in all copies or substantial portions of the Software.
      13             :  *
      14             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      15             :  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      16             :  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      17             :  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      18             :  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      19             :  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      20             :  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      21             :  */
      22             : 
      23             : #ifndef INCLUDED_MODIO
      24             : #define INCLUDED_MODIO
      25             : 
      26             : #include "lib/external_libraries/curl.h"
      27             : #include "lib/os_path.h"
      28             : #include "scriptinterface/ScriptForward.h"
      29             : 
      30             : #include <map>
      31             : #include <sodium.h>
      32             : #include <string>
      33             : #include <vector>
      34             : 
      35             : // TODO: Allocate instance of the below two using sodium_malloc?
      36           0 : struct PKStruct
      37             : {
      38             :     unsigned char sig_alg[2] = {}; // == "Ed"
      39             :     unsigned char keynum[8] = {}; // should match the keynum in the sigstruct, else this is the wrong key
      40             :     unsigned char pk[crypto_sign_PUBLICKEYBYTES] = {};
      41             : };
      42             : 
      43          35 : struct SigStruct
      44             : {
      45             :     unsigned char sig_alg[2] = {}; // "ED" (since we only support the hashed mode)
      46             :     unsigned char keynum[8] = {}; // should match the keynum in the PKStruct
      47             :     unsigned char sig[crypto_sign_BYTES] = {};
      48             : };
      49             : 
      50          70 : struct ModIoModData
      51             : {
      52             :     std::map<std::string, std::string> properties;
      53             :     std::vector<std::string> dependencies;
      54             :     SigStruct sig;
      55             : };
      56             : 
      57             : enum class DownloadProgressStatus {
      58             :     NONE, // Default state
      59             :     GAMEID, // The game ID is being downloaded
      60             :     READY, // The game ID has been downloaded
      61             :     LISTING, // The mod list is being downloaded
      62             :     LISTED, // The mod list has been downloaded
      63             :     DOWNLOADING, // A mod file is being downloaded
      64             :     SUCCESS, // A mod file has been downloaded
      65             : 
      66             :     FAILED_GAMEID, // Game ID couldn't be retrieved
      67             :     FAILED_LISTING, // Mod list couldn't be retrieved
      68             :     FAILED_DOWNLOADING, // File couldn't be retrieved
      69             :     FAILED_FILECHECK // The file is corrupted
      70             : };
      71             : 
      72           0 : struct DownloadProgressData
      73             : {
      74             :     DownloadProgressStatus status;
      75             :     double progress;
      76             :     std::string error;
      77             : };
      78             : 
      79             : struct DownloadCallbackData;
      80             : 
      81             : /**
      82             :  * mod.io API interfacing code.
      83             :  *
      84             :  * Overview
      85             :  *
      86             :  * This class interfaces with a remote API provider that returns a list of mod files.
      87             :  * These can then be downloaded after some cursory checking of well-formedness of the returned
      88             :  * metadata.
      89             :  * Downloaded files are checked for well formedness by validating that they fit the size and hash
      90             :  * indicated by the API, then we check if the file is actually signed by a trusted key, and only
      91             :  * if all of that is success the file is actually possible to be loaded as a mod.
      92             :  *
      93             :  * Security considerations
      94             :  *
      95             :  * This both distrusts the loaded JS mods, and the API as much as possible.
      96             :  * We do not want a malicious mod to use this to download arbitrary files, nor do we want the API
      97             :  * to make us download something we have not verified.
      98             :  * Therefore we only allow mods to download one of the mods returned by this class (using indices).
      99             :  *
     100             :  * This (mostly) necessitates parsing the API responses here, as opposed to in JS.
     101             :  * One could alternatively parse the responses in a locked down JS context, but that would require
     102             :  * storing that code in here, or making sure nobody can overwrite it. Also this would possibly make
     103             :  * some of the needed accesses for downloading and verifying files a bit more complicated.
     104             :  *
     105             :  * Everything downloaded from the API has its signature verified against our public key.
     106             :  * This is a requirement, as otherwise a compromise of the API would result in users installing
     107             :  * possibly malicious files.
     108             :  * So a compromised API can just serve old files that we signed, so in that case there would need
     109             :  * to be an issue in that old file that was missed.
     110             :  *
     111             :  * To limit the extend to how old those files could be the signing key should be rotated
     112             :  * regularly (e.g. every release). To allow old versions of the engine to still use the API
     113             :  * files can be signed by both the old and the new key for some amount of time, that however
     114             :  * only makes sense in case a mod is compatible with both engine versions.
     115             :  *
     116             :  * Note that this does not prevent all possible attacks a package manager/update system should
     117             :  * defend against. This is intentionally not an update system since proper package managers already
     118             :  * exist. However there is some possible overlap in attack vectors and these should be evalutated
     119             :  * whether they apply and to what extend we can fix that on our side (or how to get the API provider
     120             :  * to help us do so). For a list of some possible issues see:
     121             :  * https://github.com/theupdateframework/specification/blob/master/tuf-spec.md
     122             :  *
     123             :  * The mod.io settings are also locked down such that only mods that have been authorized by us
     124             :  * show up in API queries. This is both done so that all required information (dependencies)
     125             :  * are stored for the files, and that only mods that have been checked for being ok are actually
     126             :  * shown to users.
     127             :  */
     128             : class ModIo
     129             : {
     130             :     NONCOPYABLE(ModIo);
     131             : public:
     132             :     ModIo();
     133             :     ~ModIo();
     134             : 
     135             :     // Async requests
     136             :     void StartGetGameId();
     137             :     void StartListMods();
     138             :     void StartDownloadMod(u32 idx);
     139             : 
     140             :     /**
     141             :      * Advance the current async request and perform final steps if the download is complete.
     142             :      *
     143             :      * @param scriptInterface used for parsing the data and possibly install the mod.
     144             :      * @return true if the download is complete (successful or not), false otherwise.
     145             :      */
     146             :     bool AdvanceRequest(const ScriptInterface& scriptInterface);
     147             : 
     148             :     /**
     149             :      * Cancel the current async request and clean things up
     150             :      */
     151             :     void CancelRequest();
     152             : 
     153           0 :     const std::vector<ModIoModData>& GetMods() const
     154             :     {
     155           0 :         return m_ModData;
     156             :     }
     157           0 :     const DownloadProgressData& GetDownloadProgress() const
     158             :     {
     159           0 :         return m_DownloadProgressData;
     160             :     }
     161             : 
     162             : private:
     163             :     static size_t ReceiveCallback(void* buffer, size_t size, size_t nmemb, void* userp);
     164             :     static size_t DownloadCallback(void* buffer, size_t size, size_t nmemb, void* userp);
     165             :     static int DownloadProgressCallback(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
     166             : 
     167             :     CURLMcode SetupRequest(const std::string& url, bool fileDownload);
     168             :     void TearDownRequest();
     169             : 
     170             :     bool ParseGameId(const ScriptInterface& scriptInterface, std::string& err);
     171             :     bool ParseMods(const ScriptInterface& scriptInterface, std::string& err);
     172             : 
     173             :     void DeleteDownloadedFile();
     174             :     bool VerifyDownloadedFile(std::string& err);
     175             : 
     176             :     // Utility methods for parsing mod.io responses and metadata
     177             :     static bool ParseGameIdResponse(const ScriptInterface& scriptInterface, const std::string& responseData, int& id, std::string& err);
     178             :     static bool ParseModsResponse(const ScriptInterface& scriptInterface, const std::string& responseData, std::vector<ModIoModData>& modData, const PKStruct& pk, std::string& err);
     179             :     static bool ParseSignature(const std::vector<std::string>& minisigs, SigStruct& sig, const PKStruct& pk, std::string& err);
     180             : 
     181             :     // Url parts
     182             :     std::string m_BaseUrl;
     183             :     std::string m_GamesRequest;
     184             :     std::string m_GameId;
     185             : 
     186             :     // Query parameters
     187             :     std::string m_ApiKey;
     188             :     std::string m_IdQuery;
     189             : 
     190             :     CURL* m_Curl;
     191             :     CURLM* m_CurlMulti;
     192             :     curl_slist* m_Headers;
     193             :     char m_ErrorBuffer[CURL_ERROR_SIZE];
     194             :     std::string m_ResponseData;
     195             : 
     196             :     // Current mod download
     197             :     int m_DownloadModID;
     198             :     OsPath m_DownloadFilePath;
     199             :     DownloadCallbackData* m_CallbackData;
     200             :     DownloadProgressData m_DownloadProgressData;
     201             : 
     202             :     PKStruct m_pk;
     203             : 
     204             :     std::vector<ModIoModData> m_ModData;
     205             : 
     206             :     friend class TestModIo;
     207             : };
     208             : 
     209             : extern ModIo* g_ModIo;
     210             : 
     211             : #endif // INCLUDED_MODIO

Generated by: LCOV version 1.13