LCOV - code coverage report
Current view: top level - port - cpl_vsil_crypt.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 729 776 93.9 %
Date: 2026-03-25 02:32:38 Functions: 39 40 97.5 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Project:  CPL - Common Portability Library
       4             :  * Purpose:  Implement VSI large file api for encrypted files.
       5             :  * Author:   Even Rouault, even.rouault at spatialys.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, Even Rouault <even.rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "cpl_vsi_virtual.h"
      15             : 
      16             : #include <cstddef>
      17             : #include <algorithm>
      18             : 
      19             : #include "cpl_conv.h"
      20             : #include "cpl_error.h"
      21             : #include "cpl_vsi.h"
      22             : 
      23             : CPL_C_START
      24             : void CPL_DLL VSIInstallCryptFileHandler();
      25             : void CPL_DLL VSISetCryptKey(const GByte *pabyKey, int nKeySize);
      26             : CPL_C_END
      27             : 
      28             : constexpr char VSICRYPT_PREFIX[] = "/vsicrypt/";
      29             : 
      30             : #if defined(HAVE_CRYPTOPP) || defined(DOXYGEN_SKIP)
      31             : 
      32             : //! @cond Doxygen_Suppress
      33             : 
      34             : /* Increase Major in case of backward incompatible changes */
      35             : constexpr int VSICRYPT_CURRENT_MAJOR = 1;
      36             : constexpr int VSICRYPT_CURRENT_MINOR = 0;
      37             : constexpr char VSICRYPT_SIGNATURE[] = "VSICRYPT";  // Must be 8 chars.
      38             : 
      39             : constexpr char VSICRYPT_PREFIX_WITHOUT_SLASH[] = "/vsicrypt";
      40             : 
      41             : constexpr unsigned int VSICRYPT_READ = 0x1;
      42             : constexpr unsigned int VSICRYPT_WRITE = 0x2;
      43             : 
      44             : /* Begin of crypto++ headers */
      45             : #ifdef _MSC_VER
      46             : #pragma warning(push)
      47             : #pragma warning(disable : 4189)
      48             : #pragma warning(disable : 4512)
      49             : #pragma warning(disable : 4244)
      50             : #pragma warning(disable : 4505)
      51             : #endif
      52             : 
      53             : #ifdef __GNUC__
      54             : #pragma GCC diagnostic push
      55             : #pragma GCC diagnostic ignored "-Weffc++"
      56             : #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
      57             : #pragma GCC diagnostic ignored "-Wold-style-cast"
      58             : #endif
      59             : 
      60             : #ifdef USE_ONLY_CRYPTODLL_ALG
      61             : #include "cryptopp/dll.h"
      62             : #else
      63             : #include "cryptopp/aes.h"
      64             : #include "cryptopp/blowfish.h"
      65             : #include "cryptopp/camellia.h"
      66             : #include "cryptopp/cast.h"
      67             : #include "cryptopp/des.h"
      68             : #include "cryptopp/mars.h"
      69             : #include "cryptopp/idea.h"
      70             : #include "cryptopp/rc5.h"
      71             : #include "cryptopp/rc6.h"
      72             : #include "cryptopp/serpent.h"
      73             : #include "cryptopp/shacal2.h"
      74             : #include "cryptopp/skipjack.h"
      75             : #include "cryptopp/tea.h"
      76             : #include "cryptopp/twofish.h"
      77             : #endif
      78             : 
      79             : #include "cryptopp/filters.h"
      80             : #include "cryptopp/modes.h"
      81             : #include "cryptopp/osrng.h"
      82             : 
      83             : #ifdef __GNUC__
      84             : #pragma GCC diagnostic pop
      85             : #endif
      86             : 
      87             : #ifdef _MSC_VER
      88             : #pragma warning(pop)
      89             : #endif
      90             : 
      91             : // Fix compatibility with Crypto++
      92             : #if CRYPTOPP_VERSION >= 600
      93             : typedef CryptoPP::byte cryptopp_byte;
      94             : #else
      95             : typedef byte cryptopp_byte;
      96             : #endif
      97             : 
      98             : /* End of crypto++ headers */
      99             : 
     100             : // I don't really understand why this is necessary, especially
     101             : // when cryptopp.dll and GDAL have been compiled with the same
     102             : // VC version and /MD. But otherwise you'll get crashes
     103             : // Borrowed from dlltest.cpp of crypto++
     104             : #if defined(_WIN32) && defined(USE_ONLY_CRYPTODLL_ALG)
     105             : 
     106             : static CryptoPP::PNew s_pNew = nullptr;
     107             : static CryptoPP::PDelete s_pDelete = nullptr;
     108             : 
     109             : extern "C" __declspec(dllexport) void __cdecl
     110             : SetNewAndDeleteFromCryptoPP(CryptoPP::PNew pNew, CryptoPP::PDelete pDelete,
     111             :                             CryptoPP::PSetNewHandler pSetNewHandler)
     112             : {
     113             :     s_pNew = pNew;
     114             :     s_pDelete = pDelete;
     115             : }
     116             : 
     117             : void *__cdecl operator new(size_t size)
     118             : {
     119             :     return s_pNew(size);
     120             : }
     121             : 
     122             : void __cdecl operator delete(void *p)
     123             : {
     124             :     s_pDelete(p);
     125             : }
     126             : 
     127             : #endif  // defined(_WIN32) && defined(USE_ONLY_CRYPTODLL_ALG)
     128             : 
     129             : static GByte *pabyGlobalKey = nullptr;
     130             : static int nGlobalKeySize = 0;
     131             : 
     132             : typedef enum
     133             : {
     134             :     ALG_AES,
     135             :     ALG_Blowfish,
     136             :     ALG_Camellia,
     137             :     // ALG_CAST128, (obsolete)
     138             :     ALG_CAST256,
     139             :     // ALG_DES, (obsolete)
     140             :     ALG_DES_EDE2,
     141             :     ALG_DES_EDE3,
     142             :     // ALG_DES_XEX3, (obsolete)
     143             :     // ALG_Gost, (obsolete)
     144             :     ALG_MARS,
     145             :     ALG_IDEA,
     146             :     // ALG_RC2, (obsolete)
     147             :     ALG_RC5,
     148             :     ALG_RC6,
     149             :     // ALG_SAFER_K, (obsolete)
     150             :     // ALG_SAFER_SK, (obsolete)
     151             :     ALG_Serpent,
     152             :     ALG_SHACAL2,
     153             :     // ALG_SHARK, (obsolete)
     154             :     ALG_SKIPJACK,
     155             :     ALG_Twofish,
     156             :     // ALG_ThreeWay, (obsolete)
     157             :     ALG_XTEA,
     158             :     ALG_MAX = ALG_XTEA
     159             : } VSICryptAlg;
     160             : 
     161             : typedef enum
     162             : {
     163             :     MODE_CBC,
     164             :     MODE_CFB,
     165             :     MODE_OFB,
     166             :     MODE_CTR,
     167             :     MODE_CBC_CTS,
     168             :     MODE_MAX = MODE_CBC_CTS
     169             : } VSICryptMode;
     170             : 
     171             : //! @endcond
     172             : 
     173             : /************************************************************************/
     174             : /*                           VSISetCryptKey()                           */
     175             : /************************************************************************/
     176             : 
     177             : /** Installs the encryption/decryption key.
     178             :  *
     179             :  * By passing a NULL key, the previously installed key will be cleared.
     180             :  * Note, however, that it is not guaranteed that there won't be trace of it
     181             :  * in other places in memory or in on-disk temporary file.
     182             :  *
     183             :  * @param pabyKey key. Might be NULL to clear previously set key.
     184             :  * @param nKeySize length of the key in bytes. Might be 0 to clear
     185             :  * previously set key.
     186             :  *
     187             :  * @see VSIInstallCryptFileHandler() for documentation on /vsicrypt/
     188             :  */
     189           3 : void VSISetCryptKey(const GByte *pabyKey, int nKeySize)
     190             : {
     191           3 :     CPLAssert((pabyKey != nullptr && nKeySize != 0) ||
     192             :               (pabyKey == nullptr && nKeySize == 0));
     193           3 :     if (pabyGlobalKey)
     194             :     {
     195             :         // Make some effort to clear the memory, although it could have leaked
     196             :         // elsewhere...
     197           2 :         memset(pabyGlobalKey, 0, nGlobalKeySize);
     198           2 :         CPLFree(pabyGlobalKey);
     199           2 :         pabyGlobalKey = nullptr;
     200           2 :         nGlobalKeySize = 0;
     201             :     }
     202           3 :     if (pabyKey)
     203             :     {
     204           2 :         pabyGlobalKey = static_cast<GByte *>(CPLMalloc(nKeySize));
     205           2 :         memcpy(pabyGlobalKey, pabyKey, nKeySize);
     206           2 :         nGlobalKeySize = nKeySize;
     207             :     }
     208           3 : }
     209             : 
     210             : //! @cond Doxygen_Suppress
     211             : 
     212             : /************************************************************************/
     213             : /*                               GetAlg()                               */
     214             : /************************************************************************/
     215             : 
     216             : #undef CASE_ALG
     217             : #define CASE_ALG(alg)                                                          \
     218             :     if (EQUAL(pszName, #alg))                                                  \
     219             :         return ALG_##alg;
     220             : 
     221        1044 : static VSICryptAlg GetAlg(const char *pszName)
     222             : {
     223        1044 :     CASE_ALG(AES)
     224          16 :     CASE_ALG(Blowfish)
     225          14 :     CASE_ALG(Camellia)
     226             :     // CASE_ALG(CAST128) (obsolete)
     227          13 :     CASE_ALG(CAST256)
     228             :     // CASE_ALG(DES) (obsolete)
     229          12 :     CASE_ALG(DES_EDE2)
     230          11 :     CASE_ALG(DES_EDE3)
     231             :     // CASE_ALG(DES_XEX3) (obsolete)
     232             :     // CASE_ALG(Ghost) (obsolete)
     233          10 :     CASE_ALG(MARS)
     234           9 :     CASE_ALG(IDEA)
     235             :     // CASE_ALG(RC2) (obsolete)
     236           8 :     CASE_ALG(RC5)
     237           7 :     CASE_ALG(RC6)
     238             :     // CASE_ALG(SAFER_K) (obsolete)
     239             :     // CASE_ALG(SAFER_SK) (obsolete)
     240           6 :     CASE_ALG(Serpent)
     241           5 :     CASE_ALG(SHACAL2)
     242             :     // CASE_ALG(SHARK) (obsolete)
     243           4 :     CASE_ALG(SKIPJACK)
     244             :     // CASE_ALG(ThreeWay) (obsolete)
     245           3 :     CASE_ALG(Twofish)
     246           2 :     CASE_ALG(XTEA)
     247             : 
     248           1 :     CPLError(CE_Warning, CPLE_NotSupported,
     249             :              "Unsupported cipher algorithm: %s. Using AES instead", pszName);
     250           1 :     return ALG_AES;
     251             : }
     252             : 
     253             : /************************************************************************/
     254             : /*                         GetEncBlockCipher()                          */
     255             : /************************************************************************/
     256             : 
     257             : #undef CASE_ALG
     258             : #define CASE_ALG(alg)                                                          \
     259             :     case ALG_##alg:                                                            \
     260             :         return new CryptoPP::alg::Encryption();
     261             : 
     262        2226 : static CryptoPP::BlockCipher *GetEncBlockCipher(VSICryptAlg eAlg)
     263             : {
     264        2226 :     switch (eAlg)
     265             :     {
     266        2182 :         CASE_ALG(AES)
     267             : #ifndef USE_ONLY_CRYPTODLL_ALG
     268           5 :         CASE_ALG(Blowfish)
     269           3 :         CASE_ALG(Camellia)
     270             :         // CASE_ALG(CAST128) (obsolete)
     271           3 :         CASE_ALG(CAST256)
     272             : #endif
     273             :         // CASE_ALG(DES) (obsolete)
     274           3 :         CASE_ALG(DES_EDE2)
     275           3 :         CASE_ALG(DES_EDE3)
     276             :         // CASE_ALG(DES_XEX3) (obsolete)
     277             : #ifndef USE_ONLY_CRYPTODLL_ALG
     278             :         // CASE_ALG(Gost) (obsolete)
     279           3 :         CASE_ALG(MARS)
     280           3 :         CASE_ALG(IDEA)
     281             :         // CASE_ALG(RC2) (obsolete)
     282           3 :         CASE_ALG(RC5)
     283           3 :         CASE_ALG(RC6)
     284             :         // CASE_ALG(SAFER_K) (obsolete)
     285             :         // CASE_ALG(SAFER_SK) (obsolete)
     286           3 :         CASE_ALG(Serpent)
     287           3 :         CASE_ALG(SHACAL2)
     288             :         // CASE_ALG(SHARK) (obsolete)
     289             : #endif
     290           3 :         CASE_ALG(SKIPJACK)
     291             : #ifndef USE_ONLY_CRYPTODLL_ALG
     292             :         // CASE_ALG(ThreeWay) (obsolete)
     293           3 :         CASE_ALG(Twofish)
     294           3 :         CASE_ALG(XTEA)
     295             : #endif
     296           0 :         default:
     297           0 :             return nullptr;
     298             :     }
     299             : }
     300             : 
     301             : /************************************************************************/
     302             : /*                         GetDecBlockCipher()                          */
     303             : /************************************************************************/
     304             : 
     305             : #undef CASE_ALG
     306             : #define CASE_ALG(alg)                                                          \
     307             :     case ALG_##alg:                                                            \
     308             :         return new CryptoPP::alg::Decryption();
     309             : 
     310        1173 : static CryptoPP::BlockCipher *GetDecBlockCipher(VSICryptAlg eAlg)
     311             : {
     312        1173 :     switch (eAlg)
     313             :     {
     314        1144 :         CASE_ALG(AES)
     315             : #ifndef USE_ONLY_CRYPTODLL_ALG
     316           3 :         CASE_ALG(Blowfish)
     317           2 :         CASE_ALG(Camellia)
     318             :         // CASE_ALG(CAST128) (obsolete)
     319           2 :         CASE_ALG(CAST256)
     320             : #endif
     321             :         // CASE_ALG(DES) (obsolete)
     322           2 :         CASE_ALG(DES_EDE2)
     323           2 :         CASE_ALG(DES_EDE3)
     324             :         // CASE_ALG(DES_XEX3) (obsolete)
     325             : #ifndef USE_ONLY_CRYPTODLL_ALG
     326             :         // CASE_ALG(Gost) (obsolete)
     327           2 :         CASE_ALG(MARS)
     328           2 :         CASE_ALG(IDEA)
     329             :         // CASE_ALG(RC2) (obsolete)
     330           2 :         CASE_ALG(RC5)
     331           2 :         CASE_ALG(RC6)
     332             :         // CASE_ALG(SAFER_K) (obsolete)
     333             :         // CASE_ALG(SAFER_SK) (obsolete)
     334           2 :         CASE_ALG(Serpent)
     335           2 :         CASE_ALG(SHACAL2)
     336             :         // CASE_ALG(SHARK) (obsolete)
     337             : #endif
     338           2 :         CASE_ALG(SKIPJACK)
     339             : #ifndef USE_ONLY_CRYPTODLL_ALG
     340             :         // CASE_ALG(ThreeWay) (obsolete)
     341           2 :         CASE_ALG(Twofish)
     342           2 :         CASE_ALG(XTEA)
     343             : #endif
     344           0 :         default:
     345           0 :             return nullptr;
     346             :     }
     347             : }
     348             : 
     349             : /************************************************************************/
     350             : /*                              GetMode()                               */
     351             : /************************************************************************/
     352             : 
     353        1044 : static VSICryptMode GetMode(const char *pszName)
     354             : {
     355        1044 :     if (EQUAL(pszName, "CBC"))
     356        1038 :         return MODE_CBC;
     357           6 :     if (EQUAL(pszName, "CFB"))
     358           1 :         return MODE_CFB;
     359           5 :     if (EQUAL(pszName, "OFB"))
     360           1 :         return MODE_OFB;
     361           4 :     if (EQUAL(pszName, "CTR"))
     362           1 :         return MODE_CTR;
     363           3 :     if (EQUAL(pszName, "CBC_CTS"))
     364           2 :         return MODE_CBC_CTS;
     365             : 
     366           1 :     CPLError(CE_Warning, CPLE_NotSupported,
     367             :              "Unsupported cipher block mode: %s. Using CBC instead", pszName);
     368           1 :     return MODE_CBC;
     369             : }
     370             : 
     371             : /************************************************************************/
     372             : /*                          VSICryptFileHeader                          */
     373             : /************************************************************************/
     374             : 
     375             : class VSICryptFileHeader
     376             : {
     377             :     CPL_DISALLOW_COPY_ASSIGN(VSICryptFileHeader)
     378             : 
     379             :     std::string CryptKeyCheck(CryptoPP::BlockCipher *poEncCipher);
     380             : 
     381             :   public:
     382        1275 :     VSICryptFileHeader() = default;
     383             : 
     384             :     int ReadFromFile(VSIVirtualHandle *fp, const CPLString &osKey);
     385             :     int WriteToFile(VSIVirtualHandle *fp, CryptoPP::BlockCipher *poEncCipher);
     386             : 
     387             :     uint16_t nHeaderSize = 0;
     388             :     GByte nMajorVersion = 0;
     389             :     GByte nMinorVersion = 0;
     390             :     uint16_t nSectorSize = 512;
     391             :     VSICryptAlg eAlg = ALG_AES;
     392             :     VSICryptMode eMode = MODE_CBC;
     393             :     CPLString osIV{};
     394             :     bool bAddKeyCheck = false;
     395             :     uint64_t nPayloadFileSize = 0;
     396             :     CPLString osFreeText{};
     397             :     CPLString osExtraContent{};
     398             : };
     399             : 
     400             : /************************************************************************/
     401             : /*                         VSICryptReadError()                          */
     402             : /************************************************************************/
     403             : 
     404          51 : static bool VSICryptReadError()
     405             : {
     406          51 :     CPLError(CE_Failure, CPLE_FileIO, "Cannot read header");
     407          51 :     return false;
     408             : }
     409             : 
     410             : /************************************************************************/
     411             : /*                      VSICryptGenerateSectorIV()                      */
     412             : /************************************************************************/
     413             : 
     414             : // TODO(rouault): This function really needs a comment saying what it does.
     415      145344 : static std::string VSICryptGenerateSectorIV(const std::string &osIV,
     416             :                                             vsi_l_offset nOffset)
     417             : {
     418      145344 :     std::string osSectorIV(osIV);
     419      145344 :     const size_t nLength = std::min(sizeof(vsi_l_offset), osSectorIV.size());
     420     1308100 :     for (size_t i = 0; i < nLength; i++)
     421             :     {
     422             :         // TODO(rouault): Explain what this block is trying to do?
     423     1162750 :         osSectorIV[i] = static_cast<char>((osSectorIV[i] ^ nOffset) & 0xff);
     424     1162750 :         nOffset >>= 8;
     425             :     }
     426      145344 :     return osSectorIV;
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                           CryptKeyCheck()                            */
     431             : /************************************************************************/
     432             : 
     433             : std::string
     434          14 : VSICryptFileHeader::CryptKeyCheck(CryptoPP::BlockCipher *poEncCipher)
     435             : {
     436          28 :     std::string osKeyCheckRes;
     437             : 
     438          14 :     CPLAssert(osIV.size() == poEncCipher->BlockSize());
     439             :     // Generate a unique IV with a sector offset of 0xFFFFFFFFFFFFFFFF.
     440             :     std::string osCheckIV(
     441          28 :         VSICryptGenerateSectorIV(osIV, ~(static_cast<vsi_l_offset>(0))));
     442             :     CryptoPP::StreamTransformation *poMode;
     443             :     try
     444             :     {
     445          28 :         poMode = new CryptoPP::CBC_Mode_ExternalCipher::Encryption(
     446             :             *poEncCipher,
     447          14 :             reinterpret_cast<const cryptopp_byte *>(osCheckIV.c_str()));
     448             :     }
     449           0 :     catch (const std::exception &e)
     450             :     {
     451           0 :         CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
     452           0 :                  e.what());
     453           0 :         return std::string();
     454             :     }
     455          14 :     CryptoPP::StringSink *poSink = new CryptoPP::StringSink(osKeyCheckRes);
     456             :     CryptoPP::StreamTransformationFilter *poEnc =
     457             :         new CryptoPP::StreamTransformationFilter(
     458          14 :             *poMode, poSink, CryptoPP::StreamTransformationFilter::NO_PADDING);
     459             :     // Not sure if it is add extra security, but pick up something that is
     460             :     // unlikely to be a plain text (random number).
     461          28 :     poEnc->Put(
     462             :         reinterpret_cast<const cryptopp_byte *>(
     463             :             "\xDB\x31\xB9\x1B\xD3\x1C\xFA\x3E\x84\x06\xC1\x42\xC3\xEC\xCD\x9A"
     464             :             "\x02\x36\x22\x15\x58\x88\x74\x65\x00\x2F\x98\xBC\x69\x22\xE1\x63"),
     465          14 :         std::min(32U, poEncCipher->BlockSize()));
     466          14 :     poEnc->MessageEnd();
     467          14 :     delete poEnc;
     468          14 :     delete poMode;
     469             : 
     470          14 :     return osKeyCheckRes;
     471             : }
     472             : 
     473             : /************************************************************************/
     474             : /*                            ReadFromFile()                            */
     475             : /************************************************************************/
     476             : 
     477         236 : int VSICryptFileHeader::ReadFromFile(VSIVirtualHandle *fp,
     478             :                                      const CPLString &osKey)
     479             : {
     480         236 :     GByte abySignature[8] = {};
     481         236 :     fp->Seek(0, SEEK_SET);
     482         236 :     CPL_STATIC_ASSERT(sizeof(VSICRYPT_SIGNATURE) == 8 + 1);
     483         463 :     if (fp->Read(abySignature, 8, 1) == 0 ||
     484         227 :         memcmp(abySignature, VSICRYPT_SIGNATURE, 8) != 0)
     485             :     {
     486          33 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid signature");
     487          33 :         return FALSE;
     488             :     }
     489             : 
     490         203 :     if (!fp->ReadLSB(nHeaderSize))
     491           2 :         return VSICryptReadError();
     492         201 :     if (nHeaderSize < 8 + 2 + 1 + 1)
     493             :     {
     494           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid header size : %d",
     495           1 :                  nHeaderSize);
     496           1 :         return FALSE;
     497             :     }
     498             : 
     499         200 :     if (fp->Read(&nMajorVersion, 1, 1) == 0)
     500           1 :         return VSICryptReadError();
     501         199 :     if (fp->Read(&nMinorVersion, 1, 1) == 0)
     502           1 :         return VSICryptReadError();
     503             : 
     504         198 :     if (nMajorVersion != VSICRYPT_CURRENT_MAJOR)
     505             :     {
     506           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Unhandled major version : %d",
     507           3 :                  nMajorVersion);
     508           3 :         return FALSE;
     509             :     }
     510         195 :     if (nMinorVersion != VSICRYPT_CURRENT_MINOR)
     511             :     {
     512           2 :         CPLDebug("VSICRYPT", "Minor version in file is %d", nMinorVersion);
     513             :     }
     514             : 
     515         195 :     if (!fp->ReadLSB(nSectorSize))
     516           2 :         return VSICryptReadError();
     517             : 
     518             :     GByte nAlg, nMode;
     519         193 :     if (fp->Read(&nAlg, 1, 1) == 0 || fp->Read(&nMode, 1, 1) == 0)
     520           2 :         return VSICryptReadError();
     521         191 :     if (nAlg > ALG_MAX)
     522             :     {
     523           2 :         CPLError(CE_Failure, CPLE_NotSupported,
     524             :                  "Unsupported cipher algorithm %d", nAlg);
     525           2 :         return FALSE;
     526             :     }
     527         189 :     if (nMode > MODE_MAX)
     528             :     {
     529           2 :         CPLError(CE_Failure, CPLE_NotSupported,
     530             :                  "Unsupported cipher block mode %d", nMode);
     531           2 :         return FALSE;
     532             :     }
     533         187 :     eAlg = static_cast<VSICryptAlg>(nAlg);
     534         187 :     eMode = static_cast<VSICryptMode>(nMode);
     535             : 
     536             :     GByte nIVSize;
     537         187 :     if (fp->Read(&nIVSize, 1, 1) == 0)
     538           1 :         return VSICryptReadError();
     539             : 
     540         186 :     osIV.resize(nIVSize);
     541         186 :     if (fp->Read(osIV.data(), 1, nIVSize) != nIVSize)
     542          18 :         return VSICryptReadError();
     543             : 
     544             :     GUInt16 nFreeTextSize;
     545         168 :     if (fp->Read(&nFreeTextSize, 2, 1) == 0)
     546           2 :         return VSICryptReadError();
     547             : 
     548         166 :     osFreeText.resize(nFreeTextSize);
     549         166 :     if (fp->Read(osFreeText.data(), 1, nFreeTextSize) != nFreeTextSize)
     550           5 :         return VSICryptReadError();
     551             : 
     552             :     GByte nKeyCheckSize;
     553         161 :     if (fp->Read(&nKeyCheckSize, 1, 1) == 0)
     554           1 :         return VSICryptReadError();
     555         160 :     bAddKeyCheck = nKeyCheckSize != 0;
     556         160 :     if (nKeyCheckSize)
     557             :     {
     558          11 :         CPLString osKeyCheck;
     559          11 :         osKeyCheck.resize(nKeyCheckSize);
     560          11 :         if (fp->Read(osKeyCheck.data(), 1, nKeyCheckSize) != nKeyCheckSize)
     561           2 :             return VSICryptReadError();
     562             : 
     563           9 :         if (osKey.empty() && pabyGlobalKey == nullptr)
     564             :         {
     565           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     566             :                      "Encryption key not defined as key/key_b64 parameter, "
     567             :                      "VSICRYPT_KEY/VSICRYPT_KEY_B64 configuration option or "
     568             :                      "VSISetCryptKey() API");
     569           1 :             return FALSE;
     570             :         }
     571             : 
     572           8 :         CryptoPP::BlockCipher *poEncCipher = GetEncBlockCipher(eAlg);
     573           8 :         if (poEncCipher == nullptr)
     574           0 :             return FALSE;
     575             : 
     576           8 :         if (osIV.size() != poEncCipher->BlockSize())
     577             :         {
     578           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     579             :                      "Inconsistent initial vector");
     580           1 :             delete poEncCipher;
     581           1 :             return FALSE;
     582             :         }
     583             : 
     584           7 :         int nMaxKeySize = static_cast<int>(poEncCipher->MaxKeyLength());
     585             : 
     586             :         try
     587             :         {
     588           7 :             if (!osKey.empty())
     589             :             {
     590             :                 const int nKeySize =
     591           6 :                     std::min(nMaxKeySize, static_cast<int>(osKey.size()));
     592           6 :                 poEncCipher->SetKey(
     593           6 :                     reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
     594           6 :                     nKeySize);
     595             :             }
     596           1 :             else if (pabyGlobalKey)
     597             :             {
     598           1 :                 const int nKeySize = std::min(nMaxKeySize, nGlobalKeySize);
     599           1 :                 poEncCipher->SetKey(pabyGlobalKey, nKeySize);
     600             :             }
     601             :         }
     602           1 :         catch (const std::exception &e)
     603             :         {
     604           1 :             CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
     605           1 :                      e.what());
     606           1 :             delete poEncCipher;
     607           1 :             return FALSE;
     608             :         }
     609             : 
     610           6 :         std::string osKeyCheckRes = CryptKeyCheck(poEncCipher);
     611             : 
     612           6 :         delete poEncCipher;
     613             : 
     614          12 :         if (osKeyCheck.size() != osKeyCheckRes.size() ||
     615           6 :             memcmp(osKeyCheck.c_str(), osKeyCheckRes.c_str(),
     616             :                    osKeyCheck.size()) != 0)
     617             :         {
     618           2 :             CPLError(CE_Failure, CPLE_AppDefined, "Bad key");
     619           2 :             return FALSE;
     620             :         }
     621             :     }
     622             : 
     623         153 :     if (!fp->ReadLSB(nPayloadFileSize))
     624           8 :         return VSICryptReadError();
     625             : #ifdef VERBOSE_VSICRYPT
     626             :     CPLDebug("VSICRYPT", "nPayloadFileSize read = " CPL_FRMT_GUIB,
     627             :              nPayloadFileSize);
     628             : #endif
     629             : 
     630         145 :     bool bError = false;
     631         145 :     const uint16_t nExtraContentSize = fp->ReadLSB<uint16_t>(&bError);
     632         145 :     if (bError)
     633           2 :         return VSICryptReadError();
     634             : 
     635         143 :     osExtraContent.resize(nExtraContentSize);
     636         143 :     if (fp->Read(osExtraContent.data(), 1, nExtraContentSize) !=
     637         143 :         nExtraContentSize)
     638           4 :         return VSICryptReadError();
     639             : 
     640         139 :     return TRUE;
     641             : }
     642             : 
     643             : /************************************************************************/
     644             : /*                            WriteToFile()                             */
     645             : /************************************************************************/
     646             : 
     647        2075 : int VSICryptFileHeader::WriteToFile(VSIVirtualHandle *fp,
     648             :                                     CryptoPP::BlockCipher *poEncCipher)
     649             : {
     650        2075 :     fp->Seek(0, SEEK_SET);
     651             : 
     652        2075 :     bool bRet = fp->Write(VSICRYPT_SIGNATURE, 8, 1) == 1;
     653             : 
     654        2075 :     std::string osKeyCheckRes;
     655        2075 :     if (bAddKeyCheck)
     656             :     {
     657           8 :         osKeyCheckRes = CryptKeyCheck(poEncCipher);
     658             :     }
     659             : 
     660             :     GUInt16 nHeaderSizeNew =
     661             :         static_cast<GUInt16>(8 +                         /* signature */
     662             :                              2 +                         /* header size */
     663             :                              1 +                         /* major version */
     664             :                              1 +                         /* minor version */
     665             :                              2 +                         /* sector size */
     666             :                              1 +                         /* alg */
     667             :                              1 +                         /* mode */
     668        2075 :                              1 + osIV.size() +           /* IV */
     669        2075 :                              2 + osFreeText.size() +     /* free text */
     670        2075 :                              1 + osKeyCheckRes.size() +  /* key check */
     671             :                              8 +                         /* payload size */
     672        2075 :                              2 + osExtraContent.size()); /* extra content */
     673        2075 :     if (nHeaderSize != 0)
     674        1038 :         CPLAssert(nHeaderSizeNew == nHeaderSize);
     675             :     else
     676        1037 :         nHeaderSize = nHeaderSizeNew;
     677             : 
     678        2075 :     bRet &= fp->WriteLSB(nHeaderSizeNew);
     679             : 
     680        2075 :     bRet &= fp->WriteLSB(static_cast<GByte>(VSICRYPT_CURRENT_MAJOR));
     681             : 
     682        2075 :     bRet &= fp->WriteLSB(static_cast<GByte>(VSICRYPT_CURRENT_MINOR));
     683             : 
     684        2075 :     bRet &= fp->WriteLSB(nSectorSize);
     685             : 
     686        2075 :     bRet &= fp->WriteLSB(static_cast<GByte>(eAlg));
     687             : 
     688        2075 :     bRet &= fp->WriteLSB(static_cast<GByte>(eMode));
     689             : 
     690        2075 :     GByte nIVSizeToWrite = static_cast<GByte>(osIV.size());
     691        2075 :     CPLAssert(nIVSizeToWrite == osIV.size());
     692        2075 :     bRet &= (fp->Write(&nIVSizeToWrite, 1, 1) == 1);
     693        2075 :     bRet &= (fp->Write(osIV.c_str(), 1, osIV.size()) == osIV.size());
     694             : 
     695        2075 :     bRet &= fp->WriteLSB(static_cast<uint16_t>(osFreeText.size()));
     696        2075 :     bRet &= (fp->Write(osFreeText.c_str(), 1, osFreeText.size()) ==
     697        2075 :              osFreeText.size());
     698             : 
     699        2075 :     GByte nSize = static_cast<GByte>(osKeyCheckRes.size());
     700        2075 :     bRet &= (fp->Write(&nSize, 1, 1) == 1);
     701        2075 :     bRet &= (fp->Write(osKeyCheckRes.c_str(), 1, osKeyCheckRes.size()) ==
     702        2075 :              osKeyCheckRes.size());
     703             : 
     704        2075 :     bRet &= fp->WriteLSB(nPayloadFileSize);
     705             : 
     706        2075 :     bRet &= fp->WriteLSB(static_cast<uint16_t>(osExtraContent.size()));
     707        2075 :     bRet &= (fp->Write(osExtraContent.c_str(), 1, osExtraContent.size()) ==
     708        2075 :              osExtraContent.size());
     709             : 
     710        2075 :     CPLAssert(fp->Tell() == nHeaderSize);
     711             : 
     712        4150 :     return bRet;
     713             : }
     714             : 
     715             : /************************************************************************/
     716             : /*                          VSICryptFileHandle                          */
     717             : /************************************************************************/
     718             : 
     719             : class VSICryptFileHandle final : public VSIVirtualHandle
     720             : {
     721             :     CPL_DISALLOW_COPY_ASSIGN(VSICryptFileHandle)
     722             : 
     723             :   private:
     724             :     CPLString osBaseFilename{};
     725             :     int nPerms = 0;
     726             :     VSIVirtualHandle *poBaseHandle = nullptr;
     727             :     VSICryptFileHeader *poHeader = nullptr;
     728             :     bool bUpdateHeader = false;
     729             :     vsi_l_offset nCurPos = 0;
     730             :     bool bEOF = false;
     731             :     bool bError = false;
     732             : 
     733             :     CryptoPP::BlockCipher *poEncCipher = nullptr;
     734             :     CryptoPP::BlockCipher *poDecCipher = nullptr;
     735             :     int nBlockSize = 0;
     736             : 
     737             :     vsi_l_offset nWBOffset = 0;
     738             :     GByte *pabyWB = nullptr;
     739             :     int nWBSize = 0;
     740             :     bool bWBDirty = false;
     741             : 
     742             :     bool bLastSectorWasModified = false;
     743             : 
     744             :     void EncryptBlock(GByte *pabyData, vsi_l_offset nOffset);
     745             :     bool DecryptBlock(GByte *pabyData, vsi_l_offset nOffset);
     746             :     bool FlushDirty();
     747             : 
     748             :   public:
     749             :     VSICryptFileHandle(const CPLString &osBaseFilename,
     750             :                        VSIVirtualHandle *poBaseHandle,
     751             :                        VSICryptFileHeader *poHeader, int nPerms);
     752             :     ~VSICryptFileHandle() override;
     753             : 
     754             :     int Init(const CPLString &osKey, bool bWriteHeader = false);
     755             : 
     756             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
     757             :     vsi_l_offset Tell() override;
     758             :     size_t Read(void *pBuffer, size_t nBytes) override;
     759             :     size_t Write(const void *pBuffer, size_t nBytes) override;
     760             :     int Eof() override;
     761             :     int Error() override;
     762             :     void ClearErr() override;
     763             :     int Flush() override;
     764             :     int Close() override;
     765             :     int Truncate(vsi_l_offset nNewSize) override;
     766             : };
     767             : 
     768             : /************************************************************************/
     769             : /*                         VSICryptFileHandle()                         */
     770             : /************************************************************************/
     771             : 
     772        1174 : VSICryptFileHandle::VSICryptFileHandle(const CPLString &osBaseFilenameIn,
     773             :                                        VSIVirtualHandle *poBaseHandleIn,
     774             :                                        VSICryptFileHeader *poHeaderIn,
     775        1174 :                                        int nPermsIn)
     776             :     : osBaseFilename(osBaseFilenameIn), nPerms(nPermsIn),
     777        1174 :       poBaseHandle(poBaseHandleIn), poHeader(poHeaderIn)
     778             : {
     779        1174 : }
     780             : 
     781             : /************************************************************************/
     782             : /*                        ~VSICryptFileHandle()                         */
     783             : /************************************************************************/
     784             : 
     785        2348 : VSICryptFileHandle::~VSICryptFileHandle()
     786             : {
     787        1174 :     Close();
     788        1174 :     delete poHeader;
     789        1174 :     delete poEncCipher;
     790        1174 :     delete poDecCipher;
     791        1174 :     CPLFree(pabyWB);
     792        2348 : }
     793             : 
     794             : /************************************************************************/
     795             : /*                                Init()                                */
     796             : /************************************************************************/
     797             : 
     798        1174 : int VSICryptFileHandle::Init(const CPLString &osKey, bool bWriteHeader)
     799             : {
     800        1174 :     poEncCipher = GetEncBlockCipher(poHeader->eAlg);
     801        1174 :     if (poEncCipher == nullptr)
     802             :     {
     803           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     804             :                  "Cipher algorithm not supported in this build: %d",
     805           0 :                  static_cast<int>(poHeader->eAlg));
     806           0 :         return FALSE;
     807             :     }
     808             : 
     809        1174 :     if (poHeader->osIV.size() != poEncCipher->BlockSize())
     810             :     {
     811           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent initial vector");
     812           1 :         return FALSE;
     813             :     }
     814             : 
     815        1173 :     poDecCipher = GetDecBlockCipher(poHeader->eAlg);
     816        1173 :     nBlockSize = poEncCipher->BlockSize();
     817        1173 :     int nMaxKeySize = static_cast<int>(poEncCipher->MaxKeyLength());
     818             : 
     819             :     try
     820             :     {
     821        1173 :         if (!osKey.empty())
     822             :         {
     823             :             const int nKeySize =
     824        1168 :                 std::min(nMaxKeySize, static_cast<int>(osKey.size()));
     825        1168 :             poEncCipher->SetKey(
     826        1168 :                 reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
     827        1168 :                 nKeySize);
     828        1167 :             poDecCipher->SetKey(
     829        1167 :                 reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
     830        1167 :                 nKeySize);
     831             :         }
     832           5 :         else if (pabyGlobalKey)
     833             :         {
     834           5 :             const int nKeySize = std::min(nMaxKeySize, nGlobalKeySize);
     835           5 :             poEncCipher->SetKey(pabyGlobalKey, nKeySize);
     836           4 :             poDecCipher->SetKey(pabyGlobalKey, nKeySize);
     837             :         }
     838             :         else
     839           0 :             return FALSE;
     840             :     }
     841           2 :     catch (const std::exception &e)
     842             :     {
     843           2 :         CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
     844           2 :                  e.what());
     845           2 :         return FALSE;
     846             :     }
     847             : 
     848        1171 :     pabyWB = static_cast<GByte *>(CPLCalloc(1, poHeader->nSectorSize));
     849             : 
     850        1171 :     if ((poHeader->nSectorSize % nBlockSize) != 0)
     851             :     {
     852           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     853             :                  "Sector size (%d) is not a multiple of block size (%d)",
     854           3 :                  poHeader->nSectorSize, nBlockSize);
     855           3 :         return FALSE;
     856             :     }
     857        1168 :     if (poHeader->eMode == MODE_CBC_CTS &&
     858           3 :         poHeader->nSectorSize < 2 * nBlockSize)
     859             :     {
     860           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     861             :                  "Sector size (%d) should be at least twice larger than "
     862             :                  "the block size (%d) in CBC_CTS.",
     863           1 :                  poHeader->nSectorSize, nBlockSize);
     864           1 :         return FALSE;
     865             :     }
     866             : 
     867        1167 :     if (bWriteHeader && !poHeader->WriteToFile(poBaseHandle, poEncCipher))
     868             :     {
     869           0 :         return FALSE;
     870             :     }
     871             : 
     872        1167 :     return TRUE;
     873             : }
     874             : 
     875             : /************************************************************************/
     876             : /*                            EncryptBlock()                            */
     877             : /************************************************************************/
     878             : 
     879       70111 : void VSICryptFileHandle::EncryptBlock(GByte *pabyData, vsi_l_offset nOffset)
     880             : {
     881       70111 :     std::string osRes;
     882       70111 :     std::string osIV(VSICryptGenerateSectorIV(poHeader->osIV, nOffset));
     883       70111 :     CPLAssert(static_cast<int>(osIV.size()) == nBlockSize);
     884             : 
     885             :     CryptoPP::StreamTransformation *poMode;
     886             :     try
     887             :     {
     888       70111 :         if (poHeader->eMode == MODE_CBC)
     889       70107 :             poMode = new CryptoPP::CBC_Mode_ExternalCipher::Encryption(
     890       70107 :                 *poEncCipher,
     891       70107 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     892           4 :         else if (poHeader->eMode == MODE_CFB)
     893           1 :             poMode = new CryptoPP::CFB_Mode_ExternalCipher::Encryption(
     894           1 :                 *poEncCipher,
     895           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     896           3 :         else if (poHeader->eMode == MODE_OFB)
     897           1 :             poMode = new CryptoPP::OFB_Mode_ExternalCipher::Encryption(
     898           1 :                 *poEncCipher,
     899           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     900           2 :         else if (poHeader->eMode == MODE_CTR)
     901           1 :             poMode = new CryptoPP::CTR_Mode_ExternalCipher::Encryption(
     902           1 :                 *poEncCipher,
     903           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     904             :         else
     905           1 :             poMode = new CryptoPP::CBC_CTS_Mode_ExternalCipher::Encryption(
     906           1 :                 *poEncCipher,
     907           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     908             :     }
     909           0 :     catch (const std::exception &e)
     910             :     {
     911           0 :         CPLError(CE_Failure, CPLE_AppDefined, "cryptopp exception: %s",
     912           0 :                  e.what());
     913           0 :         return;
     914             :     }
     915       70111 :     CryptoPP::StringSink *poSink = new CryptoPP::StringSink(osRes);
     916             :     CryptoPP::StreamTransformationFilter *poEnc =
     917             :         new CryptoPP::StreamTransformationFilter(
     918       70111 :             *poMode, poSink, CryptoPP::StreamTransformationFilter::NO_PADDING);
     919       70111 :     poEnc->Put(pabyData, poHeader->nSectorSize);
     920       70111 :     poEnc->MessageEnd();
     921       70111 :     delete poEnc;
     922             : 
     923       70111 :     delete poMode;
     924             : 
     925       70111 :     CPLAssert(static_cast<int>(osRes.length()) == poHeader->nSectorSize);
     926       70111 :     memcpy(pabyData, osRes.c_str(), osRes.length());
     927             : }
     928             : 
     929             : /************************************************************************/
     930             : /*                            DecryptBlock()                            */
     931             : /************************************************************************/
     932             : 
     933       75219 : bool VSICryptFileHandle::DecryptBlock(GByte *pabyData, vsi_l_offset nOffset)
     934             : {
     935      150438 :     std::string osRes;
     936      150438 :     std::string osIV(VSICryptGenerateSectorIV(poHeader->osIV, nOffset));
     937       75219 :     CPLAssert(static_cast<int>(osIV.size()) == nBlockSize);
     938       75219 :     CryptoPP::StringSink *poSink = new CryptoPP::StringSink(osRes);
     939       75219 :     CryptoPP::StreamTransformation *poMode = nullptr;
     940       75219 :     CryptoPP::StreamTransformationFilter *poDec = nullptr;
     941             : 
     942             :     try
     943             :     {
     944             :         // Yes, some modes need the encryption cipher.
     945       75219 :         if (poHeader->eMode == MODE_CBC)
     946       75215 :             poMode = new CryptoPP::CBC_Mode_ExternalCipher::Decryption(
     947       75215 :                 *poDecCipher,
     948       75215 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     949           4 :         else if (poHeader->eMode == MODE_CFB)
     950           1 :             poMode = new CryptoPP::CFB_Mode_ExternalCipher::Decryption(
     951           1 :                 *poEncCipher,
     952           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     953           3 :         else if (poHeader->eMode == MODE_OFB)
     954           1 :             poMode = new CryptoPP::OFB_Mode_ExternalCipher::Decryption(
     955           1 :                 *poEncCipher,
     956           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     957           2 :         else if (poHeader->eMode == MODE_CTR)
     958           1 :             poMode = new CryptoPP::CTR_Mode_ExternalCipher::Decryption(
     959           1 :                 *poEncCipher,
     960           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     961             :         else
     962           1 :             poMode = new CryptoPP::CBC_CTS_Mode_ExternalCipher::Decryption(
     963           1 :                 *poDecCipher,
     964           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     965       75219 :         poDec = new CryptoPP::StreamTransformationFilter(
     966       75219 :             *poMode, poSink, CryptoPP::StreamTransformationFilter::NO_PADDING);
     967       75219 :         poDec->Put(reinterpret_cast<const cryptopp_byte *>(pabyData),
     968       75219 :                    poHeader->nSectorSize);
     969       75219 :         poDec->MessageEnd();
     970       75219 :         delete poDec;
     971       75219 :         delete poMode;
     972             :     }
     973           0 :     catch (const std::exception &e)
     974             :     {
     975           0 :         delete poDec;
     976           0 :         delete poMode;
     977             : 
     978           0 :         CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
     979           0 :                  e.what());
     980           0 :         return false;
     981             :     }
     982             : 
     983       75219 :     CPLAssert(static_cast<int>(osRes.length()) == poHeader->nSectorSize);
     984       75219 :     memcpy(pabyData, osRes.c_str(), osRes.length());
     985             : 
     986       75219 :     return true;
     987             : }
     988             : 
     989             : /************************************************************************/
     990             : /*                             FlushDirty()                             */
     991             : /************************************************************************/
     992             : 
     993       55328 : bool VSICryptFileHandle::FlushDirty()
     994             : {
     995       55328 :     if (!bWBDirty)
     996       11097 :         return true;
     997       44231 :     bWBDirty = false;
     998             : 
     999       44231 :     EncryptBlock(pabyWB, nWBOffset);
    1000       44231 :     poBaseHandle->Seek(poHeader->nHeaderSize + nWBOffset, SEEK_SET);
    1001             : 
    1002       44231 :     nWBOffset = 0;
    1003       44231 :     nWBSize = 0;
    1004             : 
    1005       44231 :     if (poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1) != 1)
    1006           0 :         return false;
    1007             : 
    1008       44231 :     return true;
    1009             : }
    1010             : 
    1011             : /************************************************************************/
    1012             : /*                                Seek()                                */
    1013             : /************************************************************************/
    1014             : 
    1015       30973 : int VSICryptFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
    1016             : {
    1017             : #ifdef VERBOSE_VSICRYPT
    1018             :     CPLDebug("VSICRYPT", "Seek(nOffset=" CPL_FRMT_GUIB ", nWhence=%d)", nOffset,
    1019             :              nWhence);
    1020             : #endif
    1021             : 
    1022       30973 :     bEOF = false;
    1023             : 
    1024       30973 :     if (nWhence == SEEK_SET)
    1025       30971 :         nCurPos = nOffset;
    1026           2 :     else if (nWhence == SEEK_CUR)
    1027           0 :         nCurPos += nOffset;
    1028             :     else
    1029           2 :         nCurPos = poHeader->nPayloadFileSize;
    1030       30973 :     return 0;
    1031             : }
    1032             : 
    1033             : /************************************************************************/
    1034             : /*                                Tell()                                */
    1035             : /************************************************************************/
    1036             : 
    1037           3 : vsi_l_offset VSICryptFileHandle::Tell()
    1038             : {
    1039             : #ifdef VERBOSE_VSICRYPT
    1040             :     CPLDebug("VSICRYPT", "Tell()=" CPL_FRMT_GUIB, nCurPos);
    1041             : #endif
    1042           3 :     return nCurPos;
    1043             : }
    1044             : 
    1045             : /************************************************************************/
    1046             : /*                                Read()                                */
    1047             : /************************************************************************/
    1048             : 
    1049       11005 : size_t VSICryptFileHandle::Read(void *pBuffer, size_t const nBytes)
    1050             : {
    1051       11005 :     size_t nToRead = nBytes;
    1052       11005 :     GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
    1053             : 
    1054             : #ifdef VERBOSE_VSICRYPT
    1055             :     CPLDebug("VSICRYPT", "Read(nCurPos=" CPL_FRMT_GUIB ", nToRead=%d)", nCurPos,
    1056             :              static_cast<int>(nToRead));
    1057             : #endif
    1058             : 
    1059       11005 :     if ((nPerms & VSICRYPT_READ) == 0)
    1060             :     {
    1061           1 :         bError = true;
    1062           1 :         return 0;
    1063             :     }
    1064             : 
    1065       11004 :     if (nCurPos >= poHeader->nPayloadFileSize)
    1066             :     {
    1067        3882 :         bEOF = true;
    1068        3882 :         return 0;
    1069             :     }
    1070             : 
    1071        7122 :     if (!FlushDirty())
    1072           0 :         return 0;
    1073             : 
    1074       52089 :     while (nToRead > 0)
    1075             :     {
    1076       52089 :         if (nCurPos >= nWBOffset && nCurPos < nWBOffset + nWBSize)
    1077             :         {
    1078             :             // TODO(schwehr): Can nToCopy be a size_t to simplify casting?
    1079             :             int nToCopy =
    1080       89938 :                 std::min(static_cast<int>(nToRead),
    1081       44969 :                          static_cast<int>(nWBSize - (nCurPos - nWBOffset)));
    1082       44969 :             if (nCurPos + nToCopy > poHeader->nPayloadFileSize)
    1083             :             {
    1084        1234 :                 bEOF = true;
    1085        1234 :                 nToCopy =
    1086        1234 :                     static_cast<int>(poHeader->nPayloadFileSize - nCurPos);
    1087             :             }
    1088       44969 :             memcpy(pabyBuffer, pabyWB + nCurPos - nWBOffset, nToCopy);
    1089       44969 :             pabyBuffer += nToCopy;
    1090       44969 :             nToRead -= nToCopy;
    1091       44969 :             nCurPos += nToCopy;
    1092       44969 :             if (bEOF || nToRead == 0)
    1093             :                 break;
    1094       37888 :             CPLAssert((nCurPos % poHeader->nSectorSize) == 0);
    1095             :         }
    1096             : 
    1097       45008 :         vsi_l_offset nSectorOffset =
    1098       45008 :             (nCurPos / poHeader->nSectorSize) * poHeader->nSectorSize;
    1099       45008 :         poBaseHandle->Seek(poHeader->nHeaderSize + nSectorOffset, SEEK_SET);
    1100       45008 :         if (poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) != 1)
    1101             :         {
    1102          41 :             bEOF = poBaseHandle->Eof();
    1103          41 :             bError = poBaseHandle->Error();
    1104          41 :             break;
    1105             :         }
    1106       44967 :         if (!DecryptBlock(pabyWB, nSectorOffset))
    1107             :         {
    1108           0 :             bError = true;
    1109           0 :             break;
    1110             :         }
    1111       44967 :         if ((nPerms & VSICRYPT_WRITE) &&
    1112       44933 :             nSectorOffset + poHeader->nSectorSize > poHeader->nPayloadFileSize)
    1113             :         {
    1114             :             // If the last sector was padded with random values, decrypt it to 0
    1115             :             // in case of update scenarios.
    1116        1347 :             CPLAssert(nSectorOffset < poHeader->nPayloadFileSize);
    1117        1347 :             memset(pabyWB + poHeader->nPayloadFileSize - nSectorOffset, 0,
    1118        1347 :                    nSectorOffset + poHeader->nSectorSize -
    1119        1347 :                        poHeader->nPayloadFileSize);
    1120             :         }
    1121       44967 :         nWBOffset = nSectorOffset;
    1122       44967 :         nWBSize = poHeader->nSectorSize;
    1123             :     }
    1124             : 
    1125        7122 :     size_t nRet = nBytes - nToRead;
    1126             : #ifdef VERBOSE_VSICRYPT
    1127             :     CPLDebug("VSICRYPT", "Read ret = %d (nBytes = %d)", static_cast<int>(nRet),
    1128             :              static_cast<int>(nBytes));
    1129             : #endif
    1130        7122 :     return nRet;
    1131             : }
    1132             : 
    1133             : /************************************************************************/
    1134             : /*                               Write()                                */
    1135             : /************************************************************************/
    1136             : 
    1137       20040 : size_t VSICryptFileHandle::Write(const void *pBuffer, size_t nBytes)
    1138             : {
    1139       20040 :     size_t nToWrite = nBytes;
    1140       20040 :     const GByte *pabyBuffer = static_cast<const GByte *>(pBuffer);
    1141             : 
    1142             : #ifdef VERBOSE_VSICRYPT
    1143             :     CPLDebug("VSICRYPT",
    1144             :              "Write(nCurPos=" CPL_FRMT_GUIB ", nToWrite=%d,"
    1145             :              "nPayloadFileSize=" CPL_FRMT_GUIB
    1146             :              ",bWBDirty=%d,nWBOffset=" CPL_FRMT_GUIB ",nWBSize=%d)",
    1147             :              nCurPos, static_cast<int>(nToWrite), poHeader->nPayloadFileSize,
    1148             :              static_cast<int>(bWBDirty), nWBOffset, nWBSize);
    1149             : #endif
    1150             : 
    1151       20040 :     if ((nPerms & VSICRYPT_WRITE) == 0)
    1152           1 :         return 0;
    1153             : 
    1154       20039 :     if (nCurPos >= (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
    1155       20039 :                        poHeader->nSectorSize)
    1156             :     {
    1157        3266 :         bLastSectorWasModified = true;
    1158             :     }
    1159             : 
    1160             :     // If seeking past end of file, we need to explicitly encrypt the
    1161             :     // padding zeroes.
    1162       20039 :     if (nCurPos > poHeader->nPayloadFileSize && nCurPos > nWBOffset + nWBSize)
    1163             :     {
    1164        2962 :         if (!FlushDirty())
    1165           0 :             return 0;
    1166        2962 :         vsi_l_offset nOffset =
    1167        2962 :             (poHeader->nPayloadFileSize + poHeader->nSectorSize - 1) /
    1168        2962 :             poHeader->nSectorSize * poHeader->nSectorSize;
    1169        2962 :         const vsi_l_offset nEndOffset =
    1170        2962 :             nCurPos / poHeader->nSectorSize * poHeader->nSectorSize;
    1171       27578 :         for (; nOffset < nEndOffset; nOffset += poHeader->nSectorSize)
    1172             :         {
    1173       24616 :             memset(pabyWB, 0, poHeader->nSectorSize);
    1174       24616 :             EncryptBlock(pabyWB, nOffset);
    1175       24616 :             poBaseHandle->Seek(poHeader->nHeaderSize + nOffset, SEEK_SET);
    1176       24616 :             if (poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1) != 1)
    1177           0 :                 return 0;
    1178       24616 :             poHeader->nPayloadFileSize = nOffset + poHeader->nSectorSize;
    1179       24616 :             bUpdateHeader = true;
    1180             :         }
    1181             :     }
    1182             : 
    1183       79522 :     while (nToWrite > 0)
    1184             :     {
    1185       79133 :         if (nCurPos >= nWBOffset && nCurPos < nWBOffset + nWBSize)
    1186             :         {
    1187       35066 :             bWBDirty = true;
    1188             :             const int nToCopy =
    1189       70132 :                 std::min(static_cast<int>(nToWrite),
    1190       35066 :                          static_cast<int>(nWBSize - (nCurPos - nWBOffset)));
    1191       35066 :             memcpy(pabyWB + nCurPos - nWBOffset, pabyBuffer, nToCopy);
    1192       35066 :             pabyBuffer += nToCopy;
    1193       35066 :             nToWrite -= nToCopy;
    1194       35066 :             nCurPos += nToCopy;
    1195       35066 :             if (nCurPos > poHeader->nPayloadFileSize)
    1196             :             {
    1197        6102 :                 bUpdateHeader = true;
    1198        6102 :                 poHeader->nPayloadFileSize = nCurPos;
    1199             :             }
    1200       35066 :             if (nToWrite == 0)
    1201       19650 :                 break;
    1202       15416 :             CPLAssert((nCurPos % poHeader->nSectorSize) == 0);
    1203             :         }
    1204       44067 :         else if ((nCurPos % poHeader->nSectorSize) == 0 &&
    1205       25241 :                  nToWrite >= static_cast<size_t>(poHeader->nSectorSize))
    1206             :         {
    1207        9558 :             if (!FlushDirty())
    1208           0 :                 break;
    1209             : 
    1210        9558 :             bWBDirty = true;
    1211        9558 :             nWBOffset = nCurPos;
    1212        9558 :             nWBSize = poHeader->nSectorSize;
    1213        9558 :             memcpy(pabyWB, pabyBuffer, poHeader->nSectorSize);
    1214        9558 :             pabyBuffer += poHeader->nSectorSize;
    1215        9558 :             nToWrite -= poHeader->nSectorSize;
    1216        9558 :             nCurPos += poHeader->nSectorSize;
    1217        9558 :             if (nCurPos > poHeader->nPayloadFileSize)
    1218             :             {
    1219        1837 :                 bUpdateHeader = true;
    1220        1837 :                 poHeader->nPayloadFileSize = nCurPos;
    1221             :             }
    1222             :         }
    1223             :         else
    1224             :         {
    1225       34509 :             if (!FlushDirty())
    1226           0 :                 break;
    1227             : 
    1228       34509 :             const vsi_l_offset nSectorOffset =
    1229       34509 :                 (nCurPos / poHeader->nSectorSize) * poHeader->nSectorSize;
    1230       34509 :             const vsi_l_offset nLastSectorOffset =
    1231       34509 :                 (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
    1232       34509 :                 poHeader->nSectorSize;
    1233       34509 :             if (nSectorOffset > nLastSectorOffset &&
    1234         286 :                 (poHeader->nPayloadFileSize % poHeader->nSectorSize) != 0)
    1235             :             {
    1236         858 :                 if (poBaseHandle->Seek(
    1237         286 :                         poHeader->nHeaderSize + nLastSectorOffset, 0) == 0 &&
    1238         572 :                     poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 1 &&
    1239         286 :                     DecryptBlock(pabyWB, nLastSectorOffset))
    1240             :                 {
    1241             : #ifdef VERBOSE_VSICRYPT
    1242             :                     CPLDebug("VSICRYPT", "Filling %d trailing bytes with 0",
    1243             :                              static_cast<int>(poHeader->nSectorSize -
    1244             :                                               (poHeader->nPayloadFileSize -
    1245             :                                                nLastSectorOffset)));
    1246             : #endif
    1247             :                     // Fill with 0.
    1248         286 :                     memset(
    1249         286 :                         pabyWB + poHeader->nPayloadFileSize - nLastSectorOffset,
    1250             :                         0,
    1251             :                         static_cast<int>(
    1252         286 :                             poHeader->nSectorSize -
    1253         286 :                             (poHeader->nPayloadFileSize - nLastSectorOffset)));
    1254             : 
    1255         572 :                     if (poBaseHandle->Seek(
    1256         286 :                             poHeader->nHeaderSize + nLastSectorOffset, 0) == 0)
    1257             :                     {
    1258         286 :                         EncryptBlock(pabyWB, nLastSectorOffset);
    1259         286 :                         poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1);
    1260             :                     }
    1261             :                 }
    1262             :             }
    1263       34509 :             poBaseHandle->Seek(poHeader->nHeaderSize + nSectorOffset, SEEK_SET);
    1264       63497 :             if (poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 0 ||
    1265       28988 :                 !DecryptBlock(pabyWB, nSectorOffset))
    1266             :             {
    1267        5521 :                 memset(pabyWB, 0, poHeader->nSectorSize);
    1268             :             }
    1269       28988 :             else if (nSectorOffset + poHeader->nSectorSize >
    1270       28988 :                      poHeader->nPayloadFileSize)
    1271             :             {
    1272             :                 // If the last sector was padded with random values,
    1273             :                 // decrypt it to 0 in case of update scenarios.
    1274         735 :                 CPLAssert(nSectorOffset < poHeader->nPayloadFileSize);
    1275         735 :                 memset(pabyWB + poHeader->nPayloadFileSize - nSectorOffset, 0,
    1276         735 :                        nSectorOffset + poHeader->nSectorSize -
    1277         735 :                            poHeader->nPayloadFileSize);
    1278             :             }
    1279       34509 :             nWBOffset = nSectorOffset;
    1280       34509 :             nWBSize = poHeader->nSectorSize;
    1281             :         }
    1282             :     }
    1283             : 
    1284       20039 :     size_t nRet = nBytes - nToWrite;
    1285             : #ifdef VERBOSE_VSICRYPT
    1286             :     CPLDebug("VSICRYPT", "Write ret = %d (nBytes = %d)", static_cast<int>(nRet),
    1287             :              static_cast<int>(nBytes));
    1288             : #endif
    1289       20039 :     return nRet;
    1290             : }
    1291             : 
    1292             : /************************************************************************/
    1293             : /*                              Truncate()                              */
    1294             : /************************************************************************/
    1295             : 
    1296             : // Returns 0 on success.  Returns -1 on error.
    1297           3 : int VSICryptFileHandle::Truncate(vsi_l_offset nNewSize)
    1298             : {
    1299             : #ifdef VERBOSE_VSICRYPT
    1300             :     CPLDebug("VSICRYPT", "Truncate(" CPL_FRMT_GUIB ")", nNewSize);
    1301             : #endif
    1302           3 :     if ((nPerms & VSICRYPT_WRITE) == 0)
    1303           1 :         return -1;
    1304             : 
    1305           2 :     if (!FlushDirty())
    1306           0 :         return -1;
    1307           6 :     if (poBaseHandle->Truncate(
    1308           2 :             poHeader->nHeaderSize +
    1309           4 :             cpl::div_round_up(nNewSize, poHeader->nSectorSize) *
    1310           2 :                 poHeader->nSectorSize) != 0)
    1311           0 :         return -1;
    1312           2 :     bUpdateHeader = true;
    1313           2 :     poHeader->nPayloadFileSize = nNewSize;
    1314           2 :     return 0;
    1315             : }
    1316             : 
    1317             : /************************************************************************/
    1318             : /*                                Eof()                                 */
    1319             : /************************************************************************/
    1320             : 
    1321           4 : int VSICryptFileHandle::Eof()
    1322             : {
    1323             : #ifdef VERBOSE_VSICRYPT
    1324             :     CPLDebug("VSICRYPT", "Eof() = %d", static_cast<int>(bEOF));
    1325             : #endif
    1326           4 :     return bEOF;
    1327             : }
    1328             : 
    1329             : /************************************************************************/
    1330             : /*                               Error()                                */
    1331             : /************************************************************************/
    1332             : 
    1333           4 : int VSICryptFileHandle::Error()
    1334             : {
    1335             : #ifdef VERBOSE_VSICRYPT
    1336             :     CPLDebug("VSICRYPT", "Error() = %d", static_cast<int>(bError));
    1337             : #endif
    1338           4 :     return bError;
    1339             : }
    1340             : 
    1341             : /************************************************************************/
    1342             : /*                              ClearErr()                              */
    1343             : /************************************************************************/
    1344             : 
    1345           1 : void VSICryptFileHandle::ClearErr()
    1346             : {
    1347             : #ifdef VERBOSE_VSICRYPT
    1348             :     CPLDebug("VSICRYPT", "ClearErr()");
    1349             : #endif
    1350           1 :     bEOF = false;
    1351           1 :     bError = false;
    1352           1 :     poBaseHandle->ClearErr();
    1353           1 : }
    1354             : 
    1355             : /************************************************************************/
    1356             : /*                               Flush()                                */
    1357             : /************************************************************************/
    1358             : 
    1359        1175 : int VSICryptFileHandle::Flush()
    1360             : {
    1361             : #ifdef VERBOSE_VSICRYPT
    1362             :     CPLDebug("VSICRYPT", "Flush()");
    1363             : #endif
    1364        1175 :     if (!FlushDirty())
    1365             :     {
    1366           0 :         return -1;
    1367             :     }
    1368        1175 :     if ((nPerms & VSICRYPT_WRITE))
    1369             :     {
    1370        1045 :         if (bLastSectorWasModified &&
    1371        1038 :             (poHeader->nPayloadFileSize % poHeader->nSectorSize) != 0)
    1372             :         {
    1373        1006 :             const vsi_l_offset nLastSectorOffset =
    1374        1006 :                 (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
    1375        1006 :                 poHeader->nSectorSize;
    1376        3018 :             if (poBaseHandle->Seek(poHeader->nHeaderSize + nLastSectorOffset,
    1377        1006 :                                    0) == 0 &&
    1378        1984 :                 poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 1 &&
    1379         978 :                 DecryptBlock(pabyWB, nLastSectorOffset))
    1380             :             {
    1381             :                 // Fill with random
    1382             : #ifdef VERBOSE_VSICRYPT
    1383             :                 CPLDebug("VSICRYPT", "Filling %d trailing bytes with random",
    1384             :                          static_cast<int>(
    1385             :                              poHeader->nSectorSize -
    1386             :                              (poHeader->nPayloadFileSize - nLastSectorOffset)));
    1387             : #endif
    1388         978 :                 CryptoPP::OS_GenerateRandomBlock(
    1389             :                     false,  // Do not need cryptographic randomness.
    1390             :                     reinterpret_cast<cryptopp_byte *>(
    1391         978 :                         pabyWB + poHeader->nPayloadFileSize -
    1392             :                         nLastSectorOffset),
    1393             :                     static_cast<int>(
    1394         978 :                         poHeader->nSectorSize -
    1395         978 :                         (poHeader->nPayloadFileSize - nLastSectorOffset)));
    1396             : 
    1397        1956 :                 if (poBaseHandle->Seek(
    1398         978 :                         poHeader->nHeaderSize + nLastSectorOffset, 0) == 0)
    1399             :                 {
    1400         978 :                     EncryptBlock(pabyWB, nLastSectorOffset);
    1401         978 :                     poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1);
    1402             :                 }
    1403             :             }
    1404             :         }
    1405        1045 :         bLastSectorWasModified = false;
    1406        1045 :         if (poBaseHandle->Flush() != 0)
    1407           0 :             return -1;
    1408             :     }
    1409        1175 :     if (bUpdateHeader)
    1410             :     {
    1411             : #ifdef VERBOSE_VSICRYPT
    1412             :         CPLDebug("VSICRYPT", "nPayloadFileSize = " CPL_FRMT_GUIB,
    1413             :                  poHeader->nPayloadFileSize);
    1414             : #endif
    1415        1038 :         if (!poHeader->WriteToFile(poBaseHandle, poEncCipher))
    1416           0 :             return -1;
    1417             :     }
    1418             : 
    1419        1175 :     return 0;
    1420             : }
    1421             : 
    1422             : /************************************************************************/
    1423             : /*                               Close()                                */
    1424             : /************************************************************************/
    1425             : 
    1426        2341 : int VSICryptFileHandle::Close()
    1427             : {
    1428        2341 :     int nRet = 0;
    1429        2341 :     if (poBaseHandle != nullptr && poHeader != nullptr)
    1430             :     {
    1431        1174 :         if (Flush() != 0)
    1432           0 :             return -1;
    1433        1174 :         nRet = poBaseHandle->Close();
    1434        1174 :         delete poBaseHandle;
    1435        1174 :         poBaseHandle = nullptr;
    1436             :     }
    1437             : #ifdef VERBOSE_VSICRYPT
    1438             :     CPLDebug("VSICRYPT", "Close(%s)", osBaseFilename.c_str());
    1439             : #endif
    1440        2341 :     return nRet;
    1441             : }
    1442             : 
    1443             : /************************************************************************/
    1444             : /*                      VSICryptFilesystemHandler                       */
    1445             : /************************************************************************/
    1446             : 
    1447             : class VSICryptFilesystemHandler final : public VSIFilesystemHandler
    1448             : {
    1449             :   public:
    1450             :     VSICryptFilesystemHandler();
    1451             :     ~VSICryptFilesystemHandler() override;
    1452             : 
    1453             :     VSIVirtualHandleUniquePtr Open(const char *pszFilename,
    1454             :                                    const char *pszAccess, bool bSetError,
    1455             :                                    CSLConstList /* papszOptions */) override;
    1456             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
    1457             :              int nFlags) override;
    1458             :     int Unlink(const char *pszFilename) override;
    1459             :     int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
    1460             :                void *) override;
    1461             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
    1462             : };
    1463             : 
    1464             : /************************************************************************/
    1465             : /*                     VSICryptFilesystemHandler()                      */
    1466             : /************************************************************************/
    1467             : 
    1468        1808 : VSICryptFilesystemHandler::VSICryptFilesystemHandler()
    1469             : {
    1470        1808 : }
    1471             : 
    1472             : /************************************************************************/
    1473             : /*                     ~VSICryptFilesystemHandler()                     */
    1474             : /************************************************************************/
    1475             : 
    1476        1131 : VSICryptFilesystemHandler::~VSICryptFilesystemHandler()
    1477             : {
    1478        1131 : }
    1479             : 
    1480             : /************************************************************************/
    1481             : /*                            GetFilename()                             */
    1482             : /************************************************************************/
    1483             : 
    1484        2298 : static CPLString GetFilename(const char *pszFilename)
    1485             : {
    1486        2298 :     if (strcmp(pszFilename, VSICRYPT_PREFIX_WITHOUT_SLASH) == 0)
    1487           0 :         pszFilename = VSICRYPT_PREFIX;
    1488             : 
    1489        2298 :     CPLAssert(strncmp(pszFilename, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) ==
    1490             :               0);
    1491        2298 :     pszFilename += strlen(VSICRYPT_PREFIX);
    1492        2298 :     const char *pszFileArg = strstr(pszFilename, "file=");
    1493        2298 :     if (pszFileArg == nullptr)
    1494          13 :         return pszFilename;
    1495        4570 :     CPLString osRet(pszFileArg + strlen("file="));
    1496        2285 :     return osRet;
    1497             : }
    1498             : 
    1499             : /************************************************************************/
    1500             : /*                            GetArgument()                             */
    1501             : /************************************************************************/
    1502             : 
    1503        7562 : static CPLString GetArgument(const char *pszFilename, const char *pszParamName,
    1504             :                              const char *pszDefault = "")
    1505             : {
    1506       15124 :     CPLString osParamName(pszParamName);
    1507        7562 :     osParamName += "=";
    1508             : 
    1509        7562 :     const char *pszNeedle = strstr(pszFilename, osParamName);
    1510        7562 :     if (pszNeedle == nullptr)
    1511        5254 :         return pszDefault;
    1512             : 
    1513        4616 :     CPLString osRet(pszNeedle + osParamName.size());
    1514        2308 :     size_t nCommaPos = osRet.find(",");
    1515        2308 :     if (nCommaPos != std::string::npos)
    1516        2308 :         osRet.resize(nCommaPos);
    1517        2308 :     return osRet;
    1518             : }
    1519             : 
    1520             : /************************************************************************/
    1521             : /*                               GetKey()                               */
    1522             : /************************************************************************/
    1523             : 
    1524        1286 : static CPLString GetKey(const char *pszFilename)
    1525             : {
    1526        1286 :     CPLString osKey = GetArgument(pszFilename, "key");
    1527             :     // TODO(schwehr): Make 10U and 1024U into symbolic constants.
    1528        1286 :     if (osKey.empty())
    1529             :     {
    1530          11 :         const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY", "");
    1531             :         // Do some form of validation to please Coverity
    1532          11 :         CPLAssert(strlen(pszKey) < 10U * 1024U);
    1533             :         // coverity [tainted_data_transitive]
    1534          11 :         osKey = pszKey;
    1535             :     }
    1536        1286 :     if (osKey.empty() || EQUAL(osKey, "GENERATE_IT"))
    1537             :     {
    1538          12 :         CPLString osKeyB64(GetArgument(pszFilename, "key_b64"));
    1539          12 :         if (osKeyB64.empty())
    1540             :         {
    1541          11 :             const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY_B64", "");
    1542             :             // Do some form of validation to please Coverity
    1543          11 :             CPLAssert(strlen(pszKey) < 10U * 1024U);
    1544             :             // coverity [tainted_data_transitive]
    1545          11 :             osKeyB64 = pszKey;
    1546             :         }
    1547          12 :         if (!osKeyB64.empty())
    1548             :         {
    1549           2 :             GByte *key = reinterpret_cast<GByte *>(CPLStrdup(osKeyB64));
    1550           2 :             int nLength = CPLBase64DecodeInPlace(key);
    1551           2 :             osKey.assign(reinterpret_cast<const char *>(key), nLength);
    1552           2 :             memset(key, 0, osKeyB64.size());
    1553           2 :             CPLFree(key);
    1554             :         }
    1555             :         // coverity[tainted_data]
    1556          12 :         memset(osKeyB64.data(), 0, osKeyB64.size());
    1557             :     }
    1558        1286 :     return osKey;
    1559             : }
    1560             : 
    1561             : /************************************************************************/
    1562             : /*                                Open()                                */
    1563             : /************************************************************************/
    1564             : 
    1565             : VSIVirtualHandleUniquePtr
    1566        1281 : VSICryptFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
    1567             :                                 bool /* bSetError */,
    1568             :                                 CSLConstList /* papszOptions */)
    1569             : {
    1570             : #ifdef VERBOSE_VSICRYPT
    1571             :     CPLDebug("VSICRYPT", "Open(%s, %s)", pszFilename, pszAccess);
    1572             : #endif
    1573        2562 :     CPLString osFilename(GetFilename(pszFilename));
    1574             : 
    1575        2562 :     CPLString osKey(GetKey(pszFilename));
    1576        1281 :     if (osKey.empty() && pabyGlobalKey == nullptr)
    1577             :     {
    1578           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1579             :                  "Encryption key not defined as key/key_b64 parameter, "
    1580             :                  "VSICRYPT_KEY/VSICRYPT_KEY_B64 configuration option or "
    1581             :                  "VSISetCryptKey() API");
    1582           2 :         return nullptr;
    1583             :     }
    1584             : 
    1585        2558 :     CPLString osAccess(pszAccess);
    1586        1279 :     if (strchr(pszAccess, 'b') == nullptr)
    1587          15 :         osAccess += "b";
    1588        1279 :     if (strchr(pszAccess, 'r'))
    1589             :     {
    1590         456 :         auto fpBase = VSIFilesystemHandler::OpenStatic(osFilename, osAccess);
    1591         228 :         if (fpBase == nullptr)
    1592           1 :             return nullptr;
    1593         454 :         auto poHeader = std::make_unique<VSICryptFileHeader>();
    1594         227 :         if (!poHeader->ReadFromFile(fpBase.get(), osKey))
    1595             :         {
    1596          94 :             memset(osKey.data(), 0, osKey.size());
    1597          94 :             return nullptr;
    1598             :         }
    1599             : 
    1600             :         auto poHandle = std::make_unique<VSICryptFileHandle>(
    1601         133 :             osFilename, fpBase.release(), poHeader.release(),
    1602         133 :             strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
    1603         266 :                                    : VSICRYPT_READ);
    1604         133 :         if (!poHandle->Init(osKey, false))
    1605             :         {
    1606           4 :             memset(osKey.data(), 0, osKey.size());
    1607           4 :             poHandle.reset();
    1608             :         }
    1609         133 :         memset(osKey.data(), 0, osKey.size());
    1610         133 :         return VSIVirtualHandleUniquePtr(poHandle.release());
    1611             :     }
    1612        1051 :     else if (strchr(pszAccess, 'w'))
    1613             :     {
    1614             :         CPLString osAlg(GetArgument(pszFilename, "alg",
    1615        2088 :                                     CPLGetConfigOption("VSICRYPT_ALG", "AES")));
    1616        1044 :         VSICryptAlg eAlg = GetAlg(osAlg);
    1617             : 
    1618        1044 :         VSICryptMode eMode = GetMode(GetArgument(
    1619             :             pszFilename, "mode", CPLGetConfigOption("VSICRYPT_MODE", "CBC")));
    1620             : 
    1621             :         CPLString osFreeText =
    1622             :             GetArgument(pszFilename, "freetext",
    1623        2088 :                         CPLGetConfigOption("VSICRYPT_FREETEXT", ""));
    1624             : 
    1625             :         CPLString osIV = GetArgument(pszFilename, "iv",
    1626        2088 :                                      CPLGetConfigOption("VSICRYPT_IV", ""));
    1627             : 
    1628        1044 :         int nSectorSize = atoi(
    1629        2088 :             GetArgument(pszFilename, "sector_size",
    1630             :                         CPLGetConfigOption("VSICRYPT_SECTOR_SIZE", "512")));
    1631        1044 :         if (nSectorSize <= 0 || nSectorSize >= 65535)
    1632             :         {
    1633           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1634             :                      "Invalid value for sector_size. Defaulting to 512.");
    1635           0 :             nSectorSize = 512;
    1636             :         }
    1637             : 
    1638        1044 :         const bool bAddKeyCheck = CPLTestBool(
    1639        2088 :             GetArgument(pszFilename, "add_key_check",
    1640             :                         CPLGetConfigOption("VSICRYPT_ADD_KEY_CHECK", "NO")));
    1641             : 
    1642             :         /* Generate random initial vector */
    1643        1044 :         CryptoPP::BlockCipher *poBlock = GetEncBlockCipher(eAlg);
    1644        1044 :         if (poBlock == nullptr)
    1645             :         {
    1646           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1647             :                      "Cipher algorithm not supported in this build: %s",
    1648             :                      osAlg.c_str());
    1649           0 :             memset(osKey.data(), 0, osKey.size());
    1650           0 :             return nullptr;
    1651             :         }
    1652        1044 :         int nMinKeySize = static_cast<int>(poBlock->MinKeyLength());
    1653        1044 :         int nMaxKeySize = static_cast<int>(poBlock->MaxKeyLength());
    1654        1044 :         int nBlockSize = static_cast<int>(poBlock->BlockSize());
    1655        1044 :         delete poBlock;
    1656             : 
    1657        1044 :         if (!osIV.empty())
    1658             :         {
    1659           1 :             if (static_cast<int>(osIV.size()) != nBlockSize)
    1660             :             {
    1661           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1662             :                          "IV should be %d byte large", nBlockSize);
    1663           1 :                 memset(osKey.data(), 0, osKey.size());
    1664           1 :                 return nullptr;
    1665             :             }
    1666             :         }
    1667             :         else
    1668             :         {
    1669        1043 :             osIV.resize(nBlockSize);
    1670        2086 :             CryptoPP::OS_GenerateRandomBlock(
    1671             :                 false,  // Do not need cryptographic randomness.
    1672        1043 :                 reinterpret_cast<cryptopp_byte *>(osIV.data()), osIV.size());
    1673             :         }
    1674             : 
    1675        1043 :         if (EQUAL(osKey, "GENERATE_IT"))
    1676             :         {
    1677           1 :             osKey.resize(nMaxKeySize);
    1678           1 :             CPLDebug("VSICRYPT",
    1679             :                      "Generating key. This might take some time...");
    1680           1 :             CryptoPP::OS_GenerateRandomBlock(
    1681             :                 // Need cryptographic randomness.
    1682             :                 // Config option for speeding tests.
    1683           1 :                 CPLTestBool(
    1684             :                     CPLGetConfigOption("VSICRYPT_CRYPTO_RANDOM", "TRUE")),
    1685           1 :                 reinterpret_cast<cryptopp_byte *>(osKey.data()), osKey.size());
    1686             : 
    1687             :             char *pszB64 =
    1688           1 :                 CPLBase64Encode(static_cast<int>(osKey.size()),
    1689           1 :                                 reinterpret_cast<const GByte *>(osKey.c_str()));
    1690           1 :             if (CPLTestBool(CPLGetConfigOption("VSICRYPT_DISPLAY_GENERATED_KEY",
    1691             :                                                "TRUE")))
    1692             :             {
    1693           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1694             :                          "BASE64 key '%s' has been generated, and installed in "
    1695             :                          "the VSICRYPT_KEY_B64 configuration option.",
    1696             :                          pszB64);
    1697             :             }
    1698           1 :             CPLSetConfigOption("VSICRYPT_KEY_B64", pszB64);
    1699           1 :             CPLFree(pszB64);
    1700             :         }
    1701             : 
    1702             :         const int nKeyLength =
    1703        1043 :             !osKey.empty() ? static_cast<int>(osKey.size()) : nGlobalKeySize;
    1704        1043 :         if (nKeyLength < nMinKeySize)
    1705             :         {
    1706           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1707             :                      "Key is too short: %d bytes. Should be at least %d bytes",
    1708             :                      nKeyLength, nMinKeySize);
    1709           2 :             memset(osKey.data(), 0, osKey.size());
    1710           2 :             return nullptr;
    1711             :         }
    1712             : 
    1713        2082 :         auto fpBase = VSIFilesystemHandler::OpenStatic(osFilename, osAccess);
    1714        1041 :         if (fpBase == nullptr)
    1715             :         {
    1716           2 :             memset(osKey.data(), 0, osKey.size());
    1717           2 :             return nullptr;
    1718             :         }
    1719             : 
    1720        2078 :         auto poHeader = std::make_unique<VSICryptFileHeader>();
    1721        1039 :         poHeader->osIV = osIV;
    1722        1039 :         CPL_IGNORE_RET_VAL(osIV);
    1723        1039 :         poHeader->eAlg = eAlg;
    1724        1039 :         poHeader->eMode = eMode;
    1725        1039 :         poHeader->nSectorSize = static_cast<GUInt16>(nSectorSize);
    1726        1039 :         poHeader->osFreeText = std::move(osFreeText);
    1727        1039 :         poHeader->bAddKeyCheck = bAddKeyCheck;
    1728             : 
    1729             :         auto poHandle = std::make_unique<VSICryptFileHandle>(
    1730        1039 :             osFilename, fpBase.release(), poHeader.release(),
    1731        1039 :             strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
    1732        2078 :                                    : VSICRYPT_WRITE);
    1733        1039 :         if (!poHandle->Init(osKey, true))
    1734             :         {
    1735           2 :             memset(osKey.data(), 0, osKey.size());
    1736           2 :             poHandle.reset();
    1737             :         }
    1738        1039 :         memset(osKey.data(), 0, osKey.size());
    1739        1039 :         return VSIVirtualHandleUniquePtr(poHandle.release());
    1740             :     }
    1741           7 :     else if (strchr(pszAccess, 'a'))
    1742             :     {
    1743          12 :         auto fpBase = VSIFilesystemHandler::OpenStatic(osFilename, "rb+");
    1744           6 :         if (fpBase == nullptr)
    1745             :         {
    1746           2 :             memset(osKey.data(), 0, osKey.size());
    1747           2 :             return VSIFilesystemHandler::OpenStatic(pszFilename, "wb+");
    1748             :         }
    1749           8 :         auto poHeader = std::make_unique<VSICryptFileHeader>();
    1750           4 :         if (!poHeader->ReadFromFile(fpBase.get(), osKey))
    1751             :         {
    1752           2 :             memset(osKey.data(), 0, osKey.size());
    1753           2 :             return nullptr;
    1754             :         }
    1755             : 
    1756             :         auto poHandle = std::make_unique<VSICryptFileHandle>(
    1757           2 :             osFilename, fpBase.release(), poHeader.release(),
    1758           6 :             VSICRYPT_READ | VSICRYPT_WRITE);
    1759           2 :         if (!poHandle->Init(osKey))
    1760             :         {
    1761           1 :             poHandle.reset();
    1762             :         }
    1763           2 :         memset(osKey.data(), 0, osKey.size());
    1764           2 :         if (poHandle != nullptr)
    1765           1 :             poHandle->Seek(0, SEEK_END);
    1766           2 :         return VSIVirtualHandleUniquePtr(poHandle.release());
    1767             :     }
    1768             : 
    1769           1 :     return nullptr;
    1770             : }
    1771             : 
    1772             : /************************************************************************/
    1773             : /*                                Stat()                                */
    1774             : /************************************************************************/
    1775             : 
    1776           6 : int VSICryptFilesystemHandler::Stat(const char *pszFilename,
    1777             :                                     VSIStatBufL *pStatBuf, int nFlags)
    1778             : {
    1779             : #ifdef VERBOSE_VSICRYPT
    1780             :     CPLDebug("VSICRYPT", "Stat(%s)", pszFilename);
    1781             : #endif
    1782          12 :     CPLString osFilename(GetFilename(pszFilename));
    1783           6 :     if (VSIStatExL(osFilename, pStatBuf, nFlags) != 0)
    1784           1 :         return -1;
    1785             :     VSIVirtualHandle *fp =
    1786           5 :         reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osFilename, "rb"));
    1787           5 :     if (fp == nullptr)
    1788           0 :         return -1;
    1789           5 :     VSICryptFileHeader *poHeader = new VSICryptFileHeader();
    1790          10 :     CPLString osKey(GetKey(pszFilename));
    1791           5 :     if (!poHeader->ReadFromFile(fp, osKey))
    1792             :     {
    1793           1 :         memset(osKey.data(), 0, osKey.size());
    1794           1 :         fp->Close();
    1795           1 :         delete fp;
    1796           1 :         delete poHeader;
    1797           1 :         return -1;
    1798             :     }
    1799           4 :     memset(osKey.data(), 0, osKey.size());
    1800           4 :     fp->Close();
    1801           4 :     delete fp;
    1802           4 :     if (poHeader)
    1803             :     {
    1804           4 :         pStatBuf->st_size = poHeader->nPayloadFileSize;
    1805           4 :         delete poHeader;
    1806           4 :         return 0;
    1807             :     }
    1808             :     else
    1809           0 :         return -1;
    1810             : }
    1811             : 
    1812             : /************************************************************************/
    1813             : /*                               Unlink()                               */
    1814             : /************************************************************************/
    1815             : 
    1816        1006 : int VSICryptFilesystemHandler::Unlink(const char *pszFilename)
    1817             : {
    1818        1006 :     return VSIUnlink(GetFilename(pszFilename));
    1819             : }
    1820             : 
    1821             : /************************************************************************/
    1822             : /*                               Rename()                               */
    1823             : /************************************************************************/
    1824             : 
    1825           2 : int VSICryptFilesystemHandler::Rename(const char *oldpath, const char *newpath,
    1826             :                                       GDALProgressFunc, void *)
    1827             : {
    1828           2 :     CPLString osNewPath;
    1829           2 :     if (strncmp(newpath, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) == 0)
    1830           1 :         osNewPath = GetFilename(newpath);
    1831             :     else
    1832           1 :         osNewPath = newpath;
    1833             : 
    1834           4 :     return VSIRename(GetFilename(oldpath), osNewPath);
    1835             : }
    1836             : 
    1837             : /************************************************************************/
    1838             : /*                             ReadDirEx()                              */
    1839             : /************************************************************************/
    1840             : 
    1841           2 : char **VSICryptFilesystemHandler::ReadDirEx(const char *pszDirname,
    1842             :                                             int nMaxFiles)
    1843             : {
    1844             : #ifdef VERBOSE_VSICRYPT
    1845             :     CPLDebug("VSICRYPT", "ReadDir(%s)", pszDirname);
    1846             : #endif
    1847           2 :     return VSIReadDirEx(GetFilename(pszDirname), nMaxFiles);
    1848             : }
    1849             : 
    1850             : #ifdef VSICRYPT_DRIVER
    1851             : 
    1852             : #include "gdal_priv.h"
    1853             : 
    1854             : /**
    1855             :  * \brief Evaluate if this is a crypt file.
    1856             :  *
    1857             :  * The function signature must match GDALDataset::Identify.
    1858             :  *
    1859             :  * @param poOpenInfo The header bytes used for file identification.
    1860             :  *
    1861             :  * @return 1 if this is a crypt file or 0 otherwise.
    1862             :  */
    1863             : 
    1864             : static int VSICryptIdentify(GDALOpenInfo *poOpenInfo)
    1865             : {
    1866             :     return poOpenInfo->nHeaderBytes > 8 &&
    1867             :            memcmp(poOpenInfo->pabyHeader, VSICRYPT_SIGNATURE, 8) == 0;
    1868             : }
    1869             : 
    1870             : static GDALDataset *VSICryptOpen(GDALOpenInfo *poOpenInfo)
    1871             : {
    1872             :     if (!VSICryptIdentify(poOpenInfo))
    1873             :         return nullptr;
    1874             :     return GDALOpen(
    1875             :         (CPLString(VSICRYPT_PREFIX) + poOpenInfo->pszFilename).c_str(),
    1876             :         poOpenInfo->eAccess);
    1877             : }
    1878             : 
    1879             : #endif
    1880             : 
    1881             : //! @endcond
    1882             : 
    1883             : /************************************************************************/
    1884             : /*                     VSIInstallCryptFileHandler()                     */
    1885             : /************************************************************************/
    1886             : 
    1887             : /**
    1888             :  * \brief Install /vsicrypt/ encrypted file system handler
    1889             :  * (requires <a href="http://www.cryptopp.com/">libcrypto++</a>)
    1890             :  *
    1891             :  * A special file handler is installed that allows reading/creating/update
    1892             :  * encrypted files on the fly, with random access capabilities.
    1893             :  *
    1894             :  * The cryptographic algorithms used are
    1895             :  * <a href="https://en.wikipedia.org/wiki/Block_cipher">block ciphers</a>,
    1896             :  * with symmetric key.
    1897             :  *
    1898             :  * In their simplest form, recognized filenames are of the form
    1899             :  * /vsicrypt//absolute_path/to/file, /vsicrypt/c:/absolute_path/to/file or
    1900             :  * /vsicrypt/relative/path/to/file.
    1901             :  *
    1902             :  * Options can also be used with the following format :
    1903             :  * /vsicrypt/option1=val1,option2=val2,...,file=/path/to/file
    1904             :  *
    1905             :  * They can also be passed as configuration option/environment variable, because
    1906             :  * in some use cases, the syntax with option in the filename might not properly
    1907             :  * work with some drivers.
    1908             :  *
    1909             :  * In all modes, the encryption key must be provided. There are several ways
    1910             :  * of doing so :
    1911             :  * <ul>
    1912             :  * <li>By adding a key= parameter to the filename, like
    1913             :  *     /vsicrypt/key=my_secret_key,file=/path/to/file.  Note that this restricts
    1914             :  *     the key to be in text format, whereas at its full power, it can be binary
    1915             :  *     content.</li>
    1916             :  * <li>By adding a key_b64= parameter to the filename, to specify a binary key
    1917             :  *     expressed in Base64 encoding, like
    1918             :  *     /vsicrypt/key_b64=th1sl00kslikebase64=,file=/path/to/file.</li>
    1919             :  * <li>By setting the VSICRYPT_KEY configuration option. The key should be in
    1920             :  * text format.</li>
    1921             :  * <li>By setting the VSICRYPT_KEY_B64 configuration option. The key should be
    1922             :  * encoded in Base64.</li>
    1923             :  * <li>By using the VSISetCryptKey() C function.</li>
    1924             :  * </ul>
    1925             :  *
    1926             :  * When creating a file, if key=GENERATE_IT or VSICRYPT_KEY=GENERATE_IT is
    1927             :  * passed, the encryption key will be generated from the pseudo-random number
    1928             :  * generator of the operating system. The key will be displayed on the standard
    1929             :  * error stream in a Base64 form (unless the VSICRYPT_DISPLAY_GENERATED_KEY
    1930             :  * configuration option is set to OFF), and the VSICRYPT_KEY_B64 configuration
    1931             :  * option will also be set with the Base64 form of the key (so that
    1932             :  * CPLGetConfigOption("VSICRYPT_KEY_B64", NULL) can be used to get it back).
    1933             :  *
    1934             :  * The available options are :
    1935             :  * <ul>
    1936             : 
    1937             :  * <li>alg=AES/Blowfish/Camellia/CAST256/DES_EDE2/DES_EDE3/MARS/IDEA/RC5/RC6/Serpent/SHACAL2/SKIPJACK/Twofish/XTEA:
    1938             :  *     to specify the <a href="https://en.wikipedia.org/wiki/Block_cipher">block
    1939             :  *     cipher</a> algorithm.  The default is AES.  Only used on
    1940             :  *     creation. Ignored otherwise.  Note: depending on how GDAL is build, if
    1941             :  *     linked against the DLL version of libcrypto++, only a subset of those
    1942             :  *     algorithms will be available, namely AES, DES_EDE2, DES_EDE3 and
    1943             :  *     SKIPJACK.  Also available as VSICRYPT_ALG configuration option.</li>
    1944             :  * <li>mode=CBC/CFB/OFB/CTR/CBC_CTS: to specify the
    1945             :  *     <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">
    1946             :  *       block cipher mode of operation</a>.
    1947             :  *     The default is CBC.
    1948             :  *     Only used on creation. Ignored otherwise.
    1949             :  *     Also available as VSICRYPT_MODE configuration option.</li>
    1950             :  * <li>key=text_key: see above.</li>
    1951             :  * <li>key_b64=base64_encoded_key: see above.</li>
    1952             :  * <li>freetext=some_text: to specify a text content that will be written
    1953             :  *     *unencrypted* in the file header, for informational purposes. Default to
    1954             :  *     empty.  Only used on creation. Ignored otherwise.
    1955             :  *     Also available as VSICRYPT_FREETEXT configuration option.</li>
    1956             :  * <li>sector_size=int_value: to specify the size of the "sector", which is the
    1957             :  *     unit chunk of information that is encrypted/decrypted. Default to 512
    1958             :  *     bytes.  The valid values depend on the algorithm and block cipher mode of
    1959             :  *     operation.  Only used on creation. Ignored otherwise.  Also available as
    1960             :  *     VSICRYPT_SECTOR_SIZE configuration option.</li>
    1961             :  * <li>iv=initial_vector_as_text: to specify the Initial Vector. This is an
    1962             :  *     advanced option that should generally *NOT* be used. It is only useful to
    1963             :  *     get completely deterministic output given the plaintext, key and other
    1964             :  *     parameters, which in general *NOT* what you want to do. By default, a
    1965             :  *     random initial vector of the appropriate size will be generated for each
    1966             :  *     new file created.  Only used on creation. Ignored otherwise.  Also
    1967             :  *     available as VSICRYPT_IV configuration option.</li>
    1968             : 
    1969             :  * <li>add_key_check=YES/NO: whether a special value should be encrypted in the
    1970             :  *     header, so as to be quickly able to determine if the decryption key is
    1971             :  *     correct.  Defaults to NO.  Only used on creation. Ignored otherwise.
    1972             :  *     Also available as VSICRYPT_ADD_KEY_CHECK configuration option.</li>
    1973             :  * <li>file=filename. To specify the filename. This must be the last option put
    1974             :  *     in the option list (so as to make it possible to use filenames with comma
    1975             :  *     in them. )
    1976             :  * </ul>
    1977             :  *
    1978             :  * This special file handler can be combined with other virtual filesystems
    1979             :  * handlers, such as /vsizip. For example,
    1980             :  * /vsicrypt//vsicurl/path/to/remote/encrypted/file.tif
    1981             :  *
    1982             :  * Implementation details:
    1983             :  *
    1984             :  * The structure of encrypted files is the following: a header, immediately
    1985             :  * followed by the encrypted payload (by sectors, i.e. chunks of sector_size
    1986             :  * bytes).
    1987             :  *
    1988             :  * The header structure is the following :
    1989             :  * <ol>
    1990             :  * <li>8 bytes. Signature. Fixed value: VSICRYPT.</li>
    1991             :  * <li>UINT16_LE. Header size (including previous signature bytes).</li>
    1992             :  * <li>UINT8. Format major version. Current value: 1.</li>
    1993             :  * <li>UINT8. Format minor version. Current value: 0.</li>
    1994             :  * <li>UINT16. Sector size.</li>
    1995             :  * <li>UINT8. Cipher algorithm. Valid values are: 0 = AES (Rijndael), 1 =
    1996             :  *     Blowfish, 2 = Camellia, 3 = CAST256, 4 = DES_EDE2, 5 = DES_EDE3, 6 =
    1997             :  *     MARS, 7 = IDEA, 8 = RC5, 9 = RC6, 10 = Serpent, 11 = SHACAL2, 12 =
    1998             :  *     SKIPJACK, 13 = Twofish, 14 = XTEA.</li>
    1999             :  * <li>UINT8. Block cipher mode of operation. Valid values are: 0 = CBC, 1 =
    2000             :  *     CFB, 2 = OFB, 3 = CTR, 4 = CBC_CTS.</li>
    2001             :  * <li>UINT8. Size in bytes of the Initial Vector.</li>
    2002             :  * <li>N bytes with the content of the Initial Vector, where N is the value of
    2003             :  *     the previous field.</li>
    2004             :  * <li>UINT16_LE. Size in bytes of the free text.</li>
    2005             :  * <li>N bytes with the content of the free text, where N is the value of the
    2006             :  *     previous field.</li>
    2007             :  * <li>UINT8. Size in bytes of encrypted content (key check), or 0 if key check
    2008             :  *     is absent.</li>
    2009             :  * <li>N bytes with encrypted content (key check), where N is the value of the
    2010             :  *     previous field.</li>
    2011             :  * <li>UINT64_LE. Size of the unencrypted file, in bytes.</li>
    2012             :  * <li>UINT16_LE. Size in bytes of extra content (of unspecified semantics). For
    2013             :  *     v1.0, fixed value of 0</li>
    2014             :  * <li>N bytes with extra content (of unspecified semantics), where N is the
    2015             :  *     value of the previous field.</li>
    2016             :  * </ol>
    2017             :  *
    2018             :  * This design does not provide any means of authentication or integrity check.
    2019             :  *
    2020             :  * Each sector is encrypted/decrypted independently of other sectors.  For that,
    2021             :  * the Initial Vector contained in the header is XOR'ed with the file offset
    2022             :  * (relative to plain text file) of the start of the sector being processed, as
    2023             :  * a 8-byte integer.  More precisely, the first byte of the main IV is XOR'ed
    2024             :  * with the 8 least-significant bits of the sector offset, the second byte of
    2025             :  * the main IV is XOR'ed with the following 8 bits of the sector offset,
    2026             :  * etc... until the 8th byte.
    2027             :  *
    2028             :  * This design could potentially be prone to chosen-plaintext attack, for
    2029             :  * example if the attacker managed to get (part of) an existing encrypted file
    2030             :  * to be encrypted from plaintext he might have selected.
    2031             :  *
    2032             :  * Note: if "hostile" code can explore process content, or attach to it with a
    2033             :  * debugger, it might be relatively easy to retrieve the encryption key.  A GDAL
    2034             :  * plugin could for example get the content of configuration options, or list
    2035             :  * opened datasets and see the key/key_b64 values, so disabling plugin loading
    2036             :  * might be a first step, as well as linking statically GDAL to application
    2037             :  * code.  If plugin loading is enabled or GDAL dynamically linked, using
    2038             :  * VSISetCryptKey() to set the key might make it a bit more complicated to spy
    2039             :  * the key.  But, as said initially, this is in no way a perfect protection.
    2040             :  *
    2041             :  */
    2042        1808 : void VSIInstallCryptFileHandler(void)
    2043             : 
    2044             : {
    2045        1808 :     VSIFileManager::InstallHandler(
    2046        3616 :         VSICRYPT_PREFIX, std::make_shared<VSICryptFilesystemHandler>());
    2047             : 
    2048             : #ifdef VSICRYPT_DRIVER
    2049             :     if (GDALGetDriverByName("VSICRYPT") != nullptr)
    2050             :         return;
    2051             : 
    2052             :     GDALDriver *poDriver = new GDALDriver();
    2053             : 
    2054             :     poDriver->SetDescription("VSICRYPT");
    2055             : #ifdef GDAL_DCAP_RASTER
    2056             :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    2057             :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    2058             : #endif
    2059             :     poDriver->SetMetadataItem(
    2060             :         GDAL_DMD_LONGNAME, CPLSPrintf("Wrapper for %s files", VSICRYPT_PREFIX));
    2061             : 
    2062             :     poDriver->pfnOpen = VSICryptOpen;
    2063             :     poDriver->pfnIdentify = VSICryptIdentify;
    2064             : 
    2065             :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2066             : #endif
    2067        1808 : }
    2068             : 
    2069             : #else /* HAVE_CRYPTOPP */
    2070             : 
    2071             : class VSIDummyCryptFilesystemHandler : public VSIFilesystemHandler
    2072             : {
    2073             :   public:
    2074             :     VSIDummyCryptFilesystemHandler() = default;
    2075             : 
    2076             :     VSIVirtualHandleUniquePtr Open(const char * /* pszFilename */,
    2077             :                                    const char * /* pszAccess */,
    2078             :                                    bool /* bSetError */,
    2079             :                                    CSLConstList /* papszOptions */) override;
    2080             : 
    2081             :     int Stat(const char * /* pszFilename */, VSIStatBufL * /*pStatBuf */,
    2082             :              int /* nFlags */) override
    2083             :     {
    2084             :         CPLError(CE_Failure, CPLE_NotSupported,
    2085             :                  "%s support not available in this build", VSICRYPT_PREFIX);
    2086             :         return -1;
    2087             :     }
    2088             : };
    2089             : 
    2090             : VSIVirtualHandleUniquePtr VSIDummyCryptFilesystemHandler::Open(
    2091             :     const char * /* pszFilename */, const char * /* pszAccess */,
    2092             :     bool /* bSetError */, CSLConstList /* papszOptions */)
    2093             : {
    2094             :     CPLError(CE_Failure, CPLE_NotSupported,
    2095             :              "%s support not available in this build", VSICRYPT_PREFIX);
    2096             :     return nullptr;
    2097             : }
    2098             : 
    2099             : void VSIInstallCryptFileHandler(void)
    2100             : {
    2101             :     VSIFileManager::InstallHandler(
    2102             :         VSICRYPT_PREFIX, std::make_shared<VSIDummyCryptFilesystemHandler>());
    2103             : }
    2104             : 
    2105             : void VSISetCryptKey(const GByte * /* pabyKey */, int /* nKeySize */)
    2106             : {
    2107             :     // Not supported.
    2108             : }
    2109             : 
    2110             : #endif  // HAVE_CRYPTOPP
    2111             : 
    2112             : // Below is only useful if using as a plugin.
    2113             : #ifdef VSICRYPT_AUTOLOAD
    2114             : 
    2115             : CPL_C_START
    2116             : void CPL_DLL GDALRegisterMe();
    2117             : CPL_C_END
    2118             : 
    2119             : void GDALRegisterMe()
    2120             : {
    2121             :     VSIFilesystemHandler *poExistingHandler =
    2122             :         VSIFileManager::GetHandler(VSICRYPT_PREFIX);
    2123             :     if (poExistingHandler == VSIFileManager::GetHandler("."))
    2124             :     {
    2125             :         // In the case where VSICRYPT_PREFIX is just handled by the regular
    2126             :         // handler, install the vsicrypt handler (shouldn't happen)
    2127             :         VSIInstallCryptFileHandler();
    2128             :     }
    2129             :     else
    2130             :     {
    2131             :         // If there's already an installed handler, then check if it is a
    2132             :         // dummy one (should normally be the case) or a real one
    2133             :         CPLErrorReset();
    2134             :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2135             :         VSIStatBufL sStat;
    2136             :         CPL_IGNORE_RET_VAL(VSIStatL(
    2137             :             (CPLString(VSICRYPT_PREFIX) + "i_do_not_exist").c_str(), &sStat));
    2138             :         CPLPopErrorHandler();
    2139             :         if (strstr(CPLGetLastErrorMsg(), "support not available in this build"))
    2140             :         {
    2141             :             // Dummy handler. Register the new one, and delete the old one
    2142             :             VSIInstallCryptFileHandler();
    2143             :             delete poExistingHandler;
    2144             :         }
    2145             :         else
    2146             :         {
    2147             :             CPLDebug("VSICRYPT", "GDAL has already a working %s implementation",
    2148             :                      VSICRYPT_PREFIX);
    2149             :         }
    2150             :         CPLErrorReset();
    2151             :     }
    2152             : }
    2153             : 
    2154             : #endif /* VSICRYPT_AUTOLOAD */

Generated by: LCOV version 1.14