mirror of
https://github.com/znc/znc.git
synced 2026-03-28 17:42:41 +01:00
committed by
Alexey Sokolov
parent
88d1e27cc6
commit
cba58ca862
@@ -26,10 +26,9 @@
|
||||
// TODO:
|
||||
//
|
||||
// 1) Encrypt key storage file
|
||||
// 2) Secure key exchange using pub/priv keys and the DH algorithm
|
||||
// 3) Some way of notifying the user that the current channel is in "encryption
|
||||
// 2) Some way of notifying the user that the current channel is in "encryption
|
||||
// mode" verses plain text
|
||||
// 4) Temporarily disable a target (nick/chan)
|
||||
// 3) Temporarily disable a target (nick/chan)
|
||||
//
|
||||
// NOTE: This module is currently NOT intended to secure you from your shell
|
||||
// admin.
|
||||
@@ -43,6 +42,9 @@
|
||||
#include <znc/Chan.h>
|
||||
#include <znc/User.h>
|
||||
#include <znc/IRCNetwork.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/bn.h>
|
||||
#include <znc/SHA256.h>
|
||||
|
||||
#define REQUIRESSL 1
|
||||
// To be removed in future versions
|
||||
@@ -50,6 +52,126 @@
|
||||
#define NICK_PREFIX_KEY "@nick-prefix@"
|
||||
|
||||
class CCryptMod : public CModule {
|
||||
private:
|
||||
/*
|
||||
* As used in other implementations like KVIrc, fish10, Quassel, FiSH-irssi, ...
|
||||
* all the way back to the original located at http://mircryption.sourceforge.net/Extras/McpsFishDH.zip
|
||||
*/
|
||||
const char* m_sPrime1080 = "FBE1022E23D213E8ACFA9AE8B9DFADA3EA6B7AC7A7B7E95AB5EB2DF858921FEADE95E6AC7BE7DE6ADBAB8A783E7AF7A7FA6A2B7BEB1E72EAE2B72F9FA2BFB2A2EFBEFAC868BADB3E828FA8BADFADA3E4CC1BE7E8AFE85E9698A783EB68FA07A77AB6AD7BEB618ACF9CA2897EB28A6189EFA07AB99A8A7FA9AE299EFA7BA66DEAFEFBEFBF0B7D8B";
|
||||
/* Generate our keys once and reuse, just like ssh keys */
|
||||
std::unique_ptr<DH, decltype(&DH_free)> m_pDH;
|
||||
CString m_sPrivKey;
|
||||
CString m_sPubKey;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0X10100000L
|
||||
static int DH_set0_pqg(DH* dh, BIGNUM* p, BIGNUM* q, BIGNUM* g) {
|
||||
/* If the fields p and g in dh are nullptr, the corresponding input
|
||||
* parameters MUST be non-nullptr. q may remain nullptr.
|
||||
*/
|
||||
if (dh == nullptr || (dh->p == nullptr && p == nullptr) || (dh->g == nullptr && g == nullptr))
|
||||
return 0;
|
||||
|
||||
if (p != nullptr) {
|
||||
BN_free(dh->p);
|
||||
dh->p = p;
|
||||
}
|
||||
if (g != nullptr) {
|
||||
BN_free(dh->g);
|
||||
dh->g = g;
|
||||
}
|
||||
if (q != nullptr) {
|
||||
BN_free(dh->q);
|
||||
dh->q = q;
|
||||
dh->length = BN_num_bits(q);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void DH_get0_key(const DH* dh, const BIGNUM** pub_key, const BIGNUM** priv_key) {
|
||||
if (dh != nullptr) {
|
||||
if (pub_key != nullptr)
|
||||
*pub_key = dh->pub_key;
|
||||
if (priv_key != nullptr)
|
||||
*priv_key = dh->priv_key;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool DH1080_gen() {
|
||||
/* Generate our keys on first call */
|
||||
if (m_sPrivKey.empty() || m_sPubKey.empty()) {
|
||||
int len;
|
||||
const BIGNUM* bPrivKey = nullptr;
|
||||
const BIGNUM* bPubKey = nullptr;
|
||||
BIGNUM* bPrime = nullptr;
|
||||
BIGNUM* bGen = nullptr;
|
||||
|
||||
if (!BN_hex2bn(&bPrime, m_sPrime1080) || !BN_dec2bn(&bGen, "2") || !DH_set0_pqg(m_pDH.get(), bPrime, nullptr, bGen) || !DH_generate_key(m_pDH.get())) {
|
||||
/* one of them failed */
|
||||
if (bPrime != nullptr)
|
||||
BN_clear_free(bPrime);
|
||||
if (bGen != nullptr)
|
||||
BN_clear_free(bGen);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get our keys */
|
||||
DH_get0_key(m_pDH.get(), &bPubKey, &bPrivKey);
|
||||
|
||||
/* Get our private key */
|
||||
len = BN_num_bytes(bPrivKey);
|
||||
m_sPrivKey.resize(len);
|
||||
BN_bn2bin(bPrivKey, (unsigned char*)m_sPrivKey.data());
|
||||
m_sPrivKey.Base64Encode();
|
||||
|
||||
/* Get our public key */
|
||||
len = BN_num_bytes(bPubKey);
|
||||
m_sPubKey.resize(len);
|
||||
BN_bn2bin(bPubKey, (unsigned char*)m_sPubKey.data());
|
||||
m_sPubKey.Base64Encode();
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool DH1080_comp(CString& sOtherPubKey, CString& sSecretKey) {
|
||||
unsigned long len;
|
||||
unsigned char* key = nullptr;
|
||||
BIGNUM* bOtherPubKey = nullptr;
|
||||
|
||||
/* Prepare other public key */
|
||||
len = sOtherPubKey.Base64Decode();
|
||||
bOtherPubKey = BN_bin2bn((unsigned char*)sOtherPubKey.data(), len, nullptr);
|
||||
|
||||
/* Generate secret key */
|
||||
key = (unsigned char*)calloc(DH_size(m_pDH.get()), 1);
|
||||
if ((len = DH_compute_key(key, bOtherPubKey, m_pDH.get())) == -1) {
|
||||
sSecretKey = "";
|
||||
if (bOtherPubKey != nullptr)
|
||||
BN_clear_free(bOtherPubKey);
|
||||
if (key != nullptr)
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get our secret key */
|
||||
sSecretKey.resize(SHA256_DIGEST_SIZE);
|
||||
sha256(key, len, (unsigned char*)sSecretKey.data());
|
||||
sSecretKey.Base64Encode();
|
||||
sSecretKey.TrimRight("=");
|
||||
|
||||
if (bOtherPubKey != nullptr)
|
||||
BN_clear_free(bOtherPubKey);
|
||||
if (key != nullptr)
|
||||
free(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CString NickPrefix() {
|
||||
MCString::iterator it = FindNV(NICK_PREFIX_KEY);
|
||||
/*
|
||||
@@ -67,8 +189,10 @@ class CCryptMod : public CModule {
|
||||
return sStatusPrefix.StartsWith("*") ? "." : "*";
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
MODCONSTRUCTOR(CCryptMod) {
|
||||
/* MODCONSTRUCTOR(CLASS) is of form "CLASS(...) : CModule(...)" */
|
||||
MODCONSTRUCTOR(CCryptMod) , m_pDH(DH_new(), DH_free) {
|
||||
AddHelpCommand();
|
||||
AddCommand("DelKey", static_cast<CModCommand::ModCmdFunc>(
|
||||
&CCryptMod::OnDelKeyCommand),
|
||||
@@ -79,9 +203,13 @@ class CCryptMod : public CModule {
|
||||
AddCommand("ListKeys", static_cast<CModCommand::ModCmdFunc>(
|
||||
&CCryptMod::OnListKeysCommand),
|
||||
"", "List all keys");
|
||||
AddCommand("KeyX", static_cast<CModCommand::ModCmdFunc>(
|
||||
&CCryptMod::OnKeyXCommand),
|
||||
"<Nick>", "Start a DH1080 key exchange with nick");
|
||||
}
|
||||
|
||||
~CCryptMod() override {}
|
||||
~CCryptMod() override {
|
||||
}
|
||||
|
||||
bool OnLoad(const CString& sArgsi, CString& sMessage) override {
|
||||
MCString::iterator it = FindNV(NICK_PREFIX_KEY);
|
||||
@@ -151,7 +279,7 @@ class CCryptMod : public CModule {
|
||||
sMessage);
|
||||
GetUser()->PutUser(":" + NickPrefix() + sNickMask + " NOTICE " +
|
||||
sTarget + " :" + sMessage,
|
||||
NULL, GetClient());
|
||||
nullptr, GetClient());
|
||||
}
|
||||
|
||||
CString sMsg = MakeIvec() + sMessage;
|
||||
@@ -187,7 +315,7 @@ class CCryptMod : public CModule {
|
||||
GetUser()->PutUser(":" + NickPrefix() + sNickMask +
|
||||
" PRIVMSG " + sTarget + " :\001ACTION " +
|
||||
sMessage + "\001",
|
||||
NULL, GetClient());
|
||||
nullptr, GetClient());
|
||||
}
|
||||
|
||||
CString sMsg = MakeIvec() + sMessage;
|
||||
@@ -227,6 +355,41 @@ class CCryptMod : public CModule {
|
||||
}
|
||||
|
||||
EModRet OnPrivNotice(CNick& Nick, CString& sMessage) override {
|
||||
CString sCommand = sMessage.Token(0);
|
||||
CString sOtherPubKey = sMessage.Token(1);
|
||||
|
||||
if ((sCommand.Equals("DH1080_INIT") || sCommand.Equals("DH1080_INIT_CBC")) && !sOtherPubKey.empty()) {
|
||||
CString sSecretKey;
|
||||
CString sTail = sMessage.Token(2); /* For fish10 */
|
||||
|
||||
/* remove trailing A */
|
||||
if (sOtherPubKey.TrimSuffix("A") && DH1080_gen() && DH1080_comp(sOtherPubKey, sSecretKey)) {
|
||||
PutModule("Received DH1080 public key from " + Nick.GetNick() + ", sending mine...");
|
||||
PutIRC("NOTICE " + Nick.GetNick() + " :DH1080_FINISH " + m_sPubKey + "A" + (sTail.empty()?"":(" " + sTail)));
|
||||
SetNV(Nick.GetNick().AsLower(), sSecretKey);
|
||||
PutModule("Key for " + Nick.GetNick() + " successfully set.");
|
||||
return HALT;
|
||||
}
|
||||
PutModule("Error in " + sCommand + " with " + Nick.GetNick() + ": " + (sSecretKey.empty()?"no secret key computed":sSecretKey));
|
||||
return CONTINUE;
|
||||
|
||||
} else if (sCommand.Equals("DH1080_FINISH") && !sOtherPubKey.empty()) {
|
||||
/*
|
||||
* In theory we could get a DH1080_FINISH without us having sent a DH1080_INIT first,
|
||||
* but then to have any use for the other user, they'd already have our pub key
|
||||
*/
|
||||
CString sSecretKey;
|
||||
|
||||
/* remove trailing A */
|
||||
if (sOtherPubKey.TrimSuffix("A") && DH1080_gen() && DH1080_comp(sOtherPubKey, sSecretKey)) {
|
||||
SetNV(Nick.GetNick().AsLower(), sSecretKey);
|
||||
PutModule("Key for " + Nick.GetNick() + " successfully set.");
|
||||
return HALT;
|
||||
}
|
||||
PutModule("Error in " + sCommand + " with " + Nick.GetNick() + ": " + (sSecretKey.empty()?"no secret key computed":sSecretKey));
|
||||
return CONTINUE;
|
||||
}
|
||||
|
||||
FilterIncoming(Nick.GetNick(), Nick, sMessage);
|
||||
return CONTINUE;
|
||||
}
|
||||
@@ -323,6 +486,21 @@ class CCryptMod : public CModule {
|
||||
}
|
||||
}
|
||||
|
||||
void OnKeyXCommand(const CString& sCommand) {
|
||||
CString sTarget = sCommand.Token(1);
|
||||
|
||||
if (!sTarget.empty()) {
|
||||
if (DH1080_gen()) {
|
||||
PutIRC("NOTICE " + sTarget + " :DH1080_INIT " + m_sPubKey + "A");
|
||||
PutModule("Sent my DH1080 public key to " + sTarget + ", waiting for reply ...");
|
||||
} else {
|
||||
PutModule("Error generating our keys, nothing sent.");
|
||||
}
|
||||
} else {
|
||||
PutModule("Usage: KeyX <Nick>");
|
||||
}
|
||||
}
|
||||
|
||||
void OnListKeysCommand(const CString& sCommand) {
|
||||
if (BeginNV() == EndNV()) {
|
||||
PutModule("You have no encryption keys set.");
|
||||
|
||||
Reference in New Issue
Block a user