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

Generated by: LCOV version 1.14