Skip to content

Instantly share code, notes, and snippets.

@qnorsten
Last active May 12, 2021 18:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qnorsten/93a34c2a55f8b4381d18105d942ada45 to your computer and use it in GitHub Desktop.
Save qnorsten/93a34c2a55f8b4381d18105d942ada45 to your computer and use it in GitHub Desktop.
#include <znc/main.h>
#include <znc/User.h>
#include <znc/Nick.h>
#include <znc/Modules.h>
#include <znc/Chan.h>
#include <znc/IRCNetwork.h>
#include <string.h>
using std::vector;
using std::pair;
using std::map;
#include <netinet/in.h>
#include <openssl/opensslv.h>
#include <openssl/blowfish.h>
#define REQUIRESSL 1
#if (OPENSSL_VERSION_NUMBER < 0x0090800f)
#error "We require openssl >= 0.9.8"
#endif
/*
Public Base64 conversion tables
*/
unsigned char B64ABC[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned char b64buf[256];
/*
void initb64();
Initializes the base64->base16 conversion tab.
Call this function once when your program starts.
and always after your B64 table has been changed.
*/
void initb64(){
unsigned int i;
for (i=0; i<256; i++) b64buf[i]=0x00;
for (i=0; i<64; i++) b64buf[(B64ABC[i])]=i;
}
/*
int b64toh(lpBase64String, lpDestinationBuffer);
Converts base64 string b to hexnumber d.
Returns size of hexnumber in bytes.
*/
int b64toh(char *b, char *d){
int i,k,l;
l=strlen(b);
if (l<2) return 0;
for (i=l-1;i>-1;i--){
if (b64buf[(unsigned char)(b[i])]==0) l--;
else break;
}
if (l<2) return 0;
i=0, k=0;
while (1) {
i++;
if (k+1<l) d[i-1]=((b64buf[(unsigned char)(b[k])])<<2);
else break;
k++;
if (k<l) d[i-1]|=((b64buf[(unsigned char)(b[k])])>>4);
else break;
i++;
if (k+1<l) d[i-1]=((b64buf[(unsigned char)(b[k])])<<4);
else break;
k++;
if (k<l) d[i-1]|=((b64buf[(unsigned char)(b[k])])>>2);
else break;
i++;
if (k+1<l) d[i-1]=((b64buf[(unsigned char)(b[k])])<<6);
else break;
k++;
if (k<l) d[i-1]|=(b64buf[(unsigned char)(b[k])]);
else break;
k++;
}
return i-1;
}
/*
int htob64(lpHexNumber, lpDestinationBuffer);
Converts hexnumber h (with length l bytes) to base64 string d.
Returns length of base64 string.
*/
int htob64(char *h, char *d, unsigned int l){
unsigned int i,j,k;
unsigned char m,t;
if (!l) return 0;
l<<=3; // no. bits
m=0x80;
for (i=0,j=0,k=0,t=0; i<l; i++){
if (h[(i>>3)]&m) t|=1;
j++;
if (!(m>>=1)) m=0x80;
if (!(j%6)) {
d[k]=B64ABC[t];
t&=0;
k++;
}
t<<=1;
}
m=5-(j%6);
t<<=m;
if (m) {
d[k]=B64ABC[t];
k++;
}
d[k]&=0;
return strlen(d);
}
unsigned char B64[]="./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
const char *prime1080="FBE1022E23D213E8ACFA9AE8B9DFADA3EA6B7AC7A7B7E95AB5EB2DF858921FEADE95E6AC7BE7DE6ADBAB8A783E7AF7A7FA6A2B7BEB1E72EAE2B72F9FA2BFB2A2EFBEFAC868BADB3E828FA8BADFADA3E4CC1BE7E8AFE85E9698A783EB68FA07A77AB6AD7BEB618ACF9CA2897EB28A6189EFA07AB99A8A7FA9AE299EFA7BA66DEAFEFBEFBF0B7D8B";
int base64dec(char c)
{
int i;
for (i = 0; i < 64; i++)
if (B64[i] == c) return i;
return 0;
}
char *encrypts(char *key,char *str) {
char *result;
unsigned int length;
unsigned int left,right;
char *s,*d;
unsigned char *p;
BF_KEY bfkey;
int i;
if(key==NULL||str==NULL) return NULL;
length=strlen(str);
BF_set_key(&bfkey, strlen(key), (const unsigned char *)key);
s=(char *)malloc(length+9);
strncpy(s,str,length);
memset(s+length,0,9);
result=(char *)malloc(((length%8==0) ? length/8*12 : 12+length/8*12)+1);
p=(unsigned char *)s;
d=result;
while(*p) {
BF_ecb_encrypt((const unsigned char *)p, (unsigned char *)p, &bfkey, BF_ENCRYPT);
left = ((*p++) << 24);
left += ((*p++) << 16);
left += ((*p++) << 8);
left += (*p++);
right = ((*p++) << 24);
right += ((*p++) << 16);
right += ((*p++) << 8);
right += (*p++);
for (i = 0; i < 6; i++) {
*d++=B64[right & 0x3f];
right = (right >> 6);
}
for (i = 0; i < 6; i++) {
*d++=B64[left & 0x3f];
left = (left >> 6);
}
}
*d = '\0';
memset(s,0,length+9);
free(s);
return result;
}
char *decrypts(char *key, char *str) {
char *result;
unsigned int length;
unsigned int left,right;
int i;
char *d;
unsigned char *c;
BF_KEY bfkey;
if(key==NULL||str==NULL) return NULL;
length=strlen(str);
BF_set_key(&bfkey,strlen(key),(const unsigned char *)key);
result=(char *)malloc((length/12*8)+1);
c=(unsigned char *)result;
d=str;
while(*d) {
right=0;
left=0;
for (i = 0; i < 6; i++) right |= (base64dec(*d++)) << (i * 6);
for (i = 0; i < 6; i++) left |= (base64dec(*d++)) << (i * 6);
right=htonl(right);
left=htonl(left);
memcpy(c,&left,4);
memcpy(c+4,&right,4);
BF_ecb_encrypt(c,c,&bfkey,BF_DECRYPT);
c+=8;
}
*c='\0';
return result;
}
class CKeyExchangeTimer : public CTimer {
public:
CKeyExchangeTimer(CModule* pModule)
: CTimer(pModule, 5, 0, "KeyExchangeTimer", "Key exchange timer removes stale exchanges") {}
protected:
virtual void RunJob();
};
class CFishMod : public CModule {
public:
MODCONSTRUCTOR(CFishMod) {}
virtual ~CFishMod() {
}
virtual bool OnLoad(const CString& sArgs, CString& sMessage) {
// if we have an 'old version', simply upgrade it
MCString::iterator version = FindNV("version");
// oldest version, before any version numbers!
// this upgrades to version 1, which changes our key database
// this does /not/ delete the old keys however, so people can go right back to the old version
if (version == EndNV()) {
if (BeginNV() != EndNV()) { // if we actually have any keys to convert
// need to do this since we're adding to NV, and if we don't it loops forever, processing new entries
MCString::iterator it = BeginNV();
while (it != EndNV()) {
it++;
}
it--;
CString LastNV = it->first;
// now loop over every key and upgrade
for (it = BeginNV(); it->first != LastNV; it++) {
SetNV("key " + it->first, it->second);
PutModule("key for " + it->first + " upgraded");
}
SetNV("key " + it->first, it->second);
PutModule("key for " + it->first + " upgraded");
PutModule("Upgraded Database to Version 1");
}
SetNV("config prefix_encrypted", "\00312e\003 ");
SetNV("config prefix_decrypted", "\00304d\003 ");
SetNV("version", "1");
}
return true;
}
virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage) {
CString command = sMessage.Token(0);
CString sOtherPub_Key = sMessage.Token(1);
if (command.CaseCmp("DH1080_INIT") == 0 && !sOtherPub_Key.empty()) {
CString sPriv_Key;
CString sPub_Key;
CString sSecretKey;
DH1080_gen(sPriv_Key, sPub_Key);
if (!DH1080_comp(sPriv_Key, sOtherPub_Key, sSecretKey)) {
PutModule("Error in DH1080 with " + Nick.GetNick() + ": " + sSecretKey);
return CONTINUE;
}
PutModule("Received DH1080 public key from " + Nick.GetNick() + ", sending mine...");
PutIRC("NOTICE " + Nick.GetNick() + " :DH1080_FINISH " + sPub_Key);
SetNV("key " + Nick.GetNick().AsLower(), sSecretKey);
PutModule("Key for " + Nick.GetNick() + " successfully set.");
return HALT;
} else if (command.CaseCmp("DH1080_FINISH") == 0 && !sOtherPub_Key.empty()) {
CString sPriv_Key;
CString sSecretKey;
map<CString, pair<time_t, CString> >::iterator it = m_msKeyExchange.find(Nick.GetNick().AsLower());
if (it == m_msKeyExchange.end()) {
PutModule("Received unexpected DH1080_FINISH from " + Nick.GetNick() + ".");
} else {
sPriv_Key = it->second.second;
if (DH1080_comp(sPriv_Key, sOtherPub_Key, sSecretKey)) {
SetNV("key " + Nick.GetNick().AsLower(), sSecretKey);
PutModule("Key for " + Nick.GetNick() + " successfully set.");
m_msKeyExchange.erase(Nick.GetNick().AsLower());
}
}
return HALT;
} else {
FilterIncoming(Nick.GetNick(), Nick, sMessage);
}
return CONTINUE;
}
virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) {
MCString::iterator it = FindNV("key " + sTarget.AsLower());
if (sMessage.Left(2) == "-e") {
sMessage.LeftChomp(3);
return CONTINUE;
}
if (it != EndNV()) {
CChan* pChan = m_pNetwork->FindChan(sTarget);
if ((pChan) && !(pChan->AutoClearChanBuffer())) {
pChan->AddBuffer(":" + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :" + sMessage);
}
char * cMsg = encrypts((char *)it->second.c_str(), (char *)sMessage.c_str());
CString sMsg = "+OK " + CString(cMsg);
PutIRC("PRIVMSG " + sTarget + " :" + sMsg);
m_pUser->PutUser(":" + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :" + sMessage, NULL, m_pClient);
free(cMsg);
// relay to other clients
const vector<CClient*>& vClients = this->m_pNetwork->GetClients();
for (unsigned int a = 0; a < vClients.size(); a++) {
CClient* pClient = vClients[a];
if (pClient != this->GetClient()) {
pClient->PutClient(":" + this->GetClient()->GetNickMask() + " PRIVMSG " + sTarget + " :" + sMessage);
}
}
// stop ZNC from handling message, or else it gets sent unencrypted to target as well
return HALTCORE;
}
return CONTINUE;
}
virtual EModRet OnUserAction(CString& sTarget, CString& sMessage) {
MCString::iterator it = FindNV("key " + sTarget.AsLower());
if (it != EndNV()) {
CChan* pChan = m_pNetwork->FindChan(sTarget);
if ((pChan) && !(pChan->AutoClearChanBuffer())) {
pChan->AddBuffer(":" + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :\001ACTION " + sMessage + "\001");
}
char * cMsg = encrypts((char *)it->second.c_str(), (char *)sMessage.c_str());
CString sMsg = "+OK " + CString(cMsg);
PutIRC("PRIVMSG " + sTarget + " :\001ACTION " + sMsg + "\001");
m_pUser->PutUser(":" + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :\001ACTION " + sMessage + "\001", NULL, m_pClient);
free(cMsg);
// relay to other clients
const vector<CClient*>& vClients = this->m_pNetwork->GetClients();
for (unsigned int a = 0; a < vClients.size(); a++) {
CClient* pClient = vClients[a];
if (pClient != this->GetClient()) {
pClient->PutClient(":" + this->GetClient()->GetNickMask() + " PRIVMSG " + sTarget + " :\001ACTION " + sMessage + "\001");
}
}
// stop ZNC from handling message, or else it gets sent unencrypted to target as well
return HALTCORE;
}
return CONTINUE;
}
virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage) {
MCString::iterator it = FindNV("key " + sTarget.AsLower());
if (it != EndNV()) {
CChan* pChan = m_pNetwork->FindChan(sTarget);
if ((pChan) && !(pChan->AutoClearChanBuffer())) {
pChan->AddBuffer(":" + m_pNetwork->GetIRCNick().GetNickMask() + " NOTICE " + sTarget + " :" + sMessage);
}
char * cMsg = encrypts((char *)it->second.c_str(), (char *)sMessage.c_str());
CString sMsg = "+OK " + CString(cMsg);
PutIRC("NOTICE " + sTarget + " :" + sMsg);
m_pUser->PutUser(":" + m_pNetwork->GetIRCNick().GetNickMask() + " NOTICE " + sTarget + " :" + sMessage, NULL, m_pClient);
free(cMsg);
// relay to other clients
const vector<CClient*>& vClients = this->m_pNetwork->GetClients();
for (unsigned int a = 0; a < vClients.size(); a++) {
CClient* pClient = vClients[a];
if (pClient != this->GetClient()) {
pClient->PutClient(":" + this->GetClient()->GetNickMask() + " NOTICE " + sTarget + " :" + sMessage);
}
}
// stop ZNC from handling message, or else it gets sent unencrypted to target as well
return HALTCORE;
}
return CONTINUE;
}
virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) {
if (!sTopic.empty()) {
MCString::iterator it = FindNV("key " + sChannel.AsLower());
if (it != EndNV()) {
char * cTopic = encrypts((char *)it->second.c_str(), (char *)sTopic.c_str());
sTopic = "+OK " + CString(cTopic);
free(cTopic);
}
}
return CONTINUE;
}
virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage) {
FilterIncoming(Nick.GetNick(), Nick, sMessage);
return CONTINUE;
}
virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {
FilterIncoming(Channel.GetName(), Nick, sMessage);
return CONTINUE;
}
virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage) {
FilterIncoming(Nick.GetNick(), Nick, sMessage);
return CONTINUE;
}
virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) {
FilterIncoming(Channel.GetName(), Nick, sMessage);
return CONTINUE;
}
virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) {
FilterIncoming(Channel.GetName(), Nick, sTopic);
return CONTINUE;
}
virtual EModRet OnRaw(CString& sLine) {
if (sLine.WildCmp(":* 332 *") && sLine.Token(1) == "332") {
CChan* pChan = m_pNetwork->FindChan(sLine.Token(3));
if (pChan) {
CNick Nick(sLine.Token(2));
CString sTopic = sLine.Token(4, true);
sTopic.LeftChomp();
FilterIncoming(pChan->GetName(), Nick, sTopic);
sLine = sLine.Token(0) + " " + sLine.Token(1) + " " + sLine.Token(2) + " " + pChan->GetName() + " :" + sTopic;
}
}
return CONTINUE;
}
void FilterIncoming(const CString& sTarget, CNick& Nick, CString& sMessage) {
MCString::iterator it = FindNV("key " + sTarget.AsLower());
if (it != EndNV()) {
if (sMessage.Left(4) == "+OK " || sMessage.Left(5) == "mcps ") {
if (sMessage.Left(4) == "+OK ") {
sMessage.LeftChomp(4);
} else if (sMessage.Left(5) == "mcps ") {
sMessage.LeftChomp(5);
}
unsigned int msg_len = strlen(sMessage.c_str());
if ((strspn(sMessage.c_str(), (char *)B64) != msg_len) || msg_len < 12) {
return;
}
unsigned int mark_broken_block = 0;
if (msg_len != (msg_len/12)*12) {
msg_len = msg_len - (msg_len/12)*12;
sMessage.RightChomp(msg_len);
mark_broken_block = 1;
}
char *cMsg = decrypts((char *)it->second.c_str(), (char *)sMessage.c_str());
sMessage = GetNV("config prefix_encrypted") + CString(cMsg); // blue 'e' for nice, encrypted
if (mark_broken_block) {
sMessage += " \002&\002";
}
free(cMsg);
} else {
sMessage = GetNV("config prefix_decrypted") + sMessage; // red 'd' for bad, not encrypted
}
}
}
virtual void OnModCommand(const CString& sCommand) {
CString sCmd = sCommand.Token(0);
if (sCmd.CaseCmp("DELKEY") == 0) {
CString sTarget = sCommand.Token(1);
if (!sTarget.empty()) {
if (DelNV("key " + sTarget.AsLower())) {
PutModule("Target [" + sTarget + "] deleted");
} else {
PutModule("Target [" + sTarget + "] not found");
}
} else {
PutModule("Usage DelKey <#chan|Nick>");
}
} else if (sCmd.CaseCmp("SETKEY") == 0) {
CString sTarget = sCommand.Token(1);
CString sKey = sCommand.Token(2, true);
if (!sKey.empty()) {
SetNV("key " + sTarget.AsLower(), sKey);
PutModule("Set encryption key for [" + sTarget + "] to [" + sKey + "]");
} else {
PutModule("Usage: SetKey <#chan|Nick> <Key>");
}
} else if (sCmd.CaseCmp("SHOWKEY") == 0) {
CString sTarget = sCommand.Token(1);
if (!sTarget.empty()) {
MCString::iterator it = FindNV("key " + sTarget.AsLower());
if (it != EndNV()) {
PutModule("Target key is " + it->second);
} else {
PutModule("Target not found.");
}
} else {
PutModule("Usage ShowKey <#chan|Nick>");
}
} else if (sCmd.CaseCmp("LISTKEYS") == 0) {
if (BeginNV() == EndNV()) {
PutModule("You have no encryption keys set.");
} else {
CTable Table;
Table.AddColumn("Target");
Table.AddColumn("Key");
// find all our keys and print them out
for (MCString::iterator it = BeginNV(); it != EndNV(); it++) {
if (it->first.Left(4) == "key ") {
Table.AddRow();
Table.SetCell("Target", it->first.LeftChomp_n(4)); // remove "key " from start
Table.SetCell("Key", it->second);
}
}
if (Table.size()) {
unsigned int uTableIdx = 0;
CString sLine;
while (Table.GetLine(uTableIdx++, sLine)) {
PutModule(sLine);
}
}
}
} else if (sCmd.CaseCmp("LISTCONFIG") == 0) {
// we would use tables here, but they mangle things like irc colour codes >.>
for (MCString::iterator it = BeginNV(); it != EndNV(); it++) {
if (it->first.Left(7) == "config ") {
PutModule(it->first.LeftChomp_n(7) + " : \"" + it->second + "\"");
}
}
} else if (sCmd.CaseCmp("SETCONFIG") == 0) {
CString sName = sCommand.Token(1);
CString sValue = sCommand.Token(2, true);
if (!sName.empty()) {
if (!sValue.empty()) {
SetNV("config " + sName.AsLower(), sValue);
PutModule("Set config option [" + sName + "] to [" + sValue + "]");
} else {
SetNV("config " + sName.AsLower(), "");
PutModule("Set config option [" + sName + "] to nothing (disabled)");
}
} else {
PutModule("Usage: SetConfig <Name> <Value>");
}
} else if (sCmd.CaseCmp("KEYX") == 0) {
CString sTarget = sCommand.Token(1);
if (sTarget.empty()) {
PutModule("You did not specify a target for the key exchange.");
} else {
map<CString, pair<time_t, CString> >::iterator it = m_msKeyExchange.find(sTarget.AsLower());
if (it != m_msKeyExchange.end()) {
PutModule("Keyexchange with " + sTarget + " already in progress.");
} else {
CString sPriv_Key;
CString sPub_Key;
DH1080_gen(sPriv_Key, sPub_Key);
m_msKeyExchange.insert(make_pair(sTarget.AsLower(), make_pair(time(NULL), sPriv_Key)));
PutIRC("NOTICE " + sTarget + " :DH1080_INIT " + sPub_Key);
PutModule("Sent my DH1080 public key to " + sTarget + ", waiting for reply ...");
if (FindTimer("KeyExchangeTimer") == NULL) {
AddTimer(new CKeyExchangeTimer(this));
}
}
}
} else if (sCmd.CaseCmp("HELP") == 0) {
CTable Table;
Table.AddColumn("Command");
Table.AddColumn("Arguments");
Table.AddColumn("Description");
// list all our commands
Table.AddRow();
Table.SetCell("Command", "SetKey");
Table.SetCell("Arguments", "<target> <key>");
Table.SetCell("Description", "Sets <target>'s FiSH encryption key");
Table.AddRow();
Table.SetCell("Command", "DelKey");
Table.SetCell("Arguments", "<target>");
Table.SetCell("Description", "Removes <target>'s FiSH encryption key");
Table.AddRow();
Table.SetCell("Command", "ShowKey");
Table.SetCell("Arguments", "<target>");
Table.SetCell("Description", "Show the encryption key of <target>, if it has one set");
Table.AddRow();
Table.SetCell("Command", "ListKeys");
Table.SetCell("Arguments", "");
Table.SetCell("Description", "Print out all of our keys");
Table.AddRow();
Table.SetCell("Command", "SetConfig");
Table.SetCell("Arguments", "<name> <value>");
Table.SetCell("Description", "Set config option <name> to <value>. Set option to empty if no <value> is specified");
Table.AddRow();
Table.SetCell("Command", "ListConfig");
Table.SetCell("Arguments", "");
Table.SetCell("Description", "Print out all of our config options");
Table.AddRow();
Table.SetCell("Command", "KeyX");
Table.SetCell("Arguments", "<target>");
Table.SetCell("Description", "Start a key exchange with <target>");
Table.AddRow();
Table.SetCell("Command", "Help");
Table.SetCell("Arguments", "");
Table.SetCell("Description", "Display this message");
PutModule(Table);
} else {
PutModule("Unknown command, try 'Help'");
}
}
void DelStaleKeyExchanges(time_t iTime) {
for (map<CString, pair<time_t, CString> >::const_iterator it = m_msKeyExchange.begin(); it != m_msKeyExchange.end(); it++) {
if (iTime - 5 >= it->second.first) {
PutModule("Keyexchange with " + it->first + " expired before completion.");
m_msKeyExchange.erase(it->first);
}
}
if (m_msKeyExchange.size() <= 0) {
RemTimer("KeyExchangeTimer");
}
}
private:
void DH1080_gen(CString& sPriv_Key, CString& sPub_Key) {
sPriv_Key = "";
sPub_Key = "";
unsigned char raw_buf[200];
unsigned long len;
unsigned char *a, *b;
DH *dh;
BIGNUM *b_prime=NULL;
BIGNUM *b_generator=NULL;
initb64();
dh=DH_new();
if (!BN_hex2bn(&b_prime, prime1080)) {
return;
}
if (!BN_dec2bn(&b_generator, "2")) {
return;
}
if (b_prime == NULL || b_generator == NULL ||
!DH_set0_pqg(dh, b_prime, NULL, b_generator))
return;
if (!DH_generate_key(dh)) {
return;
}
const BIGNUM *priv_key, *pub_key;
DH_get0_key(dh, &pub_key, &priv_key);
len = BN_num_bytes(priv_key);
a = (unsigned char *)malloc(len);
BN_bn2bin(priv_key,a);
memset(raw_buf, 0, 200);
htob64((char *)a, (char *)raw_buf, len);
sPriv_Key = CString((char *)raw_buf);
len=BN_num_bytes(pub_key);
b = (unsigned char *)malloc(len);
BN_bn2bin(pub_key,b);
memset(raw_buf, 0, 200);
htob64((char *)b, (char *)raw_buf, len);
sPub_Key = CString((char *)raw_buf);
DH_free(dh);
free(a);
free(b);
}
bool DH1080_comp(CString& sPriv_Key, CString& sOtherPub_Key, CString& sSecret_Key) {
int len;
unsigned char SHA256digest[32];
char *key;
BIGNUM *b_prime=NULL;
BIGNUM *b_myPrivkey=NULL;
BIGNUM *b_HisPubkey=NULL;
BIGNUM *b_generator=NULL;
DH *dh;
CString sSHA256digest;
unsigned char raw_buf[200];
if (!BN_hex2bn(&b_prime, prime1080)) {
return false;
}
if (!BN_dec2bn(&b_generator, "2")) {
return false;
}
dh=DH_new();
if (b_prime == NULL || b_generator == NULL ||
!DH_set0_pqg(dh, b_prime, NULL, b_generator))
return false;
memset(raw_buf, 0, 200);
len = b64toh((char *)sPriv_Key.c_str(), (char *)raw_buf);
b_myPrivkey=BN_bin2bn(raw_buf, len, NULL);
DH_set0_key(dh, NULL, b_myPrivkey);
memset(raw_buf, 0, 200);
len = b64toh((char *)sOtherPub_Key.c_str(), (char *)raw_buf);
b_HisPubkey=BN_bin2bn(raw_buf, len, NULL);
key=(char *)malloc(DH_size(dh));
memset(key, 0, DH_size(dh));
len=DH_compute_key((unsigned char *)key, b_HisPubkey, dh);
if (len == -1) {
// Bad pub key
unsigned long err = ERR_get_error();
DEBUG("** DH Error:" << ERR_error_string(err,NULL));
DH_free(dh);
BN_clear_free(b_HisPubkey);
free(key);
sSecret_Key = CString(ERR_error_string(err,NULL)).Token(4,true,":");
return false;
}
SHA256_CTX c;
SHA256_Init(&c);
memset(SHA256digest, 0, 32);
SHA256_Update(&c, key, len);
SHA256_Final(SHA256digest, &c);
memset(raw_buf, 0, 200);
len = htob64((char *)SHA256digest, (char *)raw_buf, 32);
sSecret_Key = "";
sSecret_Key.append((char *)raw_buf, len);
DH_free(dh);
BN_clear_free(b_HisPubkey);
free(key);
return true;
}
map<CString, pair<time_t, CString> > m_msKeyExchange;
};
void CKeyExchangeTimer::RunJob() {
CFishMod *p = (CFishMod *)m_pModule;
p->DelStaleKeyExchanges(time(NULL));
}
template<> void TModInfo<CFishMod>(CModInfo& Info) {
Info.SetWikiPage("CFishMod");
}
NETWORKMODULEDEFS(CFishMod, "FiSH encryption for channel/private messages")
@qnorsten
Copy link
Author

Want to look into if this is worth adding m0vie/znc-fish@0bec883

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment