├── README.md └── fish.cpp /README.md: -------------------------------------------------------------------------------- 1 | # FiSH Module for ZNC 2 | 3 | [ZNC Fish Wiki](http://en.znc.in/wiki/Fish) 4 | 5 | Orig. Module: [fish.cpp](http://znc-msvc.googlecode.com/svn/trunk/flakes/fish.cpp) 6 | 7 | ## Install 8 | 9 | ### Building 10 | 11 | $ znc-buildmod fish.cpp 12 | 13 | ### Install 14 | 15 | $ cp fish.so ~/.znc/modules/ 16 | 17 | Tell ZNC to load the module: 18 | 19 | /msg *status loadmod fish 20 | /msg *fish Help 21 | 22 | ## Usage 23 | 24 | /msg *fish SetKey #secretChannel mySuperSecretKey 25 | 26 | To disable encryption for a single message, prefix the message with `-e` 27 | 28 | /msg #secretChannel -e this won't be encrypted 29 | 30 | Decrypted messages are prefixed with a blue `(e)` 31 | 32 | If the module expected an encrypted message but received a unecrypted message it will prefix with a red `(d)` 33 | 34 | To change the prefixes: 35 | 36 | /msg *fish SetConfig prefix_encrypted (+) 37 | /msg *fish SetConfig prefix_decrypted (-) 38 | 39 | ## Commands 40 | 41 | * `Help` 42 | 43 | * `SetKey ` 44 | 45 | Sets ``'s FiSH encryption key 46 | 47 | * `DelKey ` 48 | 49 | Removes ``'s FiSH encryption key 50 | 51 | * `ShowKey ` 52 | 53 | Show the encryption key of ``, if it has one set 54 | 55 | * `ListKeys` 56 | 57 | Print out all of our keys 58 | 59 | * `SetConfig ` 60 | 61 | Set config option `` to `` 62 | 63 | * `ListConfig` 64 | 65 | Print out all of our config options 66 | 67 | * `KeyX ` 68 | 69 | Start a key exchange with `` 70 | 71 | ## Configurables 72 | 73 | * `prefix_encrypted` 74 | 75 | Default: (blue) `^12(e)^` 76 | 77 | * `prefix_decrypted` 78 | 79 | Default: (red) `^4(d)^` 80 | -------------------------------------------------------------------------------- /fish.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | using std::vector; 10 | using std::pair; 11 | using std::map; 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #define REQUIRESSL 1 19 | 20 | #if (OPENSSL_VERSION_NUMBER < 0x0090800f) 21 | #error "We require openssl >= 0.9.8" 22 | #endif 23 | 24 | /* 25 | Public Base64 conversion tables 26 | */ 27 | unsigned char B64ABC[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 28 | unsigned char b64buf[256]; 29 | 30 | 31 | /* 32 | void initb64(); 33 | Initializes the base64->base16 conversion tab. 34 | Call this function once when your program starts. 35 | and always after your B64 table has been changed. 36 | */ 37 | void initb64(){ 38 | unsigned int i; 39 | for (i=0; i<256; i++) b64buf[i]=0x00; 40 | for (i=0; i<64; i++) b64buf[(B64ABC[i])]=i; 41 | } 42 | 43 | /* 44 | int b64toh(lpBase64String, lpDestinationBuffer); 45 | Converts base64 string b to hexnumber d. 46 | Returns size of hexnumber in bytes. 47 | */ 48 | int b64toh(char *b, char *d){ 49 | int i,k,l; 50 | 51 | l=strlen(b); 52 | if (l<2) return 0; 53 | for (i=l-1;i>-1;i--){ 54 | if (b64buf[(unsigned char)(b[i])]==0) l--; 55 | else break; 56 | } 57 | 58 | if (l<2) return 0; 59 | i=0, k=0; 60 | while (1) { 61 | i++; 62 | if (k+1>4); 66 | else break; 67 | i++; 68 | if (k+1>2); 72 | else break; 73 | i++; 74 | if (k+1>3)]&m) t|=1; 98 | j++; 99 | if (!(m>>=1)) m=0x80; 100 | if (!(j%6)) { 101 | d[k]=B64ABC[t]; 102 | t&=0; 103 | k++; 104 | } 105 | t<<=1; 106 | } 107 | m=5-(j%6); 108 | t<<=m; 109 | if (m) { 110 | d[k]=B64ABC[t]; 111 | k++; 112 | } 113 | d[k]&=0; 114 | return strlen(d); 115 | } 116 | 117 | unsigned char B64[]="./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 118 | 119 | const char *prime1080="FBE1022E23D213E8ACFA9AE8B9DFADA3EA6B7AC7A7B7E95AB5EB2DF858921FEADE95E6AC7BE7DE6ADBAB8A783E7AF7A7FA6A2B7BEB1E72EAE2B72F9FA2BFB2A2EFBEFAC868BADB3E828FA8BADFADA3E4CC1BE7E8AFE85E9698A783EB68FA07A77AB6AD7BEB618ACF9CA2897EB28A6189EFA07AB99A8A7FA9AE299EFA7BA66DEAFEFBEFBF0B7D8B"; 120 | 121 | int base64dec(char c) 122 | { 123 | int i; 124 | 125 | for (i = 0; i < 64; i++) 126 | if (B64[i] == c) return i; 127 | 128 | return 0; 129 | } 130 | 131 | char *encrypts(char *key,char *str) { 132 | char *result; 133 | unsigned int length; 134 | unsigned int left,right; 135 | char *s,*d; 136 | unsigned char *p; 137 | BF_KEY bfkey; 138 | int i; 139 | 140 | if(key==NULL||str==NULL) return NULL; 141 | 142 | length=strlen(str); 143 | BF_set_key(&bfkey, strlen(key), (const unsigned char *)key); 144 | 145 | s=(char *)malloc(length+9); 146 | 147 | strncpy(s,str,length); 148 | memset(s+length,0,9); 149 | 150 | result=(char *)malloc(((length%8==0) ? length/8*12 : 12+length/8*12)+1); 151 | 152 | p=(unsigned char *)s; 153 | d=result; 154 | 155 | while(*p) { 156 | BF_ecb_encrypt((const unsigned char *)p, (unsigned char *)p, &bfkey, BF_ENCRYPT); 157 | left = ((*p++) << 24); 158 | left += ((*p++) << 16); 159 | left += ((*p++) << 8); 160 | left += (*p++); 161 | right = ((*p++) << 24); 162 | right += ((*p++) << 16); 163 | right += ((*p++) << 8); 164 | right += (*p++); 165 | for (i = 0; i < 6; i++) { 166 | *d++=B64[right & 0x3f]; 167 | right = (right >> 6); 168 | } 169 | 170 | for (i = 0; i < 6; i++) { 171 | *d++=B64[left & 0x3f]; 172 | left = (left >> 6); 173 | } 174 | } 175 | *d = '\0'; 176 | 177 | memset(s,0,length+9); 178 | free(s); 179 | return result; 180 | } 181 | 182 | char *decrypts(char *key, char *str) { 183 | char *result; 184 | unsigned int length; 185 | unsigned int left,right; 186 | int i; 187 | char *d; 188 | unsigned char *c; 189 | BF_KEY bfkey; 190 | 191 | if(key==NULL||str==NULL) return NULL; 192 | 193 | length=strlen(str); 194 | BF_set_key(&bfkey,strlen(key),(const unsigned char *)key); 195 | 196 | result=(char *)malloc((length/12*8)+1); 197 | c=(unsigned char *)result; 198 | d=str; 199 | while(*d) { 200 | right=0; 201 | left=0; 202 | for (i = 0; i < 6; i++) right |= (base64dec(*d++)) << (i * 6); 203 | for (i = 0; i < 6; i++) left |= (base64dec(*d++)) << (i * 6); 204 | right=htonl(right); 205 | left=htonl(left); 206 | memcpy(c,&left,4); 207 | memcpy(c+4,&right,4); 208 | BF_ecb_encrypt(c,c,&bfkey,BF_DECRYPT); 209 | c+=8; 210 | } 211 | *c='\0'; 212 | return result; 213 | } 214 | 215 | class CKeyExchangeTimer : public CTimer { 216 | public: 217 | CKeyExchangeTimer(CModule* pModule) 218 | : CTimer(pModule, 5, 0, "KeyExchangeTimer", "Key exchange timer removes stale exchanges") {} 219 | 220 | protected: 221 | virtual void RunJob(); 222 | }; 223 | 224 | class CFishMod : public CModule { 225 | public: 226 | MODCONSTRUCTOR(CFishMod) {} 227 | virtual ~CFishMod() { 228 | } 229 | 230 | virtual bool OnLoad(const CString& sArgs, CString& sMessage) { 231 | // if we have an 'old version', simply upgrade it 232 | MCString::iterator version = FindNV("version"); 233 | 234 | // oldest version, before any version numbers! 235 | // this upgrades to version 1, which changes our key database 236 | // this does /not/ delete the old keys however, so people can go right back to the old version 237 | if (version == EndNV()) { 238 | if (BeginNV() != EndNV()) { // if we actually have any keys to convert 239 | // need to do this since we're adding to NV, and if we don't it loops forever, processing new entries 240 | MCString::iterator it = BeginNV(); 241 | while (it != EndNV()) { 242 | it++; 243 | } 244 | it--; 245 | 246 | CString LastNV = it->first; 247 | 248 | // now loop over every key and upgrade 249 | for (it = BeginNV(); it->first != LastNV; it++) { 250 | SetNV("key " + it->first, it->second); 251 | PutModule("key for " + it->first + " upgraded"); 252 | } 253 | SetNV("key " + it->first, it->second); 254 | PutModule("key for " + it->first + " upgraded"); 255 | 256 | PutModule("Upgraded Database to Version 1"); 257 | } 258 | 259 | SetNV("config prefix_encrypted", "\00312e\003 "); 260 | SetNV("config prefix_decrypted", "\00304d\003 "); 261 | 262 | SetNV("version", "1"); 263 | } 264 | 265 | return true; 266 | } 267 | 268 | virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage) { 269 | CString command = sMessage.Token(0); 270 | CString sOtherPub_Key = sMessage.Token(1); 271 | 272 | if (command.CaseCmp("DH1080_INIT") == 0 && !sOtherPub_Key.empty()) { 273 | CString sPriv_Key; 274 | CString sPub_Key; 275 | CString sSecretKey; 276 | 277 | DH1080_gen(sPriv_Key, sPub_Key); 278 | if (!DH1080_comp(sPriv_Key, sOtherPub_Key, sSecretKey)) { 279 | PutModule("Error in DH1080 with " + Nick.GetNick() + ": " + sSecretKey); 280 | return CONTINUE; 281 | } 282 | PutModule("Received DH1080 public key from " + Nick.GetNick() + ", sending mine..."); 283 | PutIRC("NOTICE " + Nick.GetNick() + " :DH1080_FINISH " + sPub_Key); 284 | SetNV("key " + Nick.GetNick().AsLower(), sSecretKey); 285 | PutModule("Key for " + Nick.GetNick() + " successfully set."); 286 | return HALT; 287 | } else if (command.CaseCmp("DH1080_FINISH") == 0 && !sOtherPub_Key.empty()) { 288 | CString sPriv_Key; 289 | CString sSecretKey; 290 | 291 | map >::iterator it = m_msKeyExchange.find(Nick.GetNick().AsLower()); 292 | if (it == m_msKeyExchange.end()) { 293 | PutModule("Received unexpected DH1080_FINISH from " + Nick.GetNick() + "."); 294 | } else { 295 | sPriv_Key = it->second.second; 296 | if (DH1080_comp(sPriv_Key, sOtherPub_Key, sSecretKey)) { 297 | SetNV("key " + Nick.GetNick().AsLower(), sSecretKey); 298 | PutModule("Key for " + Nick.GetNick() + " successfully set."); 299 | m_msKeyExchange.erase(Nick.GetNick().AsLower()); 300 | } 301 | } 302 | return HALT; 303 | } else { 304 | FilterIncoming(Nick.GetNick(), Nick, sMessage); 305 | } 306 | 307 | return CONTINUE; 308 | } 309 | 310 | virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage) { 311 | MCString::iterator it = FindNV("key " + sTarget.AsLower()); 312 | 313 | if (sMessage.Left(2) == "-e") { 314 | sMessage.LeftChomp(3); 315 | return CONTINUE; 316 | } 317 | 318 | if (it != EndNV()) { 319 | CChan* pChan = m_pNetwork->FindChan(sTarget); 320 | if ((pChan) && !(pChan->AutoClearChanBuffer())) { 321 | pChan->AddBuffer(":" + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :" + sMessage); 322 | } 323 | char * cMsg = encrypts((char *)it->second.c_str(), (char *)sMessage.c_str()); 324 | 325 | CString sMsg = "+OK " + CString(cMsg); 326 | PutIRC("PRIVMSG " + sTarget + " :" + sMsg); 327 | m_pUser->PutUser(":" + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :" + sMessage, NULL, m_pClient); 328 | 329 | free(cMsg); 330 | 331 | // relay to other clients 332 | vector& vClients = this->m_pNetwork->GetClients(); 333 | for (unsigned int a = 0; a < vClients.size(); a++) { 334 | CClient* pClient = vClients[a]; 335 | 336 | if (pClient != this->GetClient()) { 337 | pClient->PutClient(":" + this->GetClient()->GetNickMask() + " PRIVMSG " + sTarget + " :" + sMessage); 338 | } 339 | } 340 | 341 | // stop ZNC from handling message, or else it gets sent unencrypted to target as well 342 | return HALTCORE; 343 | } 344 | 345 | return CONTINUE; 346 | } 347 | 348 | virtual EModRet OnUserAction(CString& sTarget, CString& sMessage) { 349 | MCString::iterator it = FindNV("key " + sTarget.AsLower()); 350 | 351 | if (it != EndNV()) { 352 | CChan* pChan = m_pNetwork->FindChan(sTarget); 353 | if ((pChan) && !(pChan->AutoClearChanBuffer())) { 354 | pChan->AddBuffer(":" + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :\001ACTION " + sMessage + "\001"); 355 | } 356 | char * cMsg = encrypts((char *)it->second.c_str(), (char *)sMessage.c_str()); 357 | 358 | CString sMsg = "+OK " + CString(cMsg); 359 | PutIRC("PRIVMSG " + sTarget + " :\001ACTION " + sMsg + "\001"); 360 | m_pUser->PutUser(":" + m_pNetwork->GetIRCNick().GetNickMask() + " PRIVMSG " + sTarget + " :\001ACTION " + sMessage + "\001", NULL, m_pClient); 361 | 362 | free(cMsg); 363 | 364 | // relay to other clients 365 | vector& vClients = this->m_pNetwork->GetClients(); 366 | for (unsigned int a = 0; a < vClients.size(); a++) { 367 | CClient* pClient = vClients[a]; 368 | 369 | if (pClient != this->GetClient()) { 370 | pClient->PutClient(":" + this->GetClient()->GetNickMask() + " PRIVMSG " + sTarget + " :\001ACTION " + sMessage + "\001"); 371 | } 372 | } 373 | 374 | // stop ZNC from handling message, or else it gets sent unencrypted to target as well 375 | return HALTCORE; 376 | } 377 | 378 | return CONTINUE; 379 | } 380 | 381 | virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage) { 382 | MCString::iterator it = FindNV("key " + sTarget.AsLower()); 383 | 384 | if (it != EndNV()) { 385 | CChan* pChan = m_pNetwork->FindChan(sTarget); 386 | if ((pChan) && !(pChan->AutoClearChanBuffer())) { 387 | pChan->AddBuffer(":" + m_pNetwork->GetIRCNick().GetNickMask() + " NOTICE " + sTarget + " :" + sMessage); 388 | } 389 | char * cMsg = encrypts((char *)it->second.c_str(), (char *)sMessage.c_str()); 390 | 391 | CString sMsg = "+OK " + CString(cMsg); 392 | PutIRC("NOTICE " + sTarget + " :" + sMsg); 393 | m_pUser->PutUser(":" + m_pNetwork->GetIRCNick().GetNickMask() + " NOTICE " + sTarget + " :" + sMessage, NULL, m_pClient); 394 | 395 | free(cMsg); 396 | 397 | // relay to other clients 398 | vector& vClients = this->m_pNetwork->GetClients(); 399 | for (unsigned int a = 0; a < vClients.size(); a++) { 400 | CClient* pClient = vClients[a]; 401 | 402 | if (pClient != this->GetClient()) { 403 | pClient->PutClient(":" + this->GetClient()->GetNickMask() + " NOTICE " + sTarget + " :" + sMessage); 404 | } 405 | } 406 | 407 | // stop ZNC from handling message, or else it gets sent unencrypted to target as well 408 | return HALTCORE; 409 | } 410 | 411 | return CONTINUE; 412 | 413 | } 414 | 415 | virtual EModRet OnUserTopic(CString& sChannel, CString& sTopic) { 416 | if (!sTopic.empty()) { 417 | MCString::iterator it = FindNV("key " + sChannel.AsLower()); 418 | if (it != EndNV()) { 419 | char * cTopic = encrypts((char *)it->second.c_str(), (char *)sTopic.c_str()); 420 | sTopic = "+OK " + CString(cTopic); 421 | free(cTopic); 422 | } 423 | } 424 | 425 | return CONTINUE; 426 | } 427 | 428 | virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage) { 429 | FilterIncoming(Nick.GetNick(), Nick, sMessage); 430 | return CONTINUE; 431 | } 432 | 433 | virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) { 434 | FilterIncoming(Channel.GetName(), Nick, sMessage); 435 | return CONTINUE; 436 | } 437 | 438 | virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage) { 439 | FilterIncoming(Nick.GetNick(), Nick, sMessage); 440 | return CONTINUE; 441 | } 442 | 443 | virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage) { 444 | FilterIncoming(Channel.GetName(), Nick, sMessage); 445 | return CONTINUE; 446 | } 447 | 448 | virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic) { 449 | FilterIncoming(Channel.GetName(), Nick, sTopic); 450 | return CONTINUE; 451 | } 452 | 453 | virtual EModRet OnRaw(CString& sLine) { 454 | if (sLine.WildCmp(":* 332 *") && sLine.Token(1) == "332") { 455 | CChan* pChan = m_pNetwork->FindChan(sLine.Token(3)); 456 | if (pChan) { 457 | CNick Nick(sLine.Token(2)); 458 | CString sTopic = sLine.Token(4, true); 459 | sTopic.LeftChomp(); 460 | FilterIncoming(pChan->GetName(), Nick, sTopic); 461 | sLine = sLine.Token(0) + " " + sLine.Token(1) + " " + sLine.Token(2) + " " + pChan->GetName() + " :" + sTopic; 462 | } 463 | } 464 | return CONTINUE; 465 | } 466 | 467 | void FilterIncoming(const CString& sTarget, CNick& Nick, CString& sMessage) { 468 | MCString::iterator it = FindNV("key " + sTarget.AsLower()); 469 | 470 | if (it != EndNV()) { 471 | if (sMessage.Left(4) == "+OK " || sMessage.Left(5) == "mcps ") { 472 | if (sMessage.Left(4) == "+OK ") { 473 | sMessage.LeftChomp(4); 474 | } else if (sMessage.Left(5) == "mcps ") { 475 | sMessage.LeftChomp(5); 476 | } 477 | 478 | unsigned int msg_len = strlen(sMessage.c_str()); 479 | 480 | if ((strspn(sMessage.c_str(), (char *)B64) != msg_len) || msg_len < 12) { 481 | return; 482 | } 483 | 484 | unsigned int mark_broken_block = 0; 485 | 486 | if (msg_len != (msg_len/12)*12) { 487 | msg_len = msg_len - (msg_len/12)*12; 488 | sMessage.RightChomp(msg_len); 489 | mark_broken_block = 1; 490 | } 491 | 492 | char *cMsg = decrypts((char *)it->second.c_str(), (char *)sMessage.c_str()); 493 | sMessage = GetNV("config prefix_encrypted") + CString(cMsg); // blue 'e' for nice, encrypted 494 | 495 | if (mark_broken_block) { 496 | sMessage += " \002&\002"; 497 | } 498 | 499 | free(cMsg); 500 | } else { 501 | sMessage = GetNV("config prefix_decrypted") + sMessage; // red 'd' for bad, not encrypted 502 | } 503 | } 504 | } 505 | 506 | virtual void OnModCommand(const CString& sCommand) { 507 | CString sCmd = sCommand.Token(0); 508 | 509 | if (sCmd.CaseCmp("DELKEY") == 0) { 510 | CString sTarget = sCommand.Token(1); 511 | 512 | if (!sTarget.empty()) { 513 | if (DelNV("key " + sTarget.AsLower())) { 514 | PutModule("Target [" + sTarget + "] deleted"); 515 | } else { 516 | PutModule("Target [" + sTarget + "] not found"); 517 | } 518 | } else { 519 | PutModule("Usage DelKey <#chan|Nick>"); 520 | } 521 | } else if (sCmd.CaseCmp("SETKEY") == 0) { 522 | CString sTarget = sCommand.Token(1); 523 | CString sKey = sCommand.Token(2, true); 524 | 525 | if (!sKey.empty()) { 526 | SetNV("key " + sTarget.AsLower(), sKey); 527 | PutModule("Set encryption key for [" + sTarget + "] to [" + sKey + "]"); 528 | } else { 529 | PutModule("Usage: SetKey <#chan|Nick> "); 530 | } 531 | } else if (sCmd.CaseCmp("SHOWKEY") == 0) { 532 | CString sTarget = sCommand.Token(1); 533 | 534 | if (!sTarget.empty()) { 535 | MCString::iterator it = FindNV("key " + sTarget.AsLower()); 536 | 537 | if (it != EndNV()) { 538 | PutModule("Target key is " + it->second); 539 | } else { 540 | PutModule("Target not found."); 541 | } 542 | } else { 543 | PutModule("Usage ShowKey <#chan|Nick>"); 544 | } 545 | } else if (sCmd.CaseCmp("LISTKEYS") == 0) { 546 | if (BeginNV() == EndNV()) { 547 | PutModule("You have no encryption keys set."); 548 | } else { 549 | CTable Table; 550 | Table.AddColumn("Target"); 551 | Table.AddColumn("Key"); 552 | 553 | // find all our keys and print them out 554 | for (MCString::iterator it = BeginNV(); it != EndNV(); it++) { 555 | if (it->first.Left(4) == "key ") { 556 | Table.AddRow(); 557 | Table.SetCell("Target", it->first.LeftChomp_n(4)); // remove "key " from start 558 | Table.SetCell("Key", it->second); 559 | } 560 | } 561 | 562 | if (Table.size()) { 563 | unsigned int uTableIdx = 0; 564 | CString sLine; 565 | 566 | while (Table.GetLine(uTableIdx++, sLine)) { 567 | PutModule(sLine); 568 | } 569 | } 570 | } 571 | } else if (sCmd.CaseCmp("LISTCONFIG") == 0) { 572 | // we would use tables here, but they mangle things like irc colour codes >.> 573 | for (MCString::iterator it = BeginNV(); it != EndNV(); it++) { 574 | if (it->first.Left(7) == "config ") { 575 | PutModule(it->first.LeftChomp_n(7) + " : \"" + it->second + "\""); 576 | } 577 | } 578 | } else if (sCmd.CaseCmp("SETCONFIG") == 0) { 579 | CString sName = sCommand.Token(1); 580 | CString sValue = sCommand.Token(2, true); 581 | 582 | if (!sValue.empty()) { 583 | SetNV("config " + sName.AsLower(), sValue); 584 | PutModule("Set config option [" + sName + "] to [" + sValue + "]"); 585 | } else { 586 | PutModule("Usage: SetConfig "); 587 | } 588 | } else if (sCmd.CaseCmp("KEYX") == 0) { 589 | CString sTarget = sCommand.Token(1); 590 | 591 | if (sTarget.empty()) { 592 | PutModule("You did not specify a target for the key exchange."); 593 | } else { 594 | map >::iterator it = m_msKeyExchange.find(sTarget.AsLower()); 595 | if (it != m_msKeyExchange.end()) { 596 | PutModule("Keyexchange with " + sTarget + " already in progress."); 597 | } else { 598 | CString sPriv_Key; 599 | CString sPub_Key; 600 | 601 | DH1080_gen(sPriv_Key, sPub_Key); 602 | m_msKeyExchange.insert(make_pair(sTarget.AsLower(), make_pair(time(NULL), sPriv_Key))); 603 | PutIRC("NOTICE " + sTarget + " :DH1080_INIT " + sPub_Key); 604 | PutModule("Sent my DH1080 public key to " + sTarget + ", waiting for reply ..."); 605 | if (FindTimer("KeyExchangeTimer") == NULL) { 606 | AddTimer(new CKeyExchangeTimer(this)); 607 | } 608 | } 609 | } 610 | } else if (sCmd.CaseCmp("HELP") == 0) { 611 | CTable Table; 612 | Table.AddColumn("Command"); 613 | Table.AddColumn("Arguments"); 614 | Table.AddColumn("Description"); 615 | 616 | // list all our commands 617 | Table.AddRow(); 618 | Table.SetCell("Command", "SetKey"); 619 | Table.SetCell("Arguments", " "); 620 | Table.SetCell("Description", "Sets 's FiSH encryption key"); 621 | 622 | Table.AddRow(); 623 | Table.SetCell("Command", "DelKey"); 624 | Table.SetCell("Arguments", ""); 625 | Table.SetCell("Description", "Removes 's FiSH encryption key"); 626 | 627 | Table.AddRow(); 628 | Table.SetCell("Command", "ShowKey"); 629 | Table.SetCell("Arguments", ""); 630 | Table.SetCell("Description", "Show the encryption key of , if it has one set"); 631 | 632 | Table.AddRow(); 633 | Table.SetCell("Command", "ListKeys"); 634 | Table.SetCell("Arguments", ""); 635 | Table.SetCell("Description", "Print out all of our keys"); 636 | 637 | Table.AddRow(); 638 | Table.SetCell("Command", "SetConfig"); 639 | Table.SetCell("Arguments", " "); 640 | Table.SetCell("Description", "Set config option to "); 641 | 642 | Table.AddRow(); 643 | Table.SetCell("Command", "ListConfig"); 644 | Table.SetCell("Arguments", ""); 645 | Table.SetCell("Description", "Print out all of our config options"); 646 | 647 | Table.AddRow(); 648 | Table.SetCell("Command", "KeyX"); 649 | Table.SetCell("Arguments", ""); 650 | Table.SetCell("Description", "Start a key exchange with "); 651 | 652 | Table.AddRow(); 653 | Table.SetCell("Command", "Help"); 654 | Table.SetCell("Arguments", ""); 655 | Table.SetCell("Description", "Display this message"); 656 | 657 | PutModule(Table); 658 | } else { 659 | PutModule("Unknown command, try 'Help'"); 660 | } 661 | } 662 | 663 | void DelStaleKeyExchanges(time_t iTime) { 664 | for (map >::const_iterator it = m_msKeyExchange.begin(); it != m_msKeyExchange.end(); it++) { 665 | if (iTime - 5 >= it->second.first) { 666 | PutModule("Keyexchange with " + it->first + " expired before completion."); 667 | m_msKeyExchange.erase(it->first); 668 | } 669 | } 670 | if (m_msKeyExchange.size() <= 0) { 671 | RemTimer("KeyExchangeTimer"); 672 | } 673 | } 674 | 675 | private: 676 | 677 | void DH1080_gen(CString& sPriv_Key, CString& sPub_Key) { 678 | sPriv_Key = ""; 679 | sPub_Key = ""; 680 | 681 | unsigned char raw_buf[200]; 682 | unsigned long len; 683 | unsigned char *a, *b; 684 | 685 | DH *dh; 686 | BIGNUM *b_prime=NULL; 687 | BIGNUM *b_generator=NULL; 688 | 689 | initb64(); 690 | 691 | dh=DH_new(); 692 | 693 | if (!BN_hex2bn(&b_prime, prime1080)) { 694 | return; 695 | } 696 | 697 | if (!BN_dec2bn(&b_generator, "2")) { 698 | return; 699 | } 700 | 701 | dh->p=b_prime; 702 | dh->g=b_generator; 703 | 704 | if (!DH_generate_key(dh)) { 705 | return; 706 | } 707 | 708 | len = BN_num_bytes(dh->priv_key); 709 | a = (unsigned char *)malloc(len); 710 | BN_bn2bin(dh->priv_key,a); 711 | 712 | memset(raw_buf, 0, 200); 713 | htob64((char *)a, (char *)raw_buf, len); 714 | sPriv_Key = CString((char *)raw_buf); 715 | len=BN_num_bytes(dh->pub_key); 716 | b = (unsigned char *)malloc(len); 717 | BN_bn2bin(dh->pub_key,b); 718 | memset(raw_buf, 0, 200); 719 | htob64((char *)b, (char *)raw_buf, len); 720 | sPub_Key = CString((char *)raw_buf); 721 | DH_free(dh); 722 | free(a); 723 | free(b); 724 | } 725 | 726 | 727 | bool DH1080_comp(CString& sPriv_Key, CString& sOtherPub_Key, CString& sSecret_Key) { 728 | int len; 729 | unsigned char SHA256digest[32]; 730 | char *key; 731 | BIGNUM *b_prime=NULL; 732 | BIGNUM *b_myPrivkey=NULL; 733 | BIGNUM *b_HisPubkey=NULL; 734 | BIGNUM *b_generator=NULL; 735 | DH *dh; 736 | CString sSHA256digest; 737 | unsigned char raw_buf[200]; 738 | 739 | if (!BN_hex2bn(&b_prime, prime1080)) { 740 | return false; 741 | } 742 | 743 | if (!BN_dec2bn(&b_generator, "2")) { 744 | return false; 745 | } 746 | 747 | dh=DH_new(); 748 | dh->p=b_prime; 749 | dh->g=b_generator; 750 | 751 | memset(raw_buf, 0, 200); 752 | len = b64toh((char *)sPriv_Key.c_str(), (char *)raw_buf); 753 | b_myPrivkey=BN_bin2bn(raw_buf, len, NULL); 754 | dh->priv_key=b_myPrivkey; 755 | 756 | memset(raw_buf, 0, 200); 757 | len = b64toh((char *)sOtherPub_Key.c_str(), (char *)raw_buf); 758 | 759 | b_HisPubkey=BN_bin2bn(raw_buf, len, NULL); 760 | 761 | key=(char *)malloc(DH_size(dh)); 762 | memset(key, 0, DH_size(dh)); 763 | len=DH_compute_key((unsigned char *)key, b_HisPubkey, dh); 764 | if (len == -1) { 765 | // Bad pub key 766 | unsigned long err = ERR_get_error(); 767 | DEBUG("** DH Error:" << ERR_error_string(err,NULL)); 768 | DH_free(dh); 769 | BN_clear_free(b_HisPubkey); 770 | free(key); 771 | 772 | sSecret_Key = CString(ERR_error_string(err,NULL)).Token(4,true,":"); 773 | return false; 774 | } 775 | 776 | SHA256_CTX c; 777 | SHA256_Init(&c); 778 | memset(SHA256digest, 0, 32); 779 | SHA256_Update(&c, key, len); 780 | SHA256_Final(SHA256digest, &c); 781 | memset(raw_buf, 0, 200); 782 | len = htob64((char *)SHA256digest, (char *)raw_buf, 32); 783 | sSecret_Key = ""; 784 | sSecret_Key.append((char *)raw_buf, len); 785 | 786 | DH_free(dh); 787 | BN_clear_free(b_HisPubkey); 788 | free(key); 789 | return true; 790 | } 791 | 792 | map > m_msKeyExchange; 793 | 794 | }; 795 | 796 | void CKeyExchangeTimer::RunJob() { 797 | CFishMod *p = (CFishMod *)m_pModule; 798 | p->DelStaleKeyExchanges(time(NULL)); 799 | } 800 | 801 | template<> void TModInfo(CModInfo& Info) { 802 | Info.SetWikiPage("CFishMod"); 803 | } 804 | 805 | NETWORKMODULEDEFS(CFishMod, "FiSH encryption for channel/private messages") 806 | --------------------------------------------------------------------------------