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