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 */
|