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