Coverage Report

Created: 2025-06-13 06:18

/src/gdal/port/cpl_vsil_crypt.cpp
Line
Count
Source (jump to first uncovered line)
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 SetNewAndDeleteFromCryptoPP(
110
    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 nSize, size_t nMemb) override;
774
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) 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 nSize, size_t nMemb)
1065
{
1066
    size_t nToRead = nSize * nMemb;
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
    int nRet = static_cast<int>((nSize * nMemb - nToRead) / nSize);
1141
#ifdef VERBOSE_VSICRYPT
1142
    CPLDebug("VSICRYPT", "Read ret = %d (nMemb = %d)", nRet,
1143
             static_cast<int>(nMemb));
1144
#endif
1145
    return nRet;
1146
}
1147
1148
/************************************************************************/
1149
/*                                Write()                               */
1150
/************************************************************************/
1151
1152
size_t VSICryptFileHandle::Write(const void *pBuffer, size_t nSize,
1153
                                 size_t nMemb)
1154
{
1155
    size_t nToWrite = nSize * nMemb;
1156
    const GByte *pabyBuffer = static_cast<const GByte *>(pBuffer);
1157
1158
#ifdef VERBOSE_VSICRYPT
1159
    CPLDebug("VSICRYPT",
1160
             "Write(nCurPos=" CPL_FRMT_GUIB ", nToWrite=%d,"
1161
             "nPayloadFileSize=" CPL_FRMT_GUIB
1162
             ",bWBDirty=%d,nWBOffset=" CPL_FRMT_GUIB ",nWBSize=%d)",
1163
             nCurPos, static_cast<int>(nToWrite), poHeader->nPayloadFileSize,
1164
             static_cast<int>(bWBDirty), nWBOffset, nWBSize);
1165
#endif
1166
1167
    if ((nPerms & VSICRYPT_WRITE) == 0)
1168
        return 0;
1169
1170
    if (nCurPos >= (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
1171
                       poHeader->nSectorSize)
1172
    {
1173
        bLastSectorWasModified = true;
1174
    }
1175
1176
    // If seeking past end of file, we need to explicitly encrypt the
1177
    // padding zeroes.
1178
    if (nCurPos > poHeader->nPayloadFileSize && nCurPos > nWBOffset + nWBSize)
1179
    {
1180
        if (!FlushDirty())
1181
            return 0;
1182
        vsi_l_offset nOffset =
1183
            (poHeader->nPayloadFileSize + poHeader->nSectorSize - 1) /
1184
            poHeader->nSectorSize * poHeader->nSectorSize;
1185
        const vsi_l_offset nEndOffset =
1186
            nCurPos / poHeader->nSectorSize * poHeader->nSectorSize;
1187
        for (; nOffset < nEndOffset; nOffset += poHeader->nSectorSize)
1188
        {
1189
            memset(pabyWB, 0, poHeader->nSectorSize);
1190
            EncryptBlock(pabyWB, nOffset);
1191
            poBaseHandle->Seek(poHeader->nHeaderSize + nOffset, SEEK_SET);
1192
            if (poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1) != 1)
1193
                return 0;
1194
            poHeader->nPayloadFileSize = nOffset + poHeader->nSectorSize;
1195
            bUpdateHeader = true;
1196
        }
1197
    }
1198
1199
    while (nToWrite > 0)
1200
    {
1201
        if (nCurPos >= nWBOffset && nCurPos < nWBOffset + nWBSize)
1202
        {
1203
            bWBDirty = true;
1204
            const int nToCopy =
1205
                std::min(static_cast<int>(nToWrite),
1206
                         static_cast<int>(nWBSize - (nCurPos - nWBOffset)));
1207
            memcpy(pabyWB + nCurPos - nWBOffset, pabyBuffer, nToCopy);
1208
            pabyBuffer += nToCopy;
1209
            nToWrite -= nToCopy;
1210
            nCurPos += nToCopy;
1211
            if (nCurPos > poHeader->nPayloadFileSize)
1212
            {
1213
                bUpdateHeader = true;
1214
                poHeader->nPayloadFileSize = nCurPos;
1215
            }
1216
            if (nToWrite == 0)
1217
                break;
1218
            CPLAssert((nCurPos % poHeader->nSectorSize) == 0);
1219
        }
1220
        else if ((nCurPos % poHeader->nSectorSize) == 0 &&
1221
                 nToWrite >= static_cast<size_t>(poHeader->nSectorSize))
1222
        {
1223
            if (!FlushDirty())
1224
                break;
1225
1226
            bWBDirty = true;
1227
            nWBOffset = nCurPos;
1228
            nWBSize = poHeader->nSectorSize;
1229
            memcpy(pabyWB, pabyBuffer, poHeader->nSectorSize);
1230
            pabyBuffer += poHeader->nSectorSize;
1231
            nToWrite -= poHeader->nSectorSize;
1232
            nCurPos += poHeader->nSectorSize;
1233
            if (nCurPos > poHeader->nPayloadFileSize)
1234
            {
1235
                bUpdateHeader = true;
1236
                poHeader->nPayloadFileSize = nCurPos;
1237
            }
1238
        }
1239
        else
1240
        {
1241
            if (!FlushDirty())
1242
                break;
1243
1244
            const vsi_l_offset nSectorOffset =
1245
                (nCurPos / poHeader->nSectorSize) * poHeader->nSectorSize;
1246
            const vsi_l_offset nLastSectorOffset =
1247
                (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
1248
                poHeader->nSectorSize;
1249
            if (nSectorOffset > nLastSectorOffset &&
1250
                (poHeader->nPayloadFileSize % poHeader->nSectorSize) != 0)
1251
            {
1252
                if (poBaseHandle->Seek(
1253
                        poHeader->nHeaderSize + nLastSectorOffset, 0) == 0 &&
1254
                    poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 1 &&
1255
                    DecryptBlock(pabyWB, nLastSectorOffset))
1256
                {
1257
#ifdef VERBOSE_VSICRYPT
1258
                    CPLDebug("VSICRYPT", "Filling %d trailing bytes with 0",
1259
                             static_cast<int>(poHeader->nSectorSize -
1260
                                              (poHeader->nPayloadFileSize -
1261
                                               nLastSectorOffset)));
1262
#endif
1263
                    // Fill with 0.
1264
                    memset(
1265
                        pabyWB + poHeader->nPayloadFileSize - nLastSectorOffset,
1266
                        0,
1267
                        static_cast<int>(
1268
                            poHeader->nSectorSize -
1269
                            (poHeader->nPayloadFileSize - nLastSectorOffset)));
1270
1271
                    if (poBaseHandle->Seek(
1272
                            poHeader->nHeaderSize + nLastSectorOffset, 0) == 0)
1273
                    {
1274
                        EncryptBlock(pabyWB, nLastSectorOffset);
1275
                        poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1);
1276
                    }
1277
                }
1278
            }
1279
            poBaseHandle->Seek(poHeader->nHeaderSize + nSectorOffset, SEEK_SET);
1280
            if (poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 0 ||
1281
                !DecryptBlock(pabyWB, nSectorOffset))
1282
            {
1283
                memset(pabyWB, 0, poHeader->nSectorSize);
1284
            }
1285
            else if (nSectorOffset + poHeader->nSectorSize >
1286
                     poHeader->nPayloadFileSize)
1287
            {
1288
                // If the last sector was padded with random values,
1289
                // decrypt it to 0 in case of update scenarios.
1290
                CPLAssert(nSectorOffset < poHeader->nPayloadFileSize);
1291
                memset(pabyWB + poHeader->nPayloadFileSize - nSectorOffset, 0,
1292
                       nSectorOffset + poHeader->nSectorSize -
1293
                           poHeader->nPayloadFileSize);
1294
            }
1295
            nWBOffset = nSectorOffset;
1296
            nWBSize = poHeader->nSectorSize;
1297
        }
1298
    }
1299
1300
    int nRet = static_cast<int>((nSize * nMemb - nToWrite) / nSize);
1301
#ifdef VERBOSE_VSICRYPT
1302
    CPLDebug("VSICRYPT", "Write ret = %d (nMemb = %d)", nRet,
1303
             static_cast<int>(nMemb));
1304
#endif
1305
    return nRet;
1306
}
1307
1308
/************************************************************************/
1309
/*                             Truncate()                               */
1310
/************************************************************************/
1311
1312
// Returns 0 on success.  Returns -1 on error.
1313
int VSICryptFileHandle::Truncate(vsi_l_offset nNewSize)
1314
{
1315
#ifdef VERBOSE_VSICRYPT
1316
    CPLDebug("VSICRYPT", "Truncate(" CPL_FRMT_GUIB ")", nNewSize);
1317
#endif
1318
    if ((nPerms & VSICRYPT_WRITE) == 0)
1319
        return -1;
1320
1321
    if (!FlushDirty())
1322
        return -1;
1323
    if (poBaseHandle->Truncate(
1324
            poHeader->nHeaderSize +
1325
            cpl::div_round_up(nNewSize, poHeader->nSectorSize) *
1326
                poHeader->nSectorSize) != 0)
1327
        return -1;
1328
    bUpdateHeader = true;
1329
    poHeader->nPayloadFileSize = nNewSize;
1330
    return 0;
1331
}
1332
1333
/************************************************************************/
1334
/*                                Eof()                                 */
1335
/************************************************************************/
1336
1337
int VSICryptFileHandle::Eof()
1338
{
1339
#ifdef VERBOSE_VSICRYPT
1340
    CPLDebug("VSICRYPT", "Eof() = %d", static_cast<int>(bEOF));
1341
#endif
1342
    return bEOF;
1343
}
1344
1345
/************************************************************************/
1346
/*                              Error()                                 */
1347
/************************************************************************/
1348
1349
int VSICryptFileHandle::Error()
1350
{
1351
#ifdef VERBOSE_VSICRYPT
1352
    CPLDebug("VSICRYPT", "Error() = %d", static_cast<int>(bError));
1353
#endif
1354
    return bError;
1355
}
1356
1357
/************************************************************************/
1358
/*                             ClearErr()                               */
1359
/************************************************************************/
1360
1361
void VSICryptFileHandle::ClearErr()
1362
{
1363
#ifdef VERBOSE_VSICRYPT
1364
    CPLDebug("VSICRYPT", "ClearErr()");
1365
#endif
1366
    bEOF = false;
1367
    bError = false;
1368
    poBaseHandle->ClearErr();
1369
}
1370
1371
/************************************************************************/
1372
/*                                 Flush()                              */
1373
/************************************************************************/
1374
1375
int VSICryptFileHandle::Flush()
1376
{
1377
#ifdef VERBOSE_VSICRYPT
1378
    CPLDebug("VSICRYPT", "Flush()");
1379
#endif
1380
    if (!FlushDirty())
1381
    {
1382
        return -1;
1383
    }
1384
    if ((nPerms & VSICRYPT_WRITE))
1385
    {
1386
        if (bLastSectorWasModified &&
1387
            (poHeader->nPayloadFileSize % poHeader->nSectorSize) != 0)
1388
        {
1389
            const vsi_l_offset nLastSectorOffset =
1390
                (poHeader->nPayloadFileSize / poHeader->nSectorSize) *
1391
                poHeader->nSectorSize;
1392
            if (poBaseHandle->Seek(poHeader->nHeaderSize + nLastSectorOffset,
1393
                                   0) == 0 &&
1394
                poBaseHandle->Read(pabyWB, poHeader->nSectorSize, 1) == 1 &&
1395
                DecryptBlock(pabyWB, nLastSectorOffset))
1396
            {
1397
                // Fill with random
1398
#ifdef VERBOSE_VSICRYPT
1399
                CPLDebug("VSICRYPT", "Filling %d trailing bytes with random",
1400
                         static_cast<int>(
1401
                             poHeader->nSectorSize -
1402
                             (poHeader->nPayloadFileSize - nLastSectorOffset)));
1403
#endif
1404
                CryptoPP::OS_GenerateRandomBlock(
1405
                    false,  // Do not need cryptographic randomness.
1406
                    reinterpret_cast<cryptopp_byte *>(
1407
                        pabyWB + poHeader->nPayloadFileSize -
1408
                        nLastSectorOffset),
1409
                    static_cast<int>(
1410
                        poHeader->nSectorSize -
1411
                        (poHeader->nPayloadFileSize - nLastSectorOffset)));
1412
1413
                if (poBaseHandle->Seek(
1414
                        poHeader->nHeaderSize + nLastSectorOffset, 0) == 0)
1415
                {
1416
                    EncryptBlock(pabyWB, nLastSectorOffset);
1417
                    poBaseHandle->Write(pabyWB, poHeader->nSectorSize, 1);
1418
                }
1419
            }
1420
        }
1421
        bLastSectorWasModified = false;
1422
        if (poBaseHandle->Flush() != 0)
1423
            return -1;
1424
    }
1425
    if (bUpdateHeader)
1426
    {
1427
#ifdef VERBOSE_VSICRYPT
1428
        CPLDebug("VSICRYPT", "nPayloadFileSize = " CPL_FRMT_GUIB,
1429
                 poHeader->nPayloadFileSize);
1430
#endif
1431
        if (!poHeader->WriteToFile(poBaseHandle, poEncCipher))
1432
            return -1;
1433
    }
1434
1435
    return 0;
1436
}
1437
1438
/************************************************************************/
1439
/*                                  Close()                             */
1440
/************************************************************************/
1441
1442
int VSICryptFileHandle::Close()
1443
{
1444
    int nRet = 0;
1445
    if (poBaseHandle != nullptr && poHeader != nullptr)
1446
    {
1447
        if (Flush() != 0)
1448
            return -1;
1449
        nRet = poBaseHandle->Close();
1450
        delete poBaseHandle;
1451
        poBaseHandle = nullptr;
1452
    }
1453
#ifdef VERBOSE_VSICRYPT
1454
    CPLDebug("VSICRYPT", "Close(%s)", osBaseFilename.c_str());
1455
#endif
1456
    return nRet;
1457
}
1458
1459
/************************************************************************/
1460
/*                   VSICryptFilesystemHandler                          */
1461
/************************************************************************/
1462
1463
class VSICryptFilesystemHandler final : public VSIFilesystemHandler
1464
{
1465
  public:
1466
    VSICryptFilesystemHandler();
1467
    ~VSICryptFilesystemHandler() override;
1468
1469
    VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
1470
                           bool bSetError,
1471
                           CSLConstList /* papszOptions */) override;
