diff options
Diffstat (limited to '')
-rw-r--r-- | ipcipher.cc | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/ipcipher.cc b/ipcipher.cc new file mode 100644 index 0000000..1d64cc9 --- /dev/null +++ b/ipcipher.cc @@ -0,0 +1,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 */ |