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