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, GDALProgressFunc,
1475 : void *) override;
1476 : char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
1477 : };
1478 :
1479 : /************************************************************************/
1480 : /* VSICryptFilesystemHandler() */
1481 : /************************************************************************/
1482 :
1483 1616 : VSICryptFilesystemHandler::VSICryptFilesystemHandler()
1484 : {
1485 1616 : }
1486 :
1487 : /************************************************************************/
1488 : /* ~VSICryptFilesystemHandler() */
1489 : /************************************************************************/
1490 :
1491 2222 : VSICryptFilesystemHandler::~VSICryptFilesystemHandler()
1492 : {
1493 2222 : }
1494 :
1495 : /************************************************************************/
1496 : /* GetFilename() */
1497 : /************************************************************************/
1498 :
1499 2296 : static CPLString GetFilename(const char *pszFilename)
1500 : {
1501 2296 : if (strcmp(pszFilename, VSICRYPT_PREFIX_WITHOUT_SLASH) == 0)
1502 0 : pszFilename = VSICRYPT_PREFIX;
1503 :
1504 2296 : CPLAssert(strncmp(pszFilename, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) ==
1505 : 0);
1506 2296 : pszFilename += strlen(VSICRYPT_PREFIX);
1507 2296 : const char *pszFileArg = strstr(pszFilename, "file=");
1508 2296 : if (pszFileArg == nullptr)
1509 13 : return pszFilename;
1510 4566 : CPLString osRet(pszFileArg + strlen("file="));
1511 2283 : return osRet;
1512 : }
1513 :
1514 : /************************************************************************/
1515 : /* GetArgument() */
1516 : /************************************************************************/
1517 :
1518 7561 : static CPLString GetArgument(const char *pszFilename, const char *pszParamName,
1519 : const char *pszDefault = "")
1520 : {
1521 15122 : CPLString osParamName(pszParamName);
1522 7561 : osParamName += "=";
1523 :
1524 7561 : const char *pszNeedle = strstr(pszFilename, osParamName);
1525 7561 : if (pszNeedle == nullptr)
1526 5254 : return pszDefault;
1527 :
1528 4614 : CPLString osRet(pszNeedle + osParamName.size());
1529 2307 : size_t nCommaPos = osRet.find(",");
1530 2307 : if (nCommaPos != std::string::npos)
1531 2307 : osRet.resize(nCommaPos);
1532 2307 : return osRet;
1533 : }
1534 :
1535 : /************************************************************************/
1536 : /* GetKey() */
1537 : /************************************************************************/
1538 :
1539 1285 : static CPLString GetKey(const char *pszFilename)
1540 : {
1541 1285 : CPLString osKey = GetArgument(pszFilename, "key");
1542 : // TODO(schwehr): Make 10U and 1024U into symbolic constants.
1543 1285 : if (osKey.empty())
1544 : {
1545 11 : const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY", "");
1546 : // Do some form of validation to please Coverity
1547 11 : CPLAssert(strlen(pszKey) < 10U * 1024U);
1548 : // coverity [tainted_data_transitive]
1549 11 : osKey = pszKey;
1550 : }
1551 1285 : if (osKey.empty() || EQUAL(osKey, "GENERATE_IT"))
1552 : {
1553 12 : CPLString osKeyB64(GetArgument(pszFilename, "key_b64"));
1554 12 : if (osKeyB64.empty())
1555 : {
1556 11 : const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY_B64", "");
1557 : // Do some form of validation to please Coverity
1558 11 : CPLAssert(strlen(pszKey) < 10U * 1024U);
1559 : // coverity [tainted_data_transitive]
1560 11 : osKeyB64 = pszKey;
1561 : }
1562 12 : if (!osKeyB64.empty())
1563 : {
1564 2 : GByte *key = reinterpret_cast<GByte *>(CPLStrdup(osKeyB64));
1565 2 : int nLength = CPLBase64DecodeInPlace(key);
1566 2 : osKey.assign(reinterpret_cast<const char *>(key), nLength);
1567 2 : memset(key, 0, osKeyB64.size());
1568 2 : CPLFree(key);
1569 : }
1570 : // coverity[tainted_data]
1571 12 : memset(osKeyB64.data(), 0, osKeyB64.size());
1572 : }
1573 1285 : return osKey;
1574 : }
1575 :
1576 : /************************************************************************/
1577 : /* Open() */
1578 : /************************************************************************/
1579 :
1580 : VSIVirtualHandle *
1581 1281 : VSICryptFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
1582 : bool /* bSetError */,
1583 : CSLConstList /* papszOptions */)
1584 : {
1585 : #ifdef VERBOSE_VSICRYPT
1586 : CPLDebug("VSICRYPT", "Open(%s, %s)", pszFilename, pszAccess);
1587 : #endif
1588 2562 : CPLString osFilename(GetFilename(pszFilename));
1589 :
1590 2562 : CPLString osKey(GetKey(pszFilename));
1591 1281 : if (osKey.empty() && pabyGlobalKey == nullptr)
1592 : {
1593 2 : CPLError(CE_Failure, CPLE_AppDefined,
1594 : "Encryption key not defined as key/key_b64 parameter, "
1595 : "VSICRYPT_KEY/VSICRYPT_KEY_B64 configuration option or "
1596 : "VSISetCryptKey() API");
1597 2 : return nullptr;
1598 : }
1599 :
1600 2558 : CPLString osAccess(pszAccess);
1601 1279 : if (strchr(pszAccess, 'b') == nullptr)
1602 15 : osAccess += "b";
1603 1279 : if (strchr(pszAccess, 'r'))
1604 : {
1605 228 : VSIVirtualHandle *fpBase = reinterpret_cast<VSIVirtualHandle *>(
1606 : VSIFOpenL(osFilename, osAccess));
1607 228 : if (fpBase == nullptr)
1608 1 : return nullptr;
1609 227 : VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1610 227 : if (!poHeader->ReadFromFile(fpBase, osKey))
1611 : {
1612 94 : memset(osKey.data(), 0, osKey.size());
1613 94 : fpBase->Close();
1614 94 : delete fpBase;
1615 94 : delete poHeader;
1616 94 : return nullptr;
1617 : }
1618 :
1619 : VSICryptFileHandle *poHandle = new VSICryptFileHandle(
1620 : osFilename, fpBase, poHeader,
1621 133 : strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
1622 133 : : VSICRYPT_READ);
1623 133 : if (!poHandle->Init(osKey, false))
1624 : {
1625 4 : memset(osKey.data(), 0, osKey.size());
1626 4 : delete poHandle;
1627 4 : poHandle = nullptr;
1628 : }
1629 133 : memset(osKey.data(), 0, osKey.size());
1630 133 : return poHandle;
1631 : }
1632 1051 : else if (strchr(pszAccess, 'w'))
1633 : {
1634 : CPLString osAlg(GetArgument(pszFilename, "alg",
1635 2088 : CPLGetConfigOption("VSICRYPT_ALG", "AES")));
1636 1044 : VSICryptAlg eAlg = GetAlg(osAlg);
1637 :
1638 1044 : VSICryptMode eMode = GetMode(GetArgument(
1639 : pszFilename, "mode", CPLGetConfigOption("VSICRYPT_MODE", "CBC")));
1640 :
1641 : CPLString osFreeText =
1642 : GetArgument(pszFilename, "freetext",
1643 2088 : CPLGetConfigOption("VSICRYPT_FREETEXT", ""));
1644 :
1645 : CPLString osIV = GetArgument(pszFilename, "iv",
1646 2088 : CPLGetConfigOption("VSICRYPT_IV", ""));
1647 :
1648 1044 : int nSectorSize = atoi(
1649 2088 : GetArgument(pszFilename, "sector_size",
1650 : CPLGetConfigOption("VSICRYPT_SECTOR_SIZE", "512")));
1651 1044 : if (nSectorSize <= 0 || nSectorSize >= 65535)
1652 : {
1653 0 : CPLError(CE_Warning, CPLE_NotSupported,
1654 : "Invalid value for sector_size. Defaulting to 512.");
1655 0 : nSectorSize = 512;
1656 : }
1657 :
1658 1044 : const bool bAddKeyCheck = CPLTestBool(
1659 2088 : GetArgument(pszFilename, "add_key_check",
1660 : CPLGetConfigOption("VSICRYPT_ADD_KEY_CHECK", "NO")));
1661 :
1662 : /* Generate random initial vector */
1663 1044 : CryptoPP::BlockCipher *poBlock = GetEncBlockCipher(eAlg);
1664 1044 : if (poBlock == nullptr)
1665 : {
1666 0 : CPLError(CE_Failure, CPLE_AppDefined,
1667 : "Cipher algorithm not supported in this build: %s",
1668 : osAlg.c_str());
1669 0 : memset(osKey.data(), 0, osKey.size());
1670 0 : return nullptr;
1671 : }
1672 1044 : int nMinKeySize = static_cast<int>(poBlock->MinKeyLength());
1673 1044 : int nMaxKeySize = static_cast<int>(poBlock->MaxKeyLength());
1674 1044 : int nBlockSize = static_cast<int>(poBlock->BlockSize());
1675 1044 : delete poBlock;
1676 :
1677 1044 : if (!osIV.empty())
1678 : {
1679 1 : if (static_cast<int>(osIV.size()) != nBlockSize)
1680 : {
1681 1 : CPLError(CE_Failure, CPLE_AppDefined,
1682 : "IV should be %d byte large", nBlockSize);
1683 1 : memset(osKey.data(), 0, osKey.size());
1684 1 : return nullptr;
1685 : }
1686 : }
1687 : else
1688 : {
1689 1043 : osIV.resize(nBlockSize);
1690 2086 : CryptoPP::OS_GenerateRandomBlock(
1691 : false, // Do not need cryptographic randomness.
1692 1043 : reinterpret_cast<cryptopp_byte *>(osIV.data()), osIV.size());
1693 : }
1694 :
1695 1043 : if (EQUAL(osKey, "GENERATE_IT"))
1696 : {
1697 1 : osKey.resize(nMaxKeySize);
1698 1 : CPLDebug("VSICRYPT",
1699 : "Generating key. This might take some time...");
1700 1 : CryptoPP::OS_GenerateRandomBlock(
1701 : // Need cryptographic randomness.
1702 : // Config option for speeding tests.
1703 1 : CPLTestBool(
1704 : CPLGetConfigOption("VSICRYPT_CRYPTO_RANDOM", "TRUE")),
1705 1 : reinterpret_cast<cryptopp_byte *>(osKey.data()), osKey.size());
1706 :
1707 : char *pszB64 =
1708 1 : CPLBase64Encode(static_cast<int>(osKey.size()),
1709 1 : reinterpret_cast<const GByte *>(osKey.c_str()));
1710 1 : if (CPLTestBool(CPLGetConfigOption("VSICRYPT_DISPLAY_GENERATED_KEY",
1711 : "TRUE")))
1712 : {
1713 1 : CPLError(CE_Failure, CPLE_AppDefined,
1714 : "BASE64 key '%s' has been generated, and installed in "
1715 : "the VSICRYPT_KEY_B64 configuration option.",
1716 : pszB64);
1717 : }
1718 1 : CPLSetConfigOption("VSICRYPT_KEY_B64", pszB64);
1719 1 : CPLFree(pszB64);
1720 : }
1721 :
1722 : const int nKeyLength =
1723 1043 : !osKey.empty() ? static_cast<int>(osKey.size()) : nGlobalKeySize;
1724 1043 : if (nKeyLength < nMinKeySize)
1725 : {
1726 2 : CPLError(CE_Failure, CPLE_AppDefined,
1727 : "Key is too short: %d bytes. Should be at least %d bytes",
1728 : nKeyLength, nMinKeySize);
1729 2 : memset(osKey.data(), 0, osKey.size());
1730 2 : return nullptr;
1731 : }
1732 :
1733 1041 : VSIVirtualHandle *fpBase = reinterpret_cast<VSIVirtualHandle *>(
1734 : VSIFOpenL(osFilename, osAccess.c_str()));
1735 1041 : if (fpBase == nullptr)
1736 : {
1737 2 : memset(osKey.data(), 0, osKey.size());
1738 2 : return nullptr;
1739 : }
1740 :
1741 1039 : VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1742 1039 : poHeader->osIV = osIV;
1743 1039 : CPL_IGNORE_RET_VAL(osIV);
1744 1039 : poHeader->eAlg = eAlg;
1745 1039 : poHeader->eMode = eMode;
1746 1039 : poHeader->nSectorSize = static_cast<GUInt16>(nSectorSize);
1747 1039 : poHeader->osFreeText = std::move(osFreeText);
1748 1039 : poHeader->bAddKeyCheck = bAddKeyCheck;
1749 :
1750 : VSICryptFileHandle *poHandle = new VSICryptFileHandle(
1751 : osFilename, fpBase, poHeader,
1752 1039 : strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
1753 1039 : : VSICRYPT_WRITE);
1754 1039 : if (!poHandle->Init(osKey, true))
1755 : {
1756 2 : memset(osKey.data(), 0, osKey.size());
1757 2 : delete poHandle;
1758 2 : poHandle = nullptr;
1759 : }
1760 1039 : memset(osKey.data(), 0, osKey.size());
1761 1039 : return poHandle;
1762 : }
1763 7 : else if (strchr(pszAccess, 'a'))
1764 : {
1765 : VSIVirtualHandle *fpBase =
1766 6 : reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osFilename, "rb+"));
1767 6 : if (fpBase == nullptr)
1768 : {
1769 2 : memset(osKey.data(), 0, osKey.size());
1770 2 : return VSIFilesystemHandler::Open(pszFilename, "wb+");
1771 : }
1772 4 : VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1773 4 : if (!poHeader->ReadFromFile(fpBase, osKey))
1774 : {
1775 2 : memset(osKey.data(), 0, osKey.size());
1776 2 : fpBase->Close();
1777 2 : delete fpBase;
1778 2 : delete poHeader;
1779 2 : return nullptr;
1780 : }
1781 :
1782 : VSICryptFileHandle *poHandle = new VSICryptFileHandle(
1783 2 : osFilename, fpBase, poHeader, VSICRYPT_READ | VSICRYPT_WRITE);
1784 2 : if (!poHandle->Init(osKey))
1785 : {
1786 1 : delete poHandle;
1787 1 : poHandle = nullptr;
1788 : }
1789 2 : memset(osKey.data(), 0, osKey.size());
1790 2 : if (poHandle != nullptr)
1791 1 : poHandle->Seek(0, SEEK_END);
1792 2 : return poHandle;
1793 : }
1794 :
1795 1 : return nullptr;
1796 : }
1797 :
1798 : /************************************************************************/
1799 : /* Stat() */
1800 : /************************************************************************/
1801 :
1802 5 : int VSICryptFilesystemHandler::Stat(const char *pszFilename,
1803 : VSIStatBufL *pStatBuf, int nFlags)
1804 : {
1805 : #ifdef VERBOSE_VSICRYPT
1806 : CPLDebug("VSICRYPT", "Stat(%s)", pszFilename);
1807 : #endif
1808 10 : CPLString osFilename(GetFilename(pszFilename));
1809 5 : if (VSIStatExL(osFilename, pStatBuf, nFlags) != 0)
1810 1 : return -1;
1811 : VSIVirtualHandle *fp =
1812 4 : reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osFilename, "rb"));
1813 4 : if (fp == nullptr)
1814 0 : return -1;
1815 4 : VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1816 8 : CPLString osKey(GetKey(pszFilename));
1817 4 : if (!poHeader->ReadFromFile(fp, osKey))
1818 : {
1819 1 : memset(osKey.data(), 0, osKey.size());
1820 1 : fp->Close();
1821 1 : delete fp;
1822 1 : delete poHeader;
1823 1 : return -1;
1824 : }
1825 3 : memset(osKey.data(), 0, osKey.size());
1826 3 : fp->Close();
1827 3 : delete fp;
1828 3 : if (poHeader)
1829 : {
1830 3 : pStatBuf->st_size = poHeader->nPayloadFileSize;
1831 3 : delete poHeader;
1832 3 : return 0;
1833 : }
1834 : else
1835 0 : return -1;
1836 : }
1837 :
1838 : /************************************************************************/
1839 : /* Unlink() */
1840 : /************************************************************************/
1841 :
1842 1006 : int VSICryptFilesystemHandler::Unlink(const char *pszFilename)
1843 : {
1844 1006 : return VSIUnlink(GetFilename(pszFilename));
1845 : }
1846 :
1847 : /************************************************************************/
1848 : /* Rename() */
1849 : /************************************************************************/
1850 :
1851 2 : int VSICryptFilesystemHandler::Rename(const char *oldpath, const char *newpath,
1852 : GDALProgressFunc, void *)
1853 : {
1854 2 : CPLString osNewPath;
1855 2 : if (strncmp(newpath, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) == 0)
1856 1 : osNewPath = GetFilename(newpath);
1857 : else
1858 1 : osNewPath = newpath;
1859 :
1860 4 : return VSIRename(GetFilename(oldpath), osNewPath);
1861 : }
1862 :
1863 : /************************************************************************/
1864 : /* ReadDirEx() */
1865 : /************************************************************************/
1866 :
1867 1 : char **VSICryptFilesystemHandler::ReadDirEx(const char *pszDirname,
1868 : int nMaxFiles)
1869 : {
1870 : #ifdef VERBOSE_VSICRYPT
1871 : CPLDebug("VSICRYPT", "ReadDir(%s)", pszDirname);
1872 : #endif
1873 1 : return VSIReadDirEx(GetFilename(pszDirname), nMaxFiles);
1874 : }
1875 :
1876 : #ifdef VSICRYPT_DRIVER
1877 :
1878 : #include "gdal_priv.h"
1879 :
1880 : /**
1881 : * \brief Evaluate if this is a crypt file.
1882 : *
1883 : * The function signature must match GDALDataset::Identify.
1884 : *
1885 : * @param poOpenInfo The header bytes used for file identification.
1886 : *
1887 : * @return 1 if this is a crypt file or 0 otherwise.
1888 : */
1889 :
1890 : static int VSICryptIdentify(GDALOpenInfo *poOpenInfo)
1891 : {
1892 : return poOpenInfo->nHeaderBytes > 8 &&
1893 : memcmp(poOpenInfo->pabyHeader, VSICRYPT_SIGNATURE, 8) == 0;
1894 : }
1895 :
1896 : static GDALDataset *VSICryptOpen(GDALOpenInfo *poOpenInfo)
1897 : {
1898 : if (!VSICryptIdentify(poOpenInfo))
1899 : return nullptr;
1900 : return GDALOpen(
1901 : (CPLString(VSICRYPT_PREFIX) + poOpenInfo->pszFilename).c_str(),
1902 : poOpenInfo->eAccess);
1903 : }
1904 :
1905 : #endif
1906 :
1907 : //! @endcond
1908 :
1909 : /************************************************************************/
1910 : /* VSIInstallCryptFileHandler() */
1911 : /************************************************************************/
1912 :
1913 : /**
1914 : * \brief Install /vsicrypt/ encrypted file system handler
1915 : * (requires <a href="http://www.cryptopp.com/">libcrypto++</a>)
1916 : *
1917 : * A special file handler is installed that allows reading/creating/update
1918 : * encrypted files on the fly, with random access capabilities.
1919 : *
1920 : * The cryptographic algorithms used are
1921 : * <a href="https://en.wikipedia.org/wiki/Block_cipher">block ciphers</a>,
1922 : * with symmetric key.
1923 : *
1924 : * In their simplest form, recognized filenames are of the form
1925 : * /vsicrypt//absolute_path/to/file, /vsicrypt/c:/absolute_path/to/file or
1926 : * /vsicrypt/relative/path/to/file.
1927 : *
1928 : * Options can also be used with the following format :
1929 : * /vsicrypt/option1=val1,option2=val2,...,file=/path/to/file
1930 : *
1931 : * They can also be passed as configuration option/environment variable, because
1932 : * in some use cases, the syntax with option in the filename might not properly
1933 : * work with some drivers.
1934 : *
1935 : * In all modes, the encryption key must be provided. There are several ways
1936 : * of doing so :
1937 : * <ul>
1938 : * <li>By adding a key= parameter to the filename, like
1939 : * /vsicrypt/key=my_secret_key,file=/path/to/file. Note that this restricts
1940 : * the key to be in text format, whereas at its full power, it can be binary
1941 : * content.</li>
1942 : * <li>By adding a key_b64= parameter to the filename, to specify a binary key
1943 : * expressed in Base64 encoding, like
1944 : * /vsicrypt/key_b64=th1sl00kslikebase64=,file=/path/to/file.</li>
1945 : * <li>By setting the VSICRYPT_KEY configuration option. The key should be in
1946 : * text format.</li>
1947 : * <li>By setting the VSICRYPT_KEY_B64 configuration option. The key should be
1948 : * encoded in Base64.</li>
1949 : * <li>By using the VSISetCryptKey() C function.</li>
1950 : * </ul>
1951 : *
1952 : * When creating a file, if key=GENERATE_IT or VSICRYPT_KEY=GENERATE_IT is
1953 : * passed, the encryption key will be generated from the pseudo-random number
1954 : * generator of the operating system. The key will be displayed on the standard
1955 : * error stream in a Base64 form (unless the VSICRYPT_DISPLAY_GENERATED_KEY
1956 : * configuration option is set to OFF), and the VSICRYPT_KEY_B64 configuration
1957 : * option will also be set with the Base64 form of the key (so that
1958 : * CPLGetConfigOption("VSICRYPT_KEY_B64", NULL) can be used to get it back).
1959 : *
1960 : * The available options are :
1961 : * <ul>
1962 :
1963 : * <li>alg=AES/Blowfish/Camellia/CAST256/DES_EDE2/DES_EDE3/MARS/IDEA/RC5/RC6/Serpent/SHACAL2/SKIPJACK/Twofish/XTEA:
1964 : * to specify the <a href="https://en.wikipedia.org/wiki/Block_cipher">block
1965 : * cipher</a> algorithm. The default is AES. Only used on
1966 : * creation. Ignored otherwise. Note: depending on how GDAL is build, if
1967 : * linked against the DLL version of libcrypto++, only a subset of those
1968 : * algorithms will be available, namely AES, DES_EDE2, DES_EDE3 and
1969 : * SKIPJACK. Also available as VSICRYPT_ALG configuration option.</li>
1970 : * <li>mode=CBC/CFB/OFB/CTR/CBC_CTS: to specify the
1971 : * <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">
1972 : * block cipher mode of operation</a>.
1973 : * The default is CBC.
1974 : * Only used on creation. Ignored otherwise.
1975 : * Also available as VSICRYPT_MODE configuration option.</li>
1976 : * <li>key=text_key: see above.</li>
1977 : * <li>key_b64=base64_encoded_key: see above.</li>
1978 : * <li>freetext=some_text: to specify a text content that will be written
1979 : * *unencrypted* in the file header, for informational purposes. Default to
1980 : * empty. Only used on creation. Ignored otherwise.
1981 : * Also available as VSICRYPT_FREETEXT configuration option.</li>
1982 : * <li>sector_size=int_value: to specify the size of the "sector", which is the
1983 : * unit chunk of information that is encrypted/decrypted. Default to 512
1984 : * bytes. The valid values depend on the algorithm and block cipher mode of
1985 : * operation. Only used on creation. Ignored otherwise. Also available as
1986 : * VSICRYPT_SECTOR_SIZE configuration option.</li>
1987 : * <li>iv=initial_vector_as_text: to specify the Initial Vector. This is an
1988 : * advanced option that should generally *NOT* be used. It is only useful to
1989 : * get completely deterministic output given the plaintext, key and other
1990 : * parameters, which in general *NOT* what you want to do. By default, a
1991 : * random initial vector of the appropriate size will be generated for each
1992 : * new file created. Only used on creation. Ignored otherwise. Also
1993 : * available as VSICRYPT_IV configuration option.</li>
1994 :
1995 : * <li>add_key_check=YES/NO: whether a special value should be encrypted in the
1996 : * header, so as to be quickly able to determine if the decryption key is
1997 : * correct. Defaults to NO. Only used on creation. Ignored otherwise.
1998 : * Also available as VSICRYPT_ADD_KEY_CHECK configuration option.</li>
1999 : * <li>file=filename. To specify the filename. This must be the last option put
2000 : * in the option list (so as to make it possible to use filenames with comma
2001 : * in them. )
2002 : * </ul>
2003 : *
2004 : * This special file handler can be combined with other virtual filesystems
2005 : * handlers, such as /vsizip. For example,
2006 : * /vsicrypt//vsicurl/path/to/remote/encrypted/file.tif
2007 : *
2008 : * Implementation details:
2009 : *
2010 : * The structure of encrypted files is the following: a header, immediately
2011 : * followed by the encrypted payload (by sectors, i.e. chunks of sector_size
2012 : * bytes).
2013 : *
2014 : * The header structure is the following :
2015 : * <ol>
2016 : * <li>8 bytes. Signature. Fixed value: VSICRYPT.</li>
2017 : * <li>UINT16_LE. Header size (including previous signature bytes).</li>
2018 : * <li>UINT8. Format major version. Current value: 1.</li>
2019 : * <li>UINT8. Format minor version. Current value: 0.</li>
2020 : * <li>UINT16. Sector size.</li>
2021 : * <li>UINT8. Cipher algorithm. Valid values are: 0 = AES (Rijndael), 1 =
2022 : * Blowfish, 2 = Camellia, 3 = CAST256, 4 = DES_EDE2, 5 = DES_EDE3, 6 =
2023 : * MARS, 7 = IDEA, 8 = RC5, 9 = RC6, 10 = Serpent, 11 = SHACAL2, 12 =
2024 : * SKIPJACK, 13 = Twofish, 14 = XTEA.</li>
2025 : * <li>UINT8. Block cipher mode of operation. Valid values are: 0 = CBC, 1 =
2026 : * CFB, 2 = OFB, 3 = CTR, 4 = CBC_CTS.</li>
2027 : * <li>UINT8. Size in bytes of the Initial Vector.</li>
2028 : * <li>N bytes with the content of the Initial Vector, where N is the value of
2029 : * the previous field.</li>
2030 : * <li>UINT16_LE. Size in bytes of the free text.</li>
2031 : * <li>N bytes with the content of the free text, where N is the value of the
2032 : * previous field.</li>
2033 : * <li>UINT8. Size in bytes of encrypted content (key check), or 0 if key check
2034 : * is absent.</li>
2035 : * <li>N bytes with encrypted content (key check), where N is the value of the
2036 : * previous field.</li>
2037 : * <li>UINT64_LE. Size of the unencrypted file, in bytes.</li>
2038 : * <li>UINT16_LE. Size in bytes of extra content (of unspecified semantics). For
2039 : * v1.0, fixed value of 0</li>
2040 : * <li>N bytes with extra content (of unspecified semantics), where N is the
2041 : * value of the previous field.</li>
2042 : * </ol>
2043 : *
2044 : * This design does not provide any means of authentication or integrity check.
2045 : *
2046 : * Each sector is encrypted/decrypted independently of other sectors. For that,
2047 : * the Initial Vector contained in the header is XOR'ed with the file offset
2048 : * (relative to plain text file) of the start of the sector being processed, as
2049 : * a 8-byte integer. More precisely, the first byte of the main IV is XOR'ed
2050 : * with the 8 least-significant bits of the sector offset, the second byte of
2051 : * the main IV is XOR'ed with the following 8 bits of the sector offset,
2052 : * etc... until the 8th byte.
2053 : *
2054 : * This design could potentially be prone to chosen-plaintext attack, for
2055 : * example if the attacker managed to get (part of) an existing encrypted file
2056 : * to be encrypted from plaintext he might have selected.
2057 : *
2058 : * Note: if "hostile" code can explore process content, or attach to it with a
2059 : * debugger, it might be relatively easy to retrieve the encryption key. A GDAL
2060 : * plugin could for example get the content of configuration options, or list
2061 : * opened datasets and see the key/key_b64 values, so disabling plugin loading
2062 : * might be a first step, as well as linking statically GDAL to application
2063 : * code. If plugin loading is enabled or GDAL dynamically linked, using
2064 : * VSISetCryptKey() to set the key might make it a bit more complicated to spy
2065 : * the key. But, as said initially, this is in no way a perfect protection.
2066 : *
2067 : * @since GDAL 2.1.0
2068 : */
2069 1616 : void VSIInstallCryptFileHandler(void)
2070 :
2071 : {
2072 1616 : VSIFileManager::InstallHandler(VSICRYPT_PREFIX,
2073 1616 : new VSICryptFilesystemHandler);
2074 :
2075 : #ifdef VSICRYPT_DRIVER
2076 : if (GDALGetDriverByName("VSICRYPT") != nullptr)
2077 : return;
2078 :
2079 : GDALDriver *poDriver = new GDALDriver();
2080 :
2081 : poDriver->SetDescription("VSICRYPT");
2082 : #ifdef GDAL_DCAP_RASTER
2083 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
2084 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
2085 : #endif
2086 : poDriver->SetMetadataItem(
2087 : GDAL_DMD_LONGNAME, CPLSPrintf("Wrapper for %s files", VSICRYPT_PREFIX));
2088 :
2089 : poDriver->pfnOpen = VSICryptOpen;
2090 : poDriver->pfnIdentify = VSICryptIdentify;
2091 :
2092 : GetGDALDriverManager()->RegisterDriver(poDriver);
2093 : #endif
2094 1616 : }
2095 :
2096 : #else /* HAVE_CRYPTOPP */
2097 :
2098 : class VSIDummyCryptFilesystemHandler : public VSIFilesystemHandler
2099 : {
2100 : public:
2101 : VSIDummyCryptFilesystemHandler()
2102 : {
2103 : }
2104 :
2105 : VSIVirtualHandle *Open(const char * /* pszFilename */,
2106 : const char * /* pszAccess */, bool /* bSetError */,
2107 : CSLConstList /* papszOptions */) override
2108 : {
2109 : CPLError(CE_Failure, CPLE_NotSupported,
2110 : "%s support not available in this build", VSICRYPT_PREFIX);
2111 : return nullptr;
2112 : }
2113 :
2114 : int Stat(const char * /* pszFilename */, VSIStatBufL * /*pStatBuf */,
2115 : int /* nFlags */) override
2116 : {
2117 : CPLError(CE_Failure, CPLE_NotSupported,
2118 : "%s support not available in this build", VSICRYPT_PREFIX);
2119 : return -1;
2120 : }
2121 : };
2122 :
2123 : void VSIInstallCryptFileHandler(void)
2124 : {
2125 : VSIFileManager::InstallHandler(VSICRYPT_PREFIX,
2126 : new VSIDummyCryptFilesystemHandler);
2127 : }
2128 :
2129 : void VSISetCryptKey(const GByte * /* pabyKey */, int /* nKeySize */)
2130 : {
2131 : // Not supported.
2132 : }
2133 :
2134 : #endif // HAVE_CRYPTOPP
2135 :
2136 : // Below is only useful if using as a plugin over GDAL >= 2.0.
2137 : #ifdef VSICRYPT_AUTOLOAD
2138 :
2139 : CPL_C_START
2140 : void CPL_DLL GDALRegisterMe();
2141 : CPL_C_END
2142 :
2143 : void GDALRegisterMe()
2144 : {
2145 : VSIFilesystemHandler *poExistingHandler =
2146 : VSIFileManager::GetHandler(VSICRYPT_PREFIX);
2147 : if (poExistingHandler == VSIFileManager::GetHandler("."))
2148 : {
2149 : // In the case where VSICRYPT_PREFIX is just handled by the regular
2150 : // handler, install the vsicrypt handler (shouldn't happen)
2151 : VSIInstallCryptFileHandler();
2152 : }
2153 : else
2154 : {
2155 : // If there's already an installed handler, then check if it is a
2156 : // dummy one (should normally be the case) or a real one
2157 : CPLErrorReset();
2158 : CPLPushErrorHandler(CPLQuietErrorHandler);
2159 : VSIStatBufL sStat;
2160 : CPL_IGNORE_RET_VAL(VSIStatL(
2161 : (CPLString(VSICRYPT_PREFIX) + "i_do_not_exist").c_str(), &sStat));
2162 : CPLPopErrorHandler();
2163 : if (strstr(CPLGetLastErrorMsg(), "support not available in this build"))
2164 : {
2165 : // Dummy handler. Register the new one, and delete the old one
2166 : VSIInstallCryptFileHandler();
2167 : delete poExistingHandler;
2168 : }
2169 : else
2170 : {
2171 : CPLDebug("VSICRYPT", "GDAL has already a working %s implementation",
2172 : VSICRYPT_PREFIX);
2173 : }
2174 : CPLErrorReset();
2175 : }
2176 : }
2177 :
2178 : #endif /* VSICRYPT_AUTOLOAD */
|