LCOV - code coverage report
Current view: top level - port - cpl_vsil_crypt.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 748 795 94.1 %
Date: 2025-06-28 21:28:23 Functions: 40 40 100.0 %

          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 SetNewAndDeleteFromCryptoPP(
     110             :     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             :     GUInt16 nHeaderSize = 0;
     388             :     GByte nMajorVersion = 0;
     389             :     GByte nMinorVersion = 0;
     390             :     GUInt16 nSectorSize = 512;
     391             :     VSICryptAlg eAlg = ALG_AES;
     392             :     VSICryptMode eMode = MODE_CBC;
     393             :     CPLString osIV{};
     394             :     bool bAddKeyCheck = false;
     395             :     GUIntBig 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->Read(&nHeaderSize, 2, 1) == 0)
     491           2 :         return VSICryptReadError();
     492         201 :     nHeaderSize = CPL_LSBWORD16(nHeaderSize);
     493         201 :     if (nHeaderSize < 8 + 2 + 1 + 1)
     494             :     {
     495           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid header size : %d",
     496           1 :                  nHeaderSize);
     497           1 :         return FALSE;
     498             :     }
     499             : 
     500         200 :     if (fp->Read(&nMajorVersion, 1, 1) == 0)
     501           1 :         return VSICryptReadError();
     502         199 :     if (fp->Read(&nMinorVersion, 1, 1) == 0)
     503           1 :         return VSICryptReadError();
     504             : 
     505         198 :     if (nMajorVersion != VSICRYPT_CURRENT_MAJOR)
     506             :     {
     507           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Unhandled major version : %d",
     508           3 :                  nMajorVersion);
     509           3 :         return FALSE;
     510             :     }
     511         195 :     if (nMinorVersion != VSICRYPT_CURRENT_MINOR)
     512             :     {
     513           2 :         CPLDebug("VSICRYPT", "Minor version in file is %d", nMinorVersion);
     514             :     }
     515             : 
     516         195 :     if (fp->Read(&nSectorSize, 2, 1) == 0)
     517           2 :         return VSICryptReadError();
     518         193 :     nSectorSize = CPL_LSBWORD16(nSectorSize);
     519             : 
     520             :     GByte nAlg, nMode;
     521         193 :     if (fp->Read(&nAlg, 1, 1) == 0 || fp->Read(&nMode, 1, 1) == 0)
     522           2 :         return VSICryptReadError();
     523         191 :     if (nAlg > ALG_MAX)
     524             :     {
     525           2 :         CPLError(CE_Failure, CPLE_NotSupported,
     526             :                  "Unsupported cipher algorithm %d", nAlg);
     527           2 :         return FALSE;
     528             :     }
     529         189 :     if (nMode > MODE_MAX)
     530             :     {
     531           2 :         CPLError(CE_Failure, CPLE_NotSupported,
     532             :                  "Unsupported cipher block mode %d", nMode);
     533           2 :         return FALSE;
     534             :     }
     535         187 :     eAlg = static_cast<VSICryptAlg>(nAlg);
     536         187 :     eMode = static_cast<VSICryptMode>(nMode);
     537             : 
     538             :     GByte nIVSize;
     539         187 :     if (fp->Read(&nIVSize, 1, 1) == 0)
     540           1 :         return VSICryptReadError();
     541             : 
     542         186 :     osIV.resize(nIVSize);
     543         186 :     if (fp->Read(osIV.data(), 1, nIVSize) != nIVSize)
     544          18 :         return VSICryptReadError();
     545             : 
     546             :     GUInt16 nFreeTextSize;
     547         168 :     if (fp->Read(&nFreeTextSize, 2, 1) == 0)
     548           2 :         return VSICryptReadError();
     549             : 
     550         166 :     osFreeText.resize(nFreeTextSize);
     551         166 :     if (fp->Read(osFreeText.data(), 1, nFreeTextSize) != nFreeTextSize)
     552           5 :         return VSICryptReadError();
     553             : 
     554             :     GByte nKeyCheckSize;
     555         161 :     if (fp->Read(&nKeyCheckSize, 1, 1) == 0)
     556           1 :         return VSICryptReadError();
     557         160 :     bAddKeyCheck = nKeyCheckSize != 0;
     558         160 :     if (nKeyCheckSize)
     559             :     {
     560          11 :         CPLString osKeyCheck;
     561          11 :         osKeyCheck.resize(nKeyCheckSize);
     562          11 :         if (fp->Read(osKeyCheck.data(), 1, nKeyCheckSize) != nKeyCheckSize)
     563           2 :             return VSICryptReadError();
     564             : 
     565           9 :         if (osKey.empty() && pabyGlobalKey == nullptr)
     566             :         {
     567           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     568             :                      "Encryption key not defined as key/key_b64 parameter, "
     569             :                      "VSICRYPT_KEY/VSICRYPT_KEY_B64 configuration option or "
     570             :                      "VSISetCryptKey() API");
     571           1 :             return FALSE;
     572             :         }
     573             : 
     574           8 :         CryptoPP::BlockCipher *poEncCipher = GetEncBlockCipher(eAlg);
     575           8 :         if (poEncCipher == nullptr)
     576           0 :             return FALSE;
     577             : 
     578           8 :         if (osIV.size() != poEncCipher->BlockSize())
     579             :         {
     580           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     581             :                      "Inconsistent initial vector");
     582           1 :             delete poEncCipher;
     583           1 :             return FALSE;
     584             :         }
     585             : 
     586           7 :         int nMaxKeySize = static_cast<int>(poEncCipher->MaxKeyLength());
     587             : 
     588             :         try
     589             :         {
     590           7 :             if (!osKey.empty())
     591             :             {
     592             :                 const int nKeySize =
     593           6 :                     std::min(nMaxKeySize, static_cast<int>(osKey.size()));
     594           6 :                 poEncCipher->SetKey(
     595           6 :                     reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
     596           6 :                     nKeySize);
     597             :             }
     598           1 :             else if (pabyGlobalKey)
     599             :             {
     600           1 :                 const int nKeySize = std::min(nMaxKeySize, nGlobalKeySize);
     601           1 :                 poEncCipher->SetKey(pabyGlobalKey, nKeySize);
     602             :             }
     603             :         }
     604           1 :         catch (const std::exception &e)
     605             :         {
     606           1 :             CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
     607           1 :                      e.what());
     608           1 :             delete poEncCipher;
     609           1 :             return FALSE;
     610             :         }
     611             : 
     612           6 :         std::string osKeyCheckRes = CryptKeyCheck(poEncCipher);
     613             : 
     614           6 :         delete poEncCipher;
     615             : 
     616          12 :         if (osKeyCheck.size() != osKeyCheckRes.size() ||
     617           6 :             memcmp(osKeyCheck.c_str(), osKeyCheckRes.c_str(),
     618             :                    osKeyCheck.size()) != 0)
     619             :         {
     620           2 :             CPLError(CE_Failure, CPLE_AppDefined, "Bad key");
     621           2 :             return FALSE;
     622             :         }
     623             :     }
     624             : 
     625         153 :     if (fp->Read(&nPayloadFileSize, 8, 1) == 0)
     626           8 :         return VSICryptReadError();
     627         145 :     CPL_LSBPTR64(&nPayloadFileSize);
     628             : #ifdef VERBOSE_VSICRYPT
     629             :     CPLDebug("VSICRYPT", "nPayloadFileSize read = " CPL_FRMT_GUIB,
     630             :              nPayloadFileSize);
     631             : #endif
     632             : 
     633         145 :     GUInt16 nExtraContentSize = 0;
     634         145 :     if (fp->Read(&nExtraContentSize, 2, 1) == 0)
     635           2 :         return VSICryptReadError();
     636         143 :     nExtraContentSize = CPL_LSBWORD16(nExtraContentSize);
     637             : 
     638         143 :     osExtraContent.resize(nExtraContentSize);
     639         143 :     if (fp->Read(osExtraContent.data(), 1, nExtraContentSize) !=
     640         143 :         nExtraContentSize)
     641           4 :         return VSICryptReadError();
     642             : 
     643         139 :     return TRUE;
     644             : }
     645             : 
     646             : /************************************************************************/
     647             : /*                          WriteToFile()                               */
     648             : /************************************************************************/
     649             : 
     650        2075 : int VSICryptFileHeader::WriteToFile(VSIVirtualHandle *fp,
     651             :                                     CryptoPP::BlockCipher *poEncCipher)
     652             : {
     653        2075 :     fp->Seek(0, SEEK_SET);
     654             : 
     655        2075 :     bool bRet = fp->Write(VSICRYPT_SIGNATURE, 8, 1) == 1;
     656             : 
     657        2075 :     std::string osKeyCheckRes;
     658        2075 :     if (bAddKeyCheck)
     659             :     {
     660           8 :         osKeyCheckRes = CryptKeyCheck(poEncCipher);
     661             :     }
     662             : 
     663             :     GUInt16 nHeaderSizeNew =
     664             :         static_cast<GUInt16>(8 +                         /* signature */
     665             :                              2 +                         /* header size */
     666             :                              1 +                         /* major version */
     667             :                              1 +                         /* minor version */
     668             :                              2 +                         /* sector size */
     669             :                              1 +                         /* alg */
     670             :                              1 +                         /* mode */
     671        2075 :                              1 + osIV.size() +           /* IV */
     672        2075 :                              2 + osFreeText.size() +     /* free text */
     673        2075 :                              1 + osKeyCheckRes.size() +  /* key check */
     674             :                              8 +                         /* payload size */
     675        2075 :                              2 + osExtraContent.size()); /* extra content */
     676        2075 :     if (nHeaderSize != 0)
     677        1038 :         CPLAssert(nHeaderSizeNew == nHeaderSize);
     678             :     else
     679        1037 :         nHeaderSize = nHeaderSizeNew;
     680             : 
     681        2075 :     GUInt16 nHeaderSizeToWrite = CPL_LSBWORD16(nHeaderSizeNew);
     682        2075 :     bRet &= (fp->Write(&nHeaderSizeToWrite, 2, 1) == 1);
     683             : 
     684        2075 :     GByte nMajorVersionToWrite = VSICRYPT_CURRENT_MAJOR;
     685        2075 :     bRet &= (fp->Write(&nMajorVersionToWrite, 1, 1) == 1);
     686             : 
     687        2075 :     GByte nMinorVersionToWrite = VSICRYPT_CURRENT_MINOR;
     688        2075 :     bRet &= (fp->Write(&nMinorVersionToWrite, 1, 1) == 1);
     689             : 
     690        2075 :     GUInt16 nSectorSizeToWrite = CPL_LSBWORD16(nSectorSize);
     691        2075 :     bRet &= (fp->Write(&nSectorSizeToWrite, 2, 1) == 1);
     692             : 
     693        2075 :     GByte nAlg = static_cast<GByte>(eAlg);
     694        2075 :     bRet &= (fp->Write(&nAlg, 1, 1) == 1);
     695             : 
     696        2075 :     GByte nMode = static_cast<GByte>(eMode);
     697        2075 :     bRet &= (fp->Write(&nMode, 1, 1) == 1);
     698             : 
     699        2075 :     GByte nIVSizeToWrite = static_cast<GByte>(osIV.size());
     700        2075 :     CPLAssert(nIVSizeToWrite == osIV.size());
     701        2075 :     bRet &= (fp->Write(&nIVSizeToWrite, 1, 1) == 1);
     702        2075 :     bRet &= (fp->Write(osIV.c_str(), 1, osIV.size()) == osIV.size());
     703             : 
     704             :     GUInt16 nFreeTextSizeToWrite =
     705        2075 :         CPL_LSBWORD16(static_cast<GUInt16>(osFreeText.size()));
     706        2075 :     bRet &= (fp->Write(&nFreeTextSizeToWrite, 2, 1) == 1);
     707        2075 :     bRet &= (fp->Write(osFreeText.c_str(), 1, osFreeText.size()) ==
     708        2075 :              osFreeText.size());
     709             : 
     710        2075 :     GByte nSize = static_cast<GByte>(osKeyCheckRes.size());
     711        2075 :     bRet &= (fp->Write(&nSize, 1, 1) == 1);
     712        2075 :     bRet &= (fp->Write(osKeyCheckRes.c_str(), 1, osKeyCheckRes.size()) ==
     713        2075 :              osKeyCheckRes.size());
     714             : 
     715        2075 :     GUIntBig nPayloadFileSizeToWrite = nPayloadFileSize;
     716        2075 :     CPL_LSBPTR64(&nPayloadFileSizeToWrite);
     717        2075 :     bRet &= (fp->Write(&nPayloadFileSizeToWrite, 8, 1) == 1);
     718             : 
     719             :     GUInt16 nExtraContentSizeToWrite =
     720        2075 :         CPL_LSBWORD16(static_cast<GUInt16>(osExtraContent.size()));
     721        2075 :     bRet &= (fp->Write(&nExtraContentSizeToWrite, 2, 1) == 1);
     722        2075 :     bRet &= (fp->Write(osExtraContent.c_str(), 1, osExtraContent.size()) ==
     723        2075 :              osExtraContent.size());
     724             : 
     725        2075 :     CPLAssert(fp->Tell() == nHeaderSize);
     726             : 
     727        4150 :     return bRet;
     728             : }
     729             : 
     730             : /************************************************************************/
     731             : /*                          VSICryptFileHandle                          */
     732             : /************************************************************************/
     733             : 
     734             : class VSICryptFileHandle final : public VSIVirtualHandle
     735             : {
     736             :     CPL_DISALLOW_COPY_ASSIGN(VSICryptFileHandle)
     737             : 
     738             :   private:
     739             :     CPLString osBaseFilename{};
     740             :     int nPerms = 0;
     741             :     VSIVirtualHandle *poBaseHandle = nullptr;
     742             :     VSICryptFileHeader *poHeader = nullptr;
     743             :     bool bUpdateHeader = false;
     744             :     vsi_l_offset nCurPos = 0;
     745             :     bool bEOF = false;
     746             :     bool bError = false;
     747             : 
     748             :     CryptoPP::BlockCipher *poEncCipher = nullptr;
     749             :     CryptoPP::BlockCipher *poDecCipher = nullptr;
     750             :     int nBlockSize = 0;
     751             : 
     752             :     vsi_l_offset nWBOffset = 0;
     753             :     GByte *pabyWB = nullptr;
     754             :     int nWBSize = 0;
     755             :     bool bWBDirty = false;
     756             : 
     757             :     bool bLastSectorWasModified = false;
     758             : 
     759             :     void EncryptBlock(GByte *pabyData, vsi_l_offset nOffset);
     760             :     bool DecryptBlock(GByte *pabyData, vsi_l_offset nOffset);
     761             :     bool FlushDirty();
     762             : 
     763             :   public:
     764             :     VSICryptFileHandle(const CPLString &osBaseFilename,
     765             :                        VSIVirtualHandle *poBaseHandle,
     766             :                        VSICryptFileHeader *poHeader, int nPerms);
     767             :     ~VSICryptFileHandle() override;
     768             : 
     769             :     int Init(const CPLString &osKey, bool bWriteHeader = false);
     770             : 
     771             :     int Seek(vsi_l_offset nOffset, int nWhence) override;
     772             :     vsi_l_offset Tell() override;
     773             :     size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
     774             :     size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
     775             :     int Eof() override;
     776             :     int Error() override;
     777             :     void ClearErr() override;
     778             :     int Flush() override;
     779             :     int Close() override;
     780             :     int Truncate(vsi_l_offset nNewSize) override;
     781             : };
     782             : 
     783             : /************************************************************************/
     784             : /*                          VSICryptFileHandle()                        */
     785             : /************************************************************************/
     786             : 
     787        1174 : VSICryptFileHandle::VSICryptFileHandle(const CPLString &osBaseFilenameIn,
     788             :                                        VSIVirtualHandle *poBaseHandleIn,
     789             :                                        VSICryptFileHeader *poHeaderIn,
     790        1174 :                                        int nPermsIn)
     791             :     : osBaseFilename(osBaseFilenameIn), nPerms(nPermsIn),
     792        1174 :       poBaseHandle(poBaseHandleIn), poHeader(poHeaderIn)
     793             : {
     794        1174 : }
     795             : 
     796             : /************************************************************************/
     797             : /*                         ~VSICryptFileHandle()                        */
     798             : /************************************************************************/
     799             : 
     800        2348 : VSICryptFileHandle::~VSICryptFileHandle()
     801             : {
     802        1174 :     Close();
     803        1174 :     delete poHeader;
     804        1174 :     delete poEncCipher;
     805        1174 :     delete poDecCipher;
     806        1174 :     CPLFree(pabyWB);
     807        2348 : }
     808             : 
     809             : /************************************************************************/
     810             : /*                               Init()                                 */
     811             : /************************************************************************/
     812             : 
     813        1174 : int VSICryptFileHandle::Init(const CPLString &osKey, bool bWriteHeader)
     814             : {
     815        1174 :     poEncCipher = GetEncBlockCipher(poHeader->eAlg);
     816        1174 :     if (poEncCipher == nullptr)
     817             :     {
     818           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     819             :                  "Cipher algorithm not supported in this build: %d",
     820           0 :                  static_cast<int>(poHeader->eAlg));
     821           0 :         return FALSE;
     822             :     }
     823             : 
     824        1174 :     if (poHeader->osIV.size() != poEncCipher->BlockSize())
     825             :     {
     826           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent initial vector");
     827           1 :         return FALSE;
     828             :     }
     829             : 
     830        1173 :     poDecCipher = GetDecBlockCipher(poHeader->eAlg);
     831        1173 :     nBlockSize = poEncCipher->BlockSize();
     832        1173 :     int nMaxKeySize = static_cast<int>(poEncCipher->MaxKeyLength());
     833             : 
     834             :     try
     835             :     {
     836        1173 :         if (!osKey.empty())
     837             :         {
     838             :             const int nKeySize =
     839        1168 :                 std::min(nMaxKeySize, static_cast<int>(osKey.size()));
     840        1168 :             poEncCipher->SetKey(
     841        1168 :                 reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
     842        1168 :                 nKeySize);
     843        1167 :             poDecCipher->SetKey(
     844        1167 :                 reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
     845        1167 :                 nKeySize);
     846             :         }
     847           5 :         else if (pabyGlobalKey)
     848             :         {
     849           5 :             const int nKeySize = std::min(nMaxKeySize, nGlobalKeySize);
     850           5 :             poEncCipher->SetKey(pabyGlobalKey, nKeySize);
     851           4 :             poDecCipher->SetKey(pabyGlobalKey, nKeySize);
     852             :         }
     853             :         else
     854           0 :             return FALSE;
     855             :     }
     856           2 :     catch (const std::exception &e)
     857             :     {
     858           2 :         CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
     859           2 :                  e.what());
     860           2 :         return FALSE;
     861             :     }
     862             : 
     863        1171 :     pabyWB = static_cast<GByte *>(CPLCalloc(1, poHeader->nSectorSize));
     864             : 
     865        1171 :     if ((poHeader->nSectorSize % nBlockSize) != 0)
     866             :     {
     867           3 :         CPLError(CE_Failure, CPLE_AppDefined,
     868             :                  "Sector size (%d) is not a multiple of block size (%d)",
     869           3 :                  poHeader->nSectorSize, nBlockSize);
     870           3 :         return FALSE;
     871             :     }
     872        1168 :     if (poHeader->eMode == MODE_CBC_CTS &&
     873           3 :         poHeader->nSectorSize < 2 * nBlockSize)
     874             :     {
     875           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     876             :                  "Sector size (%d) should be at least twice larger than "
     877             :                  "the block size (%d) in CBC_CTS.",
     878           1 :                  poHeader->nSectorSize, nBlockSize);
     879           1 :         return FALSE;
     880             :     }
     881             : 
     882        1167 :     if (bWriteHeader && !poHeader->WriteToFile(poBaseHandle, poEncCipher))
     883             :     {
     884           0 :         return FALSE;
     885             :     }
     886             : 
     887        1167 :     return TRUE;
     888             : }
     889             : 
     890             : /************************************************************************/
     891             : /*                          EncryptBlock()                              */
     892             : /************************************************************************/
     893             : 
     894       70111 : void VSICryptFileHandle::EncryptBlock(GByte *pabyData, vsi_l_offset nOffset)
     895             : {
     896       70111 :     std::string osRes;
     897       70111 :     std::string osIV(VSICryptGenerateSectorIV(poHeader->osIV, nOffset));
     898       70111 :     CPLAssert(static_cast<int>(osIV.size()) == nBlockSize);
     899             : 
     900             :     CryptoPP::StreamTransformation *poMode;
     901             :     try
     902             :     {
     903       70111 :         if (poHeader->eMode == MODE_CBC)
     904       70107 :             poMode = new CryptoPP::CBC_Mode_ExternalCipher::Encryption(
     905       70107 :                 *poEncCipher,
     906       70107 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     907           4 :         else if (poHeader->eMode == MODE_CFB)
     908           1 :             poMode = new CryptoPP::CFB_Mode_ExternalCipher::Encryption(
     909           1 :                 *poEncCipher,
     910           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     911           3 :         else if (poHeader->eMode == MODE_OFB)
     912           1 :             poMode = new CryptoPP::OFB_Mode_ExternalCipher::Encryption(
     913           1 :                 *poEncCipher,
     914           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     915           2 :         else if (poHeader->eMode == MODE_CTR)
     916           1 :             poMode = new CryptoPP::CTR_Mode_ExternalCipher::Encryption(
     917           1 :                 *poEncCipher,
     918           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     919             :         else
     920           1 :             poMode = new CryptoPP::CBC_CTS_Mode_ExternalCipher::Encryption(
     921           1 :                 *poEncCipher,
     922           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     923             :     }
     924           0 :     catch (const std::exception &e)
     925             :     {
     926           0 :         CPLError(CE_Failure, CPLE_AppDefined, "cryptopp exception: %s",
     927           0 :                  e.what());
     928           0 :         return;
     929             :     }
     930       70111 :     CryptoPP::StringSink *poSink = new CryptoPP::StringSink(osRes);
     931             :     CryptoPP::StreamTransformationFilter *poEnc =
     932             :         new CryptoPP::StreamTransformationFilter(
     933       70111 :             *poMode, poSink, CryptoPP::StreamTransformationFilter::NO_PADDING);
     934       70111 :     poEnc->Put(pabyData, poHeader->nSectorSize);
     935       70111 :     poEnc->MessageEnd();
     936       70111 :     delete poEnc;
     937             : 
     938       70111 :     delete poMode;
     939             : 
     940       70111 :     CPLAssert(static_cast<int>(osRes.length()) == poHeader->nSectorSize);
     941       70111 :     memcpy(pabyData, osRes.c_str(), osRes.length());
     942             : }
     943             : 
     944             : /************************************************************************/
     945             : /*                          DecryptBlock()                              */
     946             : /************************************************************************/
     947             : 
     948       75219 : bool VSICryptFileHandle::DecryptBlock(GByte *pabyData, vsi_l_offset nOffset)
     949             : {
     950      150438 :     std::string osRes;
     951      150438 :     std::string osIV(VSICryptGenerateSectorIV(poHeader->osIV, nOffset));
     952       75219 :     CPLAssert(static_cast<int>(osIV.size()) == nBlockSize);
     953       75219 :     CryptoPP::StringSink *poSink = new CryptoPP::StringSink(osRes);
     954       75219 :     CryptoPP::StreamTransformation *poMode = nullptr;
     955       75219 :     CryptoPP::StreamTransformationFilter *poDec = nullptr;
     956             : 
     957             :     try
     958             :     {
     959             :         // Yes, some modes need the encryption cipher.
     960       75219 :         if (poHeader->eMode == MODE_CBC)
     961       75215 :             poMode = new CryptoPP::CBC_Mode_ExternalCipher::Decryption(
     962       75215 :                 *poDecCipher,
     963       75215 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     964           4 :         else if (poHeader->eMode == MODE_CFB)
     965           1 :             poMode = new CryptoPP::CFB_Mode_ExternalCipher::Decryption(
     966           1 :                 *poEncCipher,
     967           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     968           3 :         else if (poHeader->eMode == MODE_OFB)
     969           1 :             poMode = new CryptoPP::OFB_Mode_ExternalCipher::Decryption(
     970           1 :                 *poEncCipher,
     971           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     972           2 :         else if (poHeader->eMode == MODE_CTR)
     973           1 :             poMode = new CryptoPP::CTR_Mode_ExternalCipher::Decryption(
     974           1 :                 *poEncCipher,
     975           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     976             :         else
     977           1 :             poMode = new CryptoPP::CBC_CTS_Mode_ExternalCipher::Decryption(
     978           1 :                 *poDecCipher,
     979           1 :                 reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
     980       75219 :         poDec = new CryptoPP::StreamTransformationFilter(
     981       75219 :             *poMode, poSink, CryptoPP::StreamTransformationFilter::NO_PADDING);
     982       75219 :         poDec->Put(reinterpret_cast<const cryptopp_byte *>(pabyData),
     983       75219 :                    poHeader->nSectorSize);
     984       75219 :         poDec->MessageEnd();
     985       75219 :         delete poDec;
     986       75219 :         delete poMode;
     987             :     }
     988           0 :     catch (const std::exception &e)
     989             :     {
     990           0 :         delete poDec;
     991           0 :         delete poMode;
     992             : 
     993           0 :         CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
     994           0 :                  e.what());
     995           0 :         return false;
     996             :     }
     997             : 
     998       75219 :     CPLAssert(static_cast<int>(osRes.length()) == poHeader->nSectorSize);
     999       75219 :     memcpy(pabyData, osRes.c_str(), osRes.length());
    1000             : 
    1001       75219 :     return true;
    1002             : }
    1003             : 
    1004             : /************************************************************************/
    1005             : /*                             FlushDirty()                             */
    1006             : /************************************************************************/
    1007             : 
    1008       55328 : bool VSICryptFileHandle::FlushDirty()
    1009             : {
    1010       55328 :     if (!bWBDirty)
    1011       11097 :         return true;
    1012       44231 :     bWBDirty = false;
    1013             : 
    1014       44231 :     EncryptBlock(pabyWB, nWBOffset);
    1015       44231 :     poBaseHandle->Seek(poHeader->nHeaderSize + nWBOffset, SEEK_SET);
    1016             : 
    1017       44231 :     nWBOffset = 0;
    1018       44231 :     nWBSize = 0;
    1019             : 
    1020       44231 :     if (poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1) != 1)
    1021           0 :         return false;
    1022             : 
    1023       44231 :     return true;
    1024             : }
    1025             : 
    1026             : /************************************************************************/
    1027             : /*                                Seek()                                */
    1028             : /************************************************************************/
    1029             : 
    1030       30973 : int VSICryptFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
    1031             : {
    1032             : #ifdef VERBOSE_VSICRYPT
    1033             :     CPLDebug("VSICRYPT", "Seek(nOffset=" CPL_FRMT_GUIB ", nWhence=%d)", nOffset,
    1034             :              nWhence);
    1035             : #endif
    1036             : 
    1037       30973 :     bEOF = false;
    1038             : 
    1039       30973 :     if (nWhence == SEEK_SET)
    1040       30971 :         nCurPos = nOffset;
    1041           2 :     else if (nWhence == SEEK_CUR)
    1042           0 :         nCurPos += nOffset;
    1043             :     else
    1044           2 :         nCurPos = poHeader->nPayloadFileSize;
    1045       30973 :     return 0;
    1046             : }
    1047             : 
    1048             : /************************************************************************/
    1049             : /*                                  Tell()                              */
    1050             : /************************************************************************/
    1051             : 
    1052           3 : vsi_l_offset VSICryptFileHandle::Tell()
    1053             : {
    1054             : #ifdef VERBOSE_VSICRYPT
    1055             :     CPLDebug("VSICRYPT", "Tell()=" CPL_FRMT_GUIB, nCurPos);
    1056             : #endif
    1057           3 :     return nCurPos;
    1058             : }
    1059             : 
    1060             : /************************************************************************/
    1061             : /*                                Read()                                */
    1062             : /************************************************************************/
    1063             : 
    1064       11005 : size_t VSICryptFileHandle::Read(void *pBuffer, size_t nSize, size_t nMemb)
    1065             : {
    1066       11005 :     size_t nToRead = nSize * nMemb;
    1067       11005 :     GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
    1068             : 
    1069             : #ifdef VERBOSE_VSICRYPT
    1070             :     CPLDebug("VSICRYPT", "Read(nCurPos=" CPL_FRMT_GUIB ", nToRead=%d)", nCurPos,
    1071             :              static_cast<int>(nToRead));
    1072             : #endif
    1073             : 
    1074       11005 :     if ((nPerms & VSICRYPT_READ) == 0)
    1075             :     {
    1076           1 :         bError = true;
    1077           1 :         return 0;
    1078             :     }
    1079             : 
    1080       11004 :     if (nCurPos >= poHeader->nPayloadFileSize)
    1081             :     {
    1082        3882 :         bEOF = true;
    1083        3882 :         return 0;
    1084             :     }
    1085             : 
    1086        7122 :     if (!FlushDirty())
    1087           0 :         return 0;
    1088             : 
    1089       52089 :     while (nToRead > 0)
    1090             :     {
    1091       52089 :         if (nCurPos >= nWBOffset && nCurPos < nWBOffset + nWBSize)
    1092             :         {
    1093             :             // TODO(schwehr): Can nToCopy be a size_t to simplify casting?
    1094             :             int nToCopy =
    1095       89938 :                 std::min(static_cast<int>(nToRead),
    1096       44969 :                          static_cast<int>(nWBSize - (nCurPos - nWBOffset)));
    1097       44969 :             if (nCurPos + nToCopy > poHeader->nPayloadFileSize)
    1098             :             {
    1099        1234 :                 bEOF = true;
    1100        1234 :                 nToCopy =
    1101        1234 :                     static_cast<int>(poHeader->nPayloadFileSize - nCurPos);
    1102             :             }
    1103       44969 :             memcpy(pabyBuffer, pabyWB + nCurPos - nWBOffset, nToCopy);
    1104       44969 :             pabyBuffer += nToCopy;
    1105       44969 :             nToRead -= nToCopy;
    1106       44969 :             nCurPos += nToCopy;
    1107       44969 :             if (bEOF || nToRead == 0)
    1108             :                 break;
    1109       37888 :             CPLAssert((nCurPos % poHeader->nSectorSize) == 0);
    1110             :         }
    1111             : 
    1112       45008 :         vsi_l_offset nSectorOffset =
    1113       45008 :             (nCurPos / poHeader->nSectorSize) * poHeader->nSectorSize;
    1114       45008 :         poBaseHandle->Seek(poHeader->nHeaderSize + nSectorOffset, SEEK_SET);
    1115       45008 :         if (poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) != 1)
    1116             :         {
    1117          41 :             bEOF = poBaseHandle->Eof();
    1118          41 :             bError = poBaseHandle->Error();
    1119          41 :             break;
    1120             :         }
    1121       44967 :         if (!DecryptBlock(pabyWB, nSectorOffset))
    1122             :         {
    1123           0 :             bError = true;
    1124           0 :             break;
    1125             :         }
    1126       44967 :         if ((nPerms & VSICRYPT_WRITE) &&
    1127       44933 :             nSectorOffset + poHeader->nSectorSize > poHeader->nPayloadFileSize)
    1128             :         {
    1129             :             // If the last sector was padded with random values, decrypt it to 0
    1130             :             // in case of update scenarios.
    1131        1347 :             CPLAssert(nSectorOffset < poHeader->nPayloadFileSize);
    1132        1347 :             memset(pabyWB + poHeader->nPayloadFileSize - nSectorOffset, 0,
    1133        1347 :                    nSectorOffset + poHeader->nSectorSize -
    1134        1347 :                        poHeader->nPayloadFileSize);
    1135             :         }
    1136       44967 :         nWBOffset = nSectorOffset;
    1137       44967 :         nWBSize = poHeader->nSectorSize;
    1138             :     }
    1139             : 
    1140        7122 :     int nRet = static_cast<int>((nSize * nMemb - nToRead) / nSize);
    1141             : #ifdef VERBOSE_VSICRYPT
    1142             :     CPLDebug("VSICRYPT", "Read ret = %d (nMemb = %d)", nRet,
    1143             :              static_cast<int>(nMemb));
    1144             : #endif
    1145        7122 :     return nRet;
    1146             : }
    1147             : 
    1148             : /************************************************************************/
    1149             : /*                                Write()                               */
    1150             : /************************************************************************/
    1151             : 
    1152       20040 : size_t VSICryptFileHandle::Write(const void *pBuffer, size_t nSize,
    1153             :                                  size_t nMemb)
    1154             : {
    1155       20040 :     size_t nToWrite = nSize * nMemb;
    1156       20040 :     const GByte *pabyBuffer = static_cast<const GByte *>(pBuffer);
    1157             : 
    1158             : #ifdef VERBOSE_VSICRYPT
    1159             :     CPLDebug("VSICRYPT",
    1160             :              "Write(nCurPos=" CPL_FRMT_GUIB ", nToWrite=%d,"
    1161             :              "nPayloadFileSize=" CPL_FRMT_GUIB
    1162             :              ",bWBDirty=%d,nWBOffset=" CPL_FRMT_GUIB ",nWBSize=%d)",
    1163             :              nCurPos, static_cast<int>(nToWrite), poHeader->nPayloadFileSize,
    1164             :              static_cast<int>(bWBDirty), nWBOffset, nWBSize);
    1165             : #endif
    1166             : 
    1167       20040 :     if ((nPerms & VSICRYPT_WRITE) == 0)
    1168           1 :         return 0;
    1169             : 
    1170       20039 :     if (nCurPos >= (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
    1171       20039 :                        poHeader->nSectorSize)
    1172             :     {
    1173        3266 :         bLastSectorWasModified = true;
    1174             :     }
    1175             : 
    1176             :     // If seeking past end of file, we need to explicitly encrypt the
    1177             :     // padding zeroes.
    1178       20039 :     if (nCurPos > poHeader->nPayloadFileSize && nCurPos > nWBOffset + nWBSize)
    1179             :     {
    1180        2962 :         if (!FlushDirty())
    1181           0 :             return 0;
    1182        2962 :         vsi_l_offset nOffset =
    1183        2962 :             (poHeader->nPayloadFileSize + poHeader->nSectorSize - 1) /
    1184        2962 :             poHeader->nSectorSize * poHeader->nSectorSize;
    1185        2962 :         const vsi_l_offset nEndOffset =
    1186        2962 :             nCurPos / poHeader->nSectorSize * poHeader->nSectorSize;
    1187       27578 :         for (; nOffset < nEndOffset; nOffset += poHeader->nSectorSize)
    1188             :         {
    1189       24616 :             memset(pabyWB, 0, poHeader->nSectorSize);
    1190       24616 :             EncryptBlock(pabyWB, nOffset);
    1191       24616 :             poBaseHandle->Seek(poHeader->nHeaderSize + nOffset, SEEK_SET);
    1192       24616 :             if (poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1) != 1)
    1193           0 :                 return 0;
    1194       24616 :             poHeader->nPayloadFileSize = nOffset + poHeader->nSectorSize;
    1195       24616 :             bUpdateHeader = true;
    1196             :         }
    1197             :     }
    1198             : 
    1199       79522 :     while (nToWrite > 0)
    1200             :     {
    1201       79133 :         if (nCurPos >= nWBOffset && nCurPos < nWBOffset + nWBSize)
    1202             :         {
    1203       35066 :             bWBDirty = true;
    1204             :             const int nToCopy =
    1205       70132 :                 std::min(static_cast<int>(nToWrite),
    1206       35066 :                          static_cast<int>(nWBSize - (nCurPos - nWBOffset)));
    1207       35066 :             memcpy(pabyWB + nCurPos - nWBOffset, pabyBuffer, nToCopy);
    1208       35066 :             pabyBuffer += nToCopy;
    1209       35066 :             nToWrite -= nToCopy;
    1210       35066 :             nCurPos += nToCopy;
    1211       35066 :             if (nCurPos > poHeader->nPayloadFileSize)
    1212             :             {
    1213        6102 :                 bUpdateHeader = true;
    1214        6102 :                 poHeader->nPayloadFileSize = nCurPos;
    1215             :             }
    1216       35066 :             if (nToWrite == 0)
    1217       19650 :                 break;
    1218       15416 :             CPLAssert((nCurPos % poHeader->nSectorSize) == 0);
    1219             :         }
    1220       44067 :         else if ((nCurPos % poHeader->nSectorSize) == 0 &&
    1221       25241 :                  nToWrite >= static_cast<size_t>(poHeader->nSectorSize))
    1222             :         {
    1223        9558 :             if (!FlushDirty())
    1224           0 :                 break;
    1225             : 
    1226        9558 :             bWBDirty = true;
    1227        9558 :             nWBOffset = nCurPos;
    1228        9558 :             nWBSize = poHeader->nSectorSize;
    1229        9558 :             memcpy(pabyWB, pabyBuffer, poHeader->nSectorSize);
    1230        9558 :             pabyBuffer += poHeader->nSectorSize;
    1231        9558 :             nToWrite -= poHeader->nSectorSize;
    1232        9558 :             nCurPos += poHeader->nSectorSize;
    1233        9558 :             if (nCurPos > poHeader->nPayloadFileSize)
    1234             :             {
    1235        1837 :                 bUpdateHeader = true;
    1236        1837 :                 poHeader->nPayloadFileSize = nCurPos;
    1237             :             }
    1238             :         }
    1239             :         else
    1240             :         {
    1241       34509 :             if (!FlushDirty())
    1242           0 :                 break;
    1243             : 
    1244       34509 :             const vsi_l_offset nSectorOffset =
    1245       34509 :                 (nCurPos / poHeader->nSectorSize) * poHeader->nSectorSize;
    1246       34509 :             const vsi_l_offset nLastSectorOffset =
    1247       34509 :                 (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
    1248       34509 :                 poHeader->nSectorSize;
    1249       34509 :             if (nSectorOffset > nLastSectorOffset &&
    1250         286 :                 (poHeader->nPayloadFileSize % poHeader->nSectorSize) != 0)
    1251             :             {
    1252         858 :                 if (poBaseHandle->Seek(
    1253         286 :                         poHeader->nHeaderSize + nLastSectorOffset, 0) == 0 &&
    1254         572 :                     poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 1 &&
    1255         286 :                     DecryptBlock(pabyWB, nLastSectorOffset))
    1256             :                 {
    1257             : #ifdef VERBOSE_VSICRYPT
    1258             :                     CPLDebug("VSICRYPT", "Filling %d trailing bytes with 0",
    1259             :                              static_cast<int>(poHeader->nSectorSize -
    1260             :                                               (poHeader->nPayloadFileSize -
    1261             :                                                nLastSectorOffset)));
    1262             : #endif
    1263             :                     // Fill with 0.
    1264         286 :                     memset(
    1265         286 :                         pabyWB + poHeader->nPayloadFileSize - nLastSectorOffset,
    1266             :                         0,
    1267             :                         static_cast<int>(
    1268         286 :                             poHeader->nSectorSize -
    1269         286 :                             (poHeader->nPayloadFileSize - nLastSectorOffset)));
    1270             : 
    1271         572 :                     if (poBaseHandle->Seek(
    1272         286 :                             poHeader->nHeaderSize + nLastSectorOffset, 0) == 0)
    1273             :                     {
    1274         286 :                         EncryptBlock(pabyWB, nLastSectorOffset);
    1275         286 :                         poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1);
    1276             :                     }
    1277             :                 }
    1278             :             }
    1279       34509 :             poBaseHandle->Seek(poHeader->nHeaderSize + nSectorOffset, SEEK_SET);
    1280       63497 :             if (poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 0 ||
    1281       28988 :                 !DecryptBlock(pabyWB, nSectorOffset))
    1282             :             {
    1283        5521 :                 memset(pabyWB, 0, poHeader->nSectorSize);
    1284             :             }
    1285       28988 :             else if (nSectorOffset + poHeader->nSectorSize >
    1286       28988 :                      poHeader->nPayloadFileSize)
    1287             :             {
    1288             :                 // If the last sector was padded with random values,
    1289             :                 // decrypt it to 0 in case of update scenarios.
    1290         735 :                 CPLAssert(nSectorOffset < poHeader->nPayloadFileSize);
    1291         735 :                 memset(pabyWB + poHeader->nPayloadFileSize - nSectorOffset, 0,
    1292         735 :                        nSectorOffset + poHeader->nSectorSize -
    1293         735 :                            poHeader->nPayloadFileSize);
    1294             :             }
    1295       34509 :             nWBOffset = nSectorOffset;
    1296       34509 :             nWBSize = poHeader->nSectorSize;
    1297             :         }
    1298             :     }
    1299             : 
    1300       20039 :     int nRet = static_cast<int>((nSize * nMemb - nToWrite) / nSize);
    1301             : #ifdef VERBOSE_VSICRYPT
    1302             :     CPLDebug("VSICRYPT", "Write ret = %d (nMemb = %d)", nRet,
    1303             :              static_cast<int>(nMemb));
    1304             : #endif
    1305       20039 :     return nRet;
    1306             : }
    1307             : 
    1308             : /************************************************************************/
    1309             : /*                             Truncate()                               */
    1310             : /************************************************************************/
    1311             : 
    1312             : // Returns 0 on success.  Returns -1 on error.
    1313           3 : int VSICryptFileHandle::Truncate(vsi_l_offset nNewSize)
    1314             : {
    1315             : #ifdef VERBOSE_VSICRYPT
    1316             :     CPLDebug("VSICRYPT", "Truncate(" CPL_FRMT_GUIB ")", nNewSize);
    1317             : #endif
    1318           3 :     if ((nPerms & VSICRYPT_WRITE) == 0)
    1319           1 :         return -1;
    1320             : 
    1321           2 :     if (!FlushDirty())
    1322           0 :         return -1;
    1323           6 :     if (poBaseHandle->Truncate(
    1324           2 :             poHeader->nHeaderSize +
    1325           4 :             cpl::div_round_up(nNewSize, poHeader->nSectorSize) *
    1326           2 :                 poHeader->nSectorSize) != 0)
    1327           0 :         return -1;
    1328           2 :     bUpdateHeader = true;
    1329           2 :     poHeader->nPayloadFileSize = nNewSize;
    1330           2 :     return 0;
    1331             : }
    1332             : 
    1333             : /************************************************************************/
    1334             : /*                                Eof()                                 */
    1335             : /************************************************************************/
    1336             : 
    1337           4 : int VSICryptFileHandle::Eof()
    1338             : {
    1339             : #ifdef VERBOSE_VSICRYPT
    1340             :     CPLDebug("VSICRYPT", "Eof() = %d", static_cast<int>(bEOF));
    1341             : #endif
    1342           4 :     return bEOF;
    1343             : }
    1344             : 
    1345             : /************************************************************************/
    1346             : /*                              Error()                                 */
    1347             : /************************************************************************/
    1348             : 
    1349           4 : int VSICryptFileHandle::Error()
    1350             : {
    1351             : #ifdef VERBOSE_VSICRYPT
    1352             :     CPLDebug("VSICRYPT", "Error() = %d", static_cast<int>(bError));
    1353             : #endif
    1354           4 :     return bError;
    1355             : }
    1356             : 
    1357             : /************************************************************************/
    1358             : /*                             ClearErr()                               */
    1359             : /************************************************************************/
    1360             : 
    1361           1 : void VSICryptFileHandle::ClearErr()
    1362             : {
    1363             : #ifdef VERBOSE_VSICRYPT
    1364             :     CPLDebug("VSICRYPT", "ClearErr()");
    1365             : #endif
    1366           1 :     bEOF = false;
    1367           1 :     bError = false;
    1368           1 :     poBaseHandle->ClearErr();
    1369           1 : }
    1370             : 
    1371             : /************************************************************************/
    1372             : /*                                 Flush()                              */
    1373             : /************************************************************************/
    1374             : 
    1375        1175 : int VSICryptFileHandle::Flush()
    1376             : {
    1377             : #ifdef VERBOSE_VSICRYPT
    1378             :     CPLDebug("VSICRYPT", "Flush()");
    1379             : #endif
    1380        1175 :     if (!FlushDirty())
    1381             :     {
    1382           0 :         return -1;
    1383             :     }
    1384        1175 :     if ((nPerms & VSICRYPT_WRITE))
    1385             :     {
    1386        1045 :         if (bLastSectorWasModified &&
    1387        1038 :             (poHeader->nPayloadFileSize % poHeader->nSectorSize) != 0)
    1388             :         {
    1389        1006 :             const vsi_l_offset nLastSectorOffset =
    1390        1006 :                 (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
    1391        1006 :                 poHeader->nSectorSize;
    1392        3018 :             if (poBaseHandle->Seek(poHeader->nHeaderSize + nLastSectorOffset,
    1393        1006 :                                    0) == 0 &&
    1394        1984 :                 poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 1 &&
    1395         978 :                 DecryptBlock(pabyWB, nLastSectorOffset))
    1396             :             {
    1397             :                 // Fill with random
    1398             : #ifdef VERBOSE_VSICRYPT
    1399             :                 CPLDebug("VSICRYPT", "Filling %d trailing bytes with random",
    1400             :                          static_cast<int>(
    1401             :                              poHeader->nSectorSize -
    1402             :                              (poHeader->nPayloadFileSize - nLastSectorOffset)));
    1403             : #endif
    1404         978 :                 CryptoPP::OS_GenerateRandomBlock(
    1405             :                     false,  // Do not need cryptographic randomness.
    1406             :                     reinterpret_cast<cryptopp_byte *>(
    1407         978 :                         pabyWB + poHeader->nPayloadFileSize -
    1408             :                         nLastSectorOffset),
    1409             :                     static_cast<int>(
    1410         978 :                         poHeader->nSectorSize -
    1411         978 :                         (poHeader->nPayloadFileSize - nLastSectorOffset)));
    1412             : 
    1413        1956 :                 if (poBaseHandle->Seek(
    1414         978 :                         poHeader->nHeaderSize + nLastSectorOffset, 0) == 0)
    1415             :                 {
    1416         978 :                     EncryptBlock(pabyWB, nLastSectorOffset);
    1417         978 :                     poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1);
    1418             :                 }
    1419             :             }
    1420             :         }
    1421        1045 :         bLastSectorWasModified = false;
    1422        1045 :         if (poBaseHandle->Flush() != 0)
    1423           0 :             return -1;
    1424             :     }
    1425        1175 :     if (bUpdateHeader)
    1426             :     {
    1427             : #ifdef VERBOSE_VSICRYPT
    1428             :         CPLDebug("VSICRYPT", "nPayloadFileSize = " CPL_FRMT_GUIB,
    1429             :                  poHeader->nPayloadFileSize);
    1430             : #endif
    1431        1038 :         if (!poHeader->WriteToFile(poBaseHandle, poEncCipher))
    1432           0 :             return -1;
    1433             :     }
    1434             : 
    1435        1175 :     return 0;
    1436             : }
    1437             : 
    1438             : /************************************************************************/
    1439             : /*                                  Close()                             */
    1440             : /************************************************************************/
    1441             : 
    1442        2341 : int VSICryptFileHandle::Close()
    1443             : {
    1444        2341 :     int nRet = 0;
    1445        2341 :     if (poBaseHandle != nullptr && poHeader != nullptr)
    1446             :     {
    1447        1174 :         if (Flush() != 0)
    1448           0 :             return -1;
    1449        1174 :         nRet = poBaseHandle->Close();
    1450        1174 :         delete poBaseHandle;
    1451        1174 :         poBaseHandle = nullptr;
    1452             :     }
    1453             : #ifdef VERBOSE_VSICRYPT
    1454             :     CPLDebug("VSICRYPT", "Close(%s)", osBaseFilename.c_str());
    1455             : #endif
    1456        2341 :     return nRet;
    1457             : }
    1458             : 
    1459             : /************************************************************************/
    1460             : /*                   VSICryptFilesystemHandler                          */
    1461             : /************************************************************************/
    1462             : 
    1463             : class VSICryptFilesystemHandler final : public VSIFilesystemHandler
    1464             : {
    1465             :   public:
    1466             :     VSICryptFilesystemHandler();
    1467             :     ~VSICryptFilesystemHandler() override;
    1468             : 
    1469             :     VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
    1470             :                            bool bSetError,
    1471             :                            CSLConstList /* papszOptions */) override;
    1472             :     int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
    1473             :              int nFlags) override;
    1474             :     int Unlink(const char *pszFilename) override;
    1475             :     int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
    1476             :                void *) override;
    1477             :     char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
    1478             : };
    1479             : 
    1480             : /************************************************************************/
    1481             : /*                   VSICryptFilesystemHandler()                        */
    1482             : /************************************************************************/
    1483             : 
    1484        1642 : VSICryptFilesystemHandler::VSICryptFilesystemHandler()
    1485             : {
    1486        1642 : }
    1487             : 
    1488             : /************************************************************************/
    1489             : /*                    ~VSICryptFilesystemHandler()                      */
    1490             : /************************************************************************/
    1491             : 
    1492        2226 : VSICryptFilesystemHandler::~VSICryptFilesystemHandler()
    1493             : {
    1494        2226 : }
    1495             : 
    1496             : /************************************************************************/
    1497             : /*                             GetFilename()                            */
    1498             : /************************************************************************/
    1499             : 
    1500        2298 : static CPLString GetFilename(const char *pszFilename)
    1501             : {
    1502        2298 :     if (strcmp(pszFilename, VSICRYPT_PREFIX_WITHOUT_SLASH) == 0)
    1503           0 :         pszFilename = VSICRYPT_PREFIX;
    1504             : 
    1505        2298 :     CPLAssert(strncmp(pszFilename, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) ==
    1506             :               0);
    1507        2298 :     pszFilename += strlen(VSICRYPT_PREFIX);
    1508        2298 :     const char *pszFileArg = strstr(pszFilename, "file=");
    1509        2298 :     if (pszFileArg == nullptr)
    1510          13 :         return pszFilename;
    1511        4570 :     CPLString osRet(pszFileArg + strlen("file="));
    1512        2285 :     return osRet;
    1513             : }
    1514             : 
    1515             : /************************************************************************/
    1516             : /*                             GetArgument()                            */
    1517             : /************************************************************************/
    1518             : 
    1519        7562 : static CPLString GetArgument(const char *pszFilename, const char *pszParamName,
    1520             :                              const char *pszDefault = "")
    1521             : {
    1522       15124 :     CPLString osParamName(pszParamName);
    1523        7562 :     osParamName += "=";
    1524             : 
    1525        7562 :     const char *pszNeedle = strstr(pszFilename, osParamName);
    1526        7562 :     if (pszNeedle == nullptr)
    1527        5254 :         return pszDefault;
    1528             : 
    1529        4616 :     CPLString osRet(pszNeedle + osParamName.size());
    1530        2308 :     size_t nCommaPos = osRet.find(",");
    1531        2308 :     if (nCommaPos != std::string::npos)
    1532        2308 :         osRet.resize(nCommaPos);
    1533        2308 :     return osRet;
    1534             : }
    1535             : 
    1536             : /************************************************************************/
    1537             : /*                               GetKey()                               */
    1538             : /************************************************************************/
    1539             : 
    1540        1286 : static CPLString GetKey(const char *pszFilename)
    1541             : {
    1542        1286 :     CPLString osKey = GetArgument(pszFilename, "key");
    1543             :     // TODO(schwehr): Make 10U and 1024U into symbolic constants.
    1544        1286 :     if (osKey.empty())
    1545             :     {
    1546          11 :         const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY", "");
    1547             :         // Do some form of validation to please Coverity
    1548          11 :         CPLAssert(strlen(pszKey) < 10U * 1024U);
    1549             :         // coverity [tainted_data_transitive]
    1550          11 :         osKey = pszKey;
    1551             :     }
    1552        1286 :     if (osKey.empty() || EQUAL(osKey, "GENERATE_IT"))
    1553             :     {
    1554          12 :         CPLString osKeyB64(GetArgument(pszFilename, "key_b64"));
    1555          12 :         if (osKeyB64.empty())
    1556             :         {
    1557          11 :             const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY_B64", "");
    1558             :             // Do some form of validation to please Coverity
    1559          11 :             CPLAssert(strlen(pszKey) < 10U * 1024U);
    1560             :             // coverity [tainted_data_transitive]
    1561          11 :             osKeyB64 = pszKey;
    1562             :         }
    1563          12 :         if (!osKeyB64.empty())
    1564             :         {
    1565           2 :             GByte *key = reinterpret_cast<GByte *>(CPLStrdup(osKeyB64));
    1566           2 :             int nLength = CPLBase64DecodeInPlace(key);
    1567           2 :             osKey.assign(reinterpret_cast<const char *>(key), nLength);
    1568           2 :             memset(key, 0, osKeyB64.size());
    1569           2 :             CPLFree(key);
    1570             :         }
    1571             :         // coverity[tainted_data]
    1572          12 :         memset(osKeyB64.data(), 0, osKeyB64.size());
    1573             :     }
    1574        1286 :     return osKey;
    1575             : }
    1576             : 
    1577             : /************************************************************************/
    1578             : /*                                Open()                                */
    1579             : /************************************************************************/
    1580             : 
    1581             : VSIVirtualHandle *
    1582        1281 : VSICryptFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
    1583             :                                 bool /* bSetError */,
    1584             :                                 CSLConstList /* papszOptions */)
    1585             : {
    1586             : #ifdef VERBOSE_VSICRYPT
    1587             :     CPLDebug("VSICRYPT", "Open(%s, %s)", pszFilename, pszAccess);
    1588             : #endif
    1589        2562 :     CPLString osFilename(GetFilename(pszFilename));
    1590             : 
    1591        2562 :     CPLString osKey(GetKey(pszFilename));
    1592        1281 :     if (osKey.empty() && pabyGlobalKey == nullptr)
    1593             :     {
    1594           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1595             :                  "Encryption key not defined as key/key_b64 parameter, "
    1596             :                  "VSICRYPT_KEY/VSICRYPT_KEY_B64 configuration option or "
    1597             :                  "VSISetCryptKey() API");
    1598           2 :         return nullptr;
    1599             :     }
    1600             : 
    1601        2558 :     CPLString osAccess(pszAccess);
    1602        1279 :     if (strchr(pszAccess, 'b') == nullptr)
    1603          15 :         osAccess += "b";
    1604        1279 :     if (strchr(pszAccess, 'r'))
    1605             :     {
    1606         228 :         VSIVirtualHandle *fpBase = reinterpret_cast<VSIVirtualHandle *>(
    1607             :             VSIFOpenL(osFilename, osAccess));
    1608         228 :         if (fpBase == nullptr)
    1609           1 :             return nullptr;
    1610         227 :         VSICryptFileHeader *poHeader = new VSICryptFileHeader();
    1611         227 :         if (!poHeader->ReadFromFile(fpBase, osKey))
    1612             :         {
    1613          94 :             memset(osKey.data(), 0, osKey.size());
    1614          94 :             fpBase->Close();
    1615          94 :             delete fpBase;
    1616          94 :             delete poHeader;
    1617          94 :             return nullptr;
    1618             :         }
    1619             : 
    1620             :         VSICryptFileHandle *poHandle = new VSICryptFileHandle(
    1621             :             osFilename, fpBase, poHeader,
    1622         133 :             strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
    1623         133 :                                    : VSICRYPT_READ);
    1624         133 :         if (!poHandle->Init(osKey, false))
    1625             :         {
    1626           4 :             memset(osKey.data(), 0, osKey.size());
    1627           4 :             delete poHandle;
    1628           4 :             poHandle = nullptr;
    1629             :         }
    1630         133 :         memset(osKey.data(), 0, osKey.size());
    1631         133 :         return poHandle;
    1632             :     }
    1633        1051 :     else if (strchr(pszAccess, 'w'))
    1634             :     {
    1635             :         CPLString osAlg(GetArgument(pszFilename, "alg",
    1636        2088 :                                     CPLGetConfigOption("VSICRYPT_ALG", "AES")));
    1637        1044 :         VSICryptAlg eAlg = GetAlg(osAlg);
    1638             : 
    1639        1044 :         VSICryptMode eMode = GetMode(GetArgument(
    1640             :             pszFilename, "mode", CPLGetConfigOption("VSICRYPT_MODE", "CBC")));
    1641             : 
    1642             :         CPLString osFreeText =
    1643             :             GetArgument(pszFilename, "freetext",
    1644        2088 :                         CPLGetConfigOption("VSICRYPT_FREETEXT", ""));
    1645             : 
    1646             :         CPLString osIV = GetArgument(pszFilename, "iv",
    1647        2088 :                                      CPLGetConfigOption("VSICRYPT_IV", ""));
    1648             : 
    1649        1044 :         int nSectorSize = atoi(
    1650        2088 :             GetArgument(pszFilename, "sector_size",
    1651             :                         CPLGetConfigOption("VSICRYPT_SECTOR_SIZE", "512")));
    1652        1044 :         if (nSectorSize <= 0 || nSectorSize >= 65535)
    1653             :         {
    1654           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1655             :                      "Invalid value for sector_size. Defaulting to 512.");
    1656           0 :             nSectorSize = 512;
    1657             :         }
    1658             : 
    1659        1044 :         const bool bAddKeyCheck = CPLTestBool(
    1660        2088 :             GetArgument(pszFilename, "add_key_check",
    1661             :                         CPLGetConfigOption("VSICRYPT_ADD_KEY_CHECK", "NO")));
    1662             : 
    1663             :         /* Generate random initial vector */
    1664        1044 :         CryptoPP::BlockCipher *poBlock = GetEncBlockCipher(eAlg);
    1665        1044 :         if (poBlock == nullptr)
    1666             :         {
    1667           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1668             :                      "Cipher algorithm not supported in this build: %s",
    1669             :                      osAlg.c_str());
    1670           0 :             memset(osKey.data(), 0, osKey.size());
    1671           0 :             return nullptr;
    1672             :         }
    1673        1044 :         int nMinKeySize = static_cast<int>(poBlock->MinKeyLength());
    1674        1044 :         int nMaxKeySize = static_cast<int>(poBlock->MaxKeyLength());
    1675        1044 :         int nBlockSize = static_cast<int>(poBlock->BlockSize());
    1676        1044 :         delete poBlock;
    1677             : 
    1678        1044 :         if (!osIV.empty())
    1679             :         {
    1680           1 :             if (static_cast<int>(osIV.size()) != nBlockSize)
    1681             :             {
    1682           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1683             :                          "IV should be %d byte large", nBlockSize);
    1684           1 :                 memset(osKey.data(), 0, osKey.size());
    1685           1 :                 return nullptr;
    1686             :             }
    1687             :         }
    1688             :         else
    1689             :         {
    1690        1043 :             osIV.resize(nBlockSize);
    1691        2086 :             CryptoPP::OS_GenerateRandomBlock(
    1692             :                 false,  // Do not need cryptographic randomness.
    1693        1043 :                 reinterpret_cast<cryptopp_byte *>(osIV.data()), osIV.size());
    1694             :         }
    1695             : 
    1696        1043 :         if (EQUAL(osKey, "GENERATE_IT"))
    1697             :         {
    1698           1 :             osKey.resize(nMaxKeySize);
    1699           1 :             CPLDebug("VSICRYPT",
    1700             :                      "Generating key. This might take some time...");
    1701           1 :             CryptoPP::OS_GenerateRandomBlock(
    1702             :                 // Need cryptographic randomness.
    1703             :                 // Config option for speeding tests.
    1704           1 :                 CPLTestBool(
    1705             :                     CPLGetConfigOption("VSICRYPT_CRYPTO_RANDOM", "TRUE")),
    1706           1 :                 reinterpret_cast<cryptopp_byte *>(osKey.data()), osKey.size());
    1707             : 
    1708             :             char *pszB64 =
    1709           1 :                 CPLBase64Encode(static_cast<int>(osKey.size()),
    1710           1 :                                 reinterpret_cast<const GByte *>(osKey.c_str()));
    1711           1 :             if (CPLTestBool(CPLGetConfigOption("VSICRYPT_DISPLAY_GENERATED_KEY",
    1712             :                                                "TRUE")))
    1713             :             {
    1714           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1715             :                          "BASE64 key '%s' has been generated, and installed in "
    1716             :                          "the VSICRYPT_KEY_B64 configuration option.",
    1717             :                          pszB64);
    1718             :             }
    1719           1 :             CPLSetConfigOption("VSICRYPT_KEY_B64", pszB64);
    1720           1 :             CPLFree(pszB64);
    1721             :         }
    1722             : 
    1723             :         const int nKeyLength =
    1724        1043 :             !osKey.empty() ? static_cast<int>(osKey.size()) : nGlobalKeySize;
    1725        1043 :         if (nKeyLength < nMinKeySize)
    1726             :         {
    1727           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1728             :                      "Key is too short: %d bytes. Should be at least %d bytes",
    1729             :                      nKeyLength, nMinKeySize);
    1730           2 :             memset(osKey.data(), 0, osKey.size());
    1731           2 :             return nullptr;
    1732             :         }
    1733             : 
    1734        1041 :         VSIVirtualHandle *fpBase = reinterpret_cast<VSIVirtualHandle *>(
    1735             :             VSIFOpenL(osFilename, osAccess.c_str()));
    1736        1041 :         if (fpBase == nullptr)
    1737             :         {
    1738           2 :             memset(osKey.data(), 0, osKey.size());
    1739           2 :             return nullptr;
    1740             :         }
    1741             : 
    1742        1039 :         VSICryptFileHeader *poHeader = new VSICryptFileHeader();
    1743        1039 :         poHeader->osIV = osIV;
    1744        1039 :         CPL_IGNORE_RET_VAL(osIV);
    1745        1039 :         poHeader->eAlg = eAlg;
    1746        1039 :         poHeader->eMode = eMode;
    1747        1039 :         poHeader->nSectorSize = static_cast<GUInt16>(nSectorSize);
    1748        1039 :         poHeader->osFreeText = std::move(osFreeText);
    1749        1039 :         poHeader->bAddKeyCheck = bAddKeyCheck;
    1750             : 
    1751             :         VSICryptFileHandle *poHandle = new VSICryptFileHandle(
    1752             :             osFilename, fpBase, poHeader,
    1753        1039 :             strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
    1754        1039 :                                    : VSICRYPT_WRITE);
    1755        1039 :         if (!poHandle->Init(osKey, true))
    1756             :         {
    1757           2 :             memset(osKey.data(), 0, osKey.size());
    1758           2 :             delete poHandle;
    1759           2 :             poHandle = nullptr;
    1760             :         }
    1761        1039 :         memset(osKey.data(), 0, osKey.size());
    1762        1039 :         return poHandle;
    1763             :     }
    1764           7 :     else if (strchr(pszAccess, 'a'))
    1765             :     {
    1766             :         VSIVirtualHandle *fpBase =
    1767           6 :             reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osFilename, "rb+"));
    1768           6 :         if (fpBase == nullptr)
    1769             :         {
    1770           2 :             memset(osKey.data(), 0, osKey.size());
    1771           2 :             return VSIFilesystemHandler::Open(pszFilename, "wb+");
    1772             :         }
    1773           4 :         VSICryptFileHeader *poHeader = new VSICryptFileHeader();
    1774           4 :         if (!poHeader->ReadFromFile(fpBase, osKey))
    1775             :         {
    1776           2 :             memset(osKey.data(), 0, osKey.size());
    1777           2 :             fpBase->Close();
    1778           2 :             delete fpBase;
    1779           2 :             delete poHeader;
    1780           2 :             return nullptr;
    1781             :         }
    1782             : 
    1783             :         VSICryptFileHandle *poHandle = new VSICryptFileHandle(
    1784           2 :             osFilename, fpBase, poHeader, VSICRYPT_READ | VSICRYPT_WRITE);
    1785           2 :         if (!poHandle->Init(osKey))
    1786             :         {
    1787           1 :             delete poHandle;
    1788           1 :             poHandle = nullptr;
    1789             :         }
    1790           2 :         memset(osKey.data(), 0, osKey.size());
    1791           2 :         if (poHandle != nullptr)
    1792           1 :             poHandle->Seek(0, SEEK_END);
    1793           2 :         return poHandle;
    1794             :     }
    1795             : 
    1796           1 :     return nullptr;
    1797             : }
    1798             : 
    1799             : /************************************************************************/
    1800             : /*                                Stat()                                */
    1801             : /************************************************************************/
    1802             : 
    1803           6 : int VSICryptFilesystemHandler::Stat(const char *pszFilename,
    1804             :                                     VSIStatBufL *pStatBuf, int nFlags)
    1805             : {
    1806             : #ifdef VERBOSE_VSICRYPT
    1807             :     CPLDebug("VSICRYPT", "Stat(%s)", pszFilename);
    1808             : #endif
    1809          12 :     CPLString osFilename(GetFilename(pszFilename));
    1810           6 :     if (VSIStatExL(osFilename, pStatBuf, nFlags) != 0)
    1811           1 :         return -1;
    1812             :     VSIVirtualHandle *fp =
    1813           5 :         reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osFilename, "rb"));
    1814           5 :     if (fp == nullptr)
    1815           0 :         return -1;
    1816           5 :     VSICryptFileHeader *poHeader = new VSICryptFileHeader();
    1817          10 :     CPLString osKey(GetKey(pszFilename));
    1818           5 :     if (!poHeader->ReadFromFile(fp, osKey))
    1819             :     {
    1820           1 :         memset(osKey.data(), 0, osKey.size());
    1821           1 :         fp->Close();
    1822           1 :         delete fp;
    1823           1 :         delete poHeader;
    1824           1 :         return -1;
    1825             :     }
    1826           4 :     memset(osKey.data(), 0, osKey.size());
    1827           4 :     fp->Close();
    1828           4 :     delete fp;
    1829           4 :     if (poHeader)
    1830             :     {
    1831           4 :         pStatBuf->st_size = poHeader->nPayloadFileSize;
    1832           4 :         delete poHeader;
    1833           4 :         return 0;
    1834             :     }
    1835             :     else
    1836           0 :         return -1;
    1837             : }
    1838             : 
    1839             : /************************************************************************/
    1840             : /*                               Unlink()                               */
    1841             : /************************************************************************/
    1842             : 
    1843        1006 : int VSICryptFilesystemHandler::Unlink(const char *pszFilename)
    1844             : {
    1845        1006 :     return VSIUnlink(GetFilename(pszFilename));
    1846             : }
    1847             : 
    1848             : /************************************************************************/
    1849             : /*                               Rename()                               */
    1850             : /************************************************************************/
    1851             : 
    1852           2 : int VSICryptFilesystemHandler::Rename(const char *oldpath, const char *newpath,
    1853             :                                       GDALProgressFunc, void *)
    1854             : {
    1855           2 :     CPLString osNewPath;
    1856           2 :     if (strncmp(newpath, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) == 0)
    1857           1 :         osNewPath = GetFilename(newpath);
    1858             :     else
    1859           1 :         osNewPath = newpath;
    1860             : 
    1861           4 :     return VSIRename(GetFilename(oldpath), osNewPath);
    1862             : }
    1863             : 
    1864             : /************************************************************************/
    1865             : /*                               ReadDirEx()                            */
    1866             : /************************************************************************/
    1867             : 
    1868           2 : char **VSICryptFilesystemHandler::ReadDirEx(const char *pszDirname,
    1869             :                                             int nMaxFiles)
    1870             : {
    1871             : #ifdef VERBOSE_VSICRYPT
    1872             :     CPLDebug("VSICRYPT", "ReadDir(%s)", pszDirname);
    1873             : #endif
    1874           2 :     return VSIReadDirEx(GetFilename(pszDirname), nMaxFiles);
    1875             : }
    1876             : 
    1877             : #ifdef VSICRYPT_DRIVER
    1878             : 
    1879             : #include "gdal_priv.h"
    1880             : 
    1881             : /**
    1882             :  * \brief Evaluate if this is a crypt file.
    1883             :  *
    1884             :  * The function signature must match GDALDataset::Identify.
    1885             :  *
    1886             :  * @param poOpenInfo The header bytes used for file identification.
    1887             :  *
    1888             :  * @return 1 if this is a crypt file or 0 otherwise.
    1889             :  */
    1890             : 
    1891             : static int VSICryptIdentify(GDALOpenInfo *poOpenInfo)
    1892             : {
    1893             :     return poOpenInfo->nHeaderBytes > 8 &&
    1894             :            memcmp(poOpenInfo->pabyHeader, VSICRYPT_SIGNATURE, 8) == 0;
    1895             : }
    1896             : 
    1897             : static GDALDataset *VSICryptOpen(GDALOpenInfo *poOpenInfo)
    1898             : {
    1899             :     if (!VSICryptIdentify(poOpenInfo))
    1900             :         return nullptr;
    1901             :     return GDALOpen(
    1902             :         (CPLString(VSICRYPT_PREFIX) + poOpenInfo->pszFilename).c_str(),
    1903             :         poOpenInfo->eAccess);
    1904             : }
    1905             : 
    1906             : #endif
    1907             : 
    1908             : //! @endcond
    1909             : 
    1910             : /************************************************************************/
    1911             : /*                   VSIInstallCryptFileHandler()                       */
    1912             : /************************************************************************/
    1913             : 
    1914             : /**
    1915             :  * \brief Install /vsicrypt/ encrypted file system handler
    1916             :  * (requires <a href="http://www.cryptopp.com/">libcrypto++</a>)
    1917             :  *
    1918             :  * A special file handler is installed that allows reading/creating/update
    1919             :  * encrypted files on the fly, with random access capabilities.
    1920             :  *
    1921             :  * The cryptographic algorithms used are
    1922             :  * <a href="https://en.wikipedia.org/wiki/Block_cipher">block ciphers</a>,
    1923             :  * with symmetric key.
    1924             :  *
    1925             :  * In their simplest form, recognized filenames are of the form
    1926             :  * /vsicrypt//absolute_path/to/file, /vsicrypt/c:/absolute_path/to/file or
    1927             :  * /vsicrypt/relative/path/to/file.
    1928             :  *
    1929             :  * Options can also be used with the following format :
    1930             :  * /vsicrypt/option1=val1,option2=val2,...,file=/path/to/file
    1931             :  *
    1932             :  * They can also be passed as configuration option/environment variable, because
    1933             :  * in some use cases, the syntax with option in the filename might not properly
    1934             :  * work with some drivers.
    1935             :  *
    1936             :  * In all modes, the encryption key must be provided. There are several ways
    1937             :  * of doing so :
    1938             :  * <ul>
    1939             :  * <li>By adding a key= parameter to the filename, like
    1940             :  *     /vsicrypt/key=my_secret_key,file=/path/to/file.  Note that this restricts
    1941             :  *     the key to be in text format, whereas at its full power, it can be binary
    1942             :  *     content.</li>
    1943             :  * <li>By adding a key_b64= parameter to the filename, to specify a binary key
    1944             :  *     expressed in Base64 encoding, like
    1945             :  *     /vsicrypt/key_b64=th1sl00kslikebase64=,file=/path/to/file.</li>
    1946             :  * <li>By setting the VSICRYPT_KEY configuration option. The key should be in
    1947             :  * text format.</li>
    1948             :  * <li>By setting the VSICRYPT_KEY_B64 configuration option. The key should be
    1949             :  * encoded in Base64.</li>
    1950             :  * <li>By using the VSISetCryptKey() C function.</li>
    1951             :  * </ul>
    1952             :  *
    1953             :  * When creating a file, if key=GENERATE_IT or VSICRYPT_KEY=GENERATE_IT is
    1954             :  * passed, the encryption key will be generated from the pseudo-random number
    1955             :  * generator of the operating system. The key will be displayed on the standard
    1956             :  * error stream in a Base64 form (unless the VSICRYPT_DISPLAY_GENERATED_KEY
    1957             :  * configuration option is set to OFF), and the VSICRYPT_KEY_B64 configuration
    1958             :  * option will also be set with the Base64 form of the key (so that
    1959             :  * CPLGetConfigOption("VSICRYPT_KEY_B64", NULL) can be used to get it back).
    1960             :  *
    1961             :  * The available options are :
    1962             :  * <ul>
    1963             : 
    1964             :  * <li>alg=AES/Blowfish/Camellia/CAST256/DES_EDE2/DES_EDE3/MARS/IDEA/RC5/RC6/Serpent/SHACAL2/SKIPJACK/Twofish/XTEA:
    1965             :  *     to specify the <a href="https://en.wikipedia.org/wiki/Block_cipher">block
    1966             :  *     cipher</a> algorithm.  The default is AES.  Only used on
    1967             :  *     creation. Ignored otherwise.  Note: depending on how GDAL is build, if
    1968             :  *     linked against the DLL version of libcrypto++, only a subset of those
    1969             :  *     algorithms will be available, namely AES, DES_EDE2, DES_EDE3 and
    1970             :  *     SKIPJACK.  Also available as VSICRYPT_ALG configuration option.</li>
    1971             :  * <li>mode=CBC/CFB/OFB/CTR/CBC_CTS: to specify the
    1972             :  *     <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">
    1973             :  *       block cipher mode of operation</a>.
    1974             :  *     The default is CBC.
    1975             :  *     Only used on creation. Ignored otherwise.
    1976             :  *     Also available as VSICRYPT_MODE configuration option.</li>
    1977             :  * <li>key=text_key: see above.</li>
    1978             :  * <li>key_b64=base64_encoded_key: see above.</li>
    1979             :  * <li>freetext=some_text: to specify a text content that will be written
    1980             :  *     *unencrypted* in the file header, for informational purposes. Default to
    1981             :  *     empty.  Only used on creation. Ignored otherwise.
    1982             :  *     Also available as VSICRYPT_FREETEXT configuration option.</li>
    1983             :  * <li>sector_size=int_value: to specify the size of the "sector", which is the
    1984             :  *     unit chunk of information that is encrypted/decrypted. Default to 512
    1985             :  *     bytes.  The valid values depend on the algorithm and block cipher mode of
    1986             :  *     operation.  Only used on creation. Ignored otherwise.  Also available as
    1987             :  *     VSICRYPT_SECTOR_SIZE configuration option.</li>
    1988             :  * <li>iv=initial_vector_as_text: to specify the Initial Vector. This is an
    1989             :  *     advanced option that should generally *NOT* be used. It is only useful to
    1990             :  *     get completely deterministic output given the plaintext, key and other
    1991             :  *     parameters, which in general *NOT* what you want to do. By default, a
    1992             :  *     random initial vector of the appropriate size will be generated for each
    1993             :  *     new file created.  Only used on creation. Ignored otherwise.  Also
    1994             :  *     available as VSICRYPT_IV configuration option.</li>
    1995             : 
    1996             :  * <li>add_key_check=YES/NO: whether a special value should be encrypted in the
    1997             :  *     header, so as to be quickly able to determine if the decryption key is
    1998             :  *     correct.  Defaults to NO.  Only used on creation. Ignored otherwise.
    1999             :  *     Also available as VSICRYPT_ADD_KEY_CHECK configuration option.</li>
    2000             :  * <li>file=filename. To specify the filename. This must be the last option put
    2001             :  *     in the option list (so as to make it possible to use filenames with comma
    2002             :  *     in them. )
    2003             :  * </ul>
    2004             :  *
    2005             :  * This special file handler can be combined with other virtual filesystems
    2006             :  * handlers, such as /vsizip. For example,
    2007             :  * /vsicrypt//vsicurl/path/to/remote/encrypted/file.tif
    2008             :  *
    2009             :  * Implementation details:
    2010             :  *
    2011             :  * The structure of encrypted files is the following: a header, immediately
    2012             :  * followed by the encrypted payload (by sectors, i.e. chunks of sector_size
    2013             :  * bytes).
    2014             :  *
    2015             :  * The header structure is the following :
    2016             :  * <ol>
    2017             :  * <li>8 bytes. Signature. Fixed value: VSICRYPT.</li>
    2018             :  * <li>UINT16_LE. Header size (including previous signature bytes).</li>
    2019             :  * <li>UINT8. Format major version. Current value: 1.</li>
    2020             :  * <li>UINT8. Format minor version. Current value: 0.</li>
    2021             :  * <li>UINT16. Sector size.</li>
    2022             :  * <li>UINT8. Cipher algorithm. Valid values are: 0 = AES (Rijndael), 1 =
    2023             :  *     Blowfish, 2 = Camellia, 3 = CAST256, 4 = DES_EDE2, 5 = DES_EDE3, 6 =
    2024             :  *     MARS, 7 = IDEA, 8 = RC5, 9 = RC6, 10 = Serpent, 11 = SHACAL2, 12 =
    2025             :  *     SKIPJACK, 13 = Twofish, 14 = XTEA.</li>
    2026             :  * <li>UINT8. Block cipher mode of operation. Valid values are: 0 = CBC, 1 =
    2027             :  *     CFB, 2 = OFB, 3 = CTR, 4 = CBC_CTS.</li>
    2028             :  * <li>UINT8. Size in bytes of the Initial Vector.</li>
    2029             :  * <li>N bytes with the content of the Initial Vector, where N is the value of
    2030             :  *     the previous field.</li>
    2031             :  * <li>UINT16_LE. Size in bytes of the free text.</li>
    2032             :  * <li>N bytes with the content of the free text, where N is the value of the
    2033             :  *     previous field.</li>
    2034             :  * <li>UINT8. Size in bytes of encrypted content (key check), or 0 if key check
    2035             :  *     is absent.</li>
    2036             :  * <li>N bytes with encrypted content (key check), where N is the value of the
    2037             :  *     previous field.</li>
    2038             :  * <li>UINT64_LE. Size of the unencrypted file, in bytes.</li>
    2039             :  * <li>UINT16_LE. Size in bytes of extra content (of unspecified semantics). For
    2040             :  *     v1.0, fixed value of 0</li>
    2041             :  * <li>N bytes with extra content (of unspecified semantics), where N is the
    2042             :  *     value of the previous field.</li>
    2043             :  * </ol>
    2044             :  *
    2045             :  * This design does not provide any means of authentication or integrity check.
    2046             :  *
    2047             :  * Each sector is encrypted/decrypted independently of other sectors.  For that,
    2048             :  * the Initial Vector contained in the header is XOR'ed with the file offset
    2049             :  * (relative to plain text file) of the start of the sector being processed, as
    2050             :  * a 8-byte integer.  More precisely, the first byte of the main IV is XOR'ed
    2051             :  * with the 8 least-significant bits of the sector offset, the second byte of
    2052             :  * the main IV is XOR'ed with the following 8 bits of the sector offset,
    2053             :  * etc... until the 8th byte.
    2054             :  *
    2055             :  * This design could potentially be prone to chosen-plaintext attack, for
    2056             :  * example if the attacker managed to get (part of) an existing encrypted file
    2057             :  * to be encrypted from plaintext he might have selected.
    2058             :  *
    2059             :  * Note: if "hostile" code can explore process content, or attach to it with a
    2060             :  * debugger, it might be relatively easy to retrieve the encryption key.  A GDAL
    2061             :  * plugin could for example get the content of configuration options, or list
    2062             :  * opened datasets and see the key/key_b64 values, so disabling plugin loading
    2063             :  * might be a first step, as well as linking statically GDAL to application
    2064             :  * code.  If plugin loading is enabled or GDAL dynamically linked, using
    2065             :  * VSISetCryptKey() to set the key might make it a bit more complicated to spy
    2066             :  * the key.  But, as said initially, this is in no way a perfect protection.
    2067             :  *
    2068             :  * @since GDAL 2.1.0
    2069             :  */
    2070        1642 : void VSIInstallCryptFileHandler(void)
    2071             : 
    2072             : {
    2073        1642 :     VSIFileManager::InstallHandler(VSICRYPT_PREFIX,
    2074        1642 :                                    new VSICryptFilesystemHandler);
    2075             : 
    2076             : #ifdef VSICRYPT_DRIVER
    2077             :     if (GDALGetDriverByName("VSICRYPT") != nullptr)
    2078             :         return;
    2079             : 
    2080             :     GDALDriver *poDriver = new GDALDriver();
    2081             : 
    2082             :     poDriver->SetDescription("VSICRYPT");
    2083             : #ifdef GDAL_DCAP_RASTER
    2084             :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    2085             :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
    2086             : #endif
    2087             :     poDriver->SetMetadataItem(
    2088             :         GDAL_DMD_LONGNAME, CPLSPrintf("Wrapper for %s files", VSICRYPT_PREFIX));
    2089             : 
    2090             :     poDriver->pfnOpen = VSICryptOpen;
    2091             :     poDriver->pfnIdentify = VSICryptIdentify;
    2092             : 
    2093             :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2094             : #endif
    2095        1642 : }
    2096             : 
    2097             : #else /* HAVE_CRYPTOPP */
    2098             : 
    2099             : class VSIDummyCryptFilesystemHandler : public VSIFilesystemHandler
    2100             : {
    2101             :   public:
    2102             :     VSIDummyCryptFilesystemHandler() = default;
    2103             : 
    2104             :     VSIVirtualHandle *Open(const char * /* pszFilename */,
    2105             :                            const char * /* pszAccess */, bool /* bSetError */,
    2106             :                            CSLConstList /* papszOptions */) override;
    2107             : 
    2108             :     int Stat(const char * /* pszFilename */, VSIStatBufL * /*pStatBuf */,
    2109             :              int /* nFlags */) override
    2110             :     {
    2111             :         CPLError(CE_Failure, CPLE_NotSupported,
    2112             :                  "%s support not available in this build", VSICRYPT_PREFIX);
    2113             :         return -1;
    2114             :     }
    2115             : };
    2116             : 
    2117             : VSIVirtualHandle *VSIDummyCryptFilesystemHandler::Open(
    2118             :     const char * /* pszFilename */, const char * /* pszAccess */,
    2119             :     bool /* bSetError */, CSLConstList /* papszOptions */)
    2120             : {
    2121             :     CPLError(CE_Failure, CPLE_NotSupported,
    2122             :              "%s support not available in this build", VSICRYPT_PREFIX);
    2123             :     return nullptr;
    2124             : }
    2125             : 
    2126             : void VSIInstallCryptFileHandler(void)
    2127             : {
    2128             :     VSIFileManager::InstallHandler(VSICRYPT_PREFIX,
    2129             :                                    new VSIDummyCryptFilesystemHandler);
    2130             : }
    2131             : 
    2132             : void VSISetCryptKey(const GByte * /* pabyKey */, int /* nKeySize */)
    2133             : {
    2134             :     // Not supported.
    2135             : }
    2136             : 
    2137             : #endif  // HAVE_CRYPTOPP
    2138             : 
    2139             : // Below is only useful if using as a plugin over GDAL >= 2.0.
    2140             : #ifdef VSICRYPT_AUTOLOAD
    2141             : 
    2142             : CPL_C_START
    2143             : void CPL_DLL GDALRegisterMe();
    2144             : CPL_C_END
    2145             : 
    2146             : void GDALRegisterMe()
    2147             : {
    2148             :     VSIFilesystemHandler *poExistingHandler =
    2149             :         VSIFileManager::GetHandler(VSICRYPT_PREFIX);
    2150             :     if (poExistingHandler == VSIFileManager::GetHandler("."))
    2151             :     {
    2152             :         // In the case where VSICRYPT_PREFIX is just handled by the regular
    2153             :         // handler, install the vsicrypt handler (shouldn't happen)
    2154             :         VSIInstallCryptFileHandler();
    2155             :     }
    2156             :     else
    2157             :     {
    2158             :         // If there's already an installed handler, then check if it is a
    2159             :         // dummy one (should normally be the case) or a real one
    2160             :         CPLErrorReset();
    2161             :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2162             :         VSIStatBufL sStat;
    2163             :         CPL_IGNORE_RET_VAL(VSIStatL(
    2164             :             (CPLString(VSICRYPT_PREFIX) + "i_do_not_exist").c_str(), &sStat));
    2165             :         CPLPopErrorHandler();
    2166             :         if (strstr(CPLGetLastErrorMsg(), "support not available in this build"))
    2167             :         {
    2168             :             // Dummy handler. Register the new one, and delete the old one
    2169             :             VSIInstallCryptFileHandler();
    2170             :             delete poExistingHandler;
    2171             :         }
    2172             :         else
    2173             :         {
    2174             :             CPLDebug("VSICRYPT", "GDAL has already a working %s implementation",
    2175             :                      VSICRYPT_PREFIX);
    2176             :         }
    2177             :         CPLErrorReset();
    2178             :     }
    2179             : }
    2180             : 
    2181             : #endif /* VSICRYPT_AUTOLOAD */

Generated by: LCOV version 1.14