• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 16615247828

30 Jul 2025 06:33AM UTC coverage: 65.845% (-0.03%) from 65.87%
16615247828

Pull #15942

github

web-flow
Merge 3e4243857 into 4a7b6a621
Pull Request #15942: Optimize reload-zones logic to reduce thread scheduling times

42051 of 92438 branches covered (45.49%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 1 file covered. (100.0%)

48 existing lines in 12 files now uncovered.

127942 of 165732 relevant lines covered (77.2%)

5529729.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.72
/pdns/dnsdistdist/dnsdist-crypto.cc
1
/*
2
 * This file is part of PowerDNS or dnsdist.
3
 * Copyright -- PowerDNS.COM B.V. and its contributors
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of version 2 of the GNU General Public License as
7
 * published by the Free Software Foundation.
8
 *
9
 * In addition, for the avoidance of any doubt, permission is granted to
10
 * link this program with OpenSSL and to (re)distribute the binaries
11
 * produced as the result of such linking.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 */
22
#include <iostream>
23
#include <arpa/inet.h>
24

25
#include "dnsdist-crypto.hh"
26

27
#include "namespaces.hh"
28
#include "noinitvector.hh"
29
#include "misc.hh"
30
#include "base64.hh"
31

32
namespace dnsdist::crypto::authenticated
33
{
34
#ifdef HAVE_LIBSODIUM
35
string newKey(bool base64Encoded)
36
{
759✔
37
  std::string key;
759✔
38
  key.resize(crypto_secretbox_KEYBYTES);
759✔
39

40
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
41
  randombytes_buf(reinterpret_cast<unsigned char*>(key.data()), key.size());
759✔
42

43
  if (!base64Encoded) {
759!
44
    return key;
759✔
45
  }
759✔
46
  return "\"" + Base64Encode(key) + "\"";
47
}
759✔
48

49
bool isValidKey(const std::string& key)
50
{
1,227✔
51
  return key.size() == crypto_secretbox_KEYBYTES;
1,227✔
52
}
1,227✔
53

54
std::string encryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce)
55
{
447✔
56
  if (!isValidKey(key)) {
447!
57
    throw std::runtime_error("Invalid encryption key of size " + std::to_string(key.size()) + " (" + std::to_string(crypto_secretbox_KEYBYTES) + " expected), use setKey() to set a valid key");
58
  }
59

60
  std::string ciphertext;
447✔
61
  ciphertext.resize(msg.length() + crypto_secretbox_MACBYTES);
447✔
62
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
63
  crypto_secretbox_easy(reinterpret_cast<unsigned char*>(ciphertext.data()),
447✔
64
                        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
65
                        reinterpret_cast<const unsigned char*>(msg.data()),
447✔
66
                        msg.length(),
447✔
67
                        nonce.value.data(),
447✔
68
                        // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
69
                        reinterpret_cast<const unsigned char*>(key.data()));
447✔
70

71
  if (incrementNonce) {
447✔
72
    nonce.increment();
330✔
73
  }
330✔
74

75
  return ciphertext;
447✔
76
}
447✔
77

78
std::string decryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce)
79
{
447✔
80
  std::string decrypted;
447✔
81

82
  if (msg.length() < crypto_secretbox_MACBYTES) {
447!
83
    throw std::runtime_error("Could not decrypt message of size " + std::to_string(msg.length()));
84
  }
85

86
  if (!isValidKey(key)) {
447!
87
    throw std::runtime_error("Invalid decryption key of size " + std::to_string(key.size()) + ", use setKey() to set a valid key");
88
  }
89

90
  decrypted.resize(msg.length() - crypto_secretbox_MACBYTES);
447✔
91

92
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
93
  if (crypto_secretbox_open_easy(reinterpret_cast<unsigned char*>(decrypted.data()),
447!
94
                                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
95
                                 reinterpret_cast<const unsigned char*>(msg.data()),
447✔
96
                                 msg.length(),
447✔
97
                                 nonce.value.data(),
447✔
98
                                 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
99
                                 reinterpret_cast<const unsigned char*>(key.data()))
447✔
100
      != 0) {
447✔
101
    throw std::runtime_error("Could not decrypt message, please check that the key configured with setKey() is correct");
102
  }
103

104
  if (incrementNonce) {
447✔
105
    nonce.increment();
330✔
106
  }
330✔
107

108
  return decrypted;
447✔
109
}
447✔
110

111
void Nonce::init()
112
{
447✔
113
  randombytes_buf(value.data(), value.size());
447✔
114
}
447✔
115

116
#elif defined(HAVE_LIBCRYPTO)
117
#include <openssl/evp.h>
118
#include <openssl/rand.h>
119

120
static constexpr size_t s_CHACHA20_POLY1305_KEY_SIZE = 32U;
121
static constexpr size_t s_POLY1305_BLOCK_SIZE = 16U;
122

123
string newKey(bool base64Encoded)
124
{
125
  std::string key;
126
  key.resize(s_CHACHA20_POLY1305_KEY_SIZE);
127
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
128
  if (RAND_priv_bytes(reinterpret_cast<unsigned char*>(key.data()), key.size()) != 1) {
×
129
    throw std::runtime_error("Could not initialize random number generator for cryptographic functions");
130
  }
131
  if (!base64Encoded) {
×
132
    return key;
133
  }
134
  return "\"" + Base64Encode(key) + "\"";
135
}
136

137
bool isValidKey(const std::string& key)
138
{
139
  return key.size() == s_CHACHA20_POLY1305_KEY_SIZE;
140
}
141

142
std::string encryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce)
143
{
144
  if (!isValidKey(key)) {
×
145
    throw std::runtime_error("Invalid encryption key of size " + std::to_string(key.size()) + " (" + std::to_string(s_CHACHA20_POLY1305_KEY_SIZE) + " expected), use setKey() to set a valid key");
146
  }
147

148
  // Each thread gets its own cipher context
149
  static thread_local auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(nullptr, EVP_CIPHER_CTX_free);
150

151
  if (!ctx) {
×
152
    ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
153
    if (!ctx) {
×
154
      throw std::runtime_error("encryptSym: EVP_CIPHER_CTX_new() could not initialize cipher context");
155
    }
156

157
    if (EVP_EncryptInit_ex(ctx.get(), EVP_chacha20_poly1305(), nullptr, nullptr, nullptr) != 1) {
×
158
      throw std::runtime_error("encryptSym: EVP_EncryptInit_ex() could not initialize encryption operation");
159
    }
160
  }
161

162
  std::string ciphertext;
163
  /* plus one so we can access the last byte in EncryptFinal which does nothing for this algo */
164
  ciphertext.resize(s_POLY1305_BLOCK_SIZE + msg.length() + 1);
165
  int outLength{0};
166
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
167
  if (EVP_EncryptInit_ex(ctx.get(), nullptr, nullptr, reinterpret_cast<const unsigned char*>(key.c_str()), nonce.value.data()) != 1) {
×
168
    throw std::runtime_error("encryptSym: EVP_EncryptInit_ex() could not initialize encryption key and IV");
169
  }
170

171
  if (!msg.empty()) {
×
172
    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
173
    if (EVP_EncryptUpdate(ctx.get(),
×
174
                          reinterpret_cast<unsigned char*>(&ciphertext.at(s_POLY1305_BLOCK_SIZE)), &outLength,
175
                          reinterpret_cast<const unsigned char*>(msg.data()), msg.length())
176
        != 1) {
177
      throw std::runtime_error("encryptSym: EVP_EncryptUpdate() could not encrypt message");
178
    }
179
  }
180

181
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
182
  if (EVP_EncryptFinal_ex(ctx.get(), reinterpret_cast<unsigned char*>(&ciphertext.at(s_POLY1305_BLOCK_SIZE + outLength)), &outLength) != 1) {
×
183
    throw std::runtime_error("encryptSym: EVP_EncryptFinal_ex() could finalize message encryption");
184
    ;
185
  }
186

187
  /* Get the tag */
188
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
189
  if (EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_GET_TAG, s_POLY1305_BLOCK_SIZE, ciphertext.data()) != 1) {
×
190
    throw std::runtime_error("encryptSym: EVP_CIPHER_CTX_ctrl() could not get tag");
191
  }
192

193
  if (incrementNonce) {
×
194
    nonce.increment();
195
  }
196

197
  ciphertext.resize(ciphertext.size() - 1);
198
  return ciphertext;
199
}
200

201
std::string decryptSym(const std::string_view& msg, const std::string& key, Nonce& nonce, bool incrementNonce)
202
{
203
  if (msg.length() < s_POLY1305_BLOCK_SIZE) {
×
204
    throw std::runtime_error("Could not decrypt message of size " + std::to_string(msg.length()));
205
  }
206

207
  if (!isValidKey(key)) {
×
208
    throw std::runtime_error("Invalid decryption key of size " + std::to_string(key.size()) + ", use setKey() to set a valid key");
209
  }
210

211
  if (msg.length() == s_POLY1305_BLOCK_SIZE) {
×
212
    if (incrementNonce) {
×
213
      nonce.increment();
214
    }
215
    return std::string();
216
  }
217

218
  // Each thread gets its own cipher context
219
  static thread_local auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(nullptr, EVP_CIPHER_CTX_free);
220
  if (!ctx) {
×
221
    ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
222
    if (!ctx) {
×
223
      throw std::runtime_error("decryptSym: EVP_CIPHER_CTX_new() could not initialize cipher context");
224
    }
225

226
    if (EVP_DecryptInit_ex(ctx.get(), EVP_chacha20_poly1305(), nullptr, nullptr, nullptr) != 1) {
×
227
      throw std::runtime_error("decryptSym: EVP_DecryptInit_ex() could not initialize decryption operation");
228
    }
229
  }
230

231
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
232
  if (EVP_DecryptInit_ex(ctx.get(), nullptr, nullptr, reinterpret_cast<const unsigned char*>(key.c_str()), nonce.value.data()) != 1) {
×
233
    throw std::runtime_error("decryptSym: EVP_DecryptInit_ex() could not initialize decryption key and IV");
234
  }
235

236
  const auto tag = msg.substr(0, s_POLY1305_BLOCK_SIZE);
237
  std::string decrypted;
238
  /* plus one so we can access the last byte in DecryptFinal, which does nothing */
239
  decrypted.resize(msg.length() - s_POLY1305_BLOCK_SIZE + 1);
240
  int outLength{0};
241
  if (msg.size() > s_POLY1305_BLOCK_SIZE) {
×
242
    if (!EVP_DecryptUpdate(ctx.get(),
×
243
                           // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
244
                           reinterpret_cast<unsigned char*>(decrypted.data()), &outLength,
245
                           // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
246
                           reinterpret_cast<const unsigned char*>(&msg.at(s_POLY1305_BLOCK_SIZE)), msg.size() - s_POLY1305_BLOCK_SIZE)) {
247
      throw std::runtime_error("Could not decrypt message (update failed), please check that the key configured with setKey() is correct");
248
    }
249
  }
250

251
  /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
252
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): sorry, OpenSSL's API is terrible
253
  if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_AEAD_SET_TAG, s_POLY1305_BLOCK_SIZE, const_cast<char*>(tag.data()))) {
×
254
    throw std::runtime_error("Could not decrypt message (invalid tag), please check that the key configured with setKey() is correct");
255
  }
256

257
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
258
  if (!EVP_DecryptFinal_ex(ctx.get(), reinterpret_cast<unsigned char*>(&decrypted.at(outLength)), &outLength)) {
×
259
    throw std::runtime_error("Could not decrypt message (final failed), please check that the key configured with setKey() is correct");
260
  }
261

262
  if (incrementNonce) {
×
263
    nonce.increment();
264
  }
265

266
  decrypted.resize(decrypted.size() - 1);
267
  return decrypted;
268
}
269

270
void Nonce::init()
271
{
272
  if (RAND_priv_bytes(value.data(), value.size()) != 1) {
×
273
    throw std::runtime_error("Could not initialize random number generator for cryptographic functions");
274
  }
275
}
276
#endif
277

278
#if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBCRYPTO)
279
void Nonce::merge(const Nonce& lower, const Nonce& higher)
280
{
656✔
281
  constexpr size_t halfSize = std::tuple_size<decltype(value)>{} / 2;
656✔
282
  memcpy(value.data(), lower.value.data(), halfSize);
656✔
283
  memcpy(value.data() + halfSize, higher.value.data() + halfSize, halfSize);
656✔
284
}
656✔
285

286
void Nonce::increment()
287
{
660✔
288
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
289
  auto* ptr = reinterpret_cast<uint32_t*>(value.data());
660✔
290
  uint32_t count = htonl(*ptr);
660✔
291
  *ptr = ntohl(++count);
660✔
292
}
660✔
293

294
#else
295
void Nonce::init()
296
{
297
}
298

299
void Nonce::merge(const Nonce&, const Nonce&)
300
{
301
}
302

303
void Nonce::increment()
304
{
305
}
306

307
std::string encryptSym(const std::string_view& msg, const std::string&, Nonce&, bool)
308
{
309
  return std::string(msg);
310
}
311
std::string decryptSym(const std::string_view& msg, const std::string&, Nonce&, bool)
312
{
313
  return std::string(msg);
314
}
315

316
string newKey(bool)
317
{
318
  return "\"plaintext\"";
319
}
320

321
bool isValidKey(const std::string&)
322
{
323
  return true;
324
}
325

326
#endif
327
}
328

329
#include <cinttypes>
330

331
namespace anonpdns
332
{
333
static char B64Decode1(char cInChar)
334
{
42,121✔
335
  // The incoming character will be A-Z, a-z, 0-9, +, /, or =.
336
  // The idea is to quickly determine which grouping the
337
  // letter belongs to and return the associated value
338
  // without having to search the global encoding string
339
  // (the value we're looking for would be the resulting
340
  // index into that string).
341
  //
342
  // To do that, we'll play some tricks...
343
  unsigned char iIndex = '\0';
42,121✔
344
  switch (cInChar) {
42,121✔
345
  case '+':
223✔
346
    iIndex = 62;
223✔
347
    break;
223✔
348

349
  case '/':
91✔
350
    iIndex = 63;
91✔
351
    break;
91✔
352

353
  case '=':
1,036✔
354
    iIndex = 0;
1,036✔
355
    break;
1,036✔
356

357
  default:
40,771✔
358
    // Must be 'A'-'Z', 'a'-'z', '0'-'9', or an error...
359
    //
360
    // Numerically, small letters are "greater" in value than
361
    // capital letters and numerals (ASCII value), and capital
362
    // letters are "greater" than numerals (again, ASCII value),
363
    // so we check for numerals first, then capital letters,
364
    // and finally small letters.
365
    iIndex = '9' - cInChar;
40,771✔
366
    if (iIndex > 0x3F) {
40,771✔
367
      // Not from '0' to '9'...
368
      iIndex = 'Z' - cInChar;
35,269✔
369
      if (iIndex > 0x3F) {
35,269✔
370
        // Not from 'A' to 'Z'...
371
        iIndex = 'z' - cInChar;
14,487✔
372
        if (iIndex > 0x3F) {
14,487✔
373
          // Invalid character...cannot
374
          // decode!
375
          iIndex = 0x80; // set the high bit
3✔
376
        } // if
3✔
377
        else {
14,484✔
378
          // From 'a' to 'z'
379
          iIndex = (('z' - iIndex) - 'a') + 26;
14,484✔
380
        } // else
14,484✔
381
      } // if
14,487✔
382
      else {
20,782✔
383
        // From 'A' to 'Z'
384
        iIndex = ('Z' - iIndex) - 'A';
20,782✔
385
      } // else
20,782✔
386
    } // if
35,269✔
387
    else {
5,502✔
388
      // Adjust the index...
389
      iIndex = (('9' - iIndex) - '0') + 52;
5,502✔
390
    } // else
5,502✔
391
    break;
40,771✔
392

393
  } // switch
42,121✔
394

395
  return static_cast<char>(iIndex);
42,121✔
396
}
42,121✔
397

398
static inline char B64Encode1(unsigned char input)
399
{
1,793✔
400
  if (input < 26) {
1,793✔
401
    return static_cast<char>('A' + input);
938✔
402
  }
938✔
403
  if (input < 52) {
855✔
404
    return static_cast<char>('a' + (input - 26));
606✔
405
  }
606✔
406
  if (input < 62) {
249✔
407
    return static_cast<char>('0' + (input - 52));
235✔
408
  }
235✔
409
  if (input == 62) {
14!
UNCOV
410
    return '+';
×
UNCOV
411
  }
×
412
  return '/';
14✔
413
};
14✔
414

415
}
416
using namespace anonpdns;
417

418
template <typename Container>
419
int B64Decode(const std::string& strInput, Container& strOutput)
420
{
863✔
421
  // Set up a decoding buffer
422
  long cBuf = 0;
863✔
423
  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
424
  char* pBuf = reinterpret_cast<char*>(&cBuf);
863✔
425

426
  // Decoding management...
427
  int iBitGroup = 0;
863✔
428
  int iInNum = 0;
863✔
429

430
  // While there are characters to process...
431
  //
432
  // We'll decode characters in blocks of 4, as
433
  // there are 4 groups of 6 bits in 3 bytes. The
434
  // incoming Base64 character is first decoded, and
435
  // then it is inserted into the decode buffer
436
  // (with any relevant shifting, as required).
437
  // Later, after all 3 bytes have been reconstituted,
438
  // we assign them to the output string, ultimately
439
  // to be returned as the original message.
440
  int iInSize = static_cast<int>(strInput.size());
863✔
441
  unsigned char cChar = '\0';
863✔
442
  uint8_t pad = 0;
863✔
443
  while (iInNum < iInSize) {
11,392!
444
    // Fill the decode buffer with 4 groups of 6 bits
445
    cBuf = 0; // clear
10,532✔
446
    pad = 0;
10,532✔
447
    for (iBitGroup = 0; iBitGroup < 4; ++iBitGroup) {
52,654!
448
      if (iInNum < iInSize) {
42,125!
449
        // Decode a character
450
        if (strInput.at(iInNum) == '=') {
42,121!
451
          pad++;
1,036✔
452
        }
1,036✔
453
        while (isspace(strInput.at(iInNum))) {
42,121!
454
          iInNum++;
×
455
        }
×
456
        cChar = B64Decode1(strInput.at(iInNum++));
42,121✔
457

458
      } // if
42,121✔
459
      else {
4✔
460
        // Decode a padded zero
461
        cChar = '\0';
4✔
462
      } // else
4✔
463

464
      // Check for valid decode
465
      if (cChar > 0x7F) {
42,125!
466
        return -1;
3✔
467
      }
3✔
468

469
      // Adjust the bits
470
      switch (iBitGroup) {
42,122✔
471
      case 0:
10,532!
472
        // The first group is copied into
473
        // the least significant 6 bits of
474
        // the decode buffer...these 6 bits
475
        // will eventually shift over to be
476
        // the most significant bits of the
477
        // third byte.
478
        cBuf = cBuf | cChar;
10,532✔
479
        break;
10,532✔
480

481
      default:
31,590!
482
        // For groupings 1-3, simply shift
483
        // the bits in the decode buffer over
484
        // by 6 and insert the 6 from the
485
        // current decode character.
486
        cBuf = (cBuf << 6) | cChar;
31,590✔
487
        break;
31,590✔
488

489
      } // switch
42,122✔
490
    } // for
42,122✔
491

492
    // Interpret the resulting 3 bytes...note there
493
    // may have been padding, so those padded bytes
494
    // are actually ignored.
495
#if BYTE_ORDER == BIG_ENDIAN
496
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
497
    strOutput.push_back(pBuf[sizeof(long) - 3]);
498
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
499
    strOutput.push_back(pBuf[sizeof(long) - 2]);
500
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
501
    strOutput.push_back(pBuf[sizeof(long) - 1]);
502
#else
503
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
504
    strOutput.push_back(pBuf[2]);
10,529✔
505
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
506
    strOutput.push_back(pBuf[1]);
10,529✔
507
    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
508
    strOutput.push_back(pBuf[0]);
10,529✔
509
#endif
10,529✔
510
  } // while
10,529✔
511
  if (pad) {
860!
512
    strOutput.resize(strOutput.size() - pad);
740✔
513
  }
740✔
514

515
  return 1;
860✔
516
}
863✔
517

518
template int B64Decode<std::vector<uint8_t>>(const std::string& strInput, std::vector<uint8_t>& strOutput);
519
template int B64Decode<PacketBuffer>(const std::string& strInput, PacketBuffer& strOutput);
520
template int B64Decode<std::string>(const std::string& strInput, std::string& strOutput);
521

522
/*
523
www.kbcafe.com
524
Copyright 2001-2002 Randy Charles Morin
525
The Encode static method takes an array of 8-bit values and returns a base-64 stream.
526
*/
527

528
std::string Base64Encode(const std::string& src)
529
{
76✔
530
  std::string retval;
76✔
531
  if (src.empty()) {
76✔
532
    return retval;
2✔
533
  }
2✔
534
  for (unsigned int i = 0; i < src.size(); i += 3) {
542✔
535
    unsigned char by1 = 0;
468✔
536
    unsigned char by2 = 0;
468✔
537
    unsigned char by3 = 0;
468✔
538
    by1 = src[i];
468✔
539
    if (i + 1 < src.size()) {
468✔
540
      by2 = src[i + 1];
441✔
541
    };
441✔
542
    if (i + 2 < src.size()) {
468✔
543
      by3 = src[i + 2];
416✔
544
    }
416✔
545
    unsigned char by4 = 0;
468✔
546
    unsigned char by5 = 0;
468✔
547
    unsigned char by6 = 0;
468✔
548
    unsigned char by7 = 0;
468✔
549
    by4 = by1 >> 2;
468✔
550
    by5 = ((by1 & 0x3) << 4) | (by2 >> 4);
468✔
551
    by6 = ((by2 & 0xf) << 2) | (by3 >> 6);
468✔
552
    by7 = by3 & 0x3f;
468✔
553
    retval += B64Encode1(by4);
468✔
554
    retval += B64Encode1(by5);
468✔
555
    if (i + 1 < src.size()) {
468✔
556
      retval += B64Encode1(by6);
441✔
557
    }
441✔
558
    else {
27✔
559
      retval += "=";
27✔
560
    };
27✔
561
    if (i + 2 < src.size()) {
468✔
562
      retval += B64Encode1(by7);
416✔
563
    }
416✔
564
    else {
52✔
565
      retval += "=";
52✔
566
    };
52✔
567
    /*      if ((i % (76 / 4 * 3)) == 0)
568
      {
569
        retval += "\r\n";
570
        }*/
571
  };
468✔
572
  return retval;
74✔
573
};
76✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc