MemoryReadStream *blowfishEBC(SeekableReadStream &input, const std::vector<byte> &key, Mode mode) { BlowfishContext ctx; blowfishSetKey(ctx, &key[0], key.size()); size_t inputSize = input.size() - input.pos(); // Round up to the next multiple of the block size const size_t outputSize = ((inputSize + kBlockSize - 1) / kBlockSize) * kBlockSize; ScopedArray<byte> output(new byte[outputSize]); byte buffer[kBlockSize]; byte *data = output.get(); while (inputSize > 0) { const size_t toRead = MIN<size_t>(inputSize, kBlockSize); if (input.read(buffer, toRead) != toRead) throw Exception(kReadError); std::memset(buffer + toRead, 0, kBlockSize - toRead); blowfishECB(ctx, mode, buffer, data); data += toRead; inputSize -= toRead; } return new MemoryReadStream(output.release(), outputSize, true); }
bool Cipher::encrypt(QByteArray& cipherText) { if (cipherText.left(3) == "+p ") // don't encode if...? cipherText = cipherText.mid(3); else { if (m_cbc) // encode in ecb or cbc decide how to determine later { QByteArray temp = blowfishCBC(cipherText, true); if (temp == cipherText) { // kDebug("CBC Encoding Failed"); return false; } cipherText = "+OK *" + temp; } else { QByteArray temp = blowfishECB(cipherText, true); if (temp == cipherText) { // kDebug("ECB Encoding Failed"); return false; } cipherText = "+OK " + temp; } } return true; }
QByteArray Cipher::decryptTopic(QByteArray cipherText) { if (cipherText.mid(0, 4) == "+OK ") // FiSH style topic cipherText = cipherText.mid(4); else if (cipherText.left(5) == "«m«") cipherText = cipherText.mid(5, cipherText.length() - 10); else return cipherText; QByteArray temp; // TODO currently no backwards sanity checks for topic, it seems to use different standards // if somebody figures them out they can enable it and add "ERROR_NONECB/CBC" warnings if (m_cbc) temp = blowfishCBC(cipherText.mid(1), false); else temp = blowfishECB(cipherText, false); if (temp == cipherText) { return cipherText; } else cipherText = temp; if (cipherText.mid(0, 2) == "@@") cipherText = cipherText.mid(2); return cipherText; }
QByteArray Cipher::decrypt(QByteArray cipherText) { QByteArray pfx = ""; bool error = false; // used to flag non cbc, seems like good practice not to parse w/o regard for set encryption type // if we get cbc if (cipherText.mid(0, 5) == "+OK *") { // if we have cbc if (m_cbc) cipherText = cipherText.mid(5); // if we don't else { cipherText = cipherText.mid(5); pfx = "ERROR_NONECB: "; error = true; } } // if we get ecb else if (cipherText.mid(0, 4) == "+OK " || cipherText.mid(0, 5) == "mcps ") { // if we had cbc if (m_cbc) { cipherText = (cipherText.mid(0, 4) == "+OK ") ? cipherText.mid(4) : cipherText.mid(5); pfx = "ERROR_NONCBC: "; error = true; } // if we don't else { if (cipherText.mid(0, 4) == "+OK ") cipherText = cipherText.mid(4); else cipherText = cipherText.mid(5); } } // all other cases we fail else return cipherText; QByteArray temp; // (if cbc and no error we parse cbc) || (if ecb and error we parse cbc) if ((m_cbc && !error) || (!m_cbc && error)) { temp = blowfishCBC(cipherText, false); if (temp == cipherText) { // kDebug("Decryption from CBC Failed"); return cipherText + ' ' + '\n'; } else cipherText = temp; } else { temp = blowfishECB(cipherText, false); if (temp == cipherText) { // kDebug("Decryption from ECB Failed"); return cipherText + ' ' + '\n'; } else cipherText = temp; } // TODO FIXME the proper fix for this is to show encryption differently e.g. [nick] instead of <nick> // don't hate me for the mircryption reference there. if (cipherText.at(0) == 1) pfx = "\x0"; cipherText = pfx + cipherText + ' ' + '\n'; // FIXME(??) why is there an added space here? return cipherText; }