1472
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
1473
             int nFlags) override;
1474
    int Unlink(const char *pszFilename) override;
1475
    int Rename(const char *oldpath, const char *newpath, GDALProgressFunc,
1476
               void *) override;
1477
    char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
1478
};
1479
1480
/************************************************************************/
1481
/*                   VSICryptFilesystemHandler()                        */
1482
/************************************************************************/
1483
1484
VSICryptFilesystemHandler::VSICryptFilesystemHandler()
1485
{
1486
}
1487
1488
/************************************************************************/
1489
/*                    ~VSICryptFilesystemHandler()                      */
1490
/************************************************************************/
1491
1492
VSICryptFilesystemHandler::~VSICryptFilesystemHandler()
1493
{
1494
}
1495
1496
/************************************************************************/
1497
/*                             GetFilename()                            */
1498
/************************************************************************/
1499
1500
static CPLString GetFilename(const char *pszFilename)
1501
{
1502
    if (strcmp(pszFilename, VSICRYPT_PREFIX_WITHOUT_SLASH) == 0)
1503
        pszFilename = VSICRYPT_PREFIX;
1504
1505
    CPLAssert(strncmp(pszFilename, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) ==
1506
              0);
1507
    pszFilename += strlen(VSICRYPT_PREFIX);
1508
    const char *pszFileArg = strstr(pszFilename, "file=");
1509
    if (pszFileArg == nullptr)
1510
        return pszFilename;
1511
    CPLString osRet(pszFileArg + strlen("file="));
1512
    return osRet;
1513
}
1514
1515
/************************************************************************/
1516
/*                             GetArgument()                            */
1517
/************************************************************************/
1518
1519
static CPLString GetArgument(const char *pszFilename, const char *pszParamName,
1520
                             const char *pszDefault = "")
1521
{
1522
    CPLString osParamName(pszParamName);
1523
    osParamName += "=";
1524
1525
    const char *pszNeedle = strstr(pszFilename, osParamName);
1526
    if (pszNeedle == nullptr)
1527
        return pszDefault;
1528
1529
    CPLString osRet(pszNeedle + osParamName.size());
1530
    size_t nCommaPos = osRet.find(",");
1531
    if (nCommaPos != std::string::npos)
1532
        osRet.resize(nCommaPos);
1533
    return osRet;
1534
}
1535
1536
/************************************************************************/
1537
/*                               GetKey()                               */
1538
/************************************************************************/
1539
1540
static CPLString GetKey(const char *pszFilename)
1541
{
1542
    CPLString osKey = GetArgument(pszFilename, "key");
1543
    // TODO(schwehr): Make 10U and 1024U into symbolic constants.
1544
    if (osKey.empty())
1545
    {
1546
        const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY", "");
1547
        // Do some form of validation to please Coverity
1548
        CPLAssert(strlen(pszKey) < 10U * 1024U);
1549
        // coverity [tainted_data_transitive]
1550
        osKey = pszKey;
1551
    }
1552
    if (osKey.empty() || EQUAL(osKey, "GENERATE_IT"))
1553
    {
1554
        CPLString osKeyB64(GetArgument(pszFilename, "key_b64"));
1555
        if (osKeyB64.empty())
1556
        {
1557
            const char *pszKey = CPLGetConfigOption("VSICRYPT_KEY_B64", "");
1558
            // Do some form of validation to please Coverity
1559
            CPLAssert(strlen(pszKey) < 10U * 1024U);
1560
            // coverity [tainted_data_transitive]
1561
            osKeyB64 = pszKey;
1562
        }
1563
        if (!osKeyB64.empty())
1564
        {
1565
            GByte *key = reinterpret_cast<GByte *>(CPLStrdup(osKeyB64));
1566
            int nLength = CPLBase64DecodeInPlace(key);
1567
            osKey.assign(reinterpret_cast<const char *>(key), nLength);
1568
            memset(key, 0, osKeyB64.size());
1569
            CPLFree(key);
1570
        }
1571
        // coverity[tainted_data]
1572
        memset(osKeyB64.data(), 0, osKeyB64.size());
1573
    }
1574
    return osKey;
1575
}
1576
1577
/************************************************************************/
1578
/*                                Open()                                */
1579
/************************************************************************/
1580
1581
VSIVirtualHandle *
1582
VSICryptFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
1583
                                bool /* bSetError */,
1584
                                CSLConstList /* papszOptions */)
1585
{
1586
#ifdef VERBOSE_VSICRYPT
1587
    CPLDebug("VSICRYPT", "Open(%s, %s)", pszFilename, pszAccess);
1588
#endif
1589
    CPLString osFilename(GetFilename(pszFilename));
1590
1591
    CPLString osKey(GetKey(pszFilename));
1592
    if (osKey.empty() && pabyGlobalKey == nullptr)
1593
    {
1594
        CPLError(CE_Failure, CPLE_AppDefined,
1595
                 "Encryption key not defined as key/key_b64 parameter, "
1596
                 "VSICRYPT_KEY/VSICRYPT_KEY_B64 configuration option or "
1597
                 "VSISetCryptKey() API");
1598
        return nullptr;
1599
    }
1600
1601
    CPLString osAccess(pszAccess);
1602
    if (strchr(pszAccess, 'b') == nullptr)
1603
        osAccess += "b";
1604
    if (strchr(pszAccess, 'r'))
1605
    {
1606
        VSIVirtualHandle *fpBase = reinterpret_cast<VSIVirtualHandle *>(
1607
            VSIFOpenL(osFilename, osAccess));
1608
        if (fpBase == nullptr)
1609
            return nullptr;
1610
        VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1611
        if (!poHeader->ReadFromFile(fpBase, osKey))
1612
        {
1613
            memset(osKey.data(), 0, osKey.size());
1614
            fpBase->Close();
1615
            delete fpBase;
1616
            delete poHeader;
1617
            return nullptr;
1618
        }
1619
1620
        VSICryptFileHandle *poHandle = new VSICryptFileHandle(
1621
            osFilename, fpBase, poHeader,
1622
            strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
1623
                                   : VSICRYPT_READ);
1624
        if (!poHandle->Init(osKey, false))
1625
        {
1626
            memset(osKey.data(), 0, osKey.size());
1627
            delete poHandle;
1628
            poHandle = nullptr;
1629
        }
1630
        memset(osKey.data(), 0, osKey.size());
1631
        return poHandle;
1632
    }
1633
    else if (strchr(pszAccess, 'w'))
1634
    {
1635
        CPLString osAlg(GetArgument(pszFilename, "alg",
1636
                                    CPLGetConfigOption("VSICRYPT_ALG", "AES")));
1637
        VSICryptAlg eAlg = GetAlg(osAlg);
1638
1639
        VSICryptMode eMode = GetMode(GetArgument(
1640
            pszFilename, "mode", CPLGetConfigOption("VSICRYPT_MODE", "CBC")));
1641
1642
        CPLString osFreeText =
1643
            GetArgument(pszFilename, "freetext",
1644
                        CPLGetConfigOption("VSICRYPT_FREETEXT", ""));
1645
1646
        CPLString osIV = GetArgument(pszFilename, "iv",
1647
                                     CPLGetConfigOption("VSICRYPT_IV", ""));
1648
1649
        int nSectorSize = atoi(
1650
            GetArgument(pszFilename, "sector_size",
1651
                        CPLGetConfigOption("VSICRYPT_SECTOR_SIZE", "512")));
1652
        if (nSectorSize <= 0 || nSectorSize >= 65535)
1653
        {
1654
            CPLError(CE_Warning, CPLE_NotSupported,
1655
                     "Invalid value for sector_size. Defaulting to 512.");
1656
            nSectorSize = 512;
1657
        }
1658
1659
        const bool bAddKeyCheck = CPLTestBool(
1660
            GetArgument(pszFilename, "add_key_check",
1661
                        CPLGetConfigOption("VSICRYPT_ADD_KEY_CHECK", "NO")));
1662
1663
        /* Generate random initial vector */
1664
        CryptoPP::BlockCipher *poBlock = GetEncBlockCipher(eAlg);
1665
        if (poBlock == nullptr)
1666
        {
1667
            CPLError(CE_Failure, CPLE_AppDefined,
1668
                     "Cipher algorithm not supported in this build: %s",
1669
                     osAlg.c_str());
1670
            memset(osKey.data(), 0, osKey.size());
1671
            return nullptr;
1672
        }
1673
        int nMinKeySize = static_cast<int>(poBlock->MinKeyLength());
1674
        int nMaxKeySize = static_cast<int>(poBlock->MaxKeyLength());
1675
        int nBlockSize = static_cast<int>(poBlock->BlockSize());
1676
        delete poBlock;
1677
1678
        if (!osIV.empty())
1679
        {
1680
            if (static_cast<int>(osIV.size()) != nBlockSize)
1681
            {
1682
                CPLError(CE_Failure, CPLE_AppDefined,
1683
                         "IV should be %d byte large", nBlockSize);
1684
                memset(osKey.data(), 0, osKey.size());
1685
                return nullptr;
1686
            }
1687
        }
1688
        else
1689
        {
1690
            osIV.resize(nBlockSize);
1691
            CryptoPP::OS_GenerateRandomBlock(
1692
                false,  // Do not need cryptographic randomness.
1693
                reinterpret_cast<cryptopp_byte *>(osIV.data()), osIV.size());
1694
        }
1695
1696
        if (EQUAL(osKey, "GENERATE_IT"))
1697
        {
1698
            osKey.resize(nMaxKeySize);
1699
            CPLDebug("VSICRYPT",
1700
                     "Generating key. This might take some time...");
1701
            CryptoPP::OS_GenerateRandomBlock(
1702
                // Need cryptographic randomness.
1703
                // Config option for speeding tests.
1704
                CPLTestBool(
1705
                    CPLGetConfigOption("VSICRYPT_CRYPTO_RANDOM", "TRUE")),
1706
                reinterpret_cast<cryptopp_byte *>(osKey.data()), osKey.size());
1707
1708
            char *pszB64 =
1709
                CPLBase64Encode(static_cast<int>(osKey.size()),
1710
                                reinterpret_cast<const GByte *>(osKey.c_str()));
1711
            if (CPLTestBool(CPLGetConfigOption("VSICRYPT_DISPLAY_GENERATED_KEY",
1712
                                               "TRUE")))
1713
            {
1714
                CPLError(CE_Failure, CPLE_AppDefined,
1715
                         "BASE64 key '%s' has been generated, and installed in "
1716
                         "the VSICRYPT_KEY_B64 configuration option.",
1717
                         pszB64);
1718
            }
1719
            CPLSetConfigOption("VSICRYPT_KEY_B64", pszB64);
1720
            CPLFree(pszB64);
1721
        }
1722
1723
        const int nKeyLength =
1724
            !osKey.empty() ? static_cast<int>(osKey.size()) : nGlobalKeySize;
1725
        if (nKeyLength < nMinKeySize)
1726
        {
1727
            CPLError(CE_Failure, CPLE_AppDefined,
1728
                     "Key is too short: %d bytes. Should be at least %d bytes",
1729
                     nKeyLength, nMinKeySize);
1730
            memset(osKey.data(), 0, osKey.size());
1731
            return nullptr;
1732
        }
1733
1734
        VSIVirtualHandle *fpBase = reinterpret_cast<VSIVirtualHandle *>(
1735
            VSIFOpenL(osFilename, osAccess.c_str()));
1736
        if (fpBase == nullptr)
1737
        {
1738
            memset(osKey.data(), 0, osKey.size());
1739
            return nullptr;
1740
        }
1741
1742
        VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1743
        poHeader->osIV = osIV;
1744
        CPL_IGNORE_RET_VAL(osIV);
1745
        poHeader->eAlg = eAlg;
1746
        poHeader->eMode = eMode;
1747
        poHeader->nSectorSize = static_cast<GUInt16>(nSectorSize);
1748
        poHeader->osFreeText = std::move(osFreeText);
1749
        poHeader->bAddKeyCheck = bAddKeyCheck;
1750
1751
        VSICryptFileHandle *poHandle = new VSICryptFileHandle(
1752
            osFilename, fpBase, poHeader,
1753
            strchr(pszAccess, '+') ? VSICRYPT_READ | VSICRYPT_WRITE
1754
                                   : VSICRYPT_WRITE);
1755
        if (!poHandle->Init(osKey, true))
1756
        {
1757
            memset(osKey.data(), 0, osKey.size());
1758
            delete poHandle;
1759
            poHandle = nullptr;
1760
        }
1761
        memset(osKey.data(), 0, osKey.size());
1762
        return poHandle;
1763
    }
1764
    else if (strchr(pszAccess, 'a'))
1765
    {
1766
        VSIVirtualHandle *fpBase =
1767
            reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osFilename, "rb+"));
1768
        if (fpBase == nullptr)
1769
        {
1770
            memset(osKey.data(), 0, osKey.size());
1771
            return VSIFilesystemHandler::Open(pszFilename, "wb+");
1772
        }
1773
        VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1774
        if (!poHeader->ReadFromFile(fpBase, osKey))
1775
        {
1776
            memset(osKey.data(), 0, osKey.size());
1777
            fpBase->Close();
1778
            delete fpBase;
1779
            delete poHeader;
1780
            return nullptr;
1781
        }
1782
1783
        VSICryptFileHandle *poHandle = new VSICryptFileHandle(
1784
            osFilename, fpBase, poHeader, VSICRYPT_READ | VSICRYPT_WRITE);
1785
        if (!poHandle->Init(osKey))
1786
        {
1787
            delete poHandle;
1788
            poHandle = nullptr;
1789
        }
1790
        memset(osKey.data(), 0, osKey.size());
1791
        if (poHandle != nullptr)
1792
            poHandle->Seek(0, SEEK_END);
1793
        return poHandle;
1794
    }
1795
1796
    return nullptr;
1797
}
1798
1799
/************************************************************************/
1800
/*                                Stat()                                */
1801
/************************************************************************/
1802
1803
int VSICryptFilesystemHandler::Stat(const char *pszFilename,
1804
                                    VSIStatBufL *pStatBuf, int nFlags)
1805
{
1806
#ifdef VERBOSE_VSICRYPT
1807
    CPLDebug("VSICRYPT", "Stat(%s)", pszFilename);
1808
#endif
1809
    CPLString osFilename(GetFilename(pszFilename));
1810
    if (VSIStatExL(osFilename, pStatBuf, nFlags) != 0)
1811
        return -1;
1812
    VSIVirtualHandle *fp =
1813
        reinterpret_cast<VSIVirtualHandle *>(VSIFOpenL(osFilename, "rb"));
1814
    if (fp == nullptr)
1815
        return -1;
1816
    VSICryptFileHeader *poHeader = new VSICryptFileHeader();
1817
    CPLString osKey(GetKey(pszFilename));
1818
    if (!poHeader->ReadFromFile(fp, osKey))
1819
    {
1820
        memset(osKey.data(), 0, osKey.size());
1821
        fp->Close();
1822
        delete fp;
1823
        delete poHeader;
1824
        return -1;
1825
    }
1826
    memset(osKey.data(), 0, osKey.size());
1827
    fp->Close();
1828
    delete fp;
1829
    if (poHeader)
1830
    {
1831
        pStatBuf->st_size = poHeader->nPayloadFileSize;
1832
        delete poHeader;
1833
        return 0;
1834
    }
1835
    else
1836
        return -1;
1837
}
1838
1839
/************************************************************************/
1840
/*                               Unlink()                               */
1841
/************************************************************************/
1842
1843
int VSICryptFilesystemHandler::Unlink(const char *pszFilename)
1844
{
1845
    return VSIUnlink(GetFilename(pszFilename));
1846
}
1847
1848
/************************************************************************/
1849
/*                               Rename()                               */
1850
/************************************************************************/
1851
1852
int VSICryptFilesystemHandler::Rename(const char *oldpath, const char *newpath,
1853
                                      GDALProgressFunc, void *)
1854
{
1855
    CPLString osNewPath;
1856
    if (strncmp(newpath, VSICRYPT_PREFIX, strlen(VSICRYPT_PREFIX)) == 0)
1857
        osNewPath = GetFilename(newpath);
1858
    else
1859
        osNewPath = newpath;
1860
1861
    return VSIRename(GetFilename(oldpath), osNewPath);
1862
}
1863
1864
/************************************************************************/
1865
/*                               ReadDirEx()                            */
1866
/************************************************************************/
1867
1868
char **VSICryptFilesystemHandler::ReadDirEx(const char *pszDirname,
1869
                                            int nMaxFiles)
1870
{
1871
#ifdef VERBOSE_VSICRYPT
1872
    CPLDebug("VSICRYPT", "ReadDir(%s)", pszDirname);
1873
#endif
1874
    return VSIReadDirEx(GetFilename(pszDirname), nMaxFiles);
1875
}
1876
1877
#ifdef VSICRYPT_DRIVER
1878
1879
#include "gdal_priv.h"
1880
1881
/**
1882
 * \brief Evaluate if this is a crypt file.
1883
 *
1884
 * The function signature must match GDALDataset::Identify.
1885
 *
1886
 * @param poOpenInfo The header bytes used for file identification.
1887
 *
1888
 * @return 1 if this is a crypt file or 0 otherwise.
1889
 */
1890
1891
static int VSICryptIdentify(GDALOpenInfo *poOpenInfo)
1892
{
1893
    return poOpenInfo->nHeaderBytes > 8 &&
1894
           memcmp(poOpenInfo->pabyHeader, VSICRYPT_SIGNATURE, 8) == 0;
1895
}
1896
1897
static GDALDataset *VSICryptOpen(GDALOpenInfo *poOpenInfo)
1898
{
1899
    if (!VSICryptIdentify(poOpenInfo))
1900
        return nullptr;
1901
    return GDALOpen(
1902
        (CPLString(VSICRYPT_PREFIX) + poOpenInfo->pszFilename).c_str(),
1903
        poOpenInfo->eAccess);
1904
}
1905
1906
#endif
1907
1908
//! @endcond
1909
1910
/************************************************************************/
1911
/*                   VSIInstallCryptFileHandler()                       */
1912
/************************************************************************/
1913
1914
/**
1915
 * \brief Install /vsicrypt/ encrypted file system handler
1916
 * (requires <a href="http://www.cryptopp.com/">libcrypto++</a>)
1917
 *
1918
 * A special file handler is installed that allows reading/creating/update
1919
 * encrypted files on the fly, with random access capabilities.
1920
 *
1921
 * The cryptographic algorithms used are
1922
 * <a href="https://en.wikipedia.org/wiki/Block_cipher">block ciphers</a>,
1923
 * with symmetric key.
1924
 *
1925
 * In their simplest form, recognized filenames are of the form
1926
 * /vsicrypt//absolute_path/to/file, /vsicrypt/c:/absolute_path/to/file or
1927
 * /vsicrypt/relative/path/to/file.
1928
 *
1929
 * Options can also be used with the following format :
1930
 * /vsicrypt/option1=val1,option2=val2,...,file=/path/to/file
1931
 *
1932
 * They can also be passed as configuration option/environment variable, because
1933
 * in some use cases, the syntax with option in the filename might not properly
1934
 * work with some drivers.
1935
 *
1936
 * In all modes, the encryption key must be provided. There are several ways
1937
 * of doing so :
1938
 * <ul>
1939
 * <li>By adding a key= parameter to the filename, like
1940
 *     /vsicrypt/key=my_secret_key,file=/path/to/file.  Note that this restricts
1941
 *     the key to be in text format, whereas at its full power, it can be binary
1942
 *     content.</li>
1943
 * <li>By adding a key_b64= parameter to the filename, to specify a binary key
1944
 *     expressed in Base64 encoding, like
1945
 *     /vsicrypt/key_b64=th1sl00kslikebase64=,file=/path/to/file.</li>
1946
 * <li>By setting the VSICRYPT_KEY configuration option. The key should be in
1947
 * text format.</li>
1948
 * <li>By setting the VSICRYPT_KEY_B64 configuration option. The key should be
1949
 * encoded in Base64.</li>
1950
 * <li>By using the VSISetCryptKey() C function.</li>
1951
 * </ul>
1952
 *
1953
 * When creating a file, if key=GENERATE_IT or VSICRYPT_KEY=GENERATE_IT is
1954
 * passed, the encryption key will be generated from the pseudo-random number
1955
 * generator of the operating system. The key will be displayed on the standard
1956
 * error stream in a Base64 form (unless the VSICRYPT_DISPLAY_GENERATED_KEY
1957
 * configuration option is set to OFF), and the VSICRYPT_KEY_B64 configuration
1958
 * option will also be set with the Base64 form of the key (so that
1959
 * CPLGetConfigOption("VSICRYPT_KEY_B64", NULL) can be used to get it back).
1960
 *
1961
 * The available options are :
1962
 * <ul>
1963
1964
 * <li>alg=AES/Blowfish/Camellia/CAST256/DES_EDE2/DES_EDE3/MARS/IDEA/RC5/RC6/Serpent/SHACAL2/SKIPJACK/Twofish/XTEA:
1965
 *     to specify the <a href="https://en.wikipedia.org/wiki/Block_cipher">block
1966
 *     cipher</a> algorithm.  The default is AES.  Only used on
1967
 *     creation. Ignored otherwise.  Note: depending on how GDAL is build, if
1968
 *     linked against the DLL version of libcrypto++, only a subset of those
1969
 *     algorithms will be available, namely AES, DES_EDE2, DES_EDE3 and
1970
 *     SKIPJACK.  Also available as VSICRYPT_ALG configuration option.</li>
1971
 * <li>mode=CBC/CFB/OFB/CTR/CBC_CTS: to specify the
1972
 *     <a href="https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation">
1973
 *       block cipher mode of operation</a>.
1974
 *     The default is CBC.
1975
 *     Only used on creation. Ignored otherwise.
1976
 *     Also available as VSICRYPT_MODE configuration option.</li>
1977
 * <li>key=text_key: see above.</li>
1978
 * <li>key_b64=base64_encoded_key: see above.</li>
1979
 * <li>freetext=some_text: to specify a text content that will be written
1980
 *     *unencrypted* in the file header, for informational purposes. Default to
1981
 *     empty.  Only used on creation. Ignored otherwise.
1982
 *     Also available as VSICRYPT_FREETEXT configuration option.</li>
1983
 * <li>sector_size=int_value: to specify the size of the "sector", which is the
1984
 *     unit chunk of information that is encrypted/decrypted. Default to 512
1985
 *     bytes.  The valid values depend on the algorithm and block cipher mode of
1986
 *     operation.  Only used on creation. Ignored otherwise.  Also available as
1987
 *     VSICRYPT_SECTOR_SIZE configuration option.</li>
1988
 * <li>iv=initial_vector_as_text: to specify the Initial Vector. This is an
1989
 *     advanced option that should generally *NOT* be used. It is only useful to
1990
 *     get completely deterministic output given the plaintext, key and other
1991
 *     parameters, which in general *NOT* what you want to do. By default, a
1992
 *     random initial vector of the appropriate size will be generated for each
1993
 *     new file created.  Only used on creation. Ignored otherwise.  Also
1994
 *     available as VSICRYPT_IV configuration option.</li>
1995
1996
 * <li>add_key_check=YES/NO: whether a special value should be encrypted in the
1997
 *     header, so as to be quickly able to determine if the decryption key is
1998
 *     correct.  Defaults to NO.  Only used on creation. Ignored otherwise.
1999
 *     Also available as VSICRYPT_ADD_KEY_CHECK configuration option.</li>
2000
 * <li>file=filename. To specify the filename. This must be the last option put
2001
 *     in the option list (so as to make it possible to use filenames with comma
2002
 *     in them. )
2003
 * </ul>
2004
 *
2005
 * This special file handler can be combined with other virtual filesystems
2006
 * handlers, such as /vsizip. For example,
2007
 * /vsicrypt//vsicurl/path/to/remote/encrypted/file.tif
2008
 *
2009
 * Implementation details:
2010
 *
2011
 * The structure of encrypted files is the following: a header, immediately
2012
 * followed by the encrypted payload (by sectors, i.e. chunks of sector_size
2013
 * bytes).
2014
 *
2015
 * The header structure is the following :
2016
 * <ol>
2017
 * <li>8 bytes. Signature. Fixed value: VSICRYPT.</li>
2018
 * <li>UINT16_LE. Header size (including previous signature bytes).</li>
2019
 * <li>UINT8. Format major version. Current value: 1.</li>
2020
 * <li>UINT8. Format minor version. Current value: 0.</li>
2021
 * <li>UINT16. Sector size.</li>
2022
 * <li>UINT8. Cipher algorithm. Valid values are: 0 = AES (Rijndael), 1 =
2023
 *     Blowfish, 2 = Camellia, 3 = CAST256, 4 = DES_EDE2, 5 = DES_EDE3, 6 =
2024
 *     MARS, 7 = IDEA, 8 = RC5, 9 = RC6, 10 = Serpent, 11 = SHACAL2, 12 =
2025
 *     SKIPJACK, 13 = Twofish, 14 = XTEA.</li>
2026
 * <li>UINT8. Block cipher mode of operation. Valid values are: 0 = CBC, 1 =
2027
 *     CFB, 2 = OFB, 3 = CTR, 4 = CBC_CTS.</li>
2028
 * <li>UINT8. Size in bytes of the Initial Vector.</li>
2029
 * <li>N bytes with the content of the Initial Vector, where N is the value of
2030
 *     the previous field.</li>
2031
 * <li>UINT16_LE. Size in bytes of the free text.</li>
2032
 * <li>N bytes with the content of the free text, where N is the value of the
2033
 *     previous field.</li>
2034
 * <li>UINT8. Size in bytes of encrypted content (key check), or 0 if key check
2035
 *     is absent.</li>
2036
 * <li>N bytes with encrypted content (key check), where N is the value of the
2037
 *     previous field.</li>
2038
 * <li>UINT64_LE. Size of the unencrypted file, in bytes.</li>
2039
 * <li>UINT16_LE. Size in bytes of extra content (of unspecified semantics). For
2040
 *     v1.0, fixed value of 0</li>
2041
 * <li>N bytes with extra content (of unspecified semantics), where N is the
2042
 *     value of the previous field.</li>
2043
 * </ol>
2044
 *
2045
 * This design does not provide any means of authentication or integrity check.
2046
 *
2047
 * Each sector is encrypted/decrypted independently of other sectors.  For that,
2048
 * the Initial Vector contained in the header is XOR'ed with the file offset
2049
 * (relative to plain text file) of the start of the sector being processed, as
2050
 * a 8-byte integer.  More precisely, the first byte of the main IV is XOR'ed
2051
 * with the 8 least-significant bits of the sector offset, the second byte of
2052
 * the main IV is XOR'ed with the following 8 bits of the sector offset,
2053
 * etc... until the 8th byte.
2054
 *
2055
 * This design could potentially be prone to chosen-plaintext attack, for
2056
 * example if the attacker managed to get (part of) an existing encrypted file
2057
 * to be encrypted from plaintext he might have selected.
2058
 *
2059
 * Note: if "hostile" code can explore process content, or attach to it with a
2060
 * debugger, it might be relatively easy to retrieve the encryption key.  A GDAL
2061
 * plugin could for example get the content of configuration options, or list
2062
 * opened datasets and see the key/key_b64 values, so disabling plugin loading
2063
 * might be a first step, as well as linking statically GDAL to application
2064
 * code.  If plugin loading is enabled or GDAL dynamically linked, using
2065
 * VSISetCryptKey() to set the key might make it a bit more complicated to spy
2066
 * the key.  But, as said initially, this is in no way a perfect protection.
2067
 *
2068
 * @since GDAL 2.1.0
2069
 */
2070
void VSIInstallCryptFileHandler(void)
2071
2072
{
2073
    VSIFileManager::InstallHandler(VSICRYPT_PREFIX,
2074
                                   new VSICryptFilesystemHandler);
2075
2076
#ifdef VSICRYPT_DRIVER
2077
    if (GDALGetDriverByName("VSICRYPT") != nullptr)
2078
        return;
2079
2080
    GDALDriver *poDriver = new GDALDriver();
2081
2082
    poDriver->SetDescription("VSICRYPT");
2083
#ifdef GDAL_DCAP_RASTER
2084
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
2085
    poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
2086
#endif
2087
    poDriver->SetMetadataItem(
2088
        GDAL_DMD_LONGNAME, CPLSPrintf("Wrapper for %s files", VSICRYPT_PREFIX));
2089
2090
    poDriver->pfnOpen = VSICryptOpen;
2091
    poDriver->pfnIdentify = VSICryptIdentify;
2092
2093
    GetGDALDriverManager()->RegisterDriver(poDriver);
2094
#endif
2095
}
2096
2097
#else /* HAVE_CRYPTOPP */
2098
2099
class VSIDummyCryptFilesystemHandler : public VSIFilesystemHandler
2100
{
2101
  public:
2102
1
    VSIDummyCryptFilesystemHandler() = default;
2103
2104
    VSIVirtualHandle *Open(const char * /* pszFilename */,
2105
                           const char * /* pszAccess */, bool /* bSetError */,
2106
                           CSLConstList /* papszOptions */) override;
2107
2108
    int Stat(const char * /* pszFilename */, VSIStatBufL * /*pStatBuf */,
2109
             int /* nFlags */) override
2110
0
    {
2111
0
        CPLError(CE_Failure, CPLE_NotSupported,
2112
0
                 "%s support not available in this build", VSICRYPT_PREFIX);
2113
0
        return -1;
2114
0
    }
2115
};
2116
2117
VSIVirtualHandle *VSIDummyCryptFilesystemHandler::Open(
2118
    const char * /* pszFilename */, const char * /* pszAccess */,
2119
    bool /* bSetError */, CSLConstList /* papszOptions */)
2120
0
{
2121
0
    CPLError(CE_Failure, CPLE_NotSupported,
2122
0
             "%s support not available in this build", VSICRYPT_PREFIX);
2123
0
    return nullptr;
2124
0
}
2125
2126
void VSIInstallCryptFileHandler(void)
2127
1
{
2128
1
    VSIFileManager::InstallHandler(VSICRYPT_PREFIX,
2129
1
                                   new VSIDummyCryptFilesystemHandler);
2130
1
}
2131
2132
void VSISetCryptKey(const GByte * /* pabyKey */, int /* nKeySize */)
2133
0
{
2134
    // Not supported.
2135
0
}
2136
2137
#endif  // HAVE_CRYPTOPP
2138
2139
// Below is only useful if using as a plugin over GDAL >= 2.0.
2140
#ifdef VSICRYPT_AUTOLOAD
2141
2142
CPL_C_START
2143
void CPL_DLL GDALRegisterMe();
2144
CPL_C_END
2145
2146
void GDALRegisterMe()
2147
{
2148
    VSIFilesystemHandler *poExistingHandler =
2149
        VSIFileManager::GetHandler(VSICRYPT_PREFIX);
2150
    if (poExistingHandler == VSIFileManager::GetHandler("."))
2151
    {
2152
        // In the case where VSICRYPT_PREFIX is just handled by the regular
2153
        // handler, install the vsicrypt handler (shouldn't happen)
2154
        VSIInstallCryptFileHandler();
2155
    }
2156
    else
2157
    {
2158
        // If there's already an installed handler, then check if it is a
2159
        // dummy one (should normally be the case) or a real one
2160
        CPLErrorReset();
2161
        CPLPushErrorHandler(CPLQuietErrorHandler);
2162
        VSIStatBufL sStat;
2163
        CPL_IGNORE_RET_VAL(VSIStatL(
2164
            (CPLString(VSICRYPT_PREFIX) + "i_do_not_exist").c_str(), &sStat));
2165
        CPLPopErrorHandler();
2166
        if (strstr(CPLGetLastErrorMsg(), "support not available in this build"))
2167
        {
2168
            // Dummy handler. Register the new one, and delete the old one
2169
            VSIInstallCryptFileHandler();
2170
            delete poExistingHandler;
2171
        }
2172
        else
2173
        {
2174
            CPLDebug("VSICRYPT", "GDAL has already a working %s implementation",
2175
                     VSICRYPT_PREFIX);
2176
        }
2177
        CPLErrorReset();
2178
    }
2179
}
2180
2181
#endif /* VSICRYPT_AUTOLOAD */