]>
Commit | Line | Data |
---|---|---|
5d5d8c2e | 1 | #include "ipcipher.hh" |
7d280342 | 2 | #include "ext/ipcrypt/ipcrypt.h" |
9b81c913 | 3 | #include <cassert> |
7d280342 | 4 | #include <openssl/aes.h> |
caff6e16 | 5 | #include <openssl/evp.h> |
6 | ||
e41aadeb | 7 | #ifdef HAVE_IPCIPHER |
caff6e16 | 8 | /* |
9 | int PKCS5_PBKDF2_HMAC_SHA1(const char *pass, int passlen, | |
10 | const unsigned char *salt, int saltlen, int iter, | |
11 | int keylen, unsigned char *out); | |
12 | */ | |
5d5d8c2e | 13 | std::string makeIPCipherKey(const std::string& password) |
caff6e16 | 14 | { |
44677590 | 15 | static const char salt[] = "ipcipheripcipher"; |
caff6e16 | 16 | unsigned char out[16]; |
51d33ccc | 17 | |
44677590 | 18 | PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), (const unsigned char*)salt, sizeof(salt) - 1, 50000, sizeof(out), out); |
caff6e16 | 19 | |
20 | return std::string((const char*)out, (const char*)out + sizeof(out)); | |
21 | } | |
7d280342 | 22 | |
44677590 | 23 | static ComboAddress encryptCA4(const ComboAddress& ca, const std::string& key) |
7d280342 | 24 | { |
dd2ef389 | 25 | if (key.size() != 16) { |
7d280342 | 26 | throw std::runtime_error("Need 128 bits of key for ipcrypt"); |
dd2ef389 | 27 | } |
488bcb39 | 28 | |
44677590 | 29 | ComboAddress ret = ca; |
7d280342 | 30 | |
31 | // always returns 0, has no failure mode | |
44677590 FM |
32 | ipcrypt_encrypt((unsigned char*)&ret.sin4.sin_addr.s_addr, |
33 | (const unsigned char*)&ca.sin4.sin_addr.s_addr, | |
34 | (const unsigned char*)key.c_str()); | |
35 | ||
7d280342 | 36 | return ret; |
37 | } | |
38 | ||
44677590 | 39 | static ComboAddress decryptCA4(const ComboAddress& ca, const std::string& key) |
7d280342 | 40 | { |
dd2ef389 | 41 | if (key.size() != 16) { |
7d280342 | 42 | throw std::runtime_error("Need 128 bits of key for ipcrypt"); |
dd2ef389 | 43 | } |
488bcb39 | 44 | |
44677590 | 45 | ComboAddress ret = ca; |
7d280342 | 46 | |
47 | // always returns 0, has no failure mode | |
44677590 FM |
48 | ipcrypt_decrypt((unsigned char*)&ret.sin4.sin_addr.s_addr, |
49 | (const unsigned char*)&ca.sin4.sin_addr.s_addr, | |
50 | (const unsigned char*)key.c_str()); | |
51 | ||
7d280342 | 52 | return ret; |
53 | } | |
54 | ||
dd2ef389 | 55 | static ComboAddress encryptCA6(const ComboAddress& address, const std::string& key) |
7d280342 | 56 | { |
dd2ef389 | 57 | if (key.size() != 16) { |
7d280342 | 58 | throw std::runtime_error("Need 128 bits of key for ipcrypt"); |
dd2ef389 | 59 | } |
7d280342 | 60 | |
dd2ef389 | 61 | ComboAddress ret = address; |
7d280342 | 62 | |
9b81c913 FM |
63 | #if OPENSSL_VERSION_MAJOR >= 3 |
64 | auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), &EVP_CIPHER_CTX_free); | |
65 | if (ctx == nullptr) { | |
66 | throw pdns::OpenSSL::error("encryptCA6: Could not initialize cipher context"); | |
67 | } | |
68 | ||
69 | auto aes128cbc = std::unique_ptr<EVP_CIPHER, decltype(&EVP_CIPHER_free)>(EVP_CIPHER_fetch(nullptr, "AES-128-CBC", nullptr), &EVP_CIPHER_free); | |
70 | ||
71 | // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs. | |
72 | if (EVP_EncryptInit(ctx.get(), aes128cbc.get(), reinterpret_cast<const unsigned char*>(key.c_str()), nullptr) == 0) { | |
73 | throw pdns::OpenSSL::error("encryptCA6: Could not initialize encryption algorithm"); | |
74 | } | |
75 | ||
76 | // Disable padding | |
77 | const auto inSize = sizeof(address.sin6.sin6_addr.s6_addr); | |
78 | static_assert(inSize == 16, "We disable padding and so we must assume a data size of 16 bytes"); | |
79 | const auto blockSize = EVP_CIPHER_get_block_size(aes128cbc.get()); | |
80 | assert(blockSize == 16); | |
81 | EVP_CIPHER_CTX_set_padding(ctx.get(), 0); | |
82 | ||
83 | int updateLen = 0; | |
84 | // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs. | |
85 | const auto* input = reinterpret_cast<const unsigned char*>(&address.sin6.sin6_addr.s6_addr); | |
86 | // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs. | |
87 | auto* output = reinterpret_cast<unsigned char*>(&ret.sin6.sin6_addr.s6_addr); | |
88 | if (EVP_EncryptUpdate(ctx.get(), output, &updateLen, input, static_cast<int>(inSize)) == 0) { | |
89 | throw pdns::OpenSSL::error("encryptCA6: Could not encrypt address"); | |
90 | } | |
91 | ||
92 | int finalLen = 0; | |
93 | if (EVP_EncryptFinal_ex(ctx.get(), output + updateLen, &finalLen) == 0) { | |
94 | throw pdns::OpenSSL::error("encryptCA6: Could not finalize address encryption"); | |
95 | } | |
96 | ||
97 | assert(updateLen + finalLen == inSize); | |
98 | #else | |
7d280342 | 99 | AES_KEY wctx; |
100 | AES_set_encrypt_key((const unsigned char*)key.c_str(), 128, &wctx); | |
dd2ef389 | 101 | AES_encrypt((const unsigned char*)&address.sin6.sin6_addr.s6_addr, |
488bcb39 | 102 | (unsigned char*)&ret.sin6.sin6_addr.s6_addr, &wctx); |
9b81c913 | 103 | #endif |
7d280342 | 104 | |
105 | return ret; | |
106 | } | |
107 | ||
dd2ef389 | 108 | static ComboAddress decryptCA6(const ComboAddress& address, const std::string& key) |
7d280342 | 109 | { |
dd2ef389 | 110 | if (key.size() != 16) { |
7d280342 | 111 | throw std::runtime_error("Need 128 bits of key for ipcrypt"); |
dd2ef389 FM |
112 | } |
113 | ||
114 | ComboAddress ret = address; | |
488bcb39 | 115 | |
3768bfad FM |
116 | #if OPENSSL_VERSION_MAJOR >= 3 |
117 | auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), &EVP_CIPHER_CTX_free); | |
118 | if (ctx == nullptr) { | |
119 | throw pdns::OpenSSL::error("decryptCA6: Could not initialize cipher context"); | |
120 | } | |
121 | ||
122 | auto aes128cbc = std::unique_ptr<EVP_CIPHER, decltype(&EVP_CIPHER_free)>(EVP_CIPHER_fetch(nullptr, "AES-128-CBC", nullptr), &EVP_CIPHER_free); | |
123 | ||
124 | // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs. | |
125 | if (EVP_DecryptInit(ctx.get(), aes128cbc.get(), reinterpret_cast<const unsigned char*>(key.c_str()), nullptr) == 0) { | |
126 | throw pdns::OpenSSL::error("decryptCA6: Could not initialize decryption algorithm"); | |
127 | } | |
128 | ||
129 | // Disable padding | |
130 | const auto inSize = sizeof(address.sin6.sin6_addr.s6_addr); | |
131 | static_assert(inSize == 16, "We disable padding and so we must assume a data size of 16 bytes"); | |
132 | const auto blockSize = EVP_CIPHER_get_block_size(aes128cbc.get()); | |
133 | assert(blockSize == 16); | |
134 | EVP_CIPHER_CTX_set_padding(ctx.get(), 0); | |
135 | ||
136 | int updateLen = 0; | |
137 | // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs. | |
138 | const auto* input = reinterpret_cast<const unsigned char*>(&address.sin6.sin6_addr.s6_addr); | |
139 | // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs. | |
140 | auto* output = reinterpret_cast<unsigned char*>(&ret.sin6.sin6_addr.s6_addr); | |
141 | if (EVP_DecryptUpdate(ctx.get(), output, &updateLen, input, static_cast<int>(inSize)) == 0) { | |
142 | throw pdns::OpenSSL::error("decryptCA6: Could not decrypt address"); | |
143 | } | |
144 | ||
145 | int finalLen = 0; | |
146 | if (EVP_DecryptFinal_ex(ctx.get(), output + updateLen, &finalLen) == 0) { | |
147 | throw pdns::OpenSSL::error("decryptCA6: Could not finalize address decryption"); | |
148 | } | |
149 | ||
150 | assert(updateLen + finalLen == inSize); | |
151 | #else | |
7d280342 | 152 | AES_KEY wctx; |
153 | AES_set_decrypt_key((const unsigned char*)key.c_str(), 128, &wctx); | |
dd2ef389 | 154 | AES_decrypt((const unsigned char*)&address.sin6.sin6_addr.s6_addr, |
488bcb39 | 155 | (unsigned char*)&ret.sin6.sin6_addr.s6_addr, &wctx); |
3768bfad | 156 | #endif |
488bcb39 | 157 | |
7d280342 | 158 | return ret; |
159 | } | |
160 | ||
7d280342 | 161 | ComboAddress encryptCA(const ComboAddress& ca, const std::string& key) |
162 | { | |
dd2ef389 | 163 | if (ca.sin4.sin_family == AF_INET) { |
7d280342 | 164 | return encryptCA4(ca, key); |
dd2ef389 FM |
165 | } |
166 | ||
167 | if (ca.sin4.sin_family == AF_INET6) { | |
7d280342 | 168 | return encryptCA6(ca, key); |
dd2ef389 FM |
169 | } |
170 | ||
171 | throw std::runtime_error("ipcrypt can't encrypt non-IP addresses"); | |
7d280342 | 172 | } |
173 | ||
174 | ComboAddress decryptCA(const ComboAddress& ca, const std::string& key) | |
175 | { | |
dd2ef389 | 176 | if (ca.sin4.sin_family == AF_INET) { |
7d280342 | 177 | return decryptCA4(ca, key); |
dd2ef389 FM |
178 | } |
179 | ||
180 | if (ca.sin4.sin_family == AF_INET6) { | |
7d280342 | 181 | return decryptCA6(ca, key); |
dd2ef389 FM |
182 | } |
183 | ||
184 | throw std::runtime_error("ipcrypt can't decrypt non-IP addresses"); | |
7d280342 | 185 | } |
e41aadeb RG |
186 | |
187 | #endif /* HAVE_IPCIPHER */ |