summaryrefslogtreecommitdiffstats
path: root/ipcipher.cc
blob: 1d64cc902dd759d6a4f136daba8bb9d5676b68f4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include "ipcipher.hh"
#include "ext/ipcrypt/ipcrypt.h"
#include <cassert>
#include <openssl/aes.h>
#include <openssl/evp.h>

#ifdef HAVE_IPCIPHER
/*
int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen,
                           const unsigned char *salt, int saltlen, int iter,
                           int keylen, unsigned char *out);
*/
std::string makeIPCipherKey(const std::string& password)
{
  static const char salt[] = "ipcipheripcipher";
  unsigned char out[16];

  PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), (const unsigned char*)salt, sizeof(salt) - 1, 50000, sizeof(out), out);

  return std::string((const char*)out, (const char*)out + sizeof(out));
}

static ComboAddress encryptCA4(const ComboAddress& ca, const std::string& key)
{
  if (key.size() != 16) {
    throw std::runtime_error("Need 128 bits of key for ipcrypt");
  }

  ComboAddress ret = ca;

  // always returns 0, has no failure mode
  ipcrypt_encrypt((unsigned char*)&ret.sin4.sin_addr.s_addr,
                  (const unsigned char*)&ca.sin4.sin_addr.s_addr,
                  (const unsigned char*)key.c_str());

  return ret;
}

static ComboAddress decryptCA4(const ComboAddress& ca, const std::string& key)
{
  if (key.size() != 16) {
    throw std::runtime_error("Need 128 bits of key for ipcrypt");
  }

  ComboAddress ret = ca;

  // always returns 0, has no failure mode
  ipcrypt_decrypt((unsigned char*)&ret.sin4.sin_addr.s_addr,
                  (const unsigned char*)&ca.sin4.sin_addr.s_addr,
                  (const unsigned char*)key.c_str());

  return ret;
}

static ComboAddress encryptCA6(const ComboAddress& address, const std::string& key)
{
  if (key.size() != 16) {
    throw std::runtime_error("Need 128 bits of key for ipcrypt");
  }

  ComboAddress ret = address;

#if OPENSSL_VERSION_MAJOR >= 3
  auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), &EVP_CIPHER_CTX_free);
  if (ctx == nullptr) {
    throw pdns::OpenSSL::error("encryptCA6: Could not initialize cipher context");
  }

  auto aes128cbc = std::unique_ptr<EVP_CIPHER, decltype(&EVP_CIPHER_free)>(EVP_CIPHER_fetch(nullptr, "AES-128-CBC", nullptr), &EVP_CIPHER_free);

  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
  if (EVP_EncryptInit(ctx.get(), aes128cbc.get(), reinterpret_cast<const unsigned char*>(key.c_str()), nullptr) == 0) {
    throw pdns::OpenSSL::error("encryptCA6: Could not initialize encryption algorithm");
  }

  // Disable padding
  const auto inSize = sizeof(address.sin6.sin6_addr.s6_addr);
  static_assert(inSize == 16, "We disable padding and so we must assume a data size of 16 bytes");
  const auto blockSize = EVP_CIPHER_get_block_size(aes128cbc.get());
  assert(blockSize == 16);
  EVP_CIPHER_CTX_set_padding(ctx.get(), 0);

  int updateLen = 0;
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
  const auto* input = reinterpret_cast<const unsigned char*>(&address.sin6.sin6_addr.s6_addr);
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
  auto* output = reinterpret_cast<unsigned char*>(&ret.sin6.sin6_addr.s6_addr);
  if (EVP_EncryptUpdate(ctx.get(), output, &updateLen, input, static_cast<int>(inSize)) == 0) {
    throw pdns::OpenSSL::error("encryptCA6: Could not encrypt address");
  }

  int finalLen = 0;
  if (EVP_EncryptFinal_ex(ctx.get(), output + updateLen, &finalLen) == 0) {
    throw pdns::OpenSSL::error("encryptCA6: Could not finalize address encryption");
  }

  assert(updateLen + finalLen == inSize);
#else
  AES_KEY wctx;
  AES_set_encrypt_key((const unsigned char*)key.c_str(), 128, &wctx);
  AES_encrypt((const unsigned char*)&address.sin6.sin6_addr.s6_addr,
              (unsigned char*)&ret.sin6.sin6_addr.s6_addr, &wctx);
#endif

  return ret;
}

static ComboAddress decryptCA6(const ComboAddress& address, const std::string& key)
{
  if (key.size() != 16) {
    throw std::runtime_error("Need 128 bits of key for ipcrypt");
  }

  ComboAddress ret = address;

#if OPENSSL_VERSION_MAJOR >= 3
  auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), &EVP_CIPHER_CTX_free);
  if (ctx == nullptr) {
    throw pdns::OpenSSL::error("decryptCA6: Could not initialize cipher context");
  }

  auto aes128cbc = std::unique_ptr<EVP_CIPHER, decltype(&EVP_CIPHER_free)>(EVP_CIPHER_fetch(nullptr, "AES-128-CBC", nullptr), &EVP_CIPHER_free);

  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
  if (EVP_DecryptInit(ctx.get(), aes128cbc.get(), reinterpret_cast<const unsigned char*>(key.c_str()), nullptr) == 0) {
    throw pdns::OpenSSL::error("decryptCA6: Could not initialize decryption algorithm");
  }

  // Disable padding
  const auto inSize = sizeof(address.sin6.sin6_addr.s6_addr);
  static_assert(inSize == 16, "We disable padding and so we must assume a data size of 16 bytes");
  const auto blockSize = EVP_CIPHER_get_block_size(aes128cbc.get());
  assert(blockSize == 16);
  EVP_CIPHER_CTX_set_padding(ctx.get(), 0);

  int updateLen = 0;
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
  const auto* input = reinterpret_cast<const unsigned char*>(&address.sin6.sin6_addr.s6_addr);
  // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
  auto* output = reinterpret_cast<unsigned char*>(&ret.sin6.sin6_addr.s6_addr);
  if (EVP_DecryptUpdate(ctx.get(), output, &updateLen, input, static_cast<int>(inSize)) == 0) {
    throw pdns::OpenSSL::error("decryptCA6: Could not decrypt address");
  }

  int finalLen = 0;
  if (EVP_DecryptFinal_ex(ctx.get(), output + updateLen, &finalLen) == 0) {
    throw pdns::OpenSSL::error("decryptCA6: Could not finalize address decryption");
  }

  assert(updateLen + finalLen == inSize);
#else
  AES_KEY wctx;
  AES_set_decrypt_key((const unsigned char*)key.c_str(), 128, &wctx);
  AES_decrypt((const unsigned char*)&address.sin6.sin6_addr.s6_addr,
              (unsigned char*)&ret.sin6.sin6_addr.s6_addr, &wctx);
#endif

  return ret;
}

ComboAddress encryptCA(const ComboAddress& ca, const std::string& key)
{
  if (ca.sin4.sin_family == AF_INET) {
    return encryptCA4(ca, key);
  }

  if (ca.sin4.sin_family == AF_INET6) {
    return encryptCA6(ca, key);
  }

  throw std::runtime_error("ipcrypt can't encrypt non-IP addresses");
}

ComboAddress decryptCA(const ComboAddress& ca, const std::string& key)
{
  if (ca.sin4.sin_family == AF_INET) {
    return decryptCA4(ca, key);
  }

  if (ca.sin4.sin_family == AF_INET6) {
    return decryptCA6(ca, key);
  }

  throw std::runtime_error("ipcrypt can't decrypt non-IP addresses");
}

#endif /* HAVE_IPCIPHER */