Coverage Report

Created: 2026-04-01 06:20

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
    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
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->ReadLSB(nHeaderSize))
491
        return VSICryptReadError();
492
    if (nHeaderSize < 8 + 2 + 1 + 1)
493
    {
494
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid header size : %d",
495
                 nHeaderSize);
496
        return FALSE;
497
    }
498
499
    if (fp->Read(&nMajorVersion, 1, 1) == 0)
500
        return VSICryptReadError();
501
    if (fp->Read(&nMinorVersion, 1, 1) == 0)
502
        return VSICryptReadError();
503
504
    if (nMajorVersion != VSICRYPT_CURRENT_MAJOR)
505
    {
506
        CPLError(CE_Failure, CPLE_AppDefined, "Unhandled major version : %d",
507
                 nMajorVersion);
508
        return FALSE;
509
    }
510
    if (nMinorVersion != VSICRYPT_CURRENT_MINOR)
511
    {
512
        CPLDebug("VSICRYPT", "Minor version in file is %d", nMinorVersion);
513
    }
514
515
    if (!fp->ReadLSB(nSectorSize))
516
        return VSICryptReadError();
517
518
    GByte nAlg, nMode;
519
    if (fp->Read(&nAlg, 1, 1) == 0 || fp->Read(&nMode, 1, 1) == 0)
520
        return VSICryptReadError();
521
    if (nAlg > ALG_MAX)
522
    {
523
        CPLError(CE_Failure, CPLE_NotSupported,
524
                 "Unsupported cipher algorithm %d", nAlg);
525
        return FALSE;
526
    }
527
    if (nMode > MODE_MAX)
528
    {
529
        CPLError(CE_Failure, CPLE_NotSupported,
530
                 "Unsupported cipher block mode %d", nMode);
531
        return FALSE;
532
    }
533
    eAlg = static_cast<VSICryptAlg>(nAlg);
534
    eMode = static_cast<VSICryptMode>(nMode);
535
536
    GByte nIVSize;
537
    if (fp->Read(&nIVSize, 1, 1) == 0)
538
        return VSICryptReadError();
539
540
    osIV.resize(nIVSize);
541
    if (fp->Read(osIV.data(), 1, nIVSize) != nIVSize)
542
        return VSICryptReadError();
543
544
    GUInt16 nFreeTextSize;
545
    if (fp->Read(&nFreeTextSize, 2, 1) == 0)
546
        return VSICryptReadError();
547
548
    osFreeText.resize(nFreeTextSize);
549
    if (fp->Read(osFreeText.data(), 1, nFreeTextSize) != nFreeTextSize)
550
        return VSICryptReadError();
551
552
    GByte nKeyCheckSize;
553
    if (fp->Read(&nKeyCheckSize, 1, 1) == 0)
554
        return VSICryptReadError();
555
    bAddKeyCheck = nKeyCheckSize != 0;
556
    if (nKeyCheckSize)
557
    {
558
        CPLString osKeyCheck;
559
        osKeyCheck.resize(nKeyCheckSize);
560
        if (fp->Read(osKeyCheck.data(), 1, nKeyCheckSize) != nKeyCheckSize)
561
            return VSICryptReadError();
562
563
        if (osKey.empty() && pabyGlobalKey == nullptr)
564
        {
565
            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
            return FALSE;
570
        }
571
572
        CryptoPP::BlockCipher *poEncCipher = GetEncBlockCipher(eAlg);
573
        if (poEncCipher == nullptr)
574
            return FALSE;
575
576
        if (osIV.size() != poEncCipher->BlockSize())
577
        {
578
            CPLError(CE_Failure, CPLE_AppDefined,
579
                     "Inconsistent initial vector");
580
            delete poEncCipher;
581
            return FALSE;
582
        }
583
584
        int nMaxKeySize = static_cast<int>(poEncCipher->MaxKeyLength());
585
586
        try
587
        {
588
            if (!osKey.empty())
589
            {
590
                const int nKeySize =
591
                    std::min(nMaxKeySize, static_cast<int>(osKey.size()));
592
                poEncCipher->SetKey(
593
                    reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
594
                    nKeySize);
595
            }
596
            else if (pabyGlobalKey)
597
            {
598
                const int nKeySize = std::min(nMaxKeySize, nGlobalKeySize);
599
                poEncCipher->SetKey(pabyGlobalKey, nKeySize);
600
            }
601
        }
602
        catch (const std::exception &e)
603
        {
604
            CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
605
                     e.what());
606
            delete poEncCipher;
607
            return FALSE;
608
        }
609
610
        std::string osKeyCheckRes = CryptKeyCheck(poEncCipher);
611
612
        delete poEncCipher;
613
614
        if (osKeyCheck.size() != osKeyCheckRes.size() ||
615
            memcmp(osKeyCheck.c_str(), osKeyCheckRes.c_str(),
616
                   osKeyCheck.size()) != 0)
617
        {
618
            CPLError(CE_Failure, CPLE_AppDefined, "Bad key");
619
            return FALSE;
620
        }
621
    }
622
623
    if (!fp->ReadLSB(nPayloadFileSize))
624
        return VSICryptReadError();
625
#ifdef VERBOSE_VSICRYPT
626
    CPLDebug("VSICRYPT", "nPayloadFileSize read = " CPL_FRMT_GUIB,
627
             nPayloadFileSize);
628
#endif
629
630
    bool bError = false;
631
    const uint16_t nExtraContentSize = fp->ReadLSB<uint16_t>(&bError);
632
    if (bError)
633
        return VSICryptReadError();
634
635
    osExtraContent.resize(nExtraContentSize);
636
    if (fp->Read(osExtraContent.data(), 1, nExtraContentSize) !=
637
        nExtraContentSize)
638
        return VSICryptReadError();
639
640
    return TRUE;
641
}
642
643
/************************************************************************/
644
/*                            WriteToFile()                             */
645
/************************************************************************/
646
647
int VSICryptFileHeader::WriteToFile(VSIVirtualHandle *fp,
648
                                    CryptoPP::BlockCipher *poEncCipher)
649
{
650
    fp->Seek(0, SEEK_SET);
651
652
    bool bRet = fp->Write(VSICRYPT_SIGNATURE, 8, 1) == 1;
653
654
    std::string osKeyCheckRes;
655
    if (bAddKeyCheck)
656
    {
657
        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
                             1 + osIV.size() +           /* IV */
669
                             2 + osFreeText.size() +     /* free text */
670
                             1 + osKeyCheckRes.size() +  /* key check */
671
                             8 +                         /* payload size */
672
                             2 + osExtraContent.size()); /* extra content */
673
    if (nHeaderSize != 0)
674
        CPLAssert(nHeaderSizeNew == nHeaderSize);
675
    else
676
        nHeaderSize = nHeaderSizeNew;
677
678
    bRet &= fp->WriteLSB(nHeaderSizeNew);
679
680
    bRet &= fp->WriteLSB(static_cast<GByte>(VSICRYPT_CURRENT_MAJOR));
681
682
    bRet &= fp->WriteLSB(static_cast<GByte>(VSICRYPT_CURRENT_MINOR));
683
684
    bRet &= fp->WriteLSB(nSectorSize);
685
686
    bRet &= fp->WriteLSB(static_cast<GByte>(eAlg));
687
688
    bRet &= fp->WriteLSB(static_cast<GByte>(eMode));
689
690
    GByte nIVSizeToWrite = static_cast<GByte>(osIV.size());
691
    CPLAssert(nIVSizeToWrite == osIV.size());
692
    bRet &= (fp->Write(&nIVSizeToWrite, 1, 1) == 1);
693
    bRet &= (fp->Write(osIV.c_str(), 1, osIV.size()) == osIV.size());
694
695
    bRet &= fp->WriteLSB(static_cast<uint16_t>(osFreeText.size()));
696
    bRet &= (fp->Write(osFreeText.c_str(), 1, osFreeText.size()) ==
697
             osFreeText.size());
698
699
    GByte nSize = static_cast<GByte>(osKeyCheckRes.size());
700
    bRet &= (fp->Write(&nSize, 1, 1) == 1);
701
    bRet &= (fp->Write(osKeyCheckRes.c_str(), 1, osKeyCheckRes.size()) ==
702
             osKeyCheckRes.size());
703
704
    bRet &= fp->WriteLSB(nPayloadFileSize);
705
706
    bRet &= fp->WriteLSB(static_cast<uint16_t>(osExtraContent.size()));
707
    bRet &= (fp->Write(osExtraContent.c_str(), 1, osExtraContent.size()) ==
708
             osExtraContent.size());
709
710
    CPLAssert(fp->Tell() == nHeaderSize);
711
712
    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
VSICryptFileHandle::VSICryptFileHandle(const CPLString &osBaseFilenameIn,
773
                                       VSIVirtualHandle *poBaseHandleIn,
774
                                       VSICryptFileHeader *poHeaderIn,
775
                                       int nPermsIn)
776
    : osBaseFilename(osBaseFilenameIn), nPerms(nPermsIn),
777
      poBaseHandle(poBaseHandleIn), poHeader(poHeaderIn)
778
{
779
}
780
781
/************************************************************************/
782
/*                        ~VSICryptFileHandle()                         */
783
/************************************************************************/
784
785
VSICryptFileHandle::~VSICryptFileHandle()
786
{
787
    Close();
788
    delete poHeader;
789
    delete poEncCipher;
790
    delete poDecCipher;
791
    CPLFree(pabyWB);
792
}
793
794
/************************************************************************/
795
/*                                Init()                                */
796
/************************************************************************/
797
798
int VSICryptFileHandle::Init(const CPLString &osKey, bool bWriteHeader)
799
{
800
    poEncCipher = GetEncBlockCipher(poHeader->eAlg);
801
    if (poEncCipher == nullptr)
802
    {
803
        CPLError(CE_Failure, CPLE_AppDefined,
804
                 "Cipher algorithm not supported in this build: %d",
805
                 static_cast<int>(poHeader->eAlg));
806
        return FALSE;
807
    }
808
809
    if (poHeader->osIV.size() != poEncCipher->BlockSize())
810
    {
811
        CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent initial vector");
812
        return FALSE;
813
    }
814
815
    poDecCipher = GetDecBlockCipher(poHeader->eAlg);
816
    nBlockSize = poEncCipher->BlockSize();
817
    int nMaxKeySize = static_cast<int>(poEncCipher->MaxKeyLength());
818
819
    try
820
    {
821
        if (!osKey.empty())
822
        {
823
            const int nKeySize =
824
                std::min(nMaxKeySize, static_cast<int>(osKey.size()));
825
            poEncCipher->SetKey(
826
                reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
827
                nKeySize);
828
            poDecCipher->SetKey(
829
                reinterpret_cast<const cryptopp_byte *>(osKey.c_str()),
830
                nKeySize);
831
        }
832
        else if (pabyGlobalKey)
833
        {
834
            const int nKeySize = std::min(nMaxKeySize, nGlobalKeySize);
835
            poEncCipher->SetKey(pabyGlobalKey, nKeySize);
836
            poDecCipher->SetKey(pabyGlobalKey, nKeySize);
837
        }
838
        else
839
            return FALSE;
840
    }
841
    catch (const std::exception &e)
842
    {
843
        CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
844
                 e.what());
845
        return FALSE;
846
    }
847
848
    pabyWB = static_cast<GByte *>(CPLCalloc(1, poHeader->nSectorSize));
849
850
    if ((poHeader->nSectorSize % nBlockSize) != 0)
851
    {
852
        CPLError(CE_Failure, CPLE_AppDefined,
853
                 "Sector size (%d) is not a multiple of block size (%d)",
854
                 poHeader->nSectorSize, nBlockSize);
855
        return FALSE;
856
    }
857
    if (poHeader->eMode == MODE_CBC_CTS &&
858
        poHeader->nSectorSize < 2 * nBlockSize)
859
    {
860
        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
                 poHeader->nSectorSize, nBlockSize);
864
        return FALSE;
865
    }
866
867
    if (bWriteHeader && !poHeader->WriteToFile(poBaseHandle, poEncCipher))
868
    {
869
        return FALSE;
870
    }
871
872
    return TRUE;
873
}
874
875
/************************************************************************/
876
/*                            EncryptBlock()                            */
877
/************************************************************************/
878
879
void VSICryptFileHandle::EncryptBlock(GByte *pabyData, vsi_l_offset nOffset)
880
{
881
    std::string osRes;
882
    std::string osIV(VSICryptGenerateSectorIV(poHeader->osIV, nOffset));
883
    CPLAssert(static_cast<int>(osIV.size()) == nBlockSize);
884
885
    CryptoPP::StreamTransformation *poMode;
886
    try
887
    {
888
        if (poHeader->eMode == MODE_CBC)
889
            poMode = new CryptoPP::CBC_Mode_ExternalCipher::Encryption(
890
                *poEncCipher,
891
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
892
        else if (poHeader->eMode == MODE_CFB)
893
            poMode = new CryptoPP::CFB_Mode_ExternalCipher::Encryption(
894
                *poEncCipher,
895
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
896
        else if (poHeader->eMode == MODE_OFB)
897
            poMode = new CryptoPP::OFB_Mode_ExternalCipher::Encryption(
898
                *poEncCipher,
899
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
900
        else if (poHeader->eMode == MODE_CTR)
901
            poMode = new CryptoPP::CTR_Mode_ExternalCipher::Encryption(
902
                *poEncCipher,
903
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
904
        else
905
            poMode = new CryptoPP::CBC_CTS_Mode_ExternalCipher::Encryption(
906
                *poEncCipher,
907
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
908
    }
909
    catch (const std::exception &e)
910
    {
911
        CPLError(CE_Failure, CPLE_AppDefined, "cryptopp exception: %s",
912
                 e.what());
913
        return;
914
    }
915
    CryptoPP::StringSink *poSink = new CryptoPP::StringSink(osRes);
916
    CryptoPP::StreamTransformationFilter *poEnc =
917
        new CryptoPP::StreamTransformationFilter(
918
            *poMode, poSink, CryptoPP::StreamTransformationFilter::NO_PADDING);
919
    poEnc->Put(pabyData, poHeader->nSectorSize);
920
    poEnc->MessageEnd();
921
    delete poEnc;
922
923
    delete poMode;
924
925
    CPLAssert(static_cast<int>(osRes.length()) == poHeader->nSectorSize);
926
    memcpy(pabyData, osRes.c_str(), osRes.length());
927
}
928
929
/************************************************************************/
930
/*                            DecryptBlock()                            */
931
/************************************************************************/
932
933
bool VSICryptFileHandle::DecryptBlock(GByte *pabyData, vsi_l_offset nOffset)
934
{
935
    std::string osRes;
936
    std::string osIV(VSICryptGenerateSectorIV(poHeader->osIV, nOffset));
937
    CPLAssert(static_cast<int>(osIV.size()) == nBlockSize);
938
    CryptoPP::StringSink *poSink = new CryptoPP::StringSink(osRes);
939
    CryptoPP::StreamTransformation *poMode = nullptr;
940
    CryptoPP::StreamTransformationFilter *poDec = nullptr;
941
942
    try
943
    {
944
        // Yes, some modes need the encryption cipher.
945
        if (poHeader->eMode == MODE_CBC)
946
            poMode = new CryptoPP::CBC_Mode_ExternalCipher::Decryption(
947
                *poDecCipher,
948
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
949
        else if (poHeader->eMode == MODE_CFB)
950
            poMode = new CryptoPP::CFB_Mode_ExternalCipher::Decryption(
951
                *poEncCipher,
952
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
953
        else if (poHeader->eMode == MODE_OFB)
954
            poMode = new CryptoPP::OFB_Mode_ExternalCipher::Decryption(
955
                *poEncCipher,
956
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
957
        else if (poHeader->eMode == MODE_CTR)
958
            poMode = new CryptoPP::CTR_Mode_ExternalCipher::Decryption(
959
                *poEncCipher,
960
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
961
        else
962
            poMode = new CryptoPP::CBC_CTS_Mode_ExternalCipher::Decryption(
963
                *poDecCipher,
964
                reinterpret_cast<const cryptopp_byte *>(osIV.c_str()));
965
        poDec = new CryptoPP::StreamTransformationFilter(
966
            *poMode, poSink, CryptoPP::StreamTransformationFilter::NO_PADDING);
967
        poDec->Put(reinterpret_cast<const cryptopp_byte *>(pabyData),
968
                   poHeader->nSectorSize);
969
        poDec->MessageEnd();
970
        delete poDec;
971
        delete poMode;
972
    }
973
    catch (const std::exception &e)
974
    {
975
        delete poDec;
976
        delete poMode;
977
978
        CPLError(CE_Failure, CPLE_AppDefined, "CryptoPP exception: %s",
979
                 e.what());
980
        return false;
981
    }
982
983
    CPLAssert(static_cast<int>(osRes.length()) == poHeader->nSectorSize);
984
    memcpy(pabyData, osRes.c_str(), osRes.length());
985
986
    return true;
987
}
988
989
/************************************************************************/
990
/*                             FlushDirty()                             */
991
/************************************************************************/
992
993
bool VSICryptFileHandle::FlushDirty()
994
{
995
    if (!bWBDirty)
996
        return true;
997
    bWBDirty = false;
998
999
    EncryptBlock(pabyWB, nWBOffset);
1000
    poBaseHandle->Seek(poHeader->nHeaderSize + nWBOffset, SEEK_SET);
1001
1002
    nWBOffset = 0;
1003
    nWBSize = 0;
1004
1005
    if (poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1) != 1)
1006
        return false;
1007
1008
    return true;
1009
}
1010
1011
/************************************************************************/
1012
/*                                Seek()                                */
1013
/************************************************************************/
1014
1015
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
    bEOF = false;
1023
1024
    if (nWhence == SEEK_SET)
1025
        nCurPos = nOffset;
1026
    else if (nWhence == SEEK_CUR)
1027
        nCurPos += nOffset;
1028
    else
1029
        nCurPos = poHeader->nPayloadFileSize;
1030
    return 0;
1031
}
1032
1033
/************************************************************************/
1034
/*                                Tell()                                */
1035
/************************************************************************/
1036
1037
vsi_l_offset VSICryptFileHandle::Tell()
1038
{
1039
#ifdef VERBOSE_VSICRYPT
1040
    CPLDebug("VSICRYPT", "Tell()=" CPL_FRMT_GUIB, nCurPos);
1041
#endif
1042
    return nCurPos;
1043
}
1044
1045
/************************************************************************/
1046
/*                                Read()                                */
1047
/************************************************************************/
1048
1049
size_t VSICryptFileHandle::Read(void *pBuffer, size_t const nBytes)
1050
{
1051
    size_t nToRead = nBytes;
1052
    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
    if ((nPerms & VSICRYPT_READ) == 0)
1060
    {
1061
        bError = true;
1062
        return 0;
1063
    }
1064
1065
    if (nCurPos >= poHeader->nPayloadFileSize)
1066
    {
1067
        bEOF = true;
1068
        return 0;
1069
    }
1070
1071
    if (!FlushDirty())
1072
        return 0;
1073
1074
    while (nToRead > 0)
1075
    {
1076
        if (nCurPos >= nWBOffset && nCurPos < nWBOffset + nWBSize)
1077
        {
1078
            // TODO(schwehr): Can nToCopy be a size_t to simplify casting?
1079
            int nToCopy =
1080
                std::min(static_cast<int>(nToRead),
1081
                         static_cast<int>(nWBSize - (nCurPos - nWBOffset)));
1082
            if (nCurPos + nToCopy > poHeader->nPayloadFileSize)
1083
            {
1084
                bEOF = true;
1085
                nToCopy =
1086
                    static_cast<int>(poHeader->nPayloadFileSize - nCurPos);
1087
            }
1088
            memcpy(pabyBuffer, pabyWB + nCurPos - nWBOffset, nToCopy);
1089
            pabyBuffer += nToCopy;
1090
            nToRead -= nToCopy;
1091
            nCurPos += nToCopy;
1092
            if (bEOF || nToRead == 0)
1093
                break;
1094
            CPLAssert((nCurPos % poHeader->nSectorSize) == 0);
1095
        }
1096
1097
        vsi_l_offset nSectorOffset =
1098
            (nCurPos / poHeader->nSectorSize) * poHeader->nSectorSize;
1099
        poBaseHandle->Seek(poHeader->nHeaderSize + nSectorOffset, SEEK_SET);
1100
        if (poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) != 1)
1101
        {
1102
            bEOF = poBaseHandle->Eof();
1103
            bError = poBaseHandle->Error();
1104
            break;
1105
        }
1106
        if (!DecryptBlock(pabyWB, nSectorOffset))
1107
        {
1108
            bError = true;
1109
            break;
1110
        }
1111
        if ((nPerms & VSICRYPT_WRITE) &&
1112
            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
            CPLAssert(nSectorOffset < poHeader->nPayloadFileSize);
1117
            memset(pabyWB + poHeader->nPayloadFileSize - nSectorOffset, 0,
1118
                   nSectorOffset + poHeader->nSectorSize -
1119
                       poHeader->nPayloadFileSize);
1120
        }
1121
        nWBOffset = nSectorOffset;
1122
        nWBSize = poHeader->nSectorSize;
1123
    }
1124
1125
    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
    return nRet;
1131
}
1132
1133
/************************************************************************/
1134
/*                               Write()                                */
1135
/************************************************************************/
1136
1137
size_t VSICryptFileHandle::Write(const void *pBuffer, size_t nBytes)
1138
{
1139
    size_t nToWrite = nBytes;
1140
    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
    if ((nPerms & VSICRYPT_WRITE) == 0)
1152
        return 0;
1153
1154
    if (nCurPos >= (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
1155
                       poHeader->nSectorSize)
1156
    {
1157
        bLastSectorWasModified = true;
1158
    }
1159
1160
    // If seeking past end of file, we need to explicitly encrypt the
1161
    // padding zeroes.
1162
    if (nCurPos > poHeader->nPayloadFileSize && nCurPos > nWBOffset + nWBSize)
1163
    {
1164
        if (!FlushDirty())
1165
            return 0;
1166
        vsi_l_offset nOffset =
1167
            (poHeader->nPayloadFileSize + poHeader->nSectorSize - 1) /
1168
            poHeader->nSectorSize * poHeader->nSectorSize;
1169
        const vsi_l_offset nEndOffset =
1170
            nCurPos / poHeader->nSectorSize * poHeader->nSectorSize;
1171
        for (; nOffset < nEndOffset; nOffset += poHeader->nSectorSize)
1172
        {
1173
            memset(pabyWB, 0, poHeader->nSectorSize);
1174
            EncryptBlock(pabyWB, nOffset);
1175
            poBaseHandle->Seek(poHeader->nHeaderSize + nOffset, SEEK_SET);
1176
            if (poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1) != 1)
1177
                return 0;
1178
            poHeader->nPayloadFileSize = nOffset + poHeader->nSectorSize;
1179
            bUpdateHeader = true;
1180
        }
1181
    }
1182
1183
    while (nToWrite > 0)
1184
    {
1185
        if (nCurPos >= nWBOffset && nCurPos < nWBOffset + nWBSize)
1186
        {
1187
            bWBDirty = true;
1188
            const int nToCopy =
1189
                std::min(static_cast<int>(nToWrite),
1190
                         static_cast<int>(nWBSize - (nCurPos - nWBOffset)));
1191
            memcpy(pabyWB + nCurPos - nWBOffset, pabyBuffer, nToCopy);
1192
            pabyBuffer += nToCopy;
1193
            nToWrite -= nToCopy;
1194
            nCurPos += nToCopy;
1195
            if (nCurPos > poHeader->nPayloadFileSize)
1196
            {
1197
                bUpdateHeader = true;
1198
                poHeader->nPayloadFileSize = nCurPos;
1199
            }
1200
            if (nToWrite == 0)
1201
                break;
1202
            CPLAssert((nCurPos % poHeader->nSectorSize) == 0);
1203
        }
1204
        else if ((nCurPos % poHeader->nSectorSize) == 0 &&
1205
                 nToWrite >= static_cast<size_t>(poHeader->nSectorSize))
1206
        {
1207
            if (!FlushDirty())
1208
                break;
1209
1210
            bWBDirty = true;
1211
            nWBOffset = nCurPos;
1212
            nWBSize = poHeader->nSectorSize;
1213
            memcpy(pabyWB, pabyBuffer, poHeader->nSectorSize);
1214
            pabyBuffer += poHeader->nSectorSize;
1215
            nToWrite -= poHeader->nSectorSize;
1216
            nCurPos += poHeader->nSectorSize;
1217
            if (nCurPos > poHeader->nPayloadFileSize)
1218
            {
1219
                bUpdateHeader = true;
1220
                poHeader->nPayloadFileSize = nCurPos;
1221
            }
1222
        }
1223
        else
1224
        {
1225
            if (!FlushDirty())
1226
                break;
1227
1228
            const vsi_l_offset nSectorOffset =
1229
                (nCurPos / poHeader->nSectorSize) * poHeader->nSectorSize;
1230
            const vsi_l_offset nLastSectorOffset =
1231
                (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
1232
                poHeader->nSectorSize;
1233
            if (nSectorOffset > nLastSectorOffset &&
1234
                (poHeader->nPayloadFileSize % poHeader->nSectorSize) != 0)
1235
            {
1236
                if (poBaseHandle->Seek(
1237
                        poHeader->nHeaderSize + nLastSectorOffset, 0) == 0 &&
1238
                    poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 1 &&
1239
                    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
                    memset(
1249
                        pabyWB + poHeader->nPayloadFileSize - nLastSectorOffset,
1250
                        0,
1251
                        static_cast<int>(
1252
                            poHeader->nSectorSize -
1253
                            (poHeader->nPayloadFileSize - nLastSectorOffset)));
1254
1255
                    if (poBaseHandle->Seek(
1256
                            poHeader->nHeaderSize + nLastSectorOffset, 0) == 0)
1257
                    {
1258
                        EncryptBlock(pabyWB, nLastSectorOffset);
1259
                        poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1);
1260
                    }
1261
                }
1262
            }
1263
            poBaseHandle->Seek(poHeader->nHeaderSize + nSectorOffset, SEEK_SET);
1264
            if (poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 0 ||
1265
                !DecryptBlock(pabyWB, nSectorOffset))
1266
            {
1267
                memset(pabyWB, 0, poHeader->nSectorSize);
1268
            }
1269
            else if (nSectorOffset + poHeader->nSectorSize >
1270
                     poHeader->nPayloadFileSize)
1271
            {
1272
                // If the last sector was padded with random values,
1273
                // decrypt it to 0 in case of update scenarios.
1274
                CPLAssert(nSectorOffset < poHeader->nPayloadFileSize);
1275
                memset(pabyWB + poHeader->nPayloadFileSize - nSectorOffset, 0,
1276
                       nSectorOffset + poHeader->nSectorSize -
1277
                           poHeader->nPayloadFileSize);
1278
            }
1279
            nWBOffset = nSectorOffset;
1280
            nWBSize = poHeader->nSectorSize;
1281
        }
1282
    }
1283
1284
    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
    return nRet;
1290
}
1291
1292
/************************************************************************/
1293
/*                              Truncate()                              */
1294
/************************************************************************/
1295
1296
// Returns 0 on success.  Returns -1 on error.
1297
int VSICryptFileHandle::Truncate(vsi_l_offset nNewSize)
1298
{
1299
#ifdef VERBOSE_VSICRYPT
1300
    CPLDebug("VSICRYPT", "Truncate(" CPL_FRMT_GUIB ")", nNewSize);
1301
#endif
1302
    if ((nPerms & VSICRYPT_WRITE) == 0)
1303
        return -1;
1304
1305
    if (!FlushDirty())
1306
        return -1;
1307
    if (poBaseHandle->Truncate(
1308
            poHeader->nHeaderSize +
1309
            cpl::div_round_up(nNewSize, poHeader->nSectorSize) *
1310
                poHeader->nSectorSize) != 0)
1311
        return -1;
1312
    bUpdateHeader = true;
1313
    poHeader->nPayloadFileSize = nNewSize;
1314
    return 0;
1315
}
1316
1317
/************************************************************************/
1318
/*                                Eof()                                 */
1319
/************************************************************************/
1320
1321
int VSICryptFileHandle::Eof()
1322
{
1323
#ifdef VERBOSE_VSICRYPT
1324
    CPLDebug("VSICRYPT", "Eof() = %d", static_cast<int>(bEOF));
1325
#endif
1326
    return bEOF;
1327
}
1328
1329
/************************************************************************/
1330
/*                               Error()                                */
1331
/************************************************************************/
1332
1333
int VSICryptFileHandle::Error()
1334
{
1335
#ifdef VERBOSE_VSICRYPT
1336
    CPLDebug("VSICRYPT", "Error() = %d", static_cast<int>(bError));
1337
#endif
1338
    return bError;
1339
}
1340
1341
/************************************************************************/
1342
/*                              ClearErr()                              */
1343
/************************************************************************/
1344
1345
void VSICryptFileHandle::ClearErr()
1346
{
1347
#ifdef VERBOSE_VSICRYPT
1348
    CPLDebug("VSICRYPT", "ClearErr()");
1349
#endif
1350
    bEOF = false;
1351
    bError = false;
1352
    poBaseHandle->ClearErr();
1353
}
1354
1355
/************************************************************************/
1356
/*                               Flush()                                */
1357
/************************************************************************/
1358
1359
int VSICryptFileHandle::Flush()
1360
{
1361
#ifdef VERBOSE_VSICRYPT
1362
    CPLDebug("VSICRYPT", "Flush()");
1363
#endif
1364
    if (!FlushDirty())
1365
    {
1366
        return -1;
1367
    }
1368
    if ((nPerms & VSICRYPT_WRITE))
1369
    {
1370
        if (bLastSectorWasModified &&
1371
            (poHeader->nPayloadFileSize % poHeader->nSectorSize) != 0)
1372
        {
1373
            const vsi_l_offset nLastSectorOffset =
1374
                (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
1375
                poHeader->nSectorSize;
1376
            if (poBaseHandle->Seek(poHeader->nHeaderSize + nLastSectorOffset,
1377
                                   0) == 0 &&
1378
                poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 1 &&
1379
                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
                CryptoPP::OS_GenerateRandomBlock(
1389
                    false,  // Do not need cryptographic randomness.
1390
                    reinterpret_cast<cryptopp_byte *>(
1391
                        pabyWB + poHeader->nPayloadFileSize -
1392
                        nLastSectorOffset),
1393
                    static_cast<int>(
1394
                        poHeader->nSectorSize -
1395
                        (poHeader->nPayloadFileSize - nLastSectorOffset)));
1396
1397
                if (poBaseHandle->Seek(
1398
                        poHeader->nHeaderSize + nLastSectorOffset, 0) == 0)
1399
                {
1400
                    EncryptBlock(pabyWB, nLastSectorOffset);
1401
                    poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1);
1402
                }
1403
            }
1404
        }
1405
        bLastSectorWasModified = false;
1406
        if (poBaseHandle->Flush() != 0)
1407
            return -1;
1408
    }
1409
    if (bUpdateHeader)
1410
    {
1411
#ifdef VERBOSE_VSICRYPT
1412
        CPLDebug("VSICRYPT", "nPayloadFileSize = " CPL_FRMT_GUIB,
1413
                 poHeader->nPayloadFileSize);
1414
#endif
1415
        if (!poHeader->WriteToFile(poBaseHandle, poEncCipher))
1416
            return -1;
1417
    }
1418
1419
    return 0;
1420
}
1421
1422
/************************************************************************/
1423
/*                               Close()                                */
1424
/************************************************************************/
1425
1426
int VSICryptFileHandle::Close()
1427
{
1428
    int nRet = 0;
1429
    if (poBaseHandle != nullptr && poHeader != nullptr)
1430
    {
1431
        if (Flush() != 0)
1432
            return -1;
1433
        nRet = poBaseHandle->Close();
1434
        delete poBaseHandle;
1435
        poBaseHandle = nullptr;
1436
    }
1437
#ifdef VERBOSE_VSICRYPT
1438
    CPLDebug("VSICRYPT", "Close(%s)", osBaseFilename.c_str());
1439
#endif
1440
    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
VSICryptFilesystemHandler::VSICryptFilesystemHandler()
1469
{
1470
}
1471
1472
/************************************************************************/
1473
/*                     ~VSICryptFilesystemHandler()                     */
1474
/************************************************************************/
1475
1476
VSICryptFilesystemHandler::~VSICryptFilesystemHandler()
1477
{
1478
}
1479
1480
/************************************************************************/
1481
/*                            GetFilename()                             */
1482
/************************************************************************/
1483
1484
static CPLString GetFilename(const char *pszFilename)
1485
{
1486
    if (strcmp(pszFilename, VSICRYPT_PREFIX_WITHOUT_SLASH) == 0)
1487
        pszFilename = VSICRYPT_PREFIX;
1488
1489
    CPLAssert(strncmp(pszFilename, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) ==
1490
              0);
1491
    pszFilename += strlen(VSICRYPT_PREFIX);
1492
    const char *pszFileArg = strstr(pszFilename, "file=");
1493
    if (pszFileArg == nullptr)
1494
        return pszFilename;
1495
    CPLString osRet(pszFileArg + strlen("file="));
1496
    return osRet;
1497
}
1498
1499
/************************************************************************/
1500
/*                            GetArgument()                             */
1501
/************************************************************************/
1502
1503
static CPLString GetArgument(const char *pszFilename, const char *pszParamName,
1504
                             const char *pszDefault = "")
1505
{
1506
    CPLString osParamName(pszParamName);
1507
    osParamName += "=";
1508
1509
    const char *pszNeedle = strstr(pszFilename, osParamName);
1510
    if (pszNeedle == nullptr)
1511
        return pszDefault;
1512
1513
    CPLString osRet(pszNeedle + osParamName.size());
1514
    size_t nCommaPos = osRet.find(",");
1515
    if (nCommaPos != std::string::npos)
1516
        osRet.resize(nCommaPos);
1517
    return osRet;
1518
}
1519
1520
/************************************************************************/
1521
/*                               GetKey()                               */
1522
/************************************************************************/
1523
1524
static CPLString GetKey(const char *pszFilename)
1525
{
1526
    CPLString osKey = GetArgument(pszFilename, "key");
1527
    // TODO(schwehr): Make 10U and 1024U into symbolic constants.
1528
    if (osKey.empty())
1529
    {
1530
        const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY", "");
1531
        // Do some form of validation to please Coverity
1532
        CPLAssert(strlen(pszKey) < 10U * 1024U);
1533
        // coverity [tainted_data_transitive]
1534
        osKey = pszKey;
1535
    }
1536
    if (osKey.empty() || EQUAL(osKey, "GENERATE_IT"))
1537
    {
1538
        CPLString osKeyB64(GetArgument(pszFilename, "key_b64"));
1539
        if (osKeyB64.empty())
1540
        {
1541
            const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY_B64", "");
1542
            // Do some form of validation to please Coverity
1543
            CPLAssert(strlen(pszKey) < 10U * 1024U);
1544
            // coverity [tainted_data_transitive]
1545
            osKeyB64 = pszKey;
1546
        }
1547
        if (!osKeyB64.empty())
1548
        {
1549
            GByte *key = reinterpret_cast<GByte *>(CPLStrdup(osKeyB64));
1550
            int nLength = CPLBase64DecodeInPlace(key);
1551
            osKey.assign(reinterpret_cast<const char *>(key), nLength);
1552
            memset(key, 0, osKeyB64.size());
1553
            CPLFree(key);
1554
        }
1555
        // coverity[tainted_data]
1556
        memset(osKeyB64.data(), 0, osKeyB64.size());
1557
    }
1558
    return osKey;
1559
}
1560
1561
/************************************************************************/
1562
/*                                Open()                                */
1563
/************************************************************************/
1564
1565
VSIVirtualHandleUniquePtr
1566
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
    CPLString osFilename(GetFilename(pszFilename));
1574
1575
    CPLString osKey(GetKey(pszFilename));
1576
    if (osKey.empty() && pabyGlobalKey == nullptr)
1577
    {
1578
        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
        return nullptr;
1583
    }
1584
1585
    CPLString osAccess(pszAccess);
1586
    if (strchr(pszAccess, 'b') == nullptr)
1587
        osAccess += "b";
1588
    if (strchr(pszAccess, 'r'))
1589
    {
1590
        auto fpBase = VSIFilesystemHandler::OpenStatic(osFilename, osAccess);
1591
        if (fpBase == nullptr)
1592
            return nullptr;
1593
        auto poHeader = std::make_unique<VSICryptFileHeader>();
1594
        if (!poHeader->ReadFromFile(fpBase.get(), osKey))
1595
        {
1596
            memset(osKey.data(), 0, osKey.size());
1597
            return nullptr;
1598
        }
1599
1600
        auto poHandle = std::make_unique<VSICryptFileHandle>(
1601
            osFilename, fpBase.release(), poHeader.release(),
1602
            strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
1603
                                   : VSICRYPT_READ);
1604
        if (!poHandle->Init(osKey, false))
1605
        {
1606
            memset(osKey.data(), 0, osKey.size());
1607
            poHandle.reset();
1608
        }
1609
        memset(osKey.data(), 0, osKey.size());
1610
        return VSIVirtualHandleUniquePtr(poHandle.release());
1611
    }
1612
    else if (strchr(pszAccess, 'w'))
1613
    {
1614
        CPLString osAlg(GetArgument(pszFilename, "alg",
1615
                                    CPLGetConfigOption("VSICRYPT_ALG", "AES")));
1616
        VSICryptAlg eAlg = GetAlg(osAlg);
1617
1618
        VSICryptMode eMode = GetMode(GetArgument(
1619
            pszFilename, "mode", CPLGetConfigOption("VSICRYPT_MODE", "CBC")));
1620
1621
        CPLString osFreeText =
1622
            GetArgument(pszFilename, "freetext",
1623
                        CPLGetConfigOption("VSICRYPT_FREETEXT", ""));
1624
1625
        CPLString osIV = GetArgument(pszFilename, "iv",
1626
                                     CPLGetConfigOption("VSICRYPT_IV", ""));
1627
1628
        int nSectorSize = atoi(
1629
            GetArgument(pszFilename, "sector_size",
1630
                        CPLGetConfigOption("VSICRYPT_SECTOR_SIZE", "512")));
1631
        if (nSectorSize <= 0 || nSectorSize >= 65535)
1632
        {
1633
            CPLError(CE_Warning, CPLE_NotSupported,
1634
                     "Invalid value for sector_size. Defaulting to 512.");
1635
            nSectorSize = 512;
1636
        }
1637
1638
        const bool bAddKeyCheck = CPLTestBool(
1639
            GetArgument(pszFilename, "add_key_check",
1640
                        CPLGetConfigOption("VSICRYPT_ADD_KEY_CHECK", "NO")));
1641
1642
        /* Generate random initial vector */
1643
        CryptoPP::BlockCipher *poBlock = GetEncBlockCipher(eAlg);
1644
        if (poBlock == nullptr)
1645
        {
1646
            CPLError(CE_Failure, CPLE_AppDefined,
1647
                     "Cipher algorithm not supported in this build: %s",
1648
                     osAlg.c_str());
1649
            memset(osKey.data(), 0, osKey.size());
1650
            return nullptr;
1651
        }
1652
        int nMinKeySize = static_cast<int>(poBlock->MinKeyLength());
1653
        int nMaxKeySize = static_cast<int>(poBlock->MaxKeyLength());
1654
        int nBlockSize = static_cast<int>(poBlock->BlockSize());
1655
        delete poBlock;
1656
1657
        if (!osIV.empty())
1658
        {
1659
            if (static_cast<int>(osIV.size()) != nBlockSize)
1660
            {
1661
                CPLError(CE_Failure, CPLE_AppDefined,
1662
                         "IV should be %d byte large", nBlockSize);
1663
                memset(osKey.data(), 0, osKey.size());
1664
                return nullptr;
1665
            }
1666
        }
1667
        else
1668
        {
1669
            osIV.resize(nBlockSize);
1670
            CryptoPP::OS_GenerateRandomBlock(
1671
                false,  // Do not need cryptographic randomness.
1672
                reinterpret_cast<cryptopp_byte *>(osIV.data()), osIV.size());
1673
        }
1674
1675
        if (EQUAL(osKey, "GENERATE_IT"))
1676
        {
1677
            osKey.resize(nMaxKeySize);
1678
            CPLDebug("VSICRYPT",
1679
                     "Generating key. This might take some time...");
1680
            CryptoPP::OS_GenerateRandomBlock(
1681
                // Need cryptographic randomness.
1682
                // Config option for speeding tests.
1683
                CPLTestBool(
1684
                    CPLGetConfigOption("VSICRYPT_CRYPTO_RANDOM", "TRUE")),
1685
                reinterpret_cast<cryptopp_byte *>(osKey.data()), osKey.size());
1686
1687
            char *pszB64 =
1688
                CPLBase64Encode(static_cast<int>(osKey.size()),
1689
                                reinterpret_cast<const GByte *>(osKey.c_str()));
1690
            if (CPLTestBool(CPLGetConfigOption("VSICRYPT_DISPLAY_GENERATED_KEY",
1691
                                               "TRUE")))
1692
            {
1693
                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
            CPLSetConfigOption("VSICRYPT_KEY_B64", pszB64);
1699
            CPLFree(pszB64);
1700
        }
1701
1702
        const int nKeyLength =
1703
            !osKey.empty() ? static_cast<int>(osKey.size()) : nGlobalKeySize;
1704
        if (nKeyLength < nMinKeySize)
1705
        {
1706
            CPLError(CE_Failure, CPLE_AppDefined,
1707
                     "Key is too short: %d bytes. Should be at least %d bytes",
1708
                     nKeyLength, nMinKeySize);
1709
            memset(osKey.data(), 0, osKey.size());
1710
            return nullptr;
1711
        }
1712
1713
        auto fpBase = VSIFilesystemHandler::OpenStatic(osFilename, osAccess);
1714
        if (fpBase == nullptr)
1715
        {
1716
            memset(osKey.data(), 0, osKey.size());
1717
            return nullptr;
1718
        }
1719
1720
        auto poHeader = std::make_unique<VSICryptFileHeader>();
1721
        poHeader->osIV = osIV;
1722
        CPL_IGNORE_RET_VAL(osIV);
1723
        poHeader->eAlg = eAlg;
1724
        poHeader->eMode = eMode;
1725
        poHeader->nSectorSize = static_cast<GUInt16>(nSectorSize);
1726
        poHeader->osFreeText = std::move(osFreeText);
1727
        poHeader->bAddKeyCheck = bAddKeyCheck;
1728
1729
        auto poHandle = std::make_unique<VSICryptFileHandle>(
1730
            osFilename, fpBase.release(), poHeader.release(),
1731
            strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
1732
                                   : VSICRYPT_WRITE);
1733
        if (!poHandle->Init(osKey, true))
1734
        {
1735
            memset(osKey.data(), 0, osKey.size());
1736
            poHandle.reset();
1737
        }
1738
        memset(osKey.data(), 0, osKey.size());
1739
        return VSIVirtualHandleUniquePtr(poHandle.release());
1740
    }
1741
    else if (strchr(pszAccess, 'a'))
1742
    {
1743
        auto fpBase = VSIFilesystemHandler::OpenStatic(osFilename, "rb+");
1744
        if (fpBase == nullptr)
1745
        {
1746
            memset(osKey.data(), 0, osKey.size());
1747
            return VSIFilesystemHandler::OpenStatic(pszFilename, "wb+");
1748
        }
1749
        auto poHeader = std::make_unique<VSICryptFileHeader>();
1750
        if (!poHeader->ReadFromFile(fpBase.get(), osKey))
1751
        {
1752
            memset(osKey.data(), 0, osKey.size());
1753
            return nullptr;
1754
        }
1755
1756
        auto poHandle = std::make_unique<VSICryptFileHandle>(
1757
            osFilename, fpBase.release(), poHeader.release(),
1758
            VSICRYPT_READ | VSICRYPT_WRITE);
1759
        if (!poHandle->Init(osKey))
1760
        {
1761
            poHandle.reset();
1762
        }
1763
        memset(osKey.data(), 0, osKey.size());
1764
        if (poHandle != nullptr)
1765
            poHandle->Seek(0, SEEK_END);
1766
        return VSIVirtualHandleUniquePtr(poHandle.release());
1767
    }
1768
1769
    return nullptr;
1770
}
1771
1772
/************************************************************************/
1773
/*                                Stat()                                */
1774
/************************************************************************/
1775
1776
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
    CPLString osFilename(GetFilename(pszFilename));
1783
    if (VSIStatExL(osFilename, pStatBuf, nFlags) != 0)
1784
        return -1;
1785
    VSIVirtualHandle *fp =
1786
        reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osFilename, "rb"));
1787
    if (fp == nullptr)
1788
        return -1;
1789
    VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1790
    CPLString osKey(GetKey(pszFilename));
1791
    if (!poHeader->ReadFromFile(fp, osKey))
1792
    {
1793
        memset(osKey.data(), 0, osKey.size());
1794
        fp->Close();
1795
        delete fp;
1796
        delete poHeader;
1797
        return -1;
1798
    }
1799
    memset(osKey.data(), 0, osKey.size());
1800
    fp->Close();
1801
    delete fp;
1802
    if (poHeader)
1803
    {
1804
        pStatBuf->st_size = poHeader->nPayloadFileSize;
1805
        delete poHeader;
1806
        return 0;
1807
    }
1808
    else
1809
        return -1;
1810
}
1811
1812
/************************************************************************/
1813
/*                               Unlink()                               */
1814
/************************************************************************/
1815
1816
int VSICryptFilesystemHandler::Unlink(const char *pszFilename)
1817
{
1818
    return VSIUnlink(GetFilename(pszFilename));
1819
}
1820
1821
/************************************************************************/
1822
/*                               Rename()                               */
1823
/************************************************************************/
1824
1825
int VSICryptFilesystemHandler::Rename(const char *oldpath, const char *newpath,
1826
                                      GDALProgressFunc, void *)
1827
{
1828
    CPLString osNewPath;
1829
    if (strncmp(newpath, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) == 0)
1830
        osNewPath = GetFilename(newpath);
1831
    else
1832
        osNewPath = newpath;
1833
1834
    return VSIRename(GetFilename(oldpath), osNewPath);
1835
}
1836
1837
/************************************************************************/
1838
/*                             ReadDirEx()                              */
1839
/************************************************************************/
1840
1841
char **VSICryptFilesystemHandler::ReadDirEx(const char *pszDirname,
1842
                                            int nMaxFiles)
1843
{
1844
#ifdef VERBOSE_VSICRYPT
1845
    CPLDebug("VSICRYPT", "ReadDir(%s)", pszDirname);
1846
#endif
1847
    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
void VSIInstallCryptFileHandler(void)
2043
2044
{
2045
    VSIFileManager::InstallHandler(
2046
        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
}
2068
2069
#else /* HAVE_CRYPTOPP */
2070
2071
class VSIDummyCryptFilesystemHandler : public VSIFilesystemHandler
2072
{
2073
  public:
2074
3
    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
122
    {
2084
122
        CPLError(CE_Failure, CPLE_NotSupported,
2085
122
                 "%s support not available in this build", VSICRYPT_PREFIX);
2086
122
        return -1;
2087
122
    }
2088
};
2089
2090
VSIVirtualHandleUniquePtr VSIDummyCryptFilesystemHandler::Open(
2091
    const char * /* pszFilename */, const char * /* pszAccess */,
2092
    bool /* bSetError */, CSLConstList /* papszOptions */)
2093
22
{
2094
22
    CPLError(CE_Failure, CPLE_NotSupported,
2095
22
             "%s support not available in this build", VSICRYPT_PREFIX);
2096
22
    return nullptr;
2097
22
}
2098
2099
void VSIInstallCryptFileHandler(void)
2100
3
{
2101
3
    VSIFileManager::InstallHandler(
2102
3
        VSICRYPT_PREFIX, std::make_shared<VSIDummyCryptFilesystemHandler>());
2103
3
}
2104
2105
void VSISetCryptKey(const GByte * /* pabyKey */, int /* nKeySize */)
2106
0
{
2107
    // Not supported.
2108
0
}
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 */