Coverage Report

Created: 2026-02-14 06:52

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