├── .gitignore ├── axolotl_global.h ├── duplicatemessageexception.h ├── ecc ├── curve.cpp ├── curve.h ├── djbec.cpp ├── djbec.h ├── eckeypair.cpp └── eckeypair.h ├── groups ├── ratchet │ ├── senderchainkey.cpp │ ├── senderchainkey.h │ ├── sendermessagekey.cpp │ └── sendermessagekey.h └── state │ ├── senderkeyrecord.cpp │ ├── senderkeyrecord.h │ ├── senderkeystate.cpp │ ├── senderkeystate.h │ └── senderkeystore.h ├── identitykey.cpp ├── identitykey.h ├── identitykeypair.cpp ├── identitykeypair.h ├── invalidkeyexception.h ├── invalidkeyidexception.h ├── invalidmessageexception.h ├── invalidversionexception.h ├── kdf ├── derivedmessagesecrets.cpp ├── derivedmessagesecrets.h ├── derivedrootsecrets.cpp ├── derivedrootsecrets.h ├── hkdf.cpp └── hkdf.h ├── legacymessageexception.h ├── libaxolotl.pro ├── nosessionexception.h ├── protobuf ├── LocalStorageProtocol.proto └── WhisperTextProtocol.proto ├── protocol ├── WhisperTextProtocol.pb.cc ├── WhisperTextProtocol.pb.h ├── ciphertextmessage.h ├── keyexchangemessage.cpp ├── keyexchangemessage.h ├── prekeywhispermessage.cpp ├── prekeywhispermessage.h ├── senderkeydistributionmessage.cpp ├── senderkeydistributionmessage.h ├── senderkeymessage.cpp ├── senderkeymessage.h ├── whispermessage.cpp └── whispermessage.h ├── ratchet ├── aliceaxolotlparameters.cpp ├── aliceaxolotlparameters.h ├── bobaxolotlparameters.cpp ├── bobaxolotlparameters.h ├── chainkey.cpp ├── chainkey.h ├── messagekeys.cpp ├── messagekeys.h ├── ratchetingsession.cpp ├── ratchetingsession.h ├── rootkey.cpp ├── rootkey.h ├── symmetricaxolotlparameters.cpp └── symmetricaxolotlparameters.h ├── sessionbuilder.cpp ├── sessionbuilder.h ├── sessioncipher.cpp ├── sessioncipher.h ├── stalekeyexchangeexception.h ├── state ├── LocalStorageProtocol.pb.cc ├── LocalStorageProtocol.pb.h ├── axolotlstore.h ├── identitykeystore.h ├── prekeybundle.cpp ├── prekeybundle.h ├── prekeyrecord.cpp ├── prekeyrecord.h ├── prekeystore.h ├── sessionrecord.cpp ├── sessionrecord.h ├── sessionstate.cpp ├── sessionstate.h ├── sessionstore.h ├── signedprekeyrecord.cpp ├── signedprekeyrecord.h └── signedprekeystore.h ├── untrustedidentityexception.h ├── util ├── byteutil.cpp ├── byteutil.h ├── keyhelper.cpp ├── keyhelper.h └── medium.h └── whisperexception.h /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.moc 20 | moc_*.cpp 21 | qrc_*.cpp 22 | ui_*.h 23 | Makefile* 24 | *-build-* 25 | 26 | # QtCreator 27 | 28 | *.autosave 29 | 30 | #QtCtreator Qml 31 | *.qmlproject.user 32 | *.qmlproject.user.* 33 | -------------------------------------------------------------------------------- /axolotl_global.h: -------------------------------------------------------------------------------- 1 | #ifndef AXOLOTL_GLOBAL_H 2 | #define AXOLOTL_GLOBAL_H 3 | 4 | // from https://gcc.gnu.org/wiki/Visibility 5 | #if defined _WIN32 || defined __CYGWIN__ 6 | #ifdef LIBAXOLOTL_LIBRARY 7 | #ifdef __GNUC__ 8 | #define LIBAXOLOTL_DLL __attribute__ ((dllexport)) 9 | #else 10 | #define LIBAXOLOTL_DLL __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. 11 | #endif 12 | #else 13 | #ifdef __GNUC__ 14 | #define LIBAXOLOTL_DLL __attribute__ ((dllimport)) 15 | #else 16 | #define LIBAXOLOTL_DLL __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. 17 | #endif 18 | #endif 19 | #define DLL_LOCAL 20 | #else 21 | #if __GNUC__ >= 4 22 | #define LIBAXOLOTL_DLL __attribute__ ((visibility ("default"))) 23 | #else 24 | #define LIBAXOLOTL_DLL 25 | #endif 26 | #endif 27 | 28 | #endif // AXOLOTL_GLOBAL_H 29 | -------------------------------------------------------------------------------- /duplicatemessageexception.h: -------------------------------------------------------------------------------- 1 | #ifndef DUPLICATEMESSAGEEXCEPTION_H 2 | #define DUPLICATEMESSAGEEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class DuplicateMessageException : public WhisperException 7 | { 8 | public: 9 | DuplicateMessageException(const QString &error): WhisperException("DuplicateMessageException", error) {} 10 | }; 11 | 12 | #endif // DUPLICATEMESSAGEEXCEPTION_H 13 | -------------------------------------------------------------------------------- /ecc/curve.cpp: -------------------------------------------------------------------------------- 1 | #include "curve.h" 2 | 3 | #include "../invalidkeyexception.h" 4 | 5 | #include "../libcurve25519/curve.h" 6 | 7 | #include 8 | 9 | const int Curve::DJB_TYPE = 5; 10 | 11 | ECKeyPair Curve::generateKeyPair() 12 | { 13 | RAND_poll(); 14 | 15 | unsigned char buff1[32]; 16 | memset(buff1, 0, 32); 17 | RAND_bytes(buff1, 32); 18 | 19 | QByteArray privateKey = QByteArray::fromRawData((const char*)buff1, 32); 20 | Curve25519::generatePrivateKey(privateKey.data()); 21 | QByteArray publicKey(32, '\0'); 22 | Curve25519::generatePublicKey(privateKey.constData(), publicKey.data()); 23 | return ECKeyPair(DjbECPublicKey(publicKey), DjbECPrivateKey(privateKey)); 24 | } 25 | 26 | DjbECPublicKey Curve::decodePoint(const QByteArray &privatePoint, int offset) 27 | { 28 | quint8 type = privatePoint[0]; 29 | 30 | if (type == Curve::DJB_TYPE) { 31 | type = privatePoint[offset] & 0xFF; 32 | if (type != Curve::DJB_TYPE) { 33 | throw InvalidKeyException(QString("Unknown key type: %1 ").arg(type)); 34 | } 35 | QByteArray keyBytes = privatePoint.mid(offset+1, 32); 36 | DjbECPublicKey pubkey(keyBytes); 37 | return pubkey; 38 | } 39 | else { 40 | throw InvalidKeyException(QString("Unknown key type: %1").arg(type)); 41 | } 42 | } 43 | 44 | DjbECPrivateKey Curve::decodePrivatePoint(const QByteArray &privatePoint) 45 | { 46 | return DjbECPrivateKey(privatePoint); 47 | } 48 | 49 | QByteArray Curve::calculateAgreement(const DjbECPublicKey &publicKey, const DjbECPrivateKey &privateKey) 50 | { 51 | if (publicKey.getType() != privateKey.getType()) { 52 | throw InvalidKeyException("Public and private keys must be of the same type!"); 53 | } 54 | 55 | if (publicKey.getType() == DJB_TYPE) { 56 | QByteArray sharedKey(32, '\0'); 57 | Curve25519::calculateAgreement(privateKey.getPrivateKey().constData(), 58 | publicKey.getPublicKey().constData(), 59 | sharedKey.data()); 60 | return sharedKey; 61 | } else { 62 | throw InvalidKeyException("Unknown type: " + publicKey.getType()); 63 | } 64 | } 65 | 66 | bool Curve::verifySignature(const DjbECPublicKey &signingKey, const QByteArray &message, const QByteArray &signature) 67 | { 68 | if (signingKey.getType() == DJB_TYPE) { 69 | return Curve25519::verifySignature((const unsigned char*)signingKey.getPublicKey().constData(), 70 | (const unsigned char*)message.constData(), 71 | message.size(), 72 | (const unsigned char*)signature.constData()) == 0; 73 | } else { 74 | throw InvalidKeyException(QString("Unknown type: %1").arg(signingKey.getType())); 75 | } 76 | } 77 | 78 | QByteArray Curve::calculateSignature(const DjbECPrivateKey &signingKey, const QByteArray &message) 79 | { 80 | if (signingKey.getType() == DJB_TYPE) { 81 | RAND_poll(); 82 | 83 | unsigned char buff1[64]; 84 | memset(buff1, 0, 64); 85 | RAND_bytes(buff1, 64); 86 | 87 | QByteArray random64 = QByteArray::fromRawData((const char*)buff1, 64); 88 | QByteArray signature(64, '\0'); 89 | Curve25519::calculateSignature((const unsigned char*)signingKey.getPrivateKey().constData(), 90 | (const unsigned char*)message.constData(), 91 | message.size(), 92 | (const unsigned char*)random64.constData(), 93 | (unsigned char*)signature.data()); 94 | return signature; 95 | } else { 96 | throw InvalidKeyException("Unknown type: " + signingKey.getType()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ecc/curve.h: -------------------------------------------------------------------------------- 1 | #ifndef CURVE_H 2 | #define CURVE_H 3 | 4 | #include "eckeypair.h" 5 | #include "djbec.h" 6 | 7 | class Curve 8 | { 9 | public: 10 | static const int DJB_TYPE; 11 | 12 | static ECKeyPair generateKeyPair(); 13 | static DjbECPublicKey decodePoint(const QByteArray &privatePoint, int offset = 0); 14 | static DjbECPrivateKey decodePrivatePoint(const QByteArray &privatePoint); 15 | static QByteArray calculateAgreement(const DjbECPublicKey &publicKey, const DjbECPrivateKey &privateKey); 16 | static bool verifySignature(const DjbECPublicKey &signingKey, const QByteArray &message, const QByteArray &signature); 17 | static QByteArray calculateSignature(const DjbECPrivateKey &signingKey, const QByteArray &message); 18 | }; 19 | 20 | #endif // CURVE_H 21 | -------------------------------------------------------------------------------- /ecc/djbec.cpp: -------------------------------------------------------------------------------- 1 | #include "djbec.h" 2 | #include "../util/byteutil.h" 3 | #include "curve.h" 4 | 5 | DjbECPublicKey::DjbECPublicKey() 6 | { 7 | this->publicKey.clear(); 8 | } 9 | 10 | DjbECPublicKey::DjbECPublicKey(const DjbECPublicKey &publicKey) 11 | { 12 | this->publicKey = publicKey.getPublicKey(); 13 | } 14 | 15 | DjbECPublicKey::DjbECPublicKey(const QByteArray &publicKey) 16 | { 17 | this->publicKey = publicKey; 18 | } 19 | 20 | QByteArray DjbECPublicKey::serialize() const 21 | { 22 | if (!publicKey.isEmpty()) { 23 | QByteArray serialized(1, (char)Curve::DJB_TYPE); 24 | serialized.append(publicKey); 25 | return serialized; 26 | } 27 | return QByteArray(); 28 | } 29 | 30 | int DjbECPublicKey::getType() const 31 | { 32 | return Curve::DJB_TYPE; 33 | } 34 | 35 | QByteArray DjbECPublicKey::getPublicKey() const 36 | { 37 | return publicKey; 38 | } 39 | 40 | bool DjbECPublicKey::operator <(const DjbECPublicKey &otherKey) 41 | { 42 | return publicKey != otherKey.publicKey; 43 | } 44 | 45 | bool DjbECPublicKey::operator ==(const DjbECPublicKey &otherKey) 46 | { 47 | return publicKey == otherKey.publicKey; 48 | } 49 | 50 | DjbECPrivateKey::DjbECPrivateKey() 51 | { 52 | this->privateKey.clear(); 53 | } 54 | 55 | DjbECPrivateKey::DjbECPrivateKey(const DjbECPrivateKey &privateKey) 56 | { 57 | this->privateKey = privateKey.getPrivateKey(); 58 | } 59 | 60 | DjbECPrivateKey::DjbECPrivateKey(const QByteArray &privateKey) 61 | { 62 | this->privateKey = privateKey; 63 | } 64 | 65 | QByteArray DjbECPrivateKey::serialize() const 66 | { 67 | return privateKey; 68 | } 69 | 70 | int DjbECPrivateKey::getType() const 71 | { 72 | return Curve::DJB_TYPE; 73 | } 74 | 75 | QByteArray DjbECPrivateKey::getPrivateKey() const 76 | { 77 | return privateKey; 78 | } 79 | 80 | bool DjbECPrivateKey::operator <(const DjbECPrivateKey &otherKey) 81 | { 82 | return privateKey != otherKey.privateKey; 83 | } 84 | 85 | bool DjbECPrivateKey::operator ==(const DjbECPrivateKey &otherKey) 86 | { 87 | return privateKey == otherKey.privateKey; 88 | } 89 | -------------------------------------------------------------------------------- /ecc/djbec.h: -------------------------------------------------------------------------------- 1 | #ifndef DJBEC_H 2 | #define DJBEC_H 3 | 4 | #include 5 | 6 | class DjbECPublicKey 7 | { 8 | public: 9 | DjbECPublicKey(); 10 | DjbECPublicKey(const DjbECPublicKey &publicKey); 11 | DjbECPublicKey(const QByteArray &publicKey); 12 | QByteArray serialize() const; 13 | int getType() const; 14 | QByteArray getPublicKey() const; 15 | bool operator <(const DjbECPublicKey &otherKey); 16 | bool operator ==(const DjbECPublicKey &otherKey); 17 | 18 | private: 19 | QByteArray publicKey; 20 | 21 | }; 22 | 23 | class DjbECPrivateKey 24 | { 25 | public: 26 | DjbECPrivateKey(); 27 | DjbECPrivateKey(const DjbECPrivateKey &privateKey); 28 | DjbECPrivateKey(const QByteArray &privateKey); 29 | QByteArray serialize() const; 30 | int getType() const; 31 | QByteArray getPrivateKey() const; 32 | bool operator <(const DjbECPrivateKey &otherKey); 33 | bool operator ==(const DjbECPrivateKey &otherKey); 34 | 35 | private: 36 | QByteArray privateKey; 37 | 38 | }; 39 | 40 | #endif // DJBEC_H 41 | -------------------------------------------------------------------------------- /ecc/eckeypair.cpp: -------------------------------------------------------------------------------- 1 | #include "eckeypair.h" 2 | 3 | ECKeyPair::ECKeyPair() 4 | { 5 | 6 | } 7 | 8 | ECKeyPair::ECKeyPair(const DjbECPublicKey &publicKey, const DjbECPrivateKey &privateKey) 9 | { 10 | this->publicKey = publicKey; 11 | this->privateKey = privateKey; 12 | } 13 | 14 | DjbECPrivateKey ECKeyPair::getPrivateKey() const 15 | { 16 | return privateKey; 17 | } 18 | 19 | DjbECPublicKey ECKeyPair::getPublicKey() const 20 | { 21 | return publicKey; 22 | } 23 | -------------------------------------------------------------------------------- /ecc/eckeypair.h: -------------------------------------------------------------------------------- 1 | #ifndef ECKEYPAIR_H 2 | #define ECKEYPAIR_H 3 | 4 | #include "djbec.h" 5 | 6 | class ECKeyPair 7 | { 8 | public: 9 | ECKeyPair(); 10 | ECKeyPair(const DjbECPublicKey &publicKey, const DjbECPrivateKey &privateKey); 11 | DjbECPrivateKey getPrivateKey() const; 12 | DjbECPublicKey getPublicKey() const; 13 | 14 | private: 15 | DjbECPublicKey publicKey; 16 | DjbECPrivateKey privateKey; 17 | 18 | }; 19 | 20 | #endif // ECKEYPAIR_H 21 | -------------------------------------------------------------------------------- /groups/ratchet/senderchainkey.cpp: -------------------------------------------------------------------------------- 1 | #include "senderchainkey.h" 2 | #include 3 | 4 | const QByteArray SenderChainKey::MESSAGE_KEY_SEED = QByteArray("\0x01"); 5 | const QByteArray SenderChainKey::CHAIN_KEY_SEED = QByteArray("\0x02"); 6 | 7 | SenderChainKey::SenderChainKey(int iteration, const QByteArray &chainKey) 8 | { 9 | this->iteration = iteration; 10 | this->chainKey = chainKey; 11 | } 12 | 13 | int SenderChainKey::getIteration() const 14 | { 15 | return iteration; 16 | } 17 | 18 | SenderMessageKey SenderChainKey::getSenderMessageKey() const 19 | { 20 | return SenderMessageKey(iteration, getDerivative(MESSAGE_KEY_SEED, chainKey)); 21 | } 22 | 23 | SenderChainKey SenderChainKey::getNext() const 24 | { 25 | return SenderChainKey(iteration + 1, getDerivative(CHAIN_KEY_SEED, chainKey)); 26 | } 27 | 28 | QByteArray SenderChainKey::getSeed() const 29 | { 30 | return chainKey; 31 | } 32 | 33 | QByteArray SenderChainKey::getDerivative(const QByteArray &seed, const QByteArray &key) const 34 | { 35 | return QMessageAuthenticationCode::hash(seed, key, QCryptographicHash::Sha256); 36 | } 37 | -------------------------------------------------------------------------------- /groups/ratchet/senderchainkey.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDERCHAINKEY_H 2 | #define SENDERCHAINKEY_H 3 | 4 | #include 5 | #include "sendermessagekey.h" 6 | 7 | class SenderChainKey 8 | { 9 | public: 10 | SenderChainKey(int iteration, const QByteArray &chainKey); 11 | 12 | int getIteration() const; 13 | SenderMessageKey getSenderMessageKey() const; 14 | SenderChainKey getNext() const; 15 | QByteArray getSeed() const; 16 | QByteArray getDerivative(const QByteArray &seed, const QByteArray &key) const; 17 | 18 | private: 19 | static const QByteArray MESSAGE_KEY_SEED; 20 | static const QByteArray CHAIN_KEY_SEED; 21 | 22 | int iteration; 23 | QByteArray chainKey; 24 | }; 25 | 26 | #endif // SENDERCHAINKEY_H 27 | -------------------------------------------------------------------------------- /groups/ratchet/sendermessagekey.cpp: -------------------------------------------------------------------------------- 1 | #include "sendermessagekey.h" 2 | #include "kdf/hkdf.h" 3 | 4 | SenderMessageKey::SenderMessageKey() 5 | { 6 | 7 | } 8 | 9 | SenderMessageKey::SenderMessageKey(int iteration, const QByteArray &seed) 10 | { 11 | QByteArray derivative = HKDF(3).deriveSecrets(seed, QByteArray("WhisperGroup"), 48); 12 | 13 | this->iteration = iteration; 14 | this->seed = seed; 15 | this->iv = derivative.left(16); 16 | this->cipherKey = derivative.mid(16, 32); 17 | } 18 | 19 | int SenderMessageKey::getIteration() const 20 | { 21 | return iteration; 22 | } 23 | 24 | QByteArray SenderMessageKey::getIv() const 25 | { 26 | return iv; 27 | } 28 | 29 | QByteArray SenderMessageKey::getCipherKey() const 30 | { 31 | return cipherKey; 32 | } 33 | 34 | QByteArray SenderMessageKey::getSeed() const 35 | { 36 | return seed; 37 | } 38 | -------------------------------------------------------------------------------- /groups/ratchet/sendermessagekey.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDERMESSAGEKEY_H 2 | #define SENDERMESSAGEKEY_H 3 | 4 | #include 5 | 6 | class SenderMessageKey 7 | { 8 | public: 9 | SenderMessageKey(); 10 | SenderMessageKey(int iteration, const QByteArray &seed); 11 | 12 | int getIteration() const; 13 | QByteArray getIv() const; 14 | QByteArray getCipherKey() const; 15 | QByteArray getSeed() const; 16 | 17 | private: 18 | int iteration; 19 | QByteArray iv; 20 | QByteArray cipherKey; 21 | QByteArray seed; 22 | }; 23 | 24 | #endif // SENDERMESSAGEKEY_H 25 | -------------------------------------------------------------------------------- /groups/state/senderkeyrecord.cpp: -------------------------------------------------------------------------------- 1 | #include "senderkeyrecord.h" 2 | 3 | #include "../../state/LocalStorageProtocol.pb.h" 4 | 5 | #include "../../invalidkeyidexception.h" 6 | 7 | SenderKeyRecord::SenderKeyRecord() 8 | { 9 | 10 | } 11 | 12 | SenderKeyRecord::SenderKeyRecord(const QByteArray &serialized) 13 | { 14 | textsecure::SenderKeyRecordStructure senderKeyRecordStructure; 15 | senderKeyRecordStructure.ParseFromArray(serialized.constData(), serialized.size()); 16 | 17 | for (int i = 0; i < senderKeyRecordStructure.senderkeystates_size(); i++) { 18 | textsecure::SenderKeyStateStructure structure = senderKeyRecordStructure.senderkeystates(i); 19 | senderKeyStates.append(new SenderKeyState(structure)); 20 | } 21 | } 22 | 23 | SenderKeyState *SenderKeyRecord::getSenderKeyState() 24 | { 25 | if (!senderKeyStates.isEmpty()) { 26 | return senderKeyStates.first(); 27 | } 28 | 29 | throw InvalidKeyIdException(QString("No key state in record!")); 30 | } 31 | 32 | SenderKeyState *SenderKeyRecord::getSenderKeyState(int keyId) 33 | { 34 | foreach (SenderKeyState *state, senderKeyStates) { 35 | if (state->getKeyId() == keyId) { 36 | return state; 37 | } 38 | } 39 | 40 | throw InvalidKeyIdException(QString("No keys for: %1").arg(keyId)); 41 | } 42 | 43 | void SenderKeyRecord::addSenderKeyState(int id, int iteration, const QByteArray &chainKey, const DjbECPublicKey &signatureKey) 44 | { 45 | senderKeyStates.prepend(new SenderKeyState(id, iteration, chainKey, signatureKey)); 46 | 47 | if (senderKeyStates.size() > SenderKeyRecord::MAX_STATES) { 48 | senderKeyStates.removeLast(); 49 | } 50 | } 51 | 52 | void SenderKeyRecord::setSenderKeyState(int id, int iteration, const QByteArray &chainKey, const ECKeyPair &signatureKey) 53 | { 54 | senderKeyStates.clear(); 55 | senderKeyStates.append(new SenderKeyState(id, iteration, chainKey, signatureKey)); 56 | } 57 | 58 | QByteArray SenderKeyRecord::serialize() const 59 | { 60 | textsecure::SenderKeyRecordStructure recordStructure; 61 | 62 | foreach (SenderKeyState *senderKeyState, senderKeyStates) { 63 | recordStructure.add_senderkeystates()->CopyFrom(senderKeyState->getStructure()); 64 | } 65 | 66 | ::std::string recordStructureString = recordStructure.SerializeAsString(); 67 | QByteArray recordStructureBytes(recordStructureString.data(), recordStructureString.length()); 68 | 69 | return recordStructureBytes; 70 | } 71 | 72 | bool SenderKeyRecord::isEmpty() const 73 | { 74 | return senderKeyStates.isEmpty(); 75 | } 76 | -------------------------------------------------------------------------------- /groups/state/senderkeyrecord.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDERKEYRECORD_H 2 | #define SENDERKEYRECORD_H 3 | 4 | #include 5 | #include 6 | 7 | #include "senderkeystate.h" 8 | #include "ecc/djbec.h" 9 | #include "ecc/eckeypair.h" 10 | 11 | class SenderKeyRecord 12 | { 13 | public: 14 | static const int MAX_STATES = 5; 15 | 16 | SenderKeyRecord(); 17 | SenderKeyRecord(const QByteArray &serialized); 18 | SenderKeyState *getSenderKeyState(); 19 | SenderKeyState *getSenderKeyState(int keyId); 20 | void addSenderKeyState(int id, int iteration, const QByteArray &chainKey, const DjbECPublicKey &signatureKey); 21 | void setSenderKeyState(int id, int iteration, const QByteArray &chainKey, const ECKeyPair &signatureKey); 22 | QByteArray serialize() const; 23 | bool isEmpty() const; 24 | 25 | private: 26 | QLinkedList senderKeyStates; 27 | }; 28 | 29 | #endif // SENDERKEYRECORD_H 30 | -------------------------------------------------------------------------------- /groups/state/senderkeystate.cpp: -------------------------------------------------------------------------------- 1 | #include "senderkeystate.h" 2 | #include "../../ecc/curve.h" 3 | 4 | SenderKeyState::SenderKeyState() 5 | { 6 | 7 | } 8 | 9 | SenderKeyState::SenderKeyState(int id, int iteration, const QByteArray &chainKey, const DjbECPublicKey &signatureKey) 10 | { 11 | senderKeyStateStructure = textsecure::SenderKeyStateStructure(); 12 | senderKeyStateStructure.set_senderkeyid(id); 13 | senderKeyStateStructure.mutable_senderchainkey()->set_iteration(iteration); 14 | senderKeyStateStructure.mutable_senderchainkey()->set_seed(chainKey.constData(), 15 | chainKey.size()); 16 | senderKeyStateStructure.mutable_sendersigningkey()->set_public_(signatureKey.serialize().constData(), 17 | signatureKey.serialize().size()); 18 | 19 | /*textsecure::SenderKeyStateStructure::SenderChainKey senderChainKeyStructure; 20 | senderChainKeyStructure.set_iteration(iteration); 21 | senderChainKeyStructure.set_seed(chainKey.constData()); 22 | 23 | textsecure::SenderKeyStateStructure::SenderSigningKey signingKeyStructure; 24 | signingKeyStructure.set_public_(signatureKey.serialize().constData()); 25 | 26 | senderKeyStateStructure = textsecure::SenderKeyStateStructure(); 27 | senderKeyStateStructure.set_senderkeyid(id); 28 | senderKeyStateStructure.mutable_senderchainkey()->CopyFrom(senderChainKeyStructure); 29 | senderKeyStateStructure.mutable_sendersigningkey()->CopyFrom(signingKeyStructure);*/ 30 | } 31 | 32 | SenderKeyState::SenderKeyState(int id, int iteration, const QByteArray &chainKey, const ECKeyPair &signatureKey) 33 | { 34 | SenderKeyState(id, iteration, chainKey, signatureKey.getPublicKey(), signatureKey.getPrivateKey()); 35 | } 36 | 37 | SenderKeyState::SenderKeyState(int id, int iteration, const QByteArray &chainKey, const DjbECPublicKey &signatureKeyPublic, const DjbECPrivateKey &signatureKeyPrivate) 38 | { 39 | senderKeyStateStructure = textsecure::SenderKeyStateStructure(); 40 | senderKeyStateStructure.set_senderkeyid(id); 41 | senderKeyStateStructure.mutable_senderchainkey()->set_iteration(iteration); 42 | senderKeyStateStructure.mutable_senderchainkey()->set_seed(chainKey.constData(), 43 | chainKey.size()); 44 | senderKeyStateStructure.mutable_sendersigningkey()->set_public_(signatureKeyPublic.serialize().constData(), 45 | signatureKeyPublic.serialize().size()); 46 | senderKeyStateStructure.mutable_sendersigningkey()->set_private_(signatureKeyPrivate.serialize().constData(), 47 | signatureKeyPrivate.serialize().size()); 48 | 49 | /*textsecure::SenderKeyStateStructure::SenderChainKey senderChainKeyStructure; 50 | senderChainKeyStructure.set_iteration(iteration); 51 | senderChainKeyStructure.set_seed(chainKey.constData()); 52 | 53 | textsecure::SenderKeyStateStructure::SenderSigningKey signingKeyStructure; 54 | signingKeyStructure.set_public_(signatureKeyPublic.serialize().constData()); 55 | 56 | signingKeyStructure.set_private_(signatureKeyPrivate.serialize().constData()); 57 | 58 | senderKeyStateStructure = textsecure::SenderKeyStateStructure(); 59 | senderKeyStateStructure.set_senderkeyid(id); 60 | senderKeyStateStructure.mutable_senderchainkey()->CopyFrom(senderChainKeyStructure); 61 | senderKeyStateStructure.mutable_sendersigningkey()->CopyFrom(signingKeyStructure);*/ 62 | } 63 | 64 | SenderKeyState::SenderKeyState(const textsecure::SenderKeyStateStructure &senderKeyStateStructure) 65 | { 66 | this->senderKeyStateStructure = senderKeyStateStructure; 67 | } 68 | 69 | int SenderKeyState::getKeyId() const 70 | { 71 | return senderKeyStateStructure.senderkeyid(); 72 | } 73 | 74 | SenderChainKey SenderKeyState::getSenderChainKey() const 75 | { 76 | ::std::string seed = senderKeyStateStructure.senderchainkey().seed(); 77 | return SenderChainKey(senderKeyStateStructure.senderchainkey().iteration(), 78 | QByteArray(seed.data(), seed.length())); 79 | } 80 | 81 | void SenderKeyState::setSenderChainKey(const SenderChainKey &chainKey) 82 | { 83 | senderKeyStateStructure.mutable_senderchainkey()->set_iteration(chainKey.getIteration()); 84 | senderKeyStateStructure.mutable_senderchainkey()->set_seed(chainKey.getSeed().constData(), 85 | chainKey.getSeed().size()); 86 | 87 | /*textsecure::SenderKeyStateStructure::SenderChainKey senderChainKeyStructure; 88 | senderChainKeyStructure.set_iteration(chainKey.getIteration()); 89 | senderChainKeyStructure.set_seed(chainKey.getSeed().constData()); 90 | 91 | senderKeyStateStructure.mutable_senderchainkey()->CopyFrom(senderChainKeyStructure);*/ 92 | } 93 | 94 | DjbECPublicKey SenderKeyState::getSigningKeyPublic() const 95 | { 96 | ::std::string sendersigningkeypublic = senderKeyStateStructure.sendersigningkey().public_(); 97 | return Curve::decodePoint(QByteArray(sendersigningkeypublic.data(), sendersigningkeypublic.length()), 0); 98 | } 99 | 100 | DjbECPrivateKey SenderKeyState::getSigningKeyPrivate() const 101 | { 102 | ::std::string sendersigningkeyprivate = senderKeyStateStructure.sendersigningkey().public_(); 103 | return Curve::decodePrivatePoint(QByteArray(sendersigningkeyprivate.data(), sendersigningkeyprivate.length())); 104 | } 105 | 106 | bool SenderKeyState::hasSenderMessageKey(uint32_t iteration) const 107 | { 108 | for (int i = 0; i < senderKeyStateStructure.sendermessagekeys_size(); i++) { 109 | textsecure::SenderKeyStateStructure::SenderMessageKey senderMessageKey = senderKeyStateStructure.sendermessagekeys(i); 110 | if (senderMessageKey.iteration() == iteration) { 111 | return true; 112 | } 113 | } 114 | 115 | return false; 116 | } 117 | 118 | void SenderKeyState::addSenderMessageKey(const SenderMessageKey &senderMessageKey) 119 | { 120 | textsecure::SenderKeyStateStructure::SenderMessageKey* sendermessagekey = senderKeyStateStructure.add_sendermessagekeys(); 121 | sendermessagekey->set_iteration(senderMessageKey.getIteration()); 122 | sendermessagekey->set_seed(senderMessageKey.getSeed().constData(), 123 | senderMessageKey.getSeed().size()); 124 | 125 | if (senderKeyStateStructure.sendermessagekeys_size() > SenderKeyState::MAX_MESSAGE_KEYS) { 126 | senderKeyStateStructure.mutable_sendermessagekeys()->DeleteSubrange(0, 1); 127 | } 128 | } 129 | 130 | SenderMessageKey SenderKeyState::removeSenderMessageKey(uint32_t iteration) 131 | { 132 | SenderMessageKey result; 133 | for (int i = 0; i < senderKeyStateStructure.sendermessagekeys_size(); i++) { 134 | textsecure::SenderKeyStateStructure::SenderMessageKey *senderMessageKey = senderKeyStateStructure.mutable_sendermessagekeys(i); 135 | if (senderMessageKey->iteration() == iteration) { 136 | ::std::string senderMessageKeySeed = senderMessageKey->seed(); 137 | result = SenderMessageKey(iteration, QByteArray(senderMessageKeySeed.data(), senderMessageKeySeed.length())); 138 | delete senderMessageKey; 139 | break; 140 | } 141 | } 142 | 143 | return result; 144 | } 145 | 146 | textsecure::SenderKeyStateStructure SenderKeyState::getStructure() const 147 | { 148 | return senderKeyStateStructure; 149 | } 150 | -------------------------------------------------------------------------------- /groups/state/senderkeystate.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDERKEYSTATE_H 2 | #define SENDERKEYSTATE_H 3 | 4 | #include "../../state/LocalStorageProtocol.pb.h" 5 | #include "../../ecc/eckeypair.h" 6 | #include "../ratchet/senderchainkey.h" 7 | 8 | class SenderKeyState 9 | { 10 | public: 11 | static const int MAX_MESSAGE_KEYS = 2000; 12 | 13 | SenderKeyState(); 14 | SenderKeyState(int id, int iteration, const QByteArray &chainKey, const DjbECPublicKey &signatureKey); 15 | SenderKeyState(int id, int iteration, const QByteArray &chainKey, const ECKeyPair &signatureKey); 16 | SenderKeyState(int id, int iteration, const QByteArray &chainKey, 17 | const DjbECPublicKey &signatureKeyPublic, const DjbECPrivateKey &signatureKeyPrivate); 18 | SenderKeyState(const textsecure::SenderKeyStateStructure &senderKeyStateStructure); 19 | 20 | int getKeyId() const; 21 | SenderChainKey getSenderChainKey() const; 22 | void setSenderChainKey(const SenderChainKey &chainKey); 23 | DjbECPublicKey getSigningKeyPublic() const; 24 | DjbECPrivateKey getSigningKeyPrivate() const; 25 | bool hasSenderMessageKey(uint32_t iteration) const; 26 | void addSenderMessageKey(const SenderMessageKey &senderMessageKey); 27 | SenderMessageKey removeSenderMessageKey(uint32_t iteration); 28 | textsecure::SenderKeyStateStructure getStructure() const; 29 | 30 | private: 31 | textsecure::SenderKeyStateStructure senderKeyStateStructure; 32 | }; 33 | 34 | #endif // SENDERKEYSTATE_H 35 | -------------------------------------------------------------------------------- /groups/state/senderkeystore.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDERKEYSTORE_H 2 | #define SENDERKEYSTORE_H 3 | 4 | #include "senderkeyrecord.h" 5 | #include "groups/senderkeyname.h" 6 | #include 7 | 8 | class SenderKeyStore 9 | { 10 | public: 11 | virtual void storeSenderKey(const SenderKeyName &senderKeyName, const SenderKeyRecord &record) = 0; 12 | virtual SenderKeyRecord loadSenderKey(const SenderKeyName &senderKeyName) const = 0; 13 | }; 14 | 15 | #endif // SENDERKEYSTORE_H 16 | -------------------------------------------------------------------------------- /identitykey.cpp: -------------------------------------------------------------------------------- 1 | #include "identitykey.h" 2 | #include "ecc/curve.h" 3 | 4 | IdentityKey::IdentityKey() 5 | { 6 | } 7 | 8 | IdentityKey::IdentityKey(const DjbECPublicKey &publicKey, int offset) 9 | { 10 | if (offset == 0) { 11 | this->publicKey = publicKey; 12 | } 13 | else { 14 | this->publicKey = Curve::decodePoint(publicKey.serialize(), offset); 15 | } 16 | } 17 | 18 | IdentityKey::IdentityKey(const QByteArray &publicKey, int offset) 19 | { 20 | this->publicKey = Curve::decodePoint(publicKey, offset); 21 | } 22 | 23 | DjbECPublicKey IdentityKey::getPublicKey() const 24 | { 25 | return publicKey; 26 | } 27 | 28 | QByteArray IdentityKey::serialize() const 29 | { 30 | return publicKey.serialize(); 31 | } 32 | 33 | QByteArray IdentityKey::getFingerprint() const 34 | { 35 | return publicKey.serialize().toHex(); 36 | } 37 | 38 | QByteArray IdentityKey::hashCode() const 39 | { 40 | return publicKey.serialize().mid(0, 4); // TODO 41 | } 42 | 43 | bool IdentityKey::operator ==(const IdentityKey &otherKey) 44 | { 45 | return publicKey.serialize() == otherKey.getPublicKey().serialize(); 46 | } 47 | -------------------------------------------------------------------------------- /identitykey.h: -------------------------------------------------------------------------------- 1 | #ifndef IDENTITYKEY_H 2 | #define IDENTITYKEY_H 3 | 4 | #include "ecc/djbec.h" 5 | 6 | class IdentityKey 7 | { 8 | public: 9 | IdentityKey(); 10 | IdentityKey(const DjbECPublicKey &publicKey, int offset = 0); 11 | IdentityKey(const QByteArray &publicKey, int offset = 0); 12 | 13 | DjbECPublicKey getPublicKey() const; 14 | QByteArray serialize() const; 15 | QByteArray getFingerprint() const; 16 | QByteArray hashCode() const; 17 | bool operator ==(const IdentityKey &otherKey); 18 | 19 | private: 20 | DjbECPublicKey publicKey; 21 | 22 | }; 23 | 24 | #endif // IDENTITYKEY_H 25 | -------------------------------------------------------------------------------- /identitykeypair.cpp: -------------------------------------------------------------------------------- 1 | #include "identitykeypair.h" 2 | #include "ecc/curve.h" 3 | #include "state/sessionstate.h" 4 | 5 | IdentityKeyPair::IdentityKeyPair() 6 | { 7 | 8 | } 9 | 10 | IdentityKeyPair::IdentityKeyPair(const IdentityKey &publicKey, const DjbECPrivateKey &privateKey) 11 | { 12 | this->publicKey = publicKey; 13 | this->privateKey = privateKey; 14 | } 15 | 16 | IdentityKeyPair::IdentityKeyPair(const QByteArray &serialized) 17 | { 18 | textsecure::IdentityKeyPairStructure structure; 19 | structure.ParseFromArray(serialized.constData(), serialized.size()); 20 | ::std::string publickey = structure.publickey(); 21 | this->publicKey = IdentityKey(QByteArray(publickey.data(), publickey.length()), 0); 22 | ::std::string privatekey = structure.privatekey(); 23 | this->privateKey = Curve::decodePrivatePoint(QByteArray(privatekey.data(), privatekey.length())); 24 | } 25 | 26 | IdentityKey IdentityKeyPair::getPublicKey() const 27 | { 28 | return publicKey; 29 | } 30 | 31 | DjbECPrivateKey IdentityKeyPair::getPrivateKey() const 32 | { 33 | return privateKey; 34 | } 35 | -------------------------------------------------------------------------------- /identitykeypair.h: -------------------------------------------------------------------------------- 1 | #ifndef IDENTITYKEYPAIR_H 2 | #define IDENTITYKEYPAIR_H 3 | 4 | #include "ecc/djbec.h" 5 | #include "identitykey.h" 6 | 7 | class IdentityKeyPair 8 | { 9 | public: 10 | IdentityKeyPair(); 11 | IdentityKeyPair(const IdentityKey &publicKey, const DjbECPrivateKey &privateKey); 12 | IdentityKeyPair(const QByteArray &serialized); 13 | 14 | IdentityKey getPublicKey() const; 15 | DjbECPrivateKey getPrivateKey() const; 16 | 17 | private: 18 | IdentityKey publicKey; 19 | DjbECPrivateKey privateKey; 20 | 21 | }; 22 | 23 | #endif // IDENTITYKEYPAIR_H 24 | -------------------------------------------------------------------------------- /invalidkeyexception.h: -------------------------------------------------------------------------------- 1 | #ifndef INVALIDKEYEXCEPTION_H 2 | #define INVALIDKEYEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class InvalidKeyException : public WhisperException 7 | { 8 | public: 9 | InvalidKeyException(const QString &error): WhisperException("InvalidKeyException", error) {} 10 | }; 11 | 12 | #endif // INVALIDKEYEXCEPTION_H 13 | -------------------------------------------------------------------------------- /invalidkeyidexception.h: -------------------------------------------------------------------------------- 1 | #ifndef INVALIDKEYIDEXCEPTION_H 2 | #define INVALIDKEYIDEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class InvalidKeyIdException : public WhisperException 7 | { 8 | public: 9 | InvalidKeyIdException(const QString &error): WhisperException("InvalidKeyIdException", error) {} 10 | }; 11 | 12 | #endif // INVALIDKEYIDEXCEPTION_H 13 | -------------------------------------------------------------------------------- /invalidmessageexception.h: -------------------------------------------------------------------------------- 1 | #ifndef INVALIDMESSAGEEXCEPTION_H 2 | #define INVALIDMESSAGEEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class InvalidMessageException : public WhisperException 7 | { 8 | public: 9 | InvalidMessageException(const QString &error) : WhisperException("InvalidMessageException", error) {} 10 | InvalidMessageException(const QString &error, const QList &exceptions) : WhisperException("InvalidMessageException", error) { 11 | foreach (const WhisperException &exception, exceptions) { 12 | _error.append(" "); 13 | _error.append(exception.errorMessage()); 14 | } 15 | } 16 | }; 17 | 18 | #endif // INVALIDMESSAGEEXCEPTION_H 19 | -------------------------------------------------------------------------------- /invalidversionexception.h: -------------------------------------------------------------------------------- 1 | #ifndef INVALIDVERSIONEXCEPTION_H 2 | #define INVALIDVERSIONEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class InvalidVersionException : public WhisperException 7 | { 8 | public: 9 | InvalidVersionException(const QString &error) : WhisperException("InvalidVersionException", error) {} 10 | }; 11 | 12 | #endif // INVALIDVERSIONEXCEPTION_H 13 | -------------------------------------------------------------------------------- /kdf/derivedmessagesecrets.cpp: -------------------------------------------------------------------------------- 1 | #include "derivedmessagesecrets.h" 2 | #include "../util/byteutil.h" 3 | 4 | #include 5 | 6 | const int DerivedMessageSecrets::SIZE = 80; 7 | const int DerivedMessageSecrets::CIPHER_KEY_LENGTH = 32; 8 | const int DerivedMessageSecrets::MAC_KEY_LENGTH = 32; 9 | const int DerivedMessageSecrets::IV_LENGTH = 16; 10 | 11 | DerivedMessageSecrets::DerivedMessageSecrets(const QByteArray &okm) 12 | { 13 | QList keys = ByteUtil::split(okm, 14 | DerivedMessageSecrets::CIPHER_KEY_LENGTH, 15 | DerivedMessageSecrets::MAC_KEY_LENGTH, 16 | DerivedMessageSecrets::IV_LENGTH); 17 | cipherKey = keys[0]; //AES 18 | macKey = keys[1]; //sha256 19 | iv = keys[2]; 20 | } 21 | 22 | QByteArray DerivedMessageSecrets::getCipherKey() const 23 | { 24 | return cipherKey; 25 | } 26 | 27 | QByteArray DerivedMessageSecrets::getMacKey() const 28 | { 29 | return macKey; 30 | } 31 | 32 | QByteArray DerivedMessageSecrets::getIv() const 33 | { 34 | return iv; 35 | } 36 | -------------------------------------------------------------------------------- /kdf/derivedmessagesecrets.h: -------------------------------------------------------------------------------- 1 | #ifndef DERIVEDMESSAGESECRETS_H 2 | #define DERIVEDMESSAGESECRETS_H 3 | 4 | #include 5 | 6 | class DerivedMessageSecrets 7 | { 8 | public: 9 | DerivedMessageSecrets(const QByteArray &okm); 10 | static const int SIZE; 11 | static const int CIPHER_KEY_LENGTH; 12 | static const int MAC_KEY_LENGTH; 13 | static const int IV_LENGTH; 14 | 15 | QByteArray getCipherKey() const; 16 | QByteArray getMacKey() const; 17 | QByteArray getIv() const; 18 | 19 | private: 20 | QByteArray cipherKey; 21 | QByteArray macKey; 22 | QByteArray iv; 23 | 24 | }; 25 | 26 | #endif // DERIVEDMESSAGESECRETS_H 27 | -------------------------------------------------------------------------------- /kdf/derivedrootsecrets.cpp: -------------------------------------------------------------------------------- 1 | #include "derivedrootsecrets.h" 2 | #include "../util/byteutil.h" 3 | 4 | const int DerivedRootSecrets::SIZE = 64; 5 | 6 | DerivedRootSecrets::DerivedRootSecrets(const QByteArray &okm) 7 | { 8 | QList keys = ByteUtil::split(okm, 32, 32); 9 | rootKey = keys[0]; 10 | chainKey = keys[1]; 11 | } 12 | 13 | QByteArray DerivedRootSecrets::getRootKey() const 14 | { 15 | return rootKey; 16 | } 17 | 18 | QByteArray DerivedRootSecrets::getChainKey() const 19 | { 20 | return chainKey; 21 | } 22 | -------------------------------------------------------------------------------- /kdf/derivedrootsecrets.h: -------------------------------------------------------------------------------- 1 | #ifndef DERIVEDROOTSECRETS_H 2 | #define DERIVEDROOTSECRETS_H 3 | 4 | #include 5 | 6 | class DerivedRootSecrets 7 | { 8 | public: 9 | DerivedRootSecrets(const QByteArray &okm); 10 | static const int SIZE; 11 | 12 | QByteArray getRootKey() const; 13 | QByteArray getChainKey() const; 14 | 15 | private: 16 | QByteArray rootKey; 17 | QByteArray chainKey; 18 | 19 | }; 20 | 21 | #endif // DERIVEDROOTSECRETS_H 22 | -------------------------------------------------------------------------------- /kdf/hkdf.cpp: -------------------------------------------------------------------------------- 1 | #include "hkdf.h" 2 | #include 3 | #include 4 | #include 5 | 6 | const float HKDF::HASH_OUTPUT_SIZE = 32; 7 | 8 | HKDF::HKDF(int messageVersion) 9 | { 10 | iterationStartOffset = 0; 11 | if (messageVersion == 2) { 12 | iterationStartOffset = 0; 13 | } 14 | else if (messageVersion == 3) { 15 | iterationStartOffset = 1; 16 | } 17 | else { 18 | // TODO exception 19 | } 20 | } 21 | 22 | int HKDF::getIterationStartOffset() const 23 | { 24 | return iterationStartOffset; 25 | } 26 | 27 | QByteArray HKDF::expand(const QByteArray &prk, const QByteArray &info, int outputSize) const 28 | { 29 | int iterations = qCeil((float)outputSize / HKDF::HASH_OUTPUT_SIZE); 30 | QByteArray mixin; 31 | QByteArray results; 32 | int remainingBytes = outputSize; 33 | 34 | for (int i = iterationStartOffset; i < (iterations + iterationStartOffset); i++) { 35 | 36 | QByteArray message; 37 | message.append(mixin); 38 | if (!info.isEmpty()) { 39 | message.append(info); 40 | } 41 | 42 | message.append(QByteArray(1, (char)(i % 256))); 43 | 44 | QByteArray stepResult = QMessageAuthenticationCode::hash(message, prk, QCryptographicHash::Sha256); 45 | int stepSize = qMin(remainingBytes, stepResult.size()); 46 | results.append(stepResult.mid(0, stepSize)); 47 | mixin = stepResult; 48 | remainingBytes -= stepSize; 49 | } 50 | return results; 51 | } 52 | 53 | QByteArray HKDF::extract(const QByteArray &salt, const QByteArray &inputKeyMaterial) const 54 | { 55 | return QMessageAuthenticationCode::hash(inputKeyMaterial, salt, QCryptographicHash::Sha256); 56 | } 57 | 58 | QByteArray HKDF::deriveSecrets(const QByteArray &inputKeyMaterial, const QByteArray &info, int outputLength, const QByteArray &saltFirst) const 59 | { 60 | QByteArray salt = saltFirst; 61 | if (salt.isEmpty()) { 62 | salt = QByteArray(HKDF::HASH_OUTPUT_SIZE, '\0'); 63 | } 64 | QByteArray prk = extract(salt, inputKeyMaterial); 65 | return expand(prk, info, outputLength); 66 | } 67 | -------------------------------------------------------------------------------- /kdf/hkdf.h: -------------------------------------------------------------------------------- 1 | #ifndef HKDF_H 2 | #define HKDF_H 3 | 4 | #include 5 | 6 | class HKDF 7 | { 8 | public: 9 | HKDF(int messageVersion = 2); 10 | static const float HASH_OUTPUT_SIZE; 11 | int getIterationStartOffset() const; 12 | QByteArray expand(const QByteArray &prk, const QByteArray &info, int outputSize) const; 13 | QByteArray extract(const QByteArray &salt, const QByteArray &inputKeyMaterial) const; 14 | QByteArray deriveSecrets(const QByteArray &inputKeyMaterial, const QByteArray &info, int outputLength, const QByteArray &saltFirst = QByteArray()) const; 15 | 16 | private: 17 | int iterationStartOffset; 18 | 19 | }; 20 | 21 | #endif // HKDF_H 22 | -------------------------------------------------------------------------------- /legacymessageexception.h: -------------------------------------------------------------------------------- 1 | #ifndef LEGACYMESSAGEEXCEPTION_H 2 | #define LEGACYMESSAGEEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class LegacyMessageException : public WhisperException 7 | { 8 | public: 9 | LegacyMessageException(const QString &error) : WhisperException("LegacyMessageException", error) {} 10 | }; 11 | 12 | #endif // LEGACYMESSAGEEXCEPTION_H 13 | -------------------------------------------------------------------------------- /libaxolotl.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = lib 2 | 3 | TARGET = axolotl 4 | isEmpty(CURRENT_RPATH_DIR) { 5 | target.path = /usr/lib 6 | } else { 7 | message("$$TARGET QMAKE_RPATHDIR and path is set to $$CURRENT_RPATH_DIR") 8 | target.path = $$CURRENT_RPATH_DIR 9 | QMAKE_RPATHDIR += $$INSTALL_ROOT$$CURRENT_RPATH_DIR 10 | } 11 | VERSION = 1.3.4 12 | INSTALLS += target 13 | 14 | CONFIG += plugin link_pkgconfig 15 | PKGCONFIG += openssl libssl libcrypto 16 | DEFINES += LIBAXOLOTL_LIBRARY 17 | 18 | LIBS += -L../libcurve25519 -lcurve25519 19 | LIBS += /usr/lib/libprotobuf.a 20 | QMAKE_CFLAGS += -fPIC -DPIC 21 | QMAKE_CXXFLAGS += -fPIC -DPIC 22 | 23 | HEADERS += \ 24 | duplicatemessageexception.h \ 25 | invalidkeyexception.h \ 26 | invalidkeyidexception.h \ 27 | invalidmessageexception.h \ 28 | invalidversionexception.h \ 29 | nosessionexception.h \ 30 | stalekeyexchangeexception.h \ 31 | untrustedidentityexception.h \ 32 | ecc/curve.h \ 33 | ecc/eckeypair.h \ 34 | util/byteutil.h \ 35 | ecc/djbec.h \ 36 | kdf/derivedmessagesecrets.h \ 37 | kdf/derivedrootsecrets.h \ 38 | kdf/hkdf.h \ 39 | util/keyhelper.h \ 40 | identitykey.h \ 41 | identitykeypair.h \ 42 | state/prekeybundle.h \ 43 | state/prekeyrecord.h \ 44 | state/LocalStorageProtocol.pb.h \ 45 | state/sessionrecord.h \ 46 | state/sessionstate.h \ 47 | ratchet/messagekeys.h \ 48 | ratchet/aliceaxolotlparameters.h \ 49 | ratchet/bobaxolotlparameters.h \ 50 | ratchet/chainkey.h \ 51 | ratchet/ratchetingsession.h \ 52 | ratchet/symmetricaxolotlparameters.h \ 53 | ratchet/rootkey.h \ 54 | state/sessionstore.h \ 55 | state/signedprekeyrecord.h \ 56 | state/signedprekeystore.h \ 57 | groups/ratchet/senderchainkey.h \ 58 | groups/ratchet/sendermessagekey.h \ 59 | groups/state/senderkeyrecord.h \ 60 | groups/state/senderkeystate.h \ 61 | groups/state/senderkeystore.h \ 62 | protocol/WhisperTextProtocol.pb.h \ 63 | protocol/ciphertextmessage.h \ 64 | protocol/keyexchangemessage.h \ 65 | legacymessageexception.h \ 66 | whisperexception.h \ 67 | protocol/prekeywhispermessage.h \ 68 | protocol/whispermessage.h \ 69 | protocol/senderkeymessage.h \ 70 | protocol/senderkeydistributionmessage.h \ 71 | sessioncipher.h \ 72 | sessionbuilder.h \ 73 | state/prekeystore.h \ 74 | state/axolotlstore.h \ 75 | state/identitykeystore.h \ 76 | util/medium.h \ 77 | axolotl_global.h \ 78 | groups/senderkeyname.h \ 79 | groups/groupsessionbuilder.h \ 80 | groups/groupcipher.h \ 81 | axolotladdress.h 82 | 83 | SOURCES += \ 84 | ecc/curve.cpp \ 85 | ecc/eckeypair.cpp \ 86 | util/byteutil.cpp \ 87 | ecc/djbec.cpp \ 88 | kdf/derivedmessagesecrets.cpp \ 89 | kdf/derivedrootsecrets.cpp \ 90 | kdf/hkdf.cpp \ 91 | util/keyhelper.cpp \ 92 | identitykey.cpp \ 93 | identitykeypair.cpp \ 94 | state/prekeybundle.cpp \ 95 | state/prekeyrecord.cpp \ 96 | state/LocalStorageProtocol.pb.cc \ 97 | state/sessionrecord.cpp \ 98 | state/sessionstate.cpp \ 99 | ratchet/messagekeys.cpp \ 100 | ratchet/aliceaxolotlparameters.cpp \ 101 | ratchet/bobaxolotlparameters.cpp \ 102 | ratchet/chainkey.cpp \ 103 | ratchet/ratchetingsession.cpp \ 104 | ratchet/symmetricaxolotlparameters.cpp \ 105 | ratchet/rootkey.cpp \ 106 | state/signedprekeyrecord.cpp \ 107 | groups/ratchet/senderchainkey.cpp \ 108 | groups/ratchet/sendermessagekey.cpp \ 109 | groups/state/senderkeyrecord.cpp \ 110 | groups/state/senderkeystate.cpp \ 111 | protocol/WhisperTextProtocol.pb.cc \ 112 | protocol/keyexchangemessage.cpp \ 113 | protocol/prekeywhispermessage.cpp \ 114 | protocol/whispermessage.cpp \ 115 | protocol/senderkeymessage.cpp \ 116 | protocol/senderkeydistributionmessage.cpp \ 117 | sessioncipher.cpp \ 118 | sessionbuilder.cpp \ 119 | groups/senderkeyname.cpp \ 120 | groups/groupsessionbuilder.cpp \ 121 | groups/groupcipher.cpp \ 122 | axolotladdress.cpp 123 | -------------------------------------------------------------------------------- /nosessionexception.h: -------------------------------------------------------------------------------- 1 | #ifndef NOSESSIONEXCEPTION_H 2 | #define NOSESSIONEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class NoSessionException : public WhisperException 7 | { 8 | public: 9 | NoSessionException(const QString &error) : WhisperException("NoSessionException", error) {} 10 | }; 11 | 12 | #endif // NOSESSIONEXCEPTION_H 13 | -------------------------------------------------------------------------------- /protobuf/LocalStorageProtocol.proto: -------------------------------------------------------------------------------- 1 | package textsecure; 2 | 3 | option java_package = "org.whispersystems.libaxolotl.state"; 4 | option java_outer_classname = "StorageProtos"; 5 | 6 | message SessionStructure { 7 | message Chain { 8 | optional bytes senderRatchetKey = 1; 9 | optional bytes senderRatchetKeyPrivate = 2; 10 | 11 | message ChainKey { 12 | optional uint32 index = 1; 13 | optional bytes key = 2; 14 | } 15 | 16 | optional ChainKey chainKey = 3; 17 | 18 | message MessageKey { 19 | optional uint32 index = 1; 20 | optional bytes cipherKey = 2; 21 | optional bytes macKey = 3; 22 | optional bytes iv = 4; 23 | } 24 | 25 | repeated MessageKey messageKeys = 4; 26 | } 27 | 28 | message PendingKeyExchange { 29 | optional uint32 sequence = 1; 30 | optional bytes localBaseKey = 2; 31 | optional bytes localBaseKeyPrivate = 3; 32 | optional bytes localRatchetKey = 4; 33 | optional bytes localRatchetKeyPrivate = 5; 34 | optional bytes localIdentityKey = 7; 35 | optional bytes localIdentityKeyPrivate = 8; 36 | } 37 | 38 | message PendingPreKey { 39 | optional uint32 preKeyId = 1; 40 | optional int32 signedPreKeyId = 3; 41 | optional bytes baseKey = 2; 42 | } 43 | 44 | optional uint32 sessionVersion = 1; 45 | optional bytes localIdentityPublic = 2; 46 | optional bytes remoteIdentityPublic = 3; 47 | 48 | optional bytes rootKey = 4; 49 | optional uint32 previousCounter = 5; 50 | 51 | optional Chain senderChain = 6; 52 | repeated Chain receiverChains = 7; 53 | 54 | optional PendingKeyExchange pendingKeyExchange = 8; 55 | optional PendingPreKey pendingPreKey = 9; 56 | 57 | optional uint32 remoteRegistrationId = 10; 58 | optional uint32 localRegistrationId = 11; 59 | 60 | optional bool needsRefresh = 12; 61 | optional bytes aliceBaseKey = 13; 62 | } 63 | 64 | message RecordStructure { 65 | optional SessionStructure currentSession = 1; 66 | repeated SessionStructure previousSessions = 2; 67 | } 68 | 69 | message PreKeyRecordStructure { 70 | optional uint32 id = 1; 71 | optional bytes publicKey = 2; 72 | optional bytes privateKey = 3; 73 | } 74 | 75 | message SignedPreKeyRecordStructure { 76 | optional uint32 id = 1; 77 | optional bytes publicKey = 2; 78 | optional bytes privateKey = 3; 79 | optional bytes signature = 4; 80 | optional fixed64 timestamp = 5; 81 | } 82 | 83 | message IdentityKeyPairStructure { 84 | optional bytes publicKey = 1; 85 | optional bytes privateKey = 2; 86 | } 87 | 88 | message SenderKeyStateStructure { 89 | message SenderChainKey { 90 | optional uint32 iteration = 1; 91 | optional bytes seed = 2; 92 | } 93 | 94 | message SenderMessageKey { 95 | optional uint32 iteration = 1; 96 | optional bytes seed = 2; 97 | } 98 | 99 | message SenderSigningKey { 100 | optional bytes public = 1; 101 | optional bytes private = 2; 102 | } 103 | 104 | optional uint32 senderKeyId = 1; 105 | optional SenderChainKey senderChainKey = 2; 106 | optional SenderSigningKey senderSigningKey = 3; 107 | repeated SenderMessageKey senderMessageKeys = 4; 108 | } 109 | 110 | message SenderKeyRecordStructure { 111 | repeated SenderKeyStateStructure senderKeyStates = 1; 112 | } -------------------------------------------------------------------------------- /protobuf/WhisperTextProtocol.proto: -------------------------------------------------------------------------------- 1 | package textsecure; 2 | 3 | option java_package = "org.whispersystems.libaxolotl.protocol"; 4 | option java_outer_classname = "WhisperProtos"; 5 | 6 | message WhisperMessage { 7 | optional bytes ratchetKey = 1; 8 | optional uint32 counter = 2; 9 | optional uint32 previousCounter = 3; 10 | optional bytes ciphertext = 4; 11 | } 12 | 13 | message PreKeyWhisperMessage { 14 | optional uint32 registrationId = 5; 15 | optional uint32 preKeyId = 1; 16 | optional uint32 signedPreKeyId = 6; 17 | optional bytes baseKey = 2; 18 | optional bytes identityKey = 3; 19 | optional bytes message = 4; // WhisperMessage 20 | } 21 | 22 | message KeyExchangeMessage { 23 | optional uint32 id = 1; 24 | optional bytes baseKey = 2; 25 | optional bytes ratchetKey = 3; 26 | optional bytes identityKey = 4; 27 | optional bytes baseKeySignature = 5; 28 | } 29 | 30 | message SenderKeyMessage { 31 | optional uint32 id = 1; 32 | optional uint32 iteration = 2; 33 | optional bytes ciphertext = 3; 34 | } 35 | 36 | message SenderKeyDistributionMessage { 37 | optional uint32 id = 1; 38 | optional uint32 iteration = 2; 39 | optional bytes chainKey = 3; 40 | optional bytes signingKey = 4; 41 | } -------------------------------------------------------------------------------- /protocol/ciphertextmessage.h: -------------------------------------------------------------------------------- 1 | #ifndef CIPHERTEXTMESSAGE_H 2 | #define CIPHERTEXTMESSAGE_H 3 | 4 | #include 5 | 6 | class CiphertextMessage 7 | { 8 | public: 9 | static const int UNSUPPORTED_VERSION = 1; 10 | static const int CURRENT_VERSION = 3; 11 | 12 | static const int WHISPER_TYPE = 2; 13 | static const int PREKEY_TYPE = 3; 14 | static const int SENDERKEY_TYPE = 4; 15 | static const int SENDERKEY_DISTRIBUTION_TYPE = 5; 16 | 17 | // This should be the worst case (worse than V2). So not always accurate, but good enough for padding. 18 | static const int ENCRYPTED_MESSAGE_OVERHEAD = 53; 19 | 20 | virtual QByteArray serialize() const = 0; 21 | virtual int getType() const = 0; 22 | virtual ~CiphertextMessage() {} 23 | }; 24 | 25 | #endif // CIPHERTEXTMESSAGE_H 26 | -------------------------------------------------------------------------------- /protocol/keyexchangemessage.cpp: -------------------------------------------------------------------------------- 1 | #include "keyexchangemessage.h" 2 | #include "ciphertextmessage.h" 3 | #include "WhisperTextProtocol.pb.h" 4 | #include "../util/byteutil.h" 5 | #include "../ecc/curve.h" 6 | 7 | #include "../legacymessageexception.h" 8 | #include "../invalidversionexception.h" 9 | #include "../invalidmessageexception.h" 10 | 11 | KeyExchangeMessage::KeyExchangeMessage() 12 | { 13 | 14 | } 15 | 16 | KeyExchangeMessage::KeyExchangeMessage(int messageVersion, int sequence, int flags, const DjbECPublicKey &baseKey, const QByteArray &baseKeySignature, const DjbECPublicKey &ratchetKey, const IdentityKey &identityKey) 17 | { 18 | this->supportedVersion = CiphertextMessage::CURRENT_VERSION; 19 | this->version = messageVersion; 20 | this->sequence = sequence; 21 | this->flags = flags; 22 | this->baseKey = baseKey; 23 | this->baseKeySignature = baseKeySignature; 24 | this->ratchetKey = ratchetKey; 25 | this->identityKey = identityKey; 26 | 27 | textsecure::KeyExchangeMessage message; 28 | message.set_id((sequence << 5) | flags); 29 | message.set_basekey(baseKey.serialize().constData()); 30 | message.set_ratchetkey(ratchetKey.serialize().constData()); 31 | message.set_identitykey(identityKey.serialize().constData()); 32 | 33 | if (messageVersion >= 3) { 34 | message.set_basekeysignature(baseKeySignature.constData()); 35 | } 36 | 37 | ::std::string serializedMessage = message.SerializeAsString(); 38 | this->serialized = QByteArray(serializedMessage.data(), serializedMessage.length()); 39 | this->serialized.prepend(ByteUtil::intsToByteHighAndLow(this->version, this->supportedVersion)); 40 | } 41 | 42 | KeyExchangeMessage::KeyExchangeMessage(const QByteArray &serialized) 43 | { 44 | QList parts = ByteUtil::split(serialized, 1, serialized.size() - 1); 45 | this->version = ByteUtil::highBitsToInt(parts[0][0]); 46 | this->supportedVersion = ByteUtil::lowBitsToInt(parts[0][0]); 47 | 48 | if (this->version <= CiphertextMessage::UNSUPPORTED_VERSION) { 49 | throw LegacyMessageException(QString("Unsupported legacy version: %1").arg(this->version)); 50 | } 51 | 52 | if (this->version > CiphertextMessage::CURRENT_VERSION) { 53 | throw InvalidVersionException(QString("Unknown version: %1").arg(this->version)); 54 | } 55 | 56 | textsecure::KeyExchangeMessage message; 57 | message.ParseFromArray(parts[1].constData(), parts[1].size()); 58 | 59 | if (!message.has_id() || !message.has_basekey() || 60 | !message.has_ratchetkey() || !message.has_identitykey() || 61 | (this->version >=3 && !message.has_basekeysignature())) 62 | { 63 | throw InvalidMessageException("Some required fields missing!"); 64 | } 65 | 66 | this->sequence = message.id() >> 5; 67 | this->flags = message.id() & 0x1f; 68 | this->serialized = serialized; 69 | ::std::string messagebasekey = message.basekey(); 70 | this->baseKey = Curve::decodePoint(QByteArray(messagebasekey.data(), messagebasekey.length()), 0); 71 | ::std::string messagebasekeysignature = message.basekeysignature(); 72 | this->baseKeySignature = QByteArray(messagebasekeysignature.data(), messagebasekeysignature.length()); 73 | ::std::string messageratchetkey = message.ratchetkey(); 74 | this->ratchetKey = Curve::decodePoint(QByteArray(messageratchetkey.data(), messageratchetkey.length()), 0); 75 | ::std::string messageidentitykey = message.identitykey(); 76 | this->identityKey = IdentityKey(QByteArray(messageidentitykey.data(), messageidentitykey.length()), 0); 77 | } 78 | 79 | int KeyExchangeMessage::getVersion() const 80 | { 81 | return version; 82 | } 83 | 84 | DjbECPublicKey KeyExchangeMessage::getBaseKey() const 85 | { 86 | return baseKey; 87 | } 88 | 89 | QByteArray KeyExchangeMessage::getBaseKeySignature() const 90 | { 91 | return baseKeySignature; 92 | } 93 | 94 | DjbECPublicKey KeyExchangeMessage::getRatchetKey() const 95 | { 96 | return ratchetKey; 97 | } 98 | 99 | IdentityKey KeyExchangeMessage::getIdentityKey() const 100 | { 101 | return identityKey; 102 | } 103 | 104 | bool KeyExchangeMessage::hasIdentityKey() const 105 | { 106 | return true; 107 | } 108 | 109 | int KeyExchangeMessage::getMaxVersion() const 110 | { 111 | return supportedVersion; 112 | } 113 | 114 | bool KeyExchangeMessage::isResponse() const 115 | { 116 | return (flags & RESPONSE_FLAG) != 0; 117 | } 118 | 119 | bool KeyExchangeMessage::isInitiate() const 120 | { 121 | return (flags & INITIATE_FLAG) != 0; 122 | } 123 | 124 | bool KeyExchangeMessage::isResponseForSimultaneousInitiate() const 125 | { 126 | return (flags & SIMULTAENOUS_INITIATE_FLAG) != 0; 127 | } 128 | 129 | int KeyExchangeMessage::getFlags() const 130 | { 131 | return flags; 132 | } 133 | 134 | int KeyExchangeMessage::getSequence() const 135 | { 136 | return sequence; 137 | } 138 | 139 | QByteArray KeyExchangeMessage::serialize() const 140 | { 141 | return serialized; 142 | } 143 | -------------------------------------------------------------------------------- /protocol/keyexchangemessage.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYEXCHANGEMESSAGE_H 2 | #define KEYEXCHANGEMESSAGE_H 3 | 4 | #include 5 | #include "../ecc/djbec.h" 6 | #include "../identitykey.h" 7 | 8 | class KeyExchangeMessage 9 | { 10 | public: 11 | KeyExchangeMessage(); 12 | KeyExchangeMessage(int messageVersion, int sequence, int flags, 13 | const DjbECPublicKey &baseKey, const QByteArray &baseKeySignature, 14 | const DjbECPublicKey &ratchetKey, const IdentityKey &identityKey); 15 | KeyExchangeMessage(const QByteArray &serialized); 16 | 17 | static const int INITIATE_FLAG = 0x01; 18 | static const int RESPONSE_FLAG = 0x02; 19 | static const int SIMULTAENOUS_INITIATE_FLAG = 0x04; 20 | 21 | int getVersion() const; 22 | DjbECPublicKey getBaseKey() const; 23 | QByteArray getBaseKeySignature() const; 24 | DjbECPublicKey getRatchetKey() const; 25 | IdentityKey getIdentityKey() const; 26 | bool hasIdentityKey() const; 27 | int getMaxVersion() const; 28 | bool isResponse() const; 29 | bool isInitiate() const; 30 | bool isResponseForSimultaneousInitiate() const; 31 | int getFlags() const; 32 | int getSequence() const; 33 | QByteArray serialize() const; 34 | 35 | private: 36 | int version; 37 | int supportedVersion; 38 | int sequence; 39 | int flags; 40 | 41 | DjbECPublicKey baseKey; 42 | QByteArray baseKeySignature; 43 | DjbECPublicKey ratchetKey; 44 | IdentityKey identityKey; 45 | QByteArray serialized; 46 | }; 47 | 48 | #endif // KEYEXCHANGEMESSAGE_H 49 | -------------------------------------------------------------------------------- /protocol/prekeywhispermessage.cpp: -------------------------------------------------------------------------------- 1 | #include "prekeywhispermessage.h" 2 | #include "../invalidkeyexception.h" 3 | #include "../legacymessageexception.h" 4 | #include "../invalidmessageexception.h" 5 | #include "../invalidversionexception.h" 6 | 7 | #include "../util/byteutil.h" 8 | #include "../ecc/curve.h" 9 | #include "WhisperTextProtocol.pb.h" 10 | 11 | #include 12 | 13 | PreKeyWhisperMessage::PreKeyWhisperMessage(const QByteArray &serialized) 14 | { 15 | try { 16 | this->version = ByteUtil::highBitsToInt(serialized[0]); 17 | 18 | if (this->version > CiphertextMessage::CURRENT_VERSION) { 19 | throw InvalidVersionException("Unknown version: " + this->version); 20 | } 21 | textsecure::PreKeyWhisperMessage preKeyWhisperMessage; 22 | QByteArray serializedMessage = serialized.mid(1); 23 | preKeyWhisperMessage.ParseFromArray(serializedMessage.constData(), serializedMessage.size()); 24 | 25 | if ((version == 2 && !preKeyWhisperMessage.has_prekeyid()) || 26 | (version == 3 && !preKeyWhisperMessage.has_signedprekeyid()) || 27 | !preKeyWhisperMessage.has_basekey() || 28 | !preKeyWhisperMessage.has_identitykey() || 29 | !preKeyWhisperMessage.has_message()) 30 | { 31 | qDebug() << "version:" << version; 32 | qDebug() << "has_prekeyid:" << preKeyWhisperMessage.has_prekeyid(); 33 | qDebug() << "has_signedprekeyid:" << preKeyWhisperMessage.has_signedprekeyid(); 34 | qDebug() << "has_basekey:" << preKeyWhisperMessage.has_basekey(); 35 | qDebug() << "has_identitykey:" << preKeyWhisperMessage.has_identitykey(); 36 | qDebug() << "has_message:" << preKeyWhisperMessage.has_message(); 37 | throw InvalidMessageException("Incomplete message."); 38 | } 39 | 40 | this->serialized = serialized; 41 | this->registrationId = preKeyWhisperMessage.registrationid(); 42 | this->preKeyId = preKeyWhisperMessage.has_prekeyid() ? preKeyWhisperMessage.prekeyid() : -1; 43 | this->signedPreKeyId = preKeyWhisperMessage.has_signedprekeyid() ? preKeyWhisperMessage.signedprekeyid() : -1; 44 | ::std::string basekey = preKeyWhisperMessage.basekey(); 45 | this->baseKey = Curve::decodePoint(QByteArray(basekey.data(), basekey.length()), 0); 46 | ::std::string identitykey = preKeyWhisperMessage.identitykey(); 47 | this->identityKey = IdentityKey(Curve::decodePoint(QByteArray(identitykey.data(), identitykey.length()), 0)); 48 | ::std::string whisperMessage = preKeyWhisperMessage.message(); 49 | QByteArray whisperMessageSerialized(whisperMessage.data(), whisperMessage.length()); 50 | this->message.reset(new WhisperMessage(whisperMessageSerialized)); 51 | } catch (const InvalidKeyException &e) { 52 | throw InvalidMessageException(__PRETTY_FUNCTION__, QList() << e); 53 | } catch (const LegacyMessageException &e) { 54 | throw InvalidMessageException(__PRETTY_FUNCTION__, QList() << e); 55 | } 56 | } 57 | 58 | PreKeyWhisperMessage::PreKeyWhisperMessage(int messageVersion, ulong registrationId, ulong preKeyId, ulong signedPreKeyId, const DjbECPublicKey &baseKey, const IdentityKey &identityKey, QSharedPointer message) 59 | { 60 | this->version = messageVersion; 61 | this->registrationId = registrationId; 62 | this->preKeyId = preKeyId; 63 | this->signedPreKeyId = signedPreKeyId; 64 | this->baseKey = baseKey; 65 | this->identityKey = identityKey; 66 | this->message = message; 67 | 68 | textsecure::PreKeyWhisperMessage preKeyWhisperMessage; 69 | preKeyWhisperMessage.set_signedprekeyid(signedPreKeyId); 70 | QByteArray basekey = baseKey.serialize(); 71 | preKeyWhisperMessage.set_basekey(basekey.constData(), basekey.size()); 72 | QByteArray identitykey = identityKey.serialize(); 73 | preKeyWhisperMessage.set_identitykey(identitykey.constData(), identitykey.size()); 74 | QByteArray bytemessage = message->serialize(); 75 | preKeyWhisperMessage.set_message(bytemessage.constData(), bytemessage.size()); 76 | preKeyWhisperMessage.set_registrationid(registrationId); 77 | 78 | if (preKeyId >= 0) { 79 | preKeyWhisperMessage.set_prekeyid(preKeyId); 80 | } 81 | 82 | ::std::string serializedmessage = preKeyWhisperMessage.SerializeAsString(); 83 | QByteArray messageBytes = QByteArray(serializedmessage.data(), serializedmessage.length()); 84 | 85 | this->serialized = messageBytes; 86 | this->serialized.prepend(ByteUtil::intsToByteHighAndLow(this->version, CURRENT_VERSION)); 87 | } 88 | 89 | int PreKeyWhisperMessage::getMessageVersion() const 90 | { 91 | return version; 92 | } 93 | 94 | IdentityKey PreKeyWhisperMessage::getIdentityKey() const 95 | { 96 | return identityKey; 97 | } 98 | 99 | ulong PreKeyWhisperMessage::getRegistrationId() const 100 | { 101 | return registrationId; 102 | } 103 | 104 | ulong PreKeyWhisperMessage::getPreKeyId() const 105 | { 106 | return preKeyId; 107 | } 108 | 109 | ulong PreKeyWhisperMessage::getSignedPreKeyId() const 110 | { 111 | return signedPreKeyId; 112 | } 113 | 114 | DjbECPublicKey PreKeyWhisperMessage::getBaseKey() const 115 | { 116 | return baseKey; 117 | } 118 | 119 | QSharedPointer PreKeyWhisperMessage::getWhisperMessage() 120 | { 121 | return message; 122 | } 123 | 124 | QByteArray PreKeyWhisperMessage::serialize() const 125 | { 126 | return serialized; 127 | } 128 | 129 | int PreKeyWhisperMessage::getType() const 130 | { 131 | return CiphertextMessage::PREKEY_TYPE; 132 | } 133 | -------------------------------------------------------------------------------- /protocol/prekeywhispermessage.h: -------------------------------------------------------------------------------- 1 | #ifndef PREKEYWHISPERMESSAGE_H 2 | #define PREKEYWHISPERMESSAGE_H 3 | 4 | #include 5 | 6 | #include "ciphertextmessage.h" 7 | #include "../ecc/djbec.h" 8 | #include "../identitykey.h" 9 | #include "whispermessage.h" 10 | 11 | class PreKeyWhisperMessage : public CiphertextMessage 12 | { 13 | public: 14 | PreKeyWhisperMessage(const QByteArray &serialized); 15 | PreKeyWhisperMessage(int messageVersion, ulong registrationId, ulong preKeyId, 16 | ulong signedPreKeyId, const DjbECPublicKey &baseKey, const IdentityKey &identityKey, 17 | QSharedPointer message); 18 | virtual ~PreKeyWhisperMessage() {} 19 | 20 | int getMessageVersion() const; 21 | IdentityKey getIdentityKey() const; 22 | ulong getRegistrationId() const; 23 | ulong getPreKeyId() const; 24 | ulong getSignedPreKeyId() const; 25 | DjbECPublicKey getBaseKey() const; 26 | QSharedPointer getWhisperMessage(); 27 | QByteArray serialize() const; 28 | int getType() const; 29 | 30 | private: 31 | int version; 32 | ulong registrationId; 33 | ulong preKeyId; 34 | ulong signedPreKeyId; 35 | DjbECPublicKey baseKey; 36 | IdentityKey identityKey; 37 | QSharedPointer message; 38 | QByteArray serialized; 39 | }; 40 | 41 | #endif // PREKEYWHISPERMESSAGE_H 42 | -------------------------------------------------------------------------------- /protocol/senderkeydistributionmessage.cpp: -------------------------------------------------------------------------------- 1 | #include "senderkeydistributionmessage.h" 2 | #include "WhisperTextProtocol.pb.h" 3 | #include "util/byteutil.h" 4 | #include "ecc/curve.h" 5 | #include "legacymessageexception.h" 6 | #include "invalidmessageexception.h" 7 | 8 | #include 9 | 10 | SenderKeyDistributionMessage::SenderKeyDistributionMessage(int id, int iteration, const QByteArray &chainKey, const DjbECPublicKey &signatureKey) 11 | { 12 | qint8 version = ByteUtil::intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION); 13 | 14 | this->id = id; 15 | this->iteration = iteration; 16 | this->chainKey = chainKey; 17 | this->signatureKey = signatureKey; 18 | textsecure::SenderKeyDistributionMessage distributionMessage; 19 | distributionMessage.set_id(id); 20 | distributionMessage.set_iteration(iteration); 21 | distributionMessage.set_chainkey(chainKey.constData()); 22 | distributionMessage.set_signingkey(signatureKey.serialize().constData()); 23 | ::std::string serializedMessage = distributionMessage.SerializeAsString(); 24 | this->serialized = QByteArray(serializedMessage.data(), serializedMessage.length()); 25 | this->serialized.prepend(version); 26 | } 27 | 28 | SenderKeyDistributionMessage::SenderKeyDistributionMessage(const QByteArray &serialized) 29 | { 30 | qint8 version = serialized[0]; 31 | QByteArray message = serialized.mid(1); 32 | 33 | if (ByteUtil::highBitsToInt(version) < CiphertextMessage::CURRENT_VERSION) { 34 | throw LegacyMessageException("Legacy message: " + ByteUtil::highBitsToInt(version)); 35 | } 36 | 37 | if (ByteUtil::highBitsToInt(version) > CURRENT_VERSION) { 38 | throw InvalidMessageException("Unknown version: " + ByteUtil::highBitsToInt(version)); 39 | } 40 | 41 | textsecure::SenderKeyDistributionMessage senderKeyDistributionMessage; 42 | senderKeyDistributionMessage.ParseFromArray(message.constData(), message.size()); 43 | 44 | if (!senderKeyDistributionMessage.has_id() || 45 | !senderKeyDistributionMessage.has_iteration() || 46 | !senderKeyDistributionMessage.has_chainkey() || 47 | !senderKeyDistributionMessage.has_signingkey()) 48 | { 49 | qDebug() << "has_id" << senderKeyDistributionMessage.has_id(); 50 | qDebug() << "has_iteration" << senderKeyDistributionMessage.has_iteration(); 51 | qDebug() << "has_chainkey" << senderKeyDistributionMessage.has_chainkey(); 52 | qDebug() << "has_signingkey" << senderKeyDistributionMessage.has_signingkey(); 53 | throw InvalidMessageException("Incomplete message."); 54 | } 55 | 56 | this->serialized = serialized; 57 | this->id = senderKeyDistributionMessage.id(); 58 | this->iteration = senderKeyDistributionMessage.iteration(); 59 | ::std::string chainKeyString = senderKeyDistributionMessage.chainkey(); 60 | this->chainKey = QByteArray(chainKeyString.data(), chainKeyString.length()); 61 | ::std::string signatureKeyString = senderKeyDistributionMessage.signingkey(); 62 | this->signatureKey = Curve::decodePoint(QByteArray(signatureKeyString.data(), signatureKeyString.length()), 0); 63 | } 64 | 65 | QByteArray SenderKeyDistributionMessage::serialize() const 66 | { 67 | return serialized; 68 | } 69 | 70 | int SenderKeyDistributionMessage::getType() const 71 | { 72 | return CiphertextMessage::SENDERKEY_DISTRIBUTION_TYPE; 73 | } 74 | 75 | int SenderKeyDistributionMessage::getIteration() const 76 | { 77 | return iteration; 78 | } 79 | 80 | QByteArray SenderKeyDistributionMessage::getChainKey() const 81 | { 82 | return chainKey; 83 | } 84 | 85 | DjbECPublicKey SenderKeyDistributionMessage::getSignatureKey() const 86 | { 87 | return signatureKey; 88 | } 89 | 90 | int SenderKeyDistributionMessage::getId() const 91 | { 92 | return id; 93 | } 94 | -------------------------------------------------------------------------------- /protocol/senderkeydistributionmessage.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDERKEYDISTRIBUTIONMESSAGE_H 2 | #define SENDERKEYDISTRIBUTIONMESSAGE_H 3 | 4 | #include "ciphertextmessage.h" 5 | #include "../ecc/djbec.h" 6 | 7 | class SenderKeyDistributionMessage : public CiphertextMessage 8 | { 9 | public: 10 | SenderKeyDistributionMessage(int id, int iteration, const QByteArray &chainKey, const DjbECPublicKey &signatureKey); 11 | SenderKeyDistributionMessage(const QByteArray &serialized); 12 | virtual ~SenderKeyDistributionMessage() {} 13 | 14 | QByteArray serialize() const; 15 | int getType() const; 16 | int getIteration() const; 17 | QByteArray getChainKey() const; 18 | DjbECPublicKey getSignatureKey() const; 19 | int getId() const; 20 | 21 | private: 22 | int id; 23 | int iteration; 24 | QByteArray chainKey; 25 | DjbECPublicKey signatureKey; 26 | QByteArray serialized; 27 | }; 28 | 29 | #endif // SENDERKEYDISTRIBUTIONMESSAGE_H 30 | -------------------------------------------------------------------------------- /protocol/senderkeymessage.cpp: -------------------------------------------------------------------------------- 1 | #include "senderkeymessage.h" 2 | 3 | #include "../util/byteutil.h" 4 | #include "../legacymessageexception.h" 5 | #include "../invalidmessageexception.h" 6 | #include "WhisperTextProtocol.pb.h" 7 | #include "../ecc/curve.h" 8 | #include "../invalidkeyexception.h" 9 | 10 | #include 11 | 12 | SenderKeyMessage::SenderKeyMessage(const QByteArray &serialized) 13 | { 14 | quint8 version = serialized[0]; 15 | QByteArray message = serialized.mid(1, serialized.size() - 1 - SIGNATURE_LENGTH); 16 | //QByteArray signature = serialized.right(SIGNATURE_LENGTH); 17 | 18 | if (ByteUtil::highBitsToInt(version) < 3) { 19 | throw LegacyMessageException("Legacy message: " + ByteUtil::highBitsToInt(version)); 20 | } 21 | 22 | if (ByteUtil::highBitsToInt(version) > CURRENT_VERSION) { 23 | throw InvalidMessageException("Unknown version: " + ByteUtil::highBitsToInt(version)); 24 | } 25 | 26 | textsecure::SenderKeyMessage senderKeyMessage; 27 | senderKeyMessage.ParseFromArray(message.constData(), message.size()); 28 | 29 | if (!senderKeyMessage.has_id() || 30 | !senderKeyMessage.has_iteration() || 31 | !senderKeyMessage.has_ciphertext()) 32 | { 33 | qDebug() << "has_id" << senderKeyMessage.has_id(); 34 | qDebug() << "has_iteration" << senderKeyMessage.has_iteration(); 35 | qDebug() << "has_ciphertext" << senderKeyMessage.has_ciphertext(); 36 | throw InvalidMessageException("Incomplete message."); 37 | } 38 | 39 | this->serialized = serialized; 40 | this->messageVersion = ByteUtil::highBitsToInt(version); 41 | this->keyId = senderKeyMessage.id(); 42 | this->iteration = senderKeyMessage.iteration(); 43 | ::std::string senderKeyMessageCiphertext = senderKeyMessage.ciphertext(); 44 | this->ciphertext = QByteArray(senderKeyMessageCiphertext.data(), senderKeyMessageCiphertext.length()); 45 | } 46 | 47 | SenderKeyMessage::SenderKeyMessage(ulong keyId, int iteration, const QByteArray &ciphertext, const DjbECPrivateKey &signatureKey) 48 | { 49 | textsecure::SenderKeyMessage senderKeyMessage; 50 | senderKeyMessage.set_id(keyId); 51 | senderKeyMessage.set_iteration(iteration); 52 | senderKeyMessage.set_ciphertext(ciphertext.constData()); 53 | ::std::string serializedMessage = senderKeyMessage.SerializeAsString(); 54 | QByteArray message = QByteArray(serializedMessage.data(), serializedMessage.length()); 55 | message.prepend(ByteUtil::intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)); 56 | message.append(getSignature(signatureKey, message)); 57 | 58 | this->serialized = message; 59 | this->messageVersion = CURRENT_VERSION; 60 | this->keyId = keyId; 61 | this->iteration = iteration; 62 | this->ciphertext = ciphertext; 63 | } 64 | 65 | void SenderKeyMessage::verifySignature(const DjbECPublicKey &signatureKey) 66 | { 67 | try { 68 | if (!Curve::verifySignature(signatureKey, serialized.left(serialized.size() - SIGNATURE_LENGTH), serialized.right(SIGNATURE_LENGTH))) { 69 | throw InvalidMessageException("Invalid signature!"); 70 | } 71 | 72 | } catch (const InvalidKeyException &e) { 73 | throw InvalidMessageException(__PRETTY_FUNCTION__, QList() << e); 74 | } 75 | } 76 | 77 | ulong SenderKeyMessage::getKeyId() const 78 | { 79 | return keyId; 80 | } 81 | 82 | int SenderKeyMessage::getIteration() const 83 | { 84 | return iteration; 85 | } 86 | 87 | QByteArray SenderKeyMessage::getCipherText() const 88 | { 89 | return ciphertext; 90 | } 91 | 92 | QByteArray SenderKeyMessage::serialize() const 93 | { 94 | return serialized; 95 | } 96 | 97 | int SenderKeyMessage::getType() const 98 | { 99 | return CiphertextMessage::SENDERKEY_TYPE; 100 | } 101 | 102 | QByteArray SenderKeyMessage::getSignature(const DjbECPrivateKey &signatureKey, const QByteArray &serialized) 103 | { 104 | return Curve::calculateSignature(signatureKey, serialized); 105 | } 106 | -------------------------------------------------------------------------------- /protocol/senderkeymessage.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDERKEYMESSAGE_H 2 | #define SENDERKEYMESSAGE_H 3 | 4 | #include "ciphertextmessage.h" 5 | #include "ecc/djbec.h" 6 | 7 | class SenderKeyMessage : public CiphertextMessage 8 | { 9 | public: 10 | SenderKeyMessage(const QByteArray &serialized); 11 | SenderKeyMessage(ulong keyId, int iteration, const QByteArray &ciphertext, const DjbECPrivateKey &signatureKey); 12 | virtual ~SenderKeyMessage() {} 13 | 14 | void verifySignature(const DjbECPublicKey &signatureKey); 15 | 16 | ulong getKeyId() const; 17 | int getIteration() const; 18 | QByteArray getCipherText() const; 19 | QByteArray serialize() const; 20 | int getType() const; 21 | 22 | private: 23 | static const int SIGNATURE_LENGTH = 64; 24 | 25 | QByteArray getSignature(const DjbECPrivateKey &signatureKey, const QByteArray &serialized); 26 | 27 | int messageVersion; 28 | ulong keyId; 29 | int iteration; 30 | QByteArray ciphertext; 31 | QByteArray serialized; 32 | }; 33 | 34 | #endif // SENDERKEYMESSAGE_H 35 | -------------------------------------------------------------------------------- /protocol/whispermessage.cpp: -------------------------------------------------------------------------------- 1 | #include "whispermessage.h" 2 | #include "../invalidkeyexception.h" 3 | #include "../invalidmessageexception.h" 4 | #include "../legacymessageexception.h" 5 | #include "WhisperTextProtocol.pb.h" 6 | #include "../ecc/curve.h" 7 | 8 | #include 9 | 10 | #include 11 | 12 | WhisperMessage::WhisperMessage() 13 | { 14 | 15 | } 16 | 17 | WhisperMessage::WhisperMessage(const QByteArray &serialized) 18 | { 19 | try { 20 | //QList messageParts = ByteUtil::split(serialized, 1, serialized.size() - 1 - MAC_LENGTH, MAC_LENGTH); 21 | qint8 version = serialized[0]; 22 | QByteArray message = serialized.mid(1, serialized.size() - MAC_LENGTH - 1); 23 | QByteArray mac = serialized.right(MAC_LENGTH); 24 | //qDebug() << "serialized size:" << serialized.size() << "message size:" << message.size(); 25 | 26 | if (ByteUtil::highBitsToInt(version) <= CiphertextMessage::UNSUPPORTED_VERSION) { 27 | throw LegacyMessageException("Legacy message: " + ByteUtil::highBitsToInt(version)); 28 | } 29 | 30 | if (ByteUtil::highBitsToInt(version) > CURRENT_VERSION) { 31 | throw InvalidMessageException("Unknown version: " + ByteUtil::highBitsToInt(version)); 32 | } 33 | 34 | textsecure::WhisperMessage whisperMessage; 35 | whisperMessage.ParsePartialFromArray(message.constData(), message.size()); 36 | 37 | if (!whisperMessage.has_ciphertext() || 38 | !whisperMessage.has_counter() || 39 | !whisperMessage.has_ratchetkey()) 40 | { 41 | qDebug() << "has_ciphertext" << whisperMessage.has_ciphertext(); 42 | qDebug() << "has_counter" << whisperMessage.has_counter(); 43 | qDebug() << "has_ratchetkey" << whisperMessage.has_ratchetkey(); 44 | throw InvalidMessageException("Incomplete message."); 45 | } 46 | 47 | this->serialized = serialized; 48 | ::std::string whisperratchetkey = whisperMessage.ratchetkey(); 49 | QByteArray whisperratchetkeybytes(whisperratchetkey.data(), whisperratchetkey.length()); 50 | this->senderRatchetKey = Curve::decodePoint(whisperratchetkeybytes, 0); 51 | this->messageVersion = ByteUtil::highBitsToInt(version); 52 | this->counter = whisperMessage.counter(); 53 | this->previousCounter = whisperMessage.previouscounter(); 54 | ::std::string whisperciphertext = whisperMessage.ciphertext(); 55 | this->ciphertext = QByteArray(whisperciphertext.data(), whisperciphertext.length()); 56 | } catch (const InvalidKeyException &e) { 57 | throw InvalidMessageException(__PRETTY_FUNCTION__, QList() << e); 58 | } 59 | } 60 | 61 | WhisperMessage::WhisperMessage(int messageVersion, const QByteArray &macKey, const DjbECPublicKey &senderRatchetKey, uint counter, uint previousCounter, const QByteArray &ciphertext, const IdentityKey &senderIdentityKey, const IdentityKey &receiverIdentityKey) 62 | { 63 | textsecure::WhisperMessage whisperMessage; 64 | QByteArray ratchetKey = senderRatchetKey.serialize(); 65 | whisperMessage.set_ratchetkey(ratchetKey.constData(), ratchetKey.size()); 66 | whisperMessage.set_counter(counter); 67 | whisperMessage.set_previouscounter(previousCounter); 68 | whisperMessage.set_ciphertext(ciphertext.constData() ,ciphertext.size()); 69 | ::std::string serializedMessage = whisperMessage.SerializeAsString(); 70 | QByteArray message = QByteArray(serializedMessage.data(), serializedMessage.length()); 71 | message.prepend(ByteUtil::intsToByteHighAndLow(messageVersion, CURRENT_VERSION)); 72 | QByteArray mac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, message); 73 | 74 | this->serialized = message; 75 | this->serialized.append(mac); 76 | this->senderRatchetKey = senderRatchetKey; 77 | this->counter = counter; 78 | this->previousCounter = previousCounter; 79 | this->ciphertext = ciphertext; 80 | this->messageVersion = messageVersion; 81 | } 82 | 83 | DjbECPublicKey WhisperMessage::getSenderRatchetKey() const 84 | { 85 | return senderRatchetKey; 86 | } 87 | 88 | int WhisperMessage::getMessageVersion() const 89 | { 90 | return messageVersion; 91 | } 92 | 93 | uint WhisperMessage::getCounter() const 94 | { 95 | return counter; 96 | } 97 | 98 | QByteArray WhisperMessage::getBody() const 99 | { 100 | return ciphertext; 101 | } 102 | 103 | QByteArray WhisperMessage::serialize() const 104 | { 105 | return serialized; 106 | } 107 | 108 | int WhisperMessage::getType() const 109 | { 110 | return CiphertextMessage::WHISPER_TYPE; 111 | } 112 | 113 | QByteArray WhisperMessage::getMac(int messageVersion, const IdentityKey &senderIdentityKey, const IdentityKey &receiverIdentityKey, const QByteArray &macKey, QByteArray &serialized) const 114 | { 115 | QMessageAuthenticationCode hmac(QCryptographicHash::Sha256, macKey); 116 | 117 | if (messageVersion >= 3) { 118 | hmac.addData(senderIdentityKey.getPublicKey().serialize()); 119 | hmac.addData(receiverIdentityKey.getPublicKey().serialize()); 120 | } 121 | 122 | hmac.addData(serialized); 123 | return ByteUtil::trim(hmac.result(), MAC_LENGTH); 124 | } 125 | 126 | void WhisperMessage::verifyMac(int messageVersion, const IdentityKey &senderIdentityKey, const IdentityKey &receiverIdentityKey, const QByteArray &macKey) const 127 | { 128 | QList parts = ByteUtil::split(serialized, serialized.size() - MAC_LENGTH, MAC_LENGTH); 129 | QByteArray ourMac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, parts[0]); 130 | QByteArray theirMac = parts[1]; 131 | 132 | if (ourMac != theirMac) { 133 | throw InvalidMessageException("Bad Mac!"); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /protocol/whispermessage.h: -------------------------------------------------------------------------------- 1 | #ifndef WHISPERMESSAGE_H 2 | #define WHISPERMESSAGE_H 3 | 4 | #include "ciphertextmessage.h" 5 | #include "../ecc/djbec.h" 6 | #include "../identitykey.h" 7 | #include "../util/byteutil.h" 8 | 9 | #include 10 | 11 | class WhisperMessage : public CiphertextMessage 12 | { 13 | public: 14 | static const int MAC_LENGTH = 8; 15 | 16 | WhisperMessage(); 17 | virtual ~WhisperMessage() {} 18 | WhisperMessage(const QByteArray &serialized); 19 | WhisperMessage(int messageVersion, const QByteArray &macKey, const DjbECPublicKey &senderRatchetKey, 20 | uint counter, uint previousCounter, const QByteArray &ciphertext, 21 | const IdentityKey &senderIdentityKey, 22 | const IdentityKey &receiverIdentityKey); 23 | void verifyMac(int messageVersion, const IdentityKey &senderIdentityKey, 24 | const IdentityKey &receiverIdentityKey, const QByteArray &macKey) const; 25 | 26 | DjbECPublicKey getSenderRatchetKey() const; 27 | int getMessageVersion() const; 28 | uint getCounter() const; 29 | QByteArray getBody() const; 30 | QByteArray serialize() const; 31 | int getType() const; 32 | 33 | static bool isLegacy(const QByteArray &message) { 34 | return !message.isEmpty() && message.length() >= 1 && 35 | ByteUtil::highBitsToInt(message[0]) <= CiphertextMessage::UNSUPPORTED_VERSION; 36 | } 37 | 38 | private: 39 | QByteArray getMac(int messageVersion, 40 | const IdentityKey &senderIdentityKey, 41 | const IdentityKey &receiverIdentityKey, 42 | const QByteArray &macKey, QByteArray &serialized) const; 43 | 44 | int messageVersion; 45 | DjbECPublicKey senderRatchetKey; 46 | uint counter; 47 | uint previousCounter; 48 | QByteArray ciphertext; 49 | QByteArray serialized; 50 | }; 51 | 52 | #endif // WHISPERMESSAGE_H 53 | -------------------------------------------------------------------------------- /ratchet/aliceaxolotlparameters.cpp: -------------------------------------------------------------------------------- 1 | #include "aliceaxolotlparameters.h" 2 | 3 | AliceAxolotlParameters::AliceAxolotlParameters() 4 | { 5 | 6 | } 7 | 8 | AliceAxolotlParameters::AliceAxolotlParameters(const IdentityKeyPair &ourIdentityKey, const ECKeyPair &ourBaseKey, const IdentityKey &theirIdentityKey, const DjbECPublicKey &theirSignedPreKey, const DjbECPublicKey &theirRatchetKey, const DjbECPublicKey &theirOneTimePreKey) 9 | { 10 | this->ourIdentityKey = ourIdentityKey; 11 | this->ourBaseKey = ourBaseKey; 12 | this->theirIdentityKey = theirIdentityKey; 13 | this->theirSignedPreKey = theirSignedPreKey; 14 | this->theirRatchetKey = theirRatchetKey; 15 | this->theirOneTimePreKey = theirOneTimePreKey; 16 | } 17 | 18 | IdentityKeyPair AliceAxolotlParameters::getOurIdentityKey() const 19 | { 20 | return ourIdentityKey; 21 | } 22 | 23 | void AliceAxolotlParameters::setOurIdentityKey(const IdentityKeyPair &ourIdentityKey) 24 | { 25 | this->ourIdentityKey = ourIdentityKey; 26 | } 27 | 28 | ECKeyPair AliceAxolotlParameters::getOurBaseKey() const 29 | { 30 | return ourBaseKey; 31 | } 32 | 33 | void AliceAxolotlParameters::setOurBaseKey(const ECKeyPair &ourBaseKey) 34 | { 35 | this->ourBaseKey = ourBaseKey; 36 | } 37 | 38 | IdentityKey AliceAxolotlParameters::getTheirIdentityKey() const 39 | { 40 | return theirIdentityKey; 41 | } 42 | 43 | void AliceAxolotlParameters::setTheirIdentityKey(const IdentityKey &theirIdentityKey) 44 | { 45 | this->theirIdentityKey = theirIdentityKey; 46 | } 47 | 48 | DjbECPublicKey AliceAxolotlParameters::getTheirSignedPreKey() const 49 | { 50 | return theirSignedPreKey; 51 | } 52 | 53 | void AliceAxolotlParameters::setTheirSignedPreKey(const DjbECPublicKey &theirSignedPreKey) 54 | { 55 | this->theirSignedPreKey = theirSignedPreKey; 56 | } 57 | 58 | DjbECPublicKey AliceAxolotlParameters::getTheirOneTimePreKey() const 59 | { 60 | return theirOneTimePreKey; 61 | } 62 | 63 | void AliceAxolotlParameters::setTheirOneTimePreKey(const DjbECPublicKey &theirOneTimePreKey) 64 | { 65 | this->theirOneTimePreKey = theirOneTimePreKey; 66 | } 67 | 68 | DjbECPublicKey AliceAxolotlParameters::getTheirRatchetKey() const 69 | { 70 | return theirRatchetKey; 71 | } 72 | 73 | void AliceAxolotlParameters::setTheirRatchetKey(const DjbECPublicKey &theirRatchetKey) 74 | { 75 | this->theirRatchetKey = theirRatchetKey; 76 | } 77 | -------------------------------------------------------------------------------- /ratchet/aliceaxolotlparameters.h: -------------------------------------------------------------------------------- 1 | #ifndef ALICEAXOLOTLPARAMETERS_H 2 | #define ALICEAXOLOTLPARAMETERS_H 3 | 4 | #include "../identitykeypair.h" 5 | #include "../ecc/eckeypair.h" 6 | 7 | class AliceAxolotlParameters 8 | { 9 | public: 10 | AliceAxolotlParameters(); 11 | AliceAxolotlParameters(const IdentityKeyPair &ourIdentityKey, const ECKeyPair &ourBaseKey, 12 | const IdentityKey &theirIdentityKey, const DjbECPublicKey &theirSignedPreKey, 13 | const DjbECPublicKey &theirRatchetKey, const DjbECPublicKey &theirOneTimePreKey); 14 | 15 | IdentityKeyPair getOurIdentityKey() const; 16 | void setOurIdentityKey(const IdentityKeyPair &ourIdentityKey); 17 | ECKeyPair getOurBaseKey() const; 18 | void setOurBaseKey(const ECKeyPair &ourBaseKey); 19 | IdentityKey getTheirIdentityKey() const; 20 | void setTheirIdentityKey(const IdentityKey &theirIdentityKey); 21 | DjbECPublicKey getTheirSignedPreKey() const; 22 | void setTheirSignedPreKey(const DjbECPublicKey &theirSignedPreKey); 23 | DjbECPublicKey getTheirOneTimePreKey() const; 24 | void setTheirOneTimePreKey(const DjbECPublicKey &theirOneTimePreKey); 25 | DjbECPublicKey getTheirRatchetKey() const; 26 | void setTheirRatchetKey(const DjbECPublicKey &theirRatchetKey); 27 | 28 | private: 29 | IdentityKeyPair ourIdentityKey; 30 | ECKeyPair ourBaseKey; 31 | 32 | IdentityKey theirIdentityKey; 33 | DjbECPublicKey theirSignedPreKey; 34 | DjbECPublicKey theirOneTimePreKey; 35 | DjbECPublicKey theirRatchetKey; 36 | 37 | }; 38 | 39 | #endif // ALICEAXOLOTLPARAMETERS_H 40 | -------------------------------------------------------------------------------- /ratchet/bobaxolotlparameters.cpp: -------------------------------------------------------------------------------- 1 | #include "bobaxolotlparameters.h" 2 | 3 | BobAxolotlParameters::BobAxolotlParameters() 4 | { 5 | 6 | } 7 | 8 | BobAxolotlParameters::BobAxolotlParameters(const IdentityKeyPair &ourIdentityKey, const ECKeyPair &ourSignedPreKey, const ECKeyPair &ourOneTimePreKey, const ECKeyPair &ourRatchetKey, const IdentityKey &theirIdentityKey, const DjbECPublicKey &theirBaseKey) 9 | { 10 | this->ourIdentityKey = ourIdentityKey; 11 | this->ourSignedPreKey = ourSignedPreKey; 12 | this->ourRatchetKey = ourRatchetKey; 13 | this->ourOneTimePreKey = ourOneTimePreKey; 14 | this->theirIdentityKey = theirIdentityKey; 15 | this->theirBaseKey = theirBaseKey; 16 | } 17 | 18 | IdentityKey BobAxolotlParameters::getTheirIdentityKey() const 19 | { 20 | return theirIdentityKey; 21 | } 22 | 23 | void BobAxolotlParameters::setTheirIdentityKey(const IdentityKey &theirIdentityKey) 24 | { 25 | this->theirIdentityKey = theirIdentityKey; 26 | } 27 | 28 | DjbECPublicKey BobAxolotlParameters::getTheirBaseKey() const 29 | { 30 | return theirBaseKey; 31 | } 32 | 33 | void BobAxolotlParameters::setTheirBaseKey(const DjbECPublicKey &theirBaseKey) 34 | { 35 | this->theirBaseKey = theirBaseKey; 36 | } 37 | 38 | IdentityKeyPair BobAxolotlParameters::getOurIdentityKey() const 39 | { 40 | return ourIdentityKey; 41 | } 42 | 43 | void BobAxolotlParameters::setOurIdentityKey(const IdentityKeyPair &ourIdentityKey) 44 | { 45 | this->ourIdentityKey = ourIdentityKey; 46 | } 47 | 48 | ECKeyPair BobAxolotlParameters::getOurSignedPreKey() const 49 | { 50 | return ourSignedPreKey; 51 | } 52 | 53 | void BobAxolotlParameters::setOurSignedPreKey(const ECKeyPair &ourSignedPreKey) 54 | { 55 | this->ourSignedPreKey = ourSignedPreKey; 56 | } 57 | 58 | ECKeyPair BobAxolotlParameters::getOurOneTimePreKey() const 59 | { 60 | return ourOneTimePreKey; 61 | } 62 | 63 | void BobAxolotlParameters::setOurOneTimePreKey(const ECKeyPair &ourOneTimePreKey) 64 | { 65 | this->ourOneTimePreKey = ourOneTimePreKey; 66 | } 67 | 68 | ECKeyPair BobAxolotlParameters::getOurRatchetKey() const 69 | { 70 | return ourRatchetKey; 71 | } 72 | 73 | void BobAxolotlParameters::setOurRatchetKey(const ECKeyPair &ourRatchetKey) 74 | { 75 | this->ourRatchetKey = ourRatchetKey; 76 | } 77 | -------------------------------------------------------------------------------- /ratchet/bobaxolotlparameters.h: -------------------------------------------------------------------------------- 1 | #ifndef BOBAXOLOTLPARAMETERS_H 2 | #define BOBAXOLOTLPARAMETERS_H 3 | 4 | #include "../identitykeypair.h" 5 | #include "../ecc/eckeypair.h" 6 | 7 | class BobAxolotlParameters 8 | { 9 | public: 10 | BobAxolotlParameters(); 11 | BobAxolotlParameters(const IdentityKeyPair &ourIdentityKey, const ECKeyPair &ourSignedPreKey, 12 | const ECKeyPair &ourOneTimePreKey, const ECKeyPair &ourRatchetKey, 13 | const IdentityKey &theirIdentityKey, const DjbECPublicKey &theirBaseKey); 14 | 15 | IdentityKey getTheirIdentityKey() const; 16 | void setTheirIdentityKey(const IdentityKey &theirIdentityKey); 17 | DjbECPublicKey getTheirBaseKey() const; 18 | void setTheirBaseKey(const DjbECPublicKey &theirBaseKey); 19 | IdentityKeyPair getOurIdentityKey() const; 20 | void setOurIdentityKey(const IdentityKeyPair &ourIdentityKey); 21 | ECKeyPair getOurSignedPreKey() const; 22 | void setOurSignedPreKey(const ECKeyPair &ourSignedPreKey); 23 | ECKeyPair getOurOneTimePreKey() const; 24 | void setOurOneTimePreKey(const ECKeyPair &ourOneTimePreKey); 25 | ECKeyPair getOurRatchetKey() const; 26 | void setOurRatchetKey(const ECKeyPair &ourRatchetKey); 27 | 28 | private: 29 | IdentityKeyPair ourIdentityKey; 30 | ECKeyPair ourSignedPreKey; 31 | ECKeyPair ourOneTimePreKey; 32 | ECKeyPair ourRatchetKey; 33 | 34 | IdentityKey theirIdentityKey; 35 | DjbECPublicKey theirBaseKey; 36 | 37 | }; 38 | 39 | #endif // BOBAXOLOTLPARAMETERS_H 40 | -------------------------------------------------------------------------------- /ratchet/chainkey.cpp: -------------------------------------------------------------------------------- 1 | #include "chainkey.h" 2 | #include 3 | #include 4 | 5 | const QByteArray ChainKey::MESSAGE_KEY_SEED = QByteArray("\x01"); 6 | const QByteArray ChainKey::CHAIN_KEY_SEED = QByteArray("\x02"); 7 | 8 | ChainKey::ChainKey() 9 | { 10 | 11 | } 12 | 13 | ChainKey::ChainKey(const HKDF &kdf, const QByteArray &key, uint index) 14 | { 15 | this->kdf = kdf; 16 | this->key = key; 17 | this->index = index; 18 | } 19 | 20 | QByteArray ChainKey::getKey() const 21 | { 22 | return key; 23 | } 24 | 25 | uint ChainKey::getIndex() const 26 | { 27 | return index; 28 | } 29 | 30 | QByteArray ChainKey::getBaseMaterial(const QByteArray &seed) const 31 | { 32 | return QMessageAuthenticationCode::hash(seed, key, QCryptographicHash::Sha256); 33 | } 34 | 35 | ChainKey ChainKey::getNextChainKey() const 36 | { 37 | QByteArray nextKey = getBaseMaterial(CHAIN_KEY_SEED); 38 | return ChainKey(kdf, nextKey, index + 1); 39 | } 40 | 41 | MessageKeys ChainKey::getMessageKeys() const 42 | { 43 | QByteArray inputKeyMaterial = getBaseMaterial(MESSAGE_KEY_SEED); 44 | QByteArray keyMaterialBytes = kdf.deriveSecrets(inputKeyMaterial, QByteArray("WhisperMessageKeys"), DerivedMessageSecrets::SIZE); 45 | DerivedMessageSecrets keyMaterial(keyMaterialBytes); 46 | return MessageKeys(keyMaterial.getCipherKey(), keyMaterial.getMacKey(), keyMaterial.getIv(), index); 47 | } 48 | -------------------------------------------------------------------------------- /ratchet/chainkey.h: -------------------------------------------------------------------------------- 1 | #ifndef CHAINKEY_H 2 | #define CHAINKEY_H 3 | 4 | #include "../kdf/hkdf.h" 5 | #include "../ratchet/messagekeys.h" 6 | #include "../kdf/derivedmessagesecrets.h" 7 | 8 | class ChainKey 9 | { 10 | public: 11 | ChainKey(); 12 | ChainKey(const HKDF &kdf, const QByteArray &key, uint index); 13 | 14 | QByteArray getKey() const; 15 | uint getIndex() const; 16 | QByteArray getBaseMaterial(const QByteArray &seed) const; 17 | ChainKey getNextChainKey() const; 18 | MessageKeys getMessageKeys() const; 19 | 20 | static const QByteArray MESSAGE_KEY_SEED; 21 | static const QByteArray CHAIN_KEY_SEED; 22 | 23 | private: 24 | HKDF kdf; 25 | QByteArray key; 26 | uint index; 27 | 28 | }; 29 | 30 | #endif // CHAINKEY_H 31 | -------------------------------------------------------------------------------- /ratchet/messagekeys.cpp: -------------------------------------------------------------------------------- 1 | #include "messagekeys.h" 2 | 3 | MessageKeys::MessageKeys() 4 | { 5 | 6 | } 7 | 8 | MessageKeys::MessageKeys(const QByteArray &cipherKey, const QByteArray &macKey, const QByteArray &iv, uint counter) 9 | { 10 | this->cipherKey = cipherKey; 11 | this->macKey = macKey; 12 | this->iv = iv; 13 | this->counter = counter; 14 | } 15 | 16 | QByteArray MessageKeys::getCipherKey() const 17 | { 18 | return cipherKey; 19 | } 20 | 21 | QByteArray MessageKeys::getMacKey() const 22 | { 23 | return macKey; 24 | } 25 | 26 | QByteArray MessageKeys::getIv() const 27 | { 28 | return iv; 29 | } 30 | 31 | uint MessageKeys::getCounter() const 32 | { 33 | return counter; 34 | } 35 | -------------------------------------------------------------------------------- /ratchet/messagekeys.h: -------------------------------------------------------------------------------- 1 | #ifndef MESSAGEKEYS_H 2 | #define MESSAGEKEYS_H 3 | 4 | #include 5 | 6 | class MessageKeys 7 | { 8 | public: 9 | MessageKeys(); 10 | MessageKeys(const QByteArray &cipherKey, const QByteArray &macKey, const QByteArray &iv, uint counter); 11 | 12 | QByteArray getCipherKey() const; 13 | QByteArray getMacKey() const; 14 | QByteArray getIv() const; 15 | uint getCounter() const; 16 | 17 | private: 18 | QByteArray cipherKey; 19 | QByteArray macKey; 20 | QByteArray iv; 21 | uint counter; 22 | 23 | }; 24 | 25 | #endif // MESSAGEKEYS_H 26 | -------------------------------------------------------------------------------- /ratchet/ratchetingsession.cpp: -------------------------------------------------------------------------------- 1 | #include "ratchetingsession.h" 2 | #include "../util/byteutil.h" 3 | #include "../ecc/curve.h" 4 | #include "../ecc/djbec.h" 5 | 6 | #include 7 | 8 | RatchetingSession::RatchetingSession() 9 | { 10 | } 11 | 12 | void RatchetingSession::initializeSession(SessionState *sessionState, int sessionVersion, const SymmetricAxolotlParameters ¶meters) 13 | { 14 | if (RatchetingSession::isAlice(parameters.getOurBaseKey().getPublicKey(), parameters.getTheirBaseKey())) { 15 | AliceAxolotlParameters aliceParameters; 16 | aliceParameters.setOurBaseKey(parameters.getOurBaseKey()); 17 | aliceParameters.setOurIdentityKey(parameters.getOurIdentityKey()); 18 | aliceParameters.setTheirRatchetKey(parameters.getTheirRatchetKey()); 19 | aliceParameters.setTheirIdentityKey(parameters.getTheirIdentityKey()); 20 | aliceParameters.setTheirSignedPreKey(parameters.getTheirBaseKey()); 21 | 22 | RatchetingSession::initializeSession(sessionState, sessionVersion, aliceParameters); 23 | } 24 | } 25 | 26 | void RatchetingSession::initializeSession(SessionState *sessionState, int sessionVersion, const AliceAxolotlParameters ¶meters) 27 | { 28 | sessionState->setSessionVersion(sessionVersion); 29 | sessionState->setRemoteIdentityKey(parameters.getTheirIdentityKey()); 30 | sessionState->setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); 31 | 32 | ECKeyPair sendingRatchetKey = Curve::generateKeyPair(); 33 | QByteArray secrets; 34 | 35 | if (sessionVersion >= 3) { 36 | secrets.append(RatchetingSession::getDiscontinuityBytes()); 37 | } 38 | 39 | secrets.append(Curve::calculateAgreement(parameters.getTheirSignedPreKey(), 40 | parameters.getOurIdentityKey().getPrivateKey())); 41 | secrets.append(Curve::calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), 42 | parameters.getOurBaseKey().getPrivateKey())); 43 | secrets.append(Curve::calculateAgreement(parameters.getTheirSignedPreKey(), 44 | parameters.getOurBaseKey().getPrivateKey())); 45 | 46 | if (sessionVersion >= 3 && !parameters.getTheirOneTimePreKey().serialize().isEmpty()) { 47 | secrets.append(Curve::calculateAgreement(parameters.getTheirOneTimePreKey(), 48 | parameters.getOurBaseKey().getPrivateKey())); 49 | } 50 | 51 | DerivedKeys derivedKeys = RatchetingSession::calculateDerivedKeys(sessionVersion, secrets); 52 | QPair sendingChain = derivedKeys.getRootKey().createChain(parameters.getTheirRatchetKey(), sendingRatchetKey); 53 | 54 | sessionState->addReceiverChain(parameters.getTheirRatchetKey(), derivedKeys.getChainKey()); 55 | sessionState->setSenderChain(sendingRatchetKey, sendingChain.second); 56 | sessionState->setRootKey(sendingChain.first); 57 | } 58 | 59 | void RatchetingSession::initializeSession(SessionState *sessionState, int sessionVersion, const BobAxolotlParameters ¶meters) 60 | { 61 | sessionState->setSessionVersion(sessionVersion); 62 | sessionState->setRemoteIdentityKey(parameters.getTheirIdentityKey()); 63 | sessionState->setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); 64 | 65 | QByteArray secrets; 66 | 67 | if (sessionVersion >= 3) { 68 | secrets.append(RatchetingSession::getDiscontinuityBytes()); 69 | } 70 | 71 | secrets.append(Curve::calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), 72 | parameters.getOurSignedPreKey().getPrivateKey())); 73 | secrets.append(Curve::calculateAgreement(parameters.getTheirBaseKey(), 74 | parameters.getOurIdentityKey().getPrivateKey())); 75 | secrets.append(Curve::calculateAgreement(parameters.getTheirBaseKey(), 76 | parameters.getOurSignedPreKey().getPrivateKey())); 77 | 78 | if (sessionVersion >= 3 79 | && !parameters.getOurOneTimePreKey().getPrivateKey().serialize().isEmpty() 80 | && !parameters.getOurOneTimePreKey().getPublicKey().serialize().isEmpty()) { 81 | secrets.append(Curve::calculateAgreement(parameters.getTheirBaseKey(), 82 | parameters.getOurOneTimePreKey().getPrivateKey())); 83 | } 84 | 85 | DerivedKeys derivedKeys = RatchetingSession::calculateDerivedKeys(sessionVersion, secrets); 86 | 87 | sessionState->setSenderChain(parameters.getOurRatchetKey(), derivedKeys.getChainKey()); 88 | sessionState->setRootKey(derivedKeys.getRootKey()); 89 | } 90 | 91 | DerivedKeys RatchetingSession::calculateDerivedKeys(int sessionVersion, const QByteArray &masterSecret) 92 | { 93 | HKDF kdf(sessionVersion); 94 | QByteArray derivedSecretBytes = kdf.deriveSecrets(masterSecret, QByteArray("WhisperText"), 64); 95 | QByteArray rootSecrets = derivedSecretBytes.left(32); 96 | QByteArray chainSecrets = derivedSecretBytes.mid(32, 32); 97 | return DerivedKeys(RootKey(kdf, rootSecrets), 98 | ChainKey(kdf, chainSecrets, 0)); 99 | } 100 | 101 | QByteArray RatchetingSession::getDiscontinuityBytes() 102 | { 103 | return QByteArray(32, '\xFF'); 104 | } 105 | 106 | bool RatchetingSession::isAlice(const DjbECPublicKey &ourKey, const DjbECPublicKey &theirKey) 107 | { 108 | return ourKey.serialize() < theirKey.serialize(); 109 | } 110 | -------------------------------------------------------------------------------- /ratchet/ratchetingsession.h: -------------------------------------------------------------------------------- 1 | #ifndef RATCHETINGSESSION_H 2 | #define RATCHETINGSESSION_H 3 | 4 | #include 5 | #include "../state/sessionstate.h" 6 | #include "aliceaxolotlparameters.h" 7 | #include "bobaxolotlparameters.h" 8 | #include "symmetricaxolotlparameters.h" 9 | #include "../ecc/djbec.h" 10 | #include "chainkey.h" 11 | #include "rootkey.h" 12 | 13 | class DerivedKeys 14 | { 15 | public: 16 | DerivedKeys() {} 17 | DerivedKeys(const RootKey &rootKey, const ChainKey &chainKey) { 18 | this->rootKey = rootKey; 19 | this->chainKey = chainKey; 20 | } 21 | 22 | RootKey getRootKey() const { 23 | return rootKey; 24 | } 25 | 26 | ChainKey getChainKey() const { 27 | return chainKey; 28 | } 29 | 30 | private: 31 | RootKey rootKey; 32 | ChainKey chainKey; 33 | }; 34 | 35 | class RatchetingSession 36 | { 37 | public: 38 | RatchetingSession(); 39 | 40 | static void initializeSession(SessionState *sessionState, 41 | int sessionVersion, 42 | const SymmetricAxolotlParameters ¶meters); 43 | static void initializeSession(SessionState *sessionState, 44 | int sessionVersion, 45 | const AliceAxolotlParameters ¶meters); 46 | static void initializeSession(SessionState *sessionState, 47 | int sessionVersion, 48 | const BobAxolotlParameters ¶meters); 49 | 50 | static DerivedKeys calculateDerivedKeys(int sessionVersion, const QByteArray &masterSecret); 51 | static QByteArray getDiscontinuityBytes(); 52 | static bool isAlice(const DjbECPublicKey &ourKey, const DjbECPublicKey &theirKey); 53 | }; 54 | 55 | #endif // RATCHETINGSESSION_H 56 | -------------------------------------------------------------------------------- /ratchet/rootkey.cpp: -------------------------------------------------------------------------------- 1 | #include "rootkey.h" 2 | #include "../ecc/curve.h" 3 | #include "../kdf/derivedrootsecrets.h" 4 | 5 | #include 6 | 7 | RootKey::RootKey() 8 | { 9 | 10 | } 11 | 12 | RootKey::RootKey(const HKDF &kdf, const QByteArray &key) 13 | { 14 | this->kdf = kdf; 15 | this->key = key; 16 | } 17 | 18 | QByteArray RootKey::getKeyBytes() const 19 | { 20 | return key; 21 | } 22 | 23 | QPair RootKey::createChain(const DjbECPublicKey &theirRatchetKey, const ECKeyPair &ourRatchetKey) 24 | { 25 | QByteArray sharedSecret = Curve::calculateAgreement(theirRatchetKey, ourRatchetKey.getPrivateKey()); 26 | QByteArray derivedSecretBytes = kdf.deriveSecrets(sharedSecret, QByteArray("WhisperRatchet"), DerivedRootSecrets::SIZE, key); 27 | DerivedRootSecrets derivedSecrets(derivedSecretBytes); 28 | 29 | RootKey newRootKey(kdf, derivedSecrets.getRootKey()); 30 | ChainKey newChainKey(kdf, derivedSecrets.getChainKey(), 0); 31 | 32 | QPair pair; 33 | pair.first = newRootKey; 34 | pair.second = newChainKey; 35 | 36 | return pair; 37 | } 38 | -------------------------------------------------------------------------------- /ratchet/rootkey.h: -------------------------------------------------------------------------------- 1 | #ifndef ROOTKEY_H 2 | #define ROOTKEY_H 3 | 4 | #include "../kdf/hkdf.h" 5 | #include "chainkey.h" 6 | #include "../ecc/eckeypair.h" 7 | 8 | #include 9 | #include 10 | 11 | class RootKey 12 | { 13 | public: 14 | RootKey(); 15 | RootKey(const HKDF &kdf, const QByteArray &key); 16 | 17 | QByteArray getKeyBytes() const; 18 | QPair createChain(const DjbECPublicKey &theirRatchetKey, const ECKeyPair &ourRatchetKey); 19 | 20 | private: 21 | HKDF kdf; 22 | QByteArray key; 23 | 24 | }; 25 | 26 | #endif // ROOTKEY_H 27 | -------------------------------------------------------------------------------- /ratchet/symmetricaxolotlparameters.cpp: -------------------------------------------------------------------------------- 1 | #include "symmetricaxolotlparameters.h" 2 | 3 | SymmetricAxolotlParameters::SymmetricAxolotlParameters() 4 | { 5 | 6 | } 7 | 8 | SymmetricAxolotlParameters::SymmetricAxolotlParameters(const ECKeyPair &ourBaseKey, const ECKeyPair &ourRatchetKey, const IdentityKeyPair &ourIdentityKey, const DjbECPublicKey &theirBaseKey, const DjbECPublicKey &theirRatchetKey, const IdentityKey &theirIdentityKey) 9 | { 10 | this->ourBaseKey = ourBaseKey; 11 | this->ourRatchetKey = ourRatchetKey; 12 | this->ourIdentityKey = ourIdentityKey; 13 | this->theirBaseKey = theirBaseKey; 14 | this->theirRatchetKey = theirRatchetKey; 15 | this->theirIdentityKey = theirIdentityKey; 16 | } 17 | 18 | ECKeyPair SymmetricAxolotlParameters::getOurBaseKey() const 19 | { 20 | return ourBaseKey; 21 | } 22 | 23 | ECKeyPair SymmetricAxolotlParameters::getOurRatchetKey() const 24 | { 25 | return ourRatchetKey; 26 | } 27 | 28 | IdentityKeyPair SymmetricAxolotlParameters::getOurIdentityKey() const 29 | { 30 | return ourIdentityKey; 31 | } 32 | 33 | DjbECPublicKey SymmetricAxolotlParameters::getTheirBaseKey() const 34 | { 35 | return theirBaseKey; 36 | } 37 | 38 | DjbECPublicKey SymmetricAxolotlParameters::getTheirRatchetKey() const 39 | { 40 | return theirRatchetKey; 41 | } 42 | 43 | IdentityKey SymmetricAxolotlParameters::getTheirIdentityKey() const 44 | { 45 | return theirIdentityKey; 46 | } 47 | 48 | void SymmetricAxolotlParameters::setOurBaseKey(const ECKeyPair &ourBaseKey) 49 | { 50 | this->ourBaseKey = ourBaseKey; 51 | } 52 | 53 | void SymmetricAxolotlParameters::setOurRatchetKey(const ECKeyPair &ourRatchetKey) 54 | { 55 | this->ourRatchetKey = ourRatchetKey; 56 | } 57 | 58 | void SymmetricAxolotlParameters::setOurIdentityKey(const IdentityKeyPair &ourIdentityKey) 59 | { 60 | this->ourIdentityKey = ourIdentityKey; 61 | } 62 | 63 | void SymmetricAxolotlParameters::setTheirBaseKey(const DjbECPublicKey &theirBaseKey) 64 | { 65 | this->theirBaseKey = theirBaseKey; 66 | } 67 | 68 | void SymmetricAxolotlParameters::setTheirRatchetKey(const DjbECPublicKey &theirRatchetKey) 69 | { 70 | this->theirRatchetKey = theirRatchetKey; 71 | } 72 | 73 | void SymmetricAxolotlParameters::setTheirIdentityKey(const IdentityKey &theirIdentityKey) 74 | { 75 | this->theirIdentityKey = theirIdentityKey; 76 | } 77 | -------------------------------------------------------------------------------- /ratchet/symmetricaxolotlparameters.h: -------------------------------------------------------------------------------- 1 | #ifndef SYMMETRICAXOLOTLPARAMETERS_H 2 | #define SYMMETRICAXOLOTLPARAMETERS_H 3 | 4 | #include "../ecc/eckeypair.h" 5 | #include "../identitykeypair.h" 6 | 7 | class SymmetricAxolotlParameters 8 | { 9 | public: 10 | SymmetricAxolotlParameters(); 11 | SymmetricAxolotlParameters(const ECKeyPair &ourBaseKey, const ECKeyPair &ourRatchetKey, 12 | const IdentityKeyPair &ourIdentityKey, const DjbECPublicKey &theirBaseKey, 13 | const DjbECPublicKey &theirRatchetKey, const IdentityKey &theirIdentityKey); 14 | 15 | ECKeyPair getOurBaseKey() const; 16 | ECKeyPair getOurRatchetKey() const; 17 | IdentityKeyPair getOurIdentityKey() const; 18 | DjbECPublicKey getTheirBaseKey() const; 19 | DjbECPublicKey getTheirRatchetKey() const; 20 | IdentityKey getTheirIdentityKey() const; 21 | 22 | void setOurBaseKey(const ECKeyPair &ourBaseKey); 23 | void setOurRatchetKey(const ECKeyPair &ourRatchetKey); 24 | void setOurIdentityKey(const IdentityKeyPair &ourIdentityKey); 25 | void setTheirBaseKey(const DjbECPublicKey &theirBaseKey); 26 | void setTheirRatchetKey(const DjbECPublicKey &theirRatchetKey); 27 | void setTheirIdentityKey(const IdentityKey &theirIdentityKey); 28 | 29 | private: 30 | ECKeyPair ourBaseKey; 31 | ECKeyPair ourRatchetKey; 32 | IdentityKeyPair ourIdentityKey; 33 | 34 | DjbECPublicKey theirBaseKey; 35 | DjbECPublicKey theirRatchetKey; 36 | IdentityKey theirIdentityKey; 37 | 38 | }; 39 | 40 | #endif // SYMMETRICAXOLOTLPARAMETERS_H 41 | -------------------------------------------------------------------------------- /sessionbuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "sessionbuilder.h" 2 | 3 | #include "untrustedidentityexception.h" 4 | #include "invalidkeyidexception.h" 5 | #include "invalidkeyexception.h" 6 | #include "invalidmessageexception.h" 7 | #include "stalekeyexchangeexception.h" 8 | 9 | #include "ratchet/bobaxolotlparameters.h" 10 | #include "ratchet/ratchetingsession.h" 11 | #include "util/medium.h" 12 | #include "util/keyhelper.h" 13 | 14 | #include 15 | #include 16 | 17 | SessionBuilder::SessionBuilder() 18 | { 19 | 20 | } 21 | 22 | SessionBuilder::SessionBuilder(QSharedPointer sessionStore, QSharedPointer preKeyStore, QSharedPointer signedPreKeyStore, QSharedPointer identityKeyStore, const AxolotlAddress &remoteAddress) 23 | { 24 | init(sessionStore, preKeyStore, signedPreKeyStore, identityKeyStore, remoteAddress); 25 | } 26 | 27 | SessionBuilder::SessionBuilder(QSharedPointer store, const AxolotlAddress &remoteAddress) 28 | { 29 | init(qSharedPointerCast(store), 30 | qSharedPointerCast(store), 31 | qSharedPointerCast(store), 32 | qSharedPointerCast(store), 33 | remoteAddress); 34 | } 35 | 36 | void SessionBuilder::init(QSharedPointer sessionStore, QSharedPointer preKeyStore, QSharedPointer signedPreKeyStore, QSharedPointer identityKeyStore, const AxolotlAddress &remoteAddress) 37 | { 38 | this->sessionStore = sessionStore; 39 | this->preKeyStore = preKeyStore; 40 | this->signedPreKeyStore = signedPreKeyStore; 41 | this->identityKeyStore = identityKeyStore; 42 | this->remoteAddress = remoteAddress; 43 | } 44 | 45 | ulong SessionBuilder::process(SessionRecord *sessionRecord, QSharedPointer message) 46 | { 47 | int messageVersion = message->getMessageVersion(); 48 | IdentityKey theirIdentityKey = message->getIdentityKey(); 49 | 50 | ulong unsignedPreKeyId; 51 | 52 | if (!identityKeyStore->isTrustedIdentity(remoteAddress.getName(), theirIdentityKey)) { 53 | throw UntrustedIdentityException(QString("Untrusted identity: %1").arg(remoteAddress.getName())); 54 | } 55 | 56 | qDebug() << messageVersion; 57 | switch (messageVersion) { 58 | case 2: unsignedPreKeyId = processV2(sessionRecord, message); break; 59 | case 3: unsignedPreKeyId = processV3(sessionRecord, message); break; 60 | default: throw InvalidMessageException("Unknown version: " + messageVersion); 61 | } 62 | 63 | identityKeyStore->saveIdentity(remoteAddress.getName(), theirIdentityKey); 64 | return unsignedPreKeyId; 65 | } 66 | 67 | ulong SessionBuilder::processV3(SessionRecord *sessionRecord, QSharedPointer message) 68 | { 69 | if (sessionRecord->hasSessionState(message->getMessageVersion(), message->getBaseKey().serialize())) { 70 | return -1; 71 | } 72 | 73 | ECKeyPair ourSignedPreKey = signedPreKeyStore->loadSignedPreKey(message->getSignedPreKeyId()).getKeyPair(); 74 | 75 | BobAxolotlParameters parameters; 76 | 77 | parameters.setTheirBaseKey(message->getBaseKey()); 78 | parameters.setTheirIdentityKey(message->getIdentityKey()); 79 | parameters.setOurIdentityKey(identityKeyStore->getIdentityKeyPair()); 80 | parameters.setOurSignedPreKey(ourSignedPreKey); 81 | parameters.setOurRatchetKey(ourSignedPreKey); 82 | 83 | if (message->getPreKeyId() >= 0) { 84 | parameters.setOurOneTimePreKey(preKeyStore->loadPreKey(message->getPreKeyId()).getKeyPair()); 85 | } else { 86 | //parameters.setOurOneTimePreKey(NULL); 87 | } 88 | 89 | if (!sessionRecord->isFresh()) sessionRecord->archiveCurrentState(); 90 | 91 | RatchetingSession::initializeSession(sessionRecord->getSessionState(), message->getMessageVersion(), parameters); 92 | 93 | sessionRecord->getSessionState()->setLocalRegistrationId(identityKeyStore->getLocalRegistrationId()); 94 | sessionRecord->getSessionState()->setRemoteRegistrationId(message->getRegistrationId()); 95 | sessionRecord->getSessionState()->setAliceBaseKey(message->getBaseKey().serialize()); 96 | 97 | if (message->getPreKeyId() != Medium::MAX_VALUE) { 98 | return message->getPreKeyId(); 99 | } else { 100 | return -1; 101 | } 102 | } 103 | 104 | ulong SessionBuilder::processV2(SessionRecord *sessionRecord, QSharedPointer message) 105 | { 106 | if (message->getPreKeyId() < 0) { 107 | throw InvalidKeyIdException("V2 message requires one time prekey id!"); 108 | } 109 | 110 | if (!preKeyStore->containsPreKey(message->getPreKeyId()) && 111 | sessionStore->containsSession(remoteAddress)) 112 | { 113 | return -1; 114 | } 115 | 116 | ECKeyPair ourPreKey = preKeyStore->loadPreKey(message->getPreKeyId()).getKeyPair(); 117 | 118 | BobAxolotlParameters parameters; 119 | 120 | parameters.setOurIdentityKey(identityKeyStore->getIdentityKeyPair()); 121 | parameters.setOurSignedPreKey(ourPreKey); 122 | parameters.setOurRatchetKey(ourPreKey); 123 | //parameters.setOurOneTimePreKey(NULL); 124 | parameters.setTheirIdentityKey(message->getIdentityKey()); 125 | parameters.setTheirBaseKey(message->getBaseKey()); 126 | 127 | if (!sessionRecord->isFresh()) sessionRecord->archiveCurrentState(); 128 | 129 | RatchetingSession::initializeSession(sessionRecord->getSessionState(), message->getMessageVersion(), parameters); 130 | 131 | sessionRecord->getSessionState()->setLocalRegistrationId(identityKeyStore->getLocalRegistrationId()); 132 | sessionRecord->getSessionState()->setRemoteRegistrationId(message->getRegistrationId()); 133 | sessionRecord->getSessionState()->setAliceBaseKey(message->getBaseKey().serialize()); 134 | 135 | if (message->getPreKeyId() != Medium::MAX_VALUE) { 136 | return message->getPreKeyId(); 137 | } else { 138 | return -1; 139 | } 140 | } 141 | 142 | void SessionBuilder::process(const PreKeyBundle &preKey) 143 | { 144 | if (!identityKeyStore->isTrustedIdentity(remoteAddress.getName(), preKey.getIdentityKey())) { 145 | throw UntrustedIdentityException(QString("Untrusted identity: %1").arg(remoteAddress.getName())); 146 | } 147 | 148 | if (!preKey.getSignedPreKey().serialize().isEmpty() && 149 | !Curve::verifySignature(preKey.getSignedPreKey(), 150 | preKey.getIdentityKey().getPublicKey().serialize(), 151 | preKey.getSignedPreKeySignature())) 152 | { 153 | qWarning() << preKey.getIdentityKey().getPublicKey().serialize().toHex(); 154 | qWarning() << preKey.getSignedPreKey().serialize().toHex(); 155 | qWarning() << preKey.getSignedPreKeySignature().toHex(); 156 | throw InvalidKeyException("Invalid signature on device key!"); 157 | } 158 | 159 | if (preKey.getSignedPreKey().serialize().isEmpty() && preKey.getPreKey().serialize().isEmpty()) { 160 | throw InvalidKeyException("Both signed and unsigned prekeys are absent!"); 161 | } 162 | 163 | bool supportsV3 = !preKey.getSignedPreKey().serialize().isEmpty(); 164 | SessionRecord *sessionRecord = sessionStore->loadSession(remoteAddress); 165 | ECKeyPair ourBaseKey = Curve::generateKeyPair(); 166 | DjbECPublicKey theirSignedPreKey = supportsV3 ? preKey.getSignedPreKey() : preKey.getPreKey(); 167 | DjbECPublicKey theirOneTimePreKey = preKey.getPreKey(); 168 | int theirOneTimePreKeyId = theirOneTimePreKey.serialize().isEmpty() ? -1 : preKey.getPreKeyId(); 169 | 170 | AliceAxolotlParameters parameters; 171 | 172 | parameters.setOurBaseKey(ourBaseKey); 173 | parameters.setOurIdentityKey(identityKeyStore->getIdentityKeyPair()); 174 | parameters.setTheirIdentityKey(preKey.getIdentityKey()); 175 | parameters.setTheirSignedPreKey(theirSignedPreKey); 176 | parameters.setTheirRatchetKey(theirSignedPreKey); 177 | if (supportsV3) { 178 | parameters.setTheirOneTimePreKey(theirOneTimePreKey); 179 | } 180 | 181 | if (!sessionRecord->isFresh()) sessionRecord->archiveCurrentState(); 182 | 183 | RatchetingSession::initializeSession(sessionRecord->getSessionState(), 184 | supportsV3 ? 3 : 2, 185 | parameters); 186 | 187 | sessionRecord->getSessionState()->setUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey()); 188 | sessionRecord->getSessionState()->setLocalRegistrationId(identityKeyStore->getLocalRegistrationId()); 189 | sessionRecord->getSessionState()->setRemoteRegistrationId(preKey.getRegistrationId()); 190 | sessionRecord->getSessionState()->setAliceBaseKey(ourBaseKey.getPublicKey().serialize()); 191 | 192 | sessionStore->storeSession(remoteAddress, sessionRecord); 193 | identityKeyStore->saveIdentity(remoteAddress.getName(), preKey.getIdentityKey()); 194 | } 195 | 196 | KeyExchangeMessage SessionBuilder::process(QSharedPointer message) 197 | { 198 | if (!identityKeyStore->isTrustedIdentity(remoteAddress.getName(), message->getIdentityKey())) { 199 | throw UntrustedIdentityException(QString("Untrusted identity: %1").arg(remoteAddress.getName())); 200 | } 201 | 202 | KeyExchangeMessage responseMessage; 203 | 204 | if (message->isInitiate()) responseMessage = processInitiate(message); 205 | else processResponse(message); 206 | 207 | return responseMessage; 208 | } 209 | 210 | KeyExchangeMessage SessionBuilder::process() 211 | { 212 | int sequence = KeyHelper::getRandomFFFF(); 213 | int flags = KeyExchangeMessage::INITIATE_FLAG; 214 | ECKeyPair baseKey = Curve::generateKeyPair(); 215 | ECKeyPair ratchetKey = Curve::generateKeyPair(); 216 | IdentityKeyPair identityKey = identityKeyStore->getIdentityKeyPair(); 217 | QByteArray baseKeySignature = Curve::calculateSignature(identityKey.getPrivateKey(), baseKey.getPublicKey().serialize()); 218 | SessionRecord *sessionRecord = sessionStore->loadSession(remoteAddress); 219 | 220 | sessionRecord->getSessionState()->setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey); 221 | sessionStore->storeSession(remoteAddress, sessionRecord); 222 | 223 | return KeyExchangeMessage(2, sequence, flags, baseKey.getPublicKey(), baseKeySignature, 224 | ratchetKey.getPublicKey(), identityKey.getPublicKey()); 225 | } 226 | 227 | KeyExchangeMessage SessionBuilder::processInitiate(QSharedPointer message) 228 | { 229 | int flags = KeyExchangeMessage::RESPONSE_FLAG; 230 | SessionRecord *sessionRecord = sessionStore->loadSession(remoteAddress); 231 | 232 | if (message->getVersion() >= 3 && 233 | !Curve::verifySignature(message->getIdentityKey().getPublicKey(), 234 | message->getBaseKey().serialize(), 235 | message->getBaseKeySignature())) 236 | { 237 | throw InvalidKeyException("Bad signature!"); 238 | } 239 | 240 | SymmetricAxolotlParameters parameters; 241 | 242 | if (!sessionRecord->getSessionState()->hasPendingKeyExchange()) { 243 | parameters.setOurIdentityKey(identityKeyStore->getIdentityKeyPair()); 244 | parameters.setOurBaseKey(Curve::generateKeyPair()); 245 | parameters.setOurRatchetKey(Curve::generateKeyPair()); 246 | } else { 247 | parameters.setOurIdentityKey(sessionRecord->getSessionState()->getPendingKeyExchangeIdentityKey()); 248 | parameters.setOurBaseKey(sessionRecord->getSessionState()->getPendingKeyExchangeBaseKey()); 249 | parameters.setOurRatchetKey(sessionRecord->getSessionState()->getPendingKeyExchangeRatchetKey()); 250 | flags |= KeyExchangeMessage::SIMULTAENOUS_INITIATE_FLAG; 251 | } 252 | 253 | parameters.setTheirBaseKey(message->getBaseKey()); 254 | parameters.setTheirRatchetKey(message->getRatchetKey()); 255 | parameters.setTheirIdentityKey(message->getIdentityKey()); 256 | 257 | if (!sessionRecord->isFresh()) sessionRecord->archiveCurrentState(); 258 | 259 | RatchetingSession::initializeSession(sessionRecord->getSessionState(), 260 | qMin(message->getMaxVersion(), CiphertextMessage::CURRENT_VERSION), 261 | parameters); 262 | 263 | sessionStore->storeSession(remoteAddress, sessionRecord); 264 | identityKeyStore->saveIdentity(remoteAddress.getName(), message->getIdentityKey()); 265 | 266 | QByteArray baseKeySignature = Curve::calculateSignature(parameters.getOurIdentityKey().getPrivateKey(), 267 | parameters.getOurBaseKey().getPublicKey().serialize()); 268 | 269 | return KeyExchangeMessage(sessionRecord->getSessionState()->getSessionVersion(), 270 | message->getSequence(), flags, 271 | parameters.getOurBaseKey().getPublicKey(), 272 | baseKeySignature, parameters.getOurRatchetKey().getPublicKey(), 273 | parameters.getOurIdentityKey().getPublicKey()); 274 | } 275 | 276 | void SessionBuilder::processResponse(QSharedPointer message) 277 | { 278 | SessionRecord *sessionRecord = sessionStore->loadSession(remoteAddress); 279 | SessionState *sessionState = sessionRecord->getSessionState(); 280 | bool hasPendingKeyExchange = sessionState->hasPendingKeyExchange(); 281 | bool isSimultaneousInitiateResponse = message->isResponseForSimultaneousInitiate(); 282 | 283 | if (!hasPendingKeyExchange || sessionState->getPendingKeyExchangeSequence() != message->getSequence()) { 284 | if (!isSimultaneousInitiateResponse) throw StaleKeyExchangeException(""); 285 | else return; 286 | } 287 | 288 | SymmetricAxolotlParameters parameters; 289 | 290 | parameters.setOurBaseKey(sessionRecord->getSessionState()->getPendingKeyExchangeBaseKey()); 291 | parameters.setOurRatchetKey(sessionRecord->getSessionState()->getPendingKeyExchangeRatchetKey()); 292 | parameters.setOurIdentityKey(sessionRecord->getSessionState()->getPendingKeyExchangeIdentityKey()); 293 | parameters.setTheirBaseKey(message->getBaseKey()); 294 | parameters.setTheirRatchetKey(message->getRatchetKey()); 295 | parameters.setTheirIdentityKey(message->getIdentityKey()); 296 | 297 | if (!sessionRecord->isFresh()) sessionRecord->archiveCurrentState(); 298 | 299 | RatchetingSession::initializeSession(sessionRecord->getSessionState(), 300 | qMin(message->getMaxVersion(), CiphertextMessage::CURRENT_VERSION), 301 | parameters); 302 | 303 | if (sessionRecord->getSessionState()->getSessionVersion() >= 3 && 304 | !Curve::verifySignature(message->getIdentityKey().getPublicKey(), 305 | message->getBaseKey().serialize(), 306 | message->getBaseKeySignature())) 307 | { 308 | throw InvalidKeyException("Base key signature doesn't match!"); 309 | } 310 | 311 | sessionStore->storeSession(remoteAddress, sessionRecord); 312 | identityKeyStore->saveIdentity(remoteAddress.getName(), message->getIdentityKey()); 313 | } 314 | -------------------------------------------------------------------------------- /sessionbuilder.h: -------------------------------------------------------------------------------- 1 | #ifndef SESSIONBUILDER_H 2 | #define SESSIONBUILDER_H 3 | 4 | #include 5 | 6 | #include "state/sessionstore.h" 7 | #include "state/signedprekeystore.h" 8 | #include "state/prekeybundle.h" 9 | #include "state/prekeystore.h" 10 | #include "state/identitykeystore.h" 11 | #include "state/axolotlstore.h" 12 | #include "protocol/prekeywhispermessage.h" 13 | #include "protocol/keyexchangemessage.h" 14 | #include "axolotladdress.h" 15 | 16 | class SessionBuilder 17 | { 18 | public: 19 | SessionBuilder(); 20 | SessionBuilder(QSharedPointer sessionStore, 21 | QSharedPointer preKeyStore, 22 | QSharedPointer signedPreKeyStore, 23 | QSharedPointer identityKeyStore, 24 | const AxolotlAddress &remoteAddress); 25 | SessionBuilder(QSharedPointer store, const AxolotlAddress &remoteAddress); 26 | 27 | ulong process(SessionRecord *sessionRecord, QSharedPointer message); 28 | ulong processV3(SessionRecord *sessionRecord, QSharedPointer message); 29 | ulong processV2(SessionRecord *sessionRecord, QSharedPointer message); 30 | void process(const PreKeyBundle &preKey); 31 | KeyExchangeMessage process(QSharedPointer message); 32 | KeyExchangeMessage process(); 33 | 34 | private: 35 | void init(QSharedPointer sessionStore, 36 | QSharedPointer preKeyStore, 37 | QSharedPointer signedPreKeyStore, 38 | QSharedPointer identityKeyStore, 39 | const AxolotlAddress &remoteAddress); 40 | KeyExchangeMessage processInitiate(QSharedPointer message); 41 | void processResponse(QSharedPointer message); 42 | 43 | private: 44 | QSharedPointer sessionStore; 45 | QSharedPointer preKeyStore; 46 | QSharedPointer signedPreKeyStore; 47 | QSharedPointer identityKeyStore; 48 | AxolotlAddress remoteAddress; 49 | }; 50 | 51 | #endif // SESSIONBUILDER_H 52 | -------------------------------------------------------------------------------- /sessioncipher.cpp: -------------------------------------------------------------------------------- 1 | #include "sessioncipher.h" 2 | #include "nosessionexception.h" 3 | #include "invalidmessageexception.h" 4 | #include "invalidkeyexception.h" 5 | #include "duplicatemessageexception.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | static void ctr128_inc(unsigned char *counter) { 16 | unsigned int n=16; 17 | unsigned char c; 18 | 19 | do { 20 | --n; 21 | c = counter[n]; 22 | ++c; 23 | counter[n] = c; 24 | if (c) return; 25 | } while (n); 26 | } 27 | 28 | static void ctr128_inc_aligned(unsigned char *counter) { 29 | size_t *data,c,n; 30 | const union { long one; char little; } is_endian = {1}; 31 | 32 | if (is_endian.little) { 33 | ctr128_inc(counter); 34 | return; 35 | } 36 | 37 | data = (size_t *)counter; 38 | n = 16/sizeof(size_t); 39 | do { 40 | --n; 41 | c = data[n]; 42 | ++c; 43 | data[n] = c; 44 | if (c) return; 45 | } while (n); 46 | } 47 | 48 | SessionCipher::SessionCipher(QSharedPointer sessionStore, QSharedPointer preKeyStore, QSharedPointer signedPreKeyStore, QSharedPointer identityKeyStore, const AxolotlAddress &remoteAddress) 49 | { 50 | init(sessionStore, preKeyStore, signedPreKeyStore, identityKeyStore, remoteAddress); 51 | } 52 | 53 | SessionCipher::SessionCipher(QSharedPointer store, const AxolotlAddress &remoteAddress) 54 | { 55 | init(qSharedPointerCast(store), 56 | qSharedPointerCast(store), 57 | qSharedPointerCast(store), 58 | qSharedPointerCast(store), 59 | remoteAddress); 60 | } 61 | 62 | void SessionCipher::init(QSharedPointer sessionStore, QSharedPointer preKeyStore, QSharedPointer signedPreKeyStore, QSharedPointer identityKeyStore, const AxolotlAddress &remoteAddress) 63 | { 64 | this->sessionStore = sessionStore; 65 | this->remoteAddress = remoteAddress; 66 | this->preKeyStore = preKeyStore; 67 | this->sessionBuilder = SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, 68 | identityKeyStore, remoteAddress); 69 | } 70 | 71 | QSharedPointer SessionCipher::encrypt(const QByteArray &paddedMessage) 72 | { 73 | QSharedPointer result; 74 | 75 | SessionRecord *sessionRecord = sessionStore->loadSession(remoteAddress); 76 | SessionState *sessionState = sessionRecord->getSessionState(); 77 | ChainKey chainKey = sessionState->getSenderChainKey(); 78 | MessageKeys messageKeys = chainKey.getMessageKeys(); 79 | DjbECPublicKey senderEphemeral = sessionState->getSenderRatchetKey(); 80 | int previousCounter = sessionState->getPreviousCounter(); 81 | int sessionVersion = sessionState->getSessionVersion(); 82 | 83 | QByteArray ciphertextBody = getCiphertext(sessionVersion, messageKeys, paddedMessage); 84 | QSharedPointer whisperMessage(new WhisperMessage(sessionVersion, messageKeys.getMacKey(), 85 | senderEphemeral, chainKey.getIndex(), 86 | previousCounter, ciphertextBody, 87 | sessionState->getLocalIdentityKey(), 88 | sessionState->getRemoteIdentityKey())); 89 | 90 | if (sessionState->hasUnacknowledgedPreKeyMessage()) { 91 | UnacknowledgedPreKeyMessageItems items = sessionState->getUnacknowledgedPreKeyMessageItems(); 92 | int localRegistrationId = sessionState->getLocalRegistrationId(); 93 | 94 | QSharedPointer preKeyWhisperMessage(new PreKeyWhisperMessage( 95 | sessionVersion, localRegistrationId, items.getPreKeyId(), 96 | items.getSignedPreKeyId(), items.getBaseKey(), 97 | sessionState->getLocalIdentityKey(), 98 | whisperMessage)); 99 | result = preKeyWhisperMessage; 100 | } 101 | else { 102 | result = whisperMessage; 103 | } 104 | 105 | sessionState->setSenderChainKey(chainKey.getNextChainKey()); 106 | sessionStore->storeSession(remoteAddress, sessionRecord); 107 | 108 | return result; 109 | } 110 | 111 | QByteArray SessionCipher::decrypt(QSharedPointer ciphertext) 112 | { 113 | SessionRecord *sessionRecord = sessionStore->loadSession(remoteAddress); 114 | qulonglong unsignedPreKeyId = sessionBuilder.process(sessionRecord, ciphertext); 115 | QByteArray plaintext = decrypt(sessionRecord, ciphertext->getWhisperMessage()); 116 | 117 | sessionStore->storeSession(remoteAddress, sessionRecord); 118 | 119 | if (unsignedPreKeyId != -1) { 120 | preKeyStore->removePreKey(unsignedPreKeyId); 121 | } 122 | 123 | return plaintext; 124 | } 125 | 126 | QByteArray SessionCipher::decrypt(QSharedPointer ciphertext) 127 | { 128 | if (!sessionStore->containsSession(remoteAddress)) { 129 | qDebug() << "No session for" << remoteAddress.getName() << remoteAddress.getDeviceId(); 130 | throw NoSessionException(QString("No session for: %1, %2").arg(remoteAddress.getName()).arg(remoteAddress.getDeviceId())); 131 | } 132 | 133 | SessionRecord *sessionRecord = sessionStore->loadSession(remoteAddress); 134 | QByteArray plaintext = decrypt(sessionRecord, ciphertext); 135 | 136 | sessionStore->storeSession(remoteAddress, sessionRecord); 137 | 138 | return plaintext; 139 | } 140 | 141 | QByteArray SessionCipher::decrypt(SessionRecord *sessionRecord, QSharedPointer ciphertext) 142 | { 143 | QList previousStatesList = sessionRecord->getPreviousSessionStates(); 144 | QMutableListIterator previousStates(previousStatesList); 145 | QList exceptions; 146 | 147 | try { 148 | SessionState *sessionState = sessionRecord->getSessionState(); 149 | QByteArray plaintext = decrypt(sessionState, ciphertext); 150 | 151 | sessionRecord->setState(sessionState); 152 | return plaintext; 153 | } catch (const InvalidMessageException &e) { 154 | exceptions.append(e); 155 | } 156 | 157 | while (previousStates.hasNext()) { 158 | try { 159 | SessionState *promotedState = previousStates.next(); 160 | QByteArray plaintext = decrypt(promotedState, ciphertext); 161 | 162 | previousStates.remove(); 163 | sessionRecord->promoteState(promotedState); 164 | 165 | return plaintext; 166 | } catch (const InvalidMessageException &e) { 167 | exceptions.append(e); 168 | } 169 | } 170 | 171 | throw InvalidMessageException("No valid sessions.", exceptions); 172 | } 173 | 174 | QByteArray SessionCipher::decrypt(SessionState *sessionState, QSharedPointer ciphertextMessage) 175 | { 176 | if (!sessionState->hasSenderChain()) { 177 | throw InvalidMessageException("Uninitialized session!"); 178 | } 179 | 180 | if (ciphertextMessage->getMessageVersion() != sessionState->getSessionVersion()) { 181 | throw InvalidMessageException(QString("Message version %1, but session version %2") 182 | .arg(ciphertextMessage->getMessageVersion()) 183 | .arg(sessionState->getSessionVersion())); 184 | } 185 | 186 | int messageVersion = ciphertextMessage->getMessageVersion(); 187 | DjbECPublicKey theirEphemeral = ciphertextMessage->getSenderRatchetKey(); 188 | uint counter = ciphertextMessage->getCounter(); 189 | ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral); 190 | MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral, 191 | chainKey, counter); 192 | 193 | ciphertextMessage->verifyMac(messageVersion, 194 | sessionState->getRemoteIdentityKey(), 195 | sessionState->getLocalIdentityKey(), 196 | messageKeys.getMacKey()); 197 | 198 | QByteArray plaintext = getPlaintext(messageVersion, messageKeys, ciphertextMessage->getBody()); 199 | 200 | sessionState->clearUnacknowledgedPreKeyMessage(); 201 | 202 | return plaintext; 203 | } 204 | 205 | int SessionCipher::getRemoteRegistrationId() 206 | { 207 | SessionRecord *record = sessionStore->loadSession(remoteAddress); 208 | return record->getSessionState()->getRemoteRegistrationId(); 209 | } 210 | 211 | int SessionCipher::getSessionVersion() 212 | { 213 | if (!sessionStore->containsSession(remoteAddress)) { 214 | qDebug() << "No session for" << remoteAddress.getName() << remoteAddress.getDeviceId(); 215 | throw NoSessionException(QString("No session for (%1, %2)!").arg(remoteAddress.getName()).arg(remoteAddress.getDeviceId())); 216 | } 217 | 218 | SessionRecord *record = sessionStore->loadSession(remoteAddress); 219 | return record->getSessionState()->getSessionVersion(); 220 | } 221 | 222 | ChainKey SessionCipher::getOrCreateChainKey(SessionState *sessionState, const DjbECPublicKey &theirEphemeral) 223 | { 224 | try { 225 | if (sessionState->hasReceiverChain(theirEphemeral)) { 226 | return sessionState->getReceiverChainKey(theirEphemeral); 227 | } else { 228 | RootKey rootKey = sessionState->getRootKey(); 229 | ECKeyPair ourEphemeral = sessionState->getSenderRatchetKeyPair(); 230 | QPair receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral); 231 | ECKeyPair ourNewEphemeral = Curve::generateKeyPair(); 232 | QPair senderChain = receiverChain.first.createChain(theirEphemeral, ourNewEphemeral); 233 | 234 | sessionState->setRootKey(senderChain.first); 235 | sessionState->addReceiverChain(theirEphemeral, receiverChain.second); 236 | sessionState->setPreviousCounter(qMax(sessionState->getSenderChainKey().getIndex() - 1, (uint)0)); 237 | sessionState->setSenderChain(ourNewEphemeral, senderChain.second); 238 | 239 | return receiverChain.second; 240 | } 241 | } catch (const InvalidKeyException &e) { 242 | throw InvalidMessageException(__PRETTY_FUNCTION__, QList() << e); 243 | } 244 | } 245 | 246 | MessageKeys SessionCipher::getOrCreateMessageKeys(SessionState *sessionState, const DjbECPublicKey &theirEphemeral, const ChainKey &chainKey, uint counter) 247 | { 248 | if (chainKey.getIndex() > counter) { 249 | if (sessionState->hasMessageKeys(theirEphemeral, counter)) { 250 | return sessionState->removeMessageKeys(theirEphemeral, counter); 251 | } else { 252 | throw DuplicateMessageException(QString("Received message with old counter: %1, %2") 253 | .arg(chainKey.getIndex()) 254 | .arg(counter)); 255 | } 256 | } 257 | 258 | if (counter - chainKey.getIndex() > 2000) { 259 | throw InvalidMessageException("Over 2000 messages into the future!"); 260 | } 261 | 262 | ChainKey nowChainKey = chainKey; 263 | while (nowChainKey.getIndex() < counter) { 264 | MessageKeys messageKeys = nowChainKey.getMessageKeys(); 265 | sessionState->setMessageKeys(theirEphemeral, messageKeys); 266 | nowChainKey = nowChainKey.getNextChainKey(); 267 | } 268 | 269 | sessionState->setReceiverChainKey(theirEphemeral, nowChainKey.getNextChainKey()); 270 | return nowChainKey.getMessageKeys(); 271 | } 272 | 273 | QByteArray SessionCipher::getCiphertext(int version, const MessageKeys &messageKeys, const QByteArray &plaintext) 274 | { 275 | AES_KEY enc_key; 276 | QByteArray key = messageKeys.getCipherKey(); 277 | if (version >= 3) { 278 | AES_set_encrypt_key((const unsigned char*)key.constData(), key.size() * 8, &enc_key); 279 | QByteArray padText = plaintext; 280 | int padlen = ((padText.size() + AES_BLOCK_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE - plaintext.size(); 281 | padText.append(QByteArray(padlen, (char)padlen)); 282 | QByteArray out(padText.size(), '\0'); 283 | QByteArray ivec(messageKeys.getIv()); 284 | AES_cbc_encrypt((const unsigned char*)padText.constData(), (unsigned char*)out.data(), 285 | padText.size(), &enc_key, 286 | (unsigned char*)ivec.data(), AES_ENCRYPT); 287 | return out; 288 | } else { 289 | AES_set_encrypt_key((const unsigned char*)key.constData(), 128, &enc_key); 290 | QByteArray out(plaintext.size(), '\0'); 291 | unsigned int counter = 0; 292 | QByteArray iv(AES_BLOCK_SIZE, '\0'); 293 | //ByteUtil::intToByteArray(iv, 0, counter); 294 | unsigned char ecount[AES_BLOCK_SIZE]; 295 | memset(ecount, 0, AES_BLOCK_SIZE); 296 | // TODO store state 297 | for (unsigned int i = 0; i < messageKeys.getCounter(); i++) { 298 | AES_encrypt((const unsigned char*)iv.constData(), ecount, &enc_key); 299 | ctr128_inc_aligned((unsigned char*)iv.data()); 300 | } 301 | AES_ctr128_encrypt((const unsigned char*)plaintext.constData(), (unsigned char*)out.data(), 302 | plaintext.size(), &enc_key, (unsigned char*)iv.data(), 303 | ecount, &counter); 304 | return out; 305 | } 306 | } 307 | 308 | QByteArray SessionCipher::getPlaintext(int version, const MessageKeys &messageKeys, const QByteArray &cipherText) 309 | { 310 | qDebug() << version << cipherText.toHex(); 311 | AES_KEY dec_key; 312 | QByteArray key = messageKeys.getCipherKey(); 313 | QByteArray out(cipherText.size(), '\0'); 314 | if (version >= 3) { 315 | AES_set_decrypt_key((const unsigned char*)key.constData(), key.size() * 8, &dec_key); 316 | QByteArray ivec(messageKeys.getIv()); 317 | AES_cbc_encrypt((const unsigned char*)cipherText.constData(), 318 | (unsigned char*)out.data(), 319 | cipherText.size(), &dec_key, 320 | (unsigned char*)ivec.data(), AES_DECRYPT); 321 | out = out.mid(0, out.size() - out.right(1)[0]); 322 | } else { 323 | AES_set_encrypt_key((const unsigned char*)key.constData(), 128, &dec_key); 324 | unsigned int counter = 0; 325 | QByteArray iv(AES_BLOCK_SIZE, '\0'); 326 | //ByteUtil::intToByteArray(iv, 0, counter); 327 | unsigned char ecount[AES_BLOCK_SIZE]; 328 | memset(ecount, 0, AES_BLOCK_SIZE); 329 | for (unsigned int i = 0; i < messageKeys.getCounter(); i++) { 330 | AES_encrypt((const unsigned char*)iv.constData(), ecount, &dec_key); 331 | ctr128_inc_aligned((unsigned char*)iv.data()); 332 | } 333 | AES_ctr128_encrypt((const unsigned char*)cipherText.constData(), (unsigned char*)out.data(), 334 | cipherText.size(), &dec_key, (unsigned char*)iv.data(), 335 | ecount, &counter); 336 | } 337 | return out; 338 | } 339 | -------------------------------------------------------------------------------- /sessioncipher.h: -------------------------------------------------------------------------------- 1 | #ifndef SESSIONCIPHER_H 2 | #define SESSIONCIPHER_H 3 | 4 | #include 5 | 6 | #include "state/sessionstore.h" 7 | #include "sessionbuilder.h" 8 | #include "ratchet/messagekeys.h" 9 | #include "axolotladdress.h" 10 | 11 | class SessionCipher 12 | { 13 | public: 14 | SessionCipher(QSharedPointer sessionStore, QSharedPointer preKeyStore, 15 | QSharedPointer signedPreKeyStore, QSharedPointer identityKeyStore, 16 | const AxolotlAddress &remoteAddress); 17 | SessionCipher(QSharedPointer store, const AxolotlAddress &remoteAddress); 18 | QSharedPointer encrypt(const QByteArray &paddedMessage); 19 | QByteArray decrypt(QSharedPointer ciphertext); 20 | QByteArray decrypt(QSharedPointer ciphertext); 21 | QByteArray decrypt(SessionRecord *sessionRecord, QSharedPointer ciphertext); 22 | QByteArray decrypt(SessionState *sessionState, QSharedPointer ciphertextMessage); 23 | int getRemoteRegistrationId(); 24 | int getSessionVersion() ; 25 | 26 | private: 27 | void init(QSharedPointer sessionStore, QSharedPointer preKeyStore, 28 | QSharedPointer signedPreKeyStore, QSharedPointer identityKeyStore, 29 | const AxolotlAddress &remoteAddress); 30 | ChainKey getOrCreateChainKey(SessionState *sessionState, const DjbECPublicKey &theirEphemeral); 31 | MessageKeys getOrCreateMessageKeys(SessionState *sessionState, 32 | const DjbECPublicKey &theirEphemeral, 33 | const ChainKey &chainKey, uint counter); 34 | QByteArray getCiphertext(int version, const MessageKeys &messageKeys, const QByteArray &plaintext); 35 | QByteArray getPlaintext(int version, const MessageKeys &messageKeys, const QByteArray &cipherText); 36 | 37 | QSharedPointer sessionStore; 38 | SessionBuilder sessionBuilder; 39 | QSharedPointer preKeyStore; 40 | AxolotlAddress remoteAddress; 41 | }; 42 | 43 | #endif // SESSIONCIPHER_H 44 | -------------------------------------------------------------------------------- /stalekeyexchangeexception.h: -------------------------------------------------------------------------------- 1 | #ifndef STALEKEYEXCHANGEEXCEPTION_H 2 | #define STALEKEYEXCHANGEEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class StaleKeyExchangeException : public WhisperException 7 | { 8 | public: 9 | StaleKeyExchangeException(const QString &error) : WhisperException("StaleKeyExchangeException", error) {} 10 | }; 11 | 12 | #endif // STALEKEYEXCHANGEEXCEPTION_H 13 | -------------------------------------------------------------------------------- /state/axolotlstore.h: -------------------------------------------------------------------------------- 1 | #ifndef AXOLOTLSTORE_H 2 | #define AXOLOTLSTORE_H 3 | 4 | #include "identitykeystore.h" 5 | #include "prekeystore.h" 6 | #include "sessionstore.h" 7 | #include "signedprekeystore.h" 8 | 9 | class AxolotlStore: public IdentityKeyStore, public PreKeyStore, public SessionStore, public SignedPreKeyStore { 10 | 11 | }; 12 | 13 | #endif // AXOLOTLSTORE_H 14 | -------------------------------------------------------------------------------- /state/identitykeystore.h: -------------------------------------------------------------------------------- 1 | #ifndef IDENTITYKEYSTORE_H 2 | #define IDENTITYKEYSTORE_H 3 | 4 | #include "../identitykeypair.h" 5 | 6 | #include 7 | 8 | class IdentityKeyStore { 9 | public: 10 | virtual IdentityKeyPair getIdentityKeyPair() = 0; 11 | virtual uint getLocalRegistrationId() = 0; 12 | virtual void storeLocalData(qulonglong registrationId, const IdentityKeyPair identityKeyPair) = 0; 13 | virtual void saveIdentity(const QString &name, const IdentityKey &identityKey) = 0; 14 | virtual bool isTrustedIdentity(const QString &name, const IdentityKey &identityKey) = 0; 15 | virtual void removeIdentity(const QString &name) = 0; 16 | }; 17 | 18 | #endif // IDENTITYKEYSTORE_H 19 | -------------------------------------------------------------------------------- /state/prekeybundle.cpp: -------------------------------------------------------------------------------- 1 | #include "prekeybundle.h" 2 | 3 | PreKeyBundle::PreKeyBundle(qulonglong registrationId, int deviceId, qulonglong preKeyId, const DjbECPublicKey &preKeyPublic, qulonglong signedPreKeyId, const DjbECPublicKey &signedPreKeyPublic, const QByteArray &signedPreKeySignature, const IdentityKey &identityKey) 4 | { 5 | this->registrationId = registrationId; 6 | this->deviceId = deviceId; 7 | this->preKeyId = preKeyId; 8 | this->preKeyPublic = preKeyPublic; 9 | this->signedPreKeyId = signedPreKeyId; 10 | this->signedPreKeyPublic = signedPreKeyPublic; 11 | this->signedPreKeySignature = signedPreKeySignature; 12 | this->identityKey = identityKey; 13 | } 14 | 15 | int PreKeyBundle::getDeviceId() const 16 | { 17 | return deviceId; 18 | } 19 | 20 | qulonglong PreKeyBundle::getPreKeyId() const 21 | { 22 | return preKeyId; 23 | } 24 | 25 | DjbECPublicKey PreKeyBundle::getPreKey() const 26 | { 27 | return preKeyPublic; 28 | } 29 | 30 | qulonglong PreKeyBundle::getSignedPreKeyId() const 31 | { 32 | return signedPreKeyId; 33 | } 34 | 35 | DjbECPublicKey PreKeyBundle::getSignedPreKey() const 36 | { 37 | return signedPreKeyPublic; 38 | } 39 | 40 | QByteArray PreKeyBundle::getSignedPreKeySignature() const 41 | { 42 | return signedPreKeySignature; 43 | } 44 | 45 | IdentityKey PreKeyBundle::getIdentityKey() const 46 | { 47 | return identityKey; 48 | } 49 | 50 | qulonglong PreKeyBundle::getRegistrationId() const 51 | { 52 | return registrationId; 53 | } 54 | -------------------------------------------------------------------------------- /state/prekeybundle.h: -------------------------------------------------------------------------------- 1 | #ifndef PREKEYBUNDLE_H 2 | #define PREKEYBUNDLE_H 3 | 4 | #include "../ecc/djbec.h" 5 | #include "../identitykey.h" 6 | 7 | class PreKeyBundle 8 | { 9 | public: 10 | PreKeyBundle(qulonglong registrationId, int deviceId, qulonglong preKeyId, const DjbECPublicKey &preKeyPublic, qulonglong signedPreKeyId, const DjbECPublicKey &signedPreKeyPublic, const QByteArray &signedPreKeySignature, const IdentityKey &identityKey); 11 | 12 | int getDeviceId() const; 13 | qulonglong getPreKeyId() const; 14 | DjbECPublicKey getPreKey() const; 15 | qulonglong getSignedPreKeyId() const; 16 | DjbECPublicKey getSignedPreKey() const; 17 | QByteArray getSignedPreKeySignature() const; 18 | IdentityKey getIdentityKey() const; 19 | qulonglong getRegistrationId() const; 20 | 21 | private: 22 | qulonglong registrationId; 23 | int deviceId; 24 | qulonglong preKeyId; 25 | DjbECPublicKey preKeyPublic; 26 | qulonglong signedPreKeyId; 27 | DjbECPublicKey signedPreKeyPublic; 28 | QByteArray signedPreKeySignature; 29 | IdentityKey identityKey; 30 | 31 | }; 32 | 33 | #endif // PREKEYBUNDLE_H 34 | -------------------------------------------------------------------------------- /state/prekeyrecord.cpp: -------------------------------------------------------------------------------- 1 | #include "prekeyrecord.h" 2 | 3 | PreKeyRecord::PreKeyRecord(qulonglong id, const ECKeyPair &keyPair) 4 | { 5 | QByteArray bytePublic = keyPair.getPublicKey().serialize(); 6 | QByteArray bytePrivate = keyPair.getPrivateKey().serialize(); 7 | 8 | structure.set_id(id); 9 | structure.set_publickey(bytePublic.constData(), bytePublic.size()); 10 | structure.set_privatekey(bytePrivate.constData(), bytePrivate.size()); 11 | } 12 | 13 | PreKeyRecord::PreKeyRecord(const QByteArray &serialized) 14 | { 15 | structure.ParsePartialFromArray(serialized.constData(), serialized.size()); 16 | } 17 | 18 | qulonglong PreKeyRecord::getId() const 19 | { 20 | return structure.id(); 21 | } 22 | 23 | ECKeyPair PreKeyRecord::getKeyPair() const 24 | { 25 | ::std::string publickey = structure.publickey(); 26 | DjbECPublicKey publicKey = Curve::decodePoint(QByteArray(publickey.data(), publickey.length()), 0); 27 | ::std::string privatekey = structure.privatekey(); 28 | DjbECPrivateKey privateKey = Curve::decodePrivatePoint(QByteArray(privatekey.data(), privatekey.length())); 29 | return ECKeyPair(publicKey, privateKey); 30 | } 31 | 32 | QByteArray PreKeyRecord::serialize() const 33 | { 34 | std::string str = structure.SerializeAsString(); 35 | QByteArray bytes(str.data(), str.size()); 36 | return bytes; 37 | } 38 | -------------------------------------------------------------------------------- /state/prekeyrecord.h: -------------------------------------------------------------------------------- 1 | #ifndef PREKEYRECORD_H 2 | #define PREKEYRECORD_H 3 | 4 | #include 5 | 6 | #include "LocalStorageProtocol.pb.h" 7 | #include "../ecc/curve.h" 8 | #include "../ecc/eckeypair.h" 9 | 10 | class PreKeyRecord 11 | { 12 | public: 13 | PreKeyRecord(qulonglong id, const ECKeyPair &keyPair); 14 | PreKeyRecord(const QByteArray &serialized); 15 | 16 | qulonglong getId() const; 17 | ECKeyPair getKeyPair() const; 18 | QByteArray serialize() const; 19 | 20 | private: 21 | textsecure::PreKeyRecordStructure structure; 22 | 23 | }; 24 | 25 | #endif // PREKEYRECORD_H 26 | -------------------------------------------------------------------------------- /state/prekeystore.h: -------------------------------------------------------------------------------- 1 | #ifndef PREKEYSTORE_H 2 | #define PREKEYSTORE_H 3 | 4 | #include "prekeyrecord.h" 5 | 6 | class PreKeyStore { 7 | public: 8 | virtual PreKeyRecord loadPreKey(qulonglong preKeyId) = 0; 9 | virtual void storePreKey(qulonglong preKeyId, const PreKeyRecord &record) = 0; 10 | virtual bool containsPreKey(qulonglong preKeyId) = 0; 11 | virtual void removePreKey(qulonglong preKeyId) = 0; 12 | virtual int countPreKeys() = 0; 13 | }; 14 | 15 | #endif // PREKEYSTORE_H 16 | -------------------------------------------------------------------------------- /state/sessionrecord.cpp: -------------------------------------------------------------------------------- 1 | #include "sessionrecord.h" 2 | 3 | const int SessionRecord::ARCHIVED_STATES_MAX_LENGTH = 50; 4 | 5 | SessionRecord::SessionRecord() 6 | { 7 | fresh = true; 8 | this->sessionState = new SessionState(); 9 | } 10 | 11 | SessionRecord::SessionRecord(SessionState *sessionState) 12 | { 13 | this->sessionState = sessionState; 14 | fresh = false; 15 | } 16 | 17 | SessionRecord::SessionRecord(const QByteArray &serialized) 18 | { 19 | textsecure::RecordStructure record; 20 | record.ParsePartialFromArray(serialized.constData(), serialized.size()); 21 | sessionState = new SessionState(record.currentsession()); 22 | fresh = false; 23 | 24 | for (int i = 0; i < record.previoussessions_size(); i++) { 25 | previousStates.append(new SessionState(record.previoussessions(i))); 26 | } 27 | } 28 | 29 | bool SessionRecord::hasSessionState(int version, const QByteArray &aliceBaseKey) 30 | { 31 | if (sessionState->getSessionVersion() == version 32 | && aliceBaseKey == sessionState->getAliceBaseKey()) 33 | { 34 | return true; 35 | } 36 | 37 | foreach (SessionState *state, previousStates) { 38 | if (state->getSessionVersion() == version 39 | && aliceBaseKey == state->getAliceBaseKey()) 40 | { 41 | return true; 42 | } 43 | } 44 | 45 | return false; 46 | } 47 | 48 | SessionState *SessionRecord::getSessionState() 49 | { 50 | return sessionState; 51 | } 52 | 53 | QList SessionRecord::getPreviousSessionStates() 54 | { 55 | return previousStates; 56 | } 57 | 58 | bool SessionRecord::isFresh() const 59 | { 60 | return fresh; 61 | } 62 | 63 | void SessionRecord::promoteState(SessionState *promotedState) 64 | { 65 | previousStates.insert(0, promotedState); 66 | sessionState = promotedState; 67 | if (previousStates.size() > ARCHIVED_STATES_MAX_LENGTH) { 68 | previousStates.removeLast(); 69 | } 70 | } 71 | 72 | void SessionRecord::archiveCurrentState() 73 | { 74 | promoteState(new SessionState()); 75 | } 76 | 77 | void SessionRecord::setState(SessionState *sessionState) 78 | { 79 | this->sessionState = sessionState; 80 | } 81 | 82 | QByteArray SessionRecord::serialize() const 83 | { 84 | textsecure::RecordStructure record; 85 | record.mutable_currentsession()->CopyFrom(sessionState->getStructure()); 86 | 87 | foreach (SessionState *previousState, previousStates) { 88 | record.add_previoussessions()->CopyFrom(previousState->getStructure()); 89 | } 90 | 91 | ::std::string serialized = record.SerializeAsString(); 92 | return QByteArray(serialized.data(), serialized.length()); 93 | } 94 | -------------------------------------------------------------------------------- /state/sessionrecord.h: -------------------------------------------------------------------------------- 1 | #ifndef SESSIONRECORD_H 2 | #define SESSIONRECORD_H 3 | 4 | #include "sessionstate.h" 5 | 6 | #include 7 | #include 8 | 9 | class SessionRecord 10 | { 11 | public: 12 | SessionRecord(); 13 | SessionRecord(SessionState *sessionState); 14 | SessionRecord(const QByteArray &serialized); 15 | 16 | bool hasSessionState(int version, const QByteArray &aliceBaseKey); 17 | SessionState *getSessionState(); 18 | QList getPreviousSessionStates(); 19 | bool isFresh() const; 20 | void promoteState(SessionState *promotedState); 21 | void archiveCurrentState(); 22 | void setState(SessionState *sessionState); 23 | QByteArray serialize() const; 24 | 25 | private: 26 | static const int ARCHIVED_STATES_MAX_LENGTH; 27 | SessionState *sessionState; 28 | QList previousStates; 29 | bool fresh; 30 | }; 31 | 32 | #endif // SESSIONRECORD_H 33 | -------------------------------------------------------------------------------- /state/sessionstate.cpp: -------------------------------------------------------------------------------- 1 | #include "sessionstate.h" 2 | #include "../ecc/curve.h" 3 | #include "../invalidkeyexception.h" 4 | 5 | #include 6 | 7 | UnacknowledgedPreKeyMessageItems::UnacknowledgedPreKeyMessageItems(int preKeyId, int signedPreKeyId, const DjbECPublicKey &baseKey) 8 | { 9 | this->preKeyId = preKeyId; 10 | this->signedPreKeyId = signedPreKeyId; 11 | this->baseKey = baseKey; 12 | } 13 | 14 | int UnacknowledgedPreKeyMessageItems::getPreKeyId() const 15 | { 16 | return preKeyId; 17 | } 18 | 19 | int UnacknowledgedPreKeyMessageItems::getSignedPreKeyId() const 20 | { 21 | return signedPreKeyId; 22 | } 23 | 24 | DjbECPublicKey UnacknowledgedPreKeyMessageItems::getBaseKey() const 25 | { 26 | return baseKey; 27 | } 28 | 29 | SessionState::SessionState() 30 | { 31 | this->sessionStructure.Clear(); 32 | } 33 | 34 | SessionState::SessionState(const textsecure::SessionStructure &sessionSctucture) 35 | { 36 | this->sessionStructure.CopyFrom(sessionSctucture); 37 | } 38 | 39 | SessionState::SessionState(const SessionState ©) 40 | { 41 | this->sessionStructure.CopyFrom(copy.getStructure()); 42 | } 43 | 44 | textsecure::SessionStructure SessionState::getStructure() const 45 | { 46 | return sessionStructure; 47 | } 48 | 49 | QByteArray SessionState::getAliceBaseKey() const 50 | { 51 | ::std::string alicebasekey = sessionStructure.alicebasekey(); 52 | QByteArray bytes(alicebasekey.data(), alicebasekey.size()); 53 | return bytes; 54 | } 55 | 56 | void SessionState::setAliceBaseKey(const QByteArray &aliceBaseKey) 57 | { 58 | sessionStructure.set_alicebasekey(aliceBaseKey.constData(), aliceBaseKey.size()); 59 | } 60 | 61 | void SessionState::setSessionVersion(int version) 62 | { 63 | sessionStructure.set_sessionversion(version); 64 | } 65 | 66 | int SessionState::getSessionVersion() const 67 | { 68 | int sessionVersion = sessionStructure.sessionversion(); 69 | 70 | if (sessionVersion == 0) return 2; 71 | else return sessionVersion; 72 | } 73 | 74 | void SessionState::setRemoteIdentityKey(const IdentityKey &identityKey) 75 | { 76 | QByteArray byteKey = identityKey.serialize(); 77 | sessionStructure.set_remoteidentitypublic(byteKey.constData(), byteKey.size()); 78 | } 79 | 80 | void SessionState::setLocalIdentityKey(const IdentityKey &identityKey) 81 | { 82 | QByteArray byteKey = identityKey.serialize(); 83 | sessionStructure.set_localidentitypublic(byteKey.constData(), byteKey.size()); 84 | } 85 | 86 | bool SessionState::hasRemoteIdentityKey() const 87 | { 88 | return sessionStructure.has_remoteidentitypublic(); 89 | } 90 | 91 | IdentityKey SessionState::getRemoteIdentityKey() const 92 | { 93 | if (!sessionStructure.has_remoteidentitypublic()) { 94 | throw InvalidKeyException("No RemoteIdentityKey"); 95 | } 96 | else { 97 | ::std::string remoteidentitypublic = sessionStructure.remoteidentitypublic(); 98 | return IdentityKey(QByteArray(remoteidentitypublic.data(), remoteidentitypublic.length()), 0); 99 | } 100 | } 101 | 102 | IdentityKey SessionState::getLocalIdentityKey() const 103 | { 104 | ::std::string localidentitypublic = sessionStructure.localidentitypublic(); 105 | return IdentityKey(QByteArray(localidentitypublic.data(), localidentitypublic.length()), 0); 106 | } 107 | 108 | int SessionState::getPreviousCounter() const 109 | { 110 | return sessionStructure.previouscounter(); 111 | } 112 | 113 | void SessionState::setPreviousCounter(int previousCounter) 114 | { 115 | sessionStructure.set_previouscounter(previousCounter); 116 | } 117 | 118 | RootKey SessionState::getRootKey() const 119 | { 120 | ::std::string rootkey = sessionStructure.rootkey(); 121 | return RootKey(HKDF(getSessionVersion()), QByteArray(rootkey.data(), rootkey.length())); 122 | } 123 | 124 | void SessionState::setRootKey(const RootKey &rootKey) 125 | { 126 | QByteArray bytesKey = rootKey.getKeyBytes(); 127 | sessionStructure.set_rootkey(bytesKey.constData(), bytesKey.size()); 128 | } 129 | 130 | DjbECPublicKey SessionState::getSenderRatchetKey() const 131 | { 132 | ::std::string senderratchetkey = sessionStructure.senderchain().senderratchetkey(); 133 | return Curve::decodePoint(QByteArray(senderratchetkey.data(), senderratchetkey.length()), 0); 134 | } 135 | 136 | ECKeyPair SessionState::getSenderRatchetKeyPair() const 137 | { 138 | DjbECPublicKey publicKey = getSenderRatchetKey(); 139 | ::std::string senderratchetkeyprivate = sessionStructure.senderchain().senderratchetkeyprivate(); 140 | DjbECPrivateKey privateKey = Curve::decodePrivatePoint(QByteArray(senderratchetkeyprivate.data(), 141 | senderratchetkeyprivate.length())); 142 | 143 | return ECKeyPair(publicKey, privateKey); 144 | } 145 | 146 | bool SessionState::hasReceiverChain(const DjbECPublicKey &senderEphemeral) 147 | { 148 | return getReceiverChain(senderEphemeral) != -1; 149 | } 150 | 151 | bool SessionState::hasSenderChain() const 152 | { 153 | return sessionStructure.has_senderchain(); 154 | } 155 | 156 | int SessionState::getReceiverChain(const DjbECPublicKey &senderEphemeral) 157 | { 158 | for (int i = 0; i < sessionStructure.receiverchains_size(); i++) { 159 | textsecure::SessionStructure::Chain *receiverChain = sessionStructure.mutable_receiverchains(i); 160 | if (receiverChain && receiverChain->has_senderratchetkey()) { 161 | ::std::string senderratchetkey = receiverChain->senderratchetkey(); 162 | QByteArray senderratchetkeybytes(senderratchetkey.data(), senderratchetkey.length()); 163 | DjbECPublicKey chainSenderRatchetKey = Curve::decodePoint(senderratchetkeybytes, 0); 164 | if (chainSenderRatchetKey == senderEphemeral) { 165 | return i; 166 | } 167 | else { 168 | 169 | } 170 | } 171 | } 172 | return -1; 173 | } 174 | 175 | ChainKey SessionState::getReceiverChainKey(const DjbECPublicKey &senderEphemeral) 176 | { 177 | int receiverChainIndex = getReceiverChain(senderEphemeral); 178 | 179 | if (receiverChainIndex == -1) { 180 | throw InvalidKeyException("ReceiverChain empty"); 181 | } else { 182 | textsecure::SessionStructure::Chain receiverChain = sessionStructure.receiverchains(receiverChainIndex); 183 | ::std::string keyfromchain = receiverChain.chainkey().key(); 184 | return ChainKey(HKDF(getSessionVersion()), 185 | QByteArray(keyfromchain.data(), keyfromchain.length()), 186 | receiverChain.chainkey().index()); 187 | } 188 | } 189 | 190 | void SessionState::addReceiverChain(const DjbECPublicKey &senderRatchetKey, const ChainKey &chainKey) 191 | { 192 | textsecure::SessionStructure::Chain::ChainKey chainKeyStructure; 193 | QByteArray byteChain = chainKey.getKey(); 194 | chainKeyStructure.set_key(byteChain.constData(), byteChain.size()); 195 | chainKeyStructure.set_index(chainKey.getIndex()); 196 | 197 | QByteArray byteRatchet = senderRatchetKey.serialize(); 198 | 199 | textsecure::SessionStructure::Chain* chain = sessionStructure.add_receiverchains(); 200 | chain->mutable_chainkey()->CopyFrom(chainKeyStructure); 201 | chain->set_senderratchetkey(byteRatchet.constData(), byteRatchet.size()); 202 | 203 | if (sessionStructure.receiverchains_size() > 5) { 204 | delete sessionStructure.mutable_receiverchains(0); 205 | } 206 | } 207 | 208 | void SessionState::setSenderChain(const ECKeyPair &senderRatchetKeyPair, const ChainKey &chainKey) 209 | { 210 | QByteArray serializedPublicKey = senderRatchetKeyPair.getPublicKey().serialize(); 211 | QByteArray serializedPrivateKey = senderRatchetKeyPair.getPrivateKey().serialize(); 212 | QByteArray serializedChainKey = chainKey.getKey(); 213 | 214 | sessionStructure.mutable_senderchain()->set_senderratchetkey(serializedPublicKey.constData(), serializedPublicKey.size()); 215 | sessionStructure.mutable_senderchain()->set_senderratchetkeyprivate(serializedPrivateKey.constData(), serializedPrivateKey.size()); 216 | sessionStructure.mutable_senderchain()->mutable_chainkey()->set_key(serializedChainKey.constData(), serializedChainKey.size()); 217 | sessionStructure.mutable_senderchain()->mutable_chainkey()->set_index(chainKey.getIndex()); 218 | 219 | /*textsecure::SessionStructure::Chain::ChainKey chainKeyStructure; 220 | chainKeyStructure.set_key(chainKey.getKey().constData()); 221 | chainKeyStructure.set_index(chainKey.getIndex()); 222 | 223 | textsecure::SessionStructure::Chain senderChain; 224 | senderChain.set_senderratchetkey(senderRatchetKeyPair.getPublicKey().serialize().constData()); 225 | senderChain.set_senderratchetkeyprivate(senderRatchetKeyPair.getPrivateKey().serialize().constData()); 226 | senderChain.mutable_chainkey()->CopyFrom(chainKeyStructure); 227 | 228 | sessionStructure.mutable_senderchain()->CopyFrom(senderChain);*/ 229 | } 230 | 231 | ChainKey SessionState::getSenderChainKey() const 232 | { 233 | textsecure::SessionStructure::Chain::ChainKey chainKeyStructure = sessionStructure.senderchain().chainkey(); 234 | ::std::string key = chainKeyStructure.key(); 235 | return ChainKey(HKDF(getSessionVersion()), 236 | QByteArray(key.data(), key.length()), 237 | chainKeyStructure.index()); 238 | } 239 | 240 | void SessionState::setSenderChainKey(const ChainKey &nextChainKey) 241 | { 242 | QByteArray serializedChainKey = nextChainKey.getKey(); 243 | 244 | sessionStructure.mutable_senderchain()->mutable_chainkey()->set_key(serializedChainKey.constData(), serializedChainKey.size()); 245 | sessionStructure.mutable_senderchain()->mutable_chainkey()->set_index(nextChainKey.getIndex()); 246 | 247 | /*textsecure::SessionStructure::Chain::ChainKey chainKey; 248 | chainKey.set_key(nextChainKey.getKey().constData()); 249 | chainKey.set_index(nextChainKey.getIndex()); 250 | 251 | textsecure::SessionStructure::Chain chain = sessionStructure.senderchain(); 252 | chain.mutable_chainkey()->CopyFrom(chainKey); 253 | 254 | sessionStructure.mutable_senderchain()->CopyFrom(chain);*/ 255 | } 256 | 257 | bool SessionState::hasMessageKeys(const DjbECPublicKey &senderEphemeral, uint counter) 258 | { 259 | int chainIndex = getReceiverChain(senderEphemeral); 260 | 261 | if (chainIndex == -1) { 262 | return false; 263 | } 264 | 265 | textsecure::SessionStructure::Chain receiverChain = sessionStructure.receiverchains(chainIndex); 266 | for (int i = 0; i < receiverChain.messagekeys_size(); i++) { 267 | textsecure::SessionStructure::Chain::MessageKey messageKey = receiverChain.messagekeys(i); 268 | if (messageKey.index() == counter) { 269 | return true; 270 | } 271 | } 272 | 273 | return false; 274 | } 275 | 276 | MessageKeys SessionState::removeMessageKeys(const DjbECPublicKey &senderEphemeral, uint counter) 277 | { 278 | int chainIndex = getReceiverChain(senderEphemeral); 279 | 280 | if (chainIndex == -1) { 281 | throw InvalidKeyException("ReceiverChain empty"); 282 | } 283 | 284 | textsecure::SessionStructure::Chain chain = sessionStructure.receiverchains(chainIndex); 285 | MessageKeys result; 286 | 287 | for (int i = 0; i < chain.messagekeys_size(); i++) { 288 | textsecure::SessionStructure::Chain::MessageKey *messageKey = chain.mutable_messagekeys(i); 289 | if (messageKey->index() == counter) { 290 | ::std::string cipherkey = messageKey->cipherkey(); 291 | ::std::string mackey = messageKey->mackey(); 292 | ::std::string iv = messageKey->iv(); 293 | result = MessageKeys(QByteArray(cipherkey.data(), cipherkey.length()), 294 | QByteArray(mackey.data(), mackey.length()), 295 | QByteArray(iv.data(), iv.length()), 296 | messageKey->index()); 297 | delete messageKey; 298 | break; 299 | } 300 | } 301 | 302 | textsecure::SessionStructure::Chain *updatedChain = sessionStructure.mutable_receiverchains(chainIndex); 303 | updatedChain->clear_messagekeys(); 304 | updatedChain->CopyFrom(chain); 305 | 306 | return result; 307 | } 308 | 309 | void SessionState::setMessageKeys(const DjbECPublicKey &senderEphemeral, const MessageKeys &messageKeys) 310 | { 311 | int chainIndex = getReceiverChain(senderEphemeral); 312 | 313 | textsecure::SessionStructure::Chain *chain = (chainIndex == -1) ? sessionStructure.add_receiverchains() : sessionStructure.mutable_receiverchains(chainIndex); 314 | textsecure::SessionStructure::Chain::MessageKey *messageKeyStructure = chain->add_messagekeys(); 315 | QByteArray byteCipher = messageKeys.getCipherKey(); 316 | messageKeyStructure->set_cipherkey(byteCipher.constData(), byteCipher.size()); 317 | QByteArray byteMac = messageKeys.getMacKey(); 318 | messageKeyStructure->set_mackey(byteMac.constData(), byteMac.size()); 319 | messageKeyStructure->set_index(messageKeys.getCounter()); 320 | QByteArray byteIv = messageKeys.getIv(); 321 | messageKeyStructure->set_iv(byteIv.constData(), byteIv.size()); 322 | 323 | if (chain->messagekeys_size() > SessionState::MAX_MESSAGE_KEYS) { 324 | chain->mutable_messagekeys()->DeleteSubrange(0, 1); 325 | } 326 | } 327 | 328 | void SessionState::setReceiverChainKey(const DjbECPublicKey &senderEphemeral, const ChainKey &chainKey) 329 | { 330 | int chainIndex = getReceiverChain(senderEphemeral); 331 | textsecure::SessionStructure::Chain *chain = (chainIndex == -1) ? sessionStructure.add_receiverchains() : sessionStructure.mutable_receiverchains(chainIndex); 332 | 333 | QByteArray serializedChainKey = chainKey.getKey(); 334 | 335 | chain->mutable_chainkey()->set_key(serializedChainKey.constData(), serializedChainKey.size()); 336 | chain->mutable_chainkey()->set_index(chainKey.getIndex()); 337 | 338 | /*textsecure::SessionStructure::Chain::ChainKey chainKeyStructure; 339 | chainKeyStructure.set_key(chainKey.getKey().constData()); 340 | chainKeyStructure.set_index(chainKey.getIndex()); 341 | 342 | chain->mutable_chainkey()->CopyFrom(chainKeyStructure);*/ 343 | } 344 | 345 | void SessionState::setPendingKeyExchange(int sequence, const ECKeyPair &ourBaseKey, const ECKeyPair &ourRatchetKey, const IdentityKeyPair &ourIdentityKey) 346 | { 347 | sessionStructure.mutable_pendingkeyexchange()->set_sequence(sequence); 348 | sessionStructure.mutable_pendingkeyexchange()->set_localbasekey(ourBaseKey.getPublicKey().serialize().constData(), 349 | ourBaseKey.getPublicKey().serialize().size()); 350 | sessionStructure.mutable_pendingkeyexchange()->set_localbasekeyprivate(ourBaseKey.getPrivateKey().serialize().constData(), 351 | ourBaseKey.getPrivateKey().serialize().size()); 352 | sessionStructure.mutable_pendingkeyexchange()->set_localratchetkey(ourRatchetKey.getPublicKey().serialize().constData(), 353 | ourRatchetKey.getPublicKey().serialize().size()); 354 | sessionStructure.mutable_pendingkeyexchange()->set_localratchetkeyprivate(ourRatchetKey.getPrivateKey().serialize().constData(), 355 | ourRatchetKey.getPrivateKey().serialize().size()); 356 | sessionStructure.mutable_pendingkeyexchange()->set_localidentitykey(ourIdentityKey.getPublicKey().serialize().constData(), 357 | ourIdentityKey.getPublicKey().serialize().size()); 358 | sessionStructure.mutable_pendingkeyexchange()->set_localidentitykeyprivate(ourIdentityKey.getPrivateKey().serialize().constData(), 359 | ourIdentityKey.getPrivateKey().serialize().size()); 360 | 361 | 362 | /*textsecure::SessionStructure::PendingKeyExchange structure; 363 | structure.set_sequence(sequence); 364 | structure.set_localbasekey(ourBaseKey.getPublicKey().serialize().constData()); 365 | structure.set_localbasekeyprivate(ourBaseKey.getPrivateKey().serialize().constData()); 366 | structure.set_localratchetkey(ourRatchetKey.getPublicKey().serialize().constData()); 367 | structure.set_localratchetkeyprivate(ourRatchetKey.getPrivateKey().serialize().constData()); 368 | structure.set_localidentitykey(ourIdentityKey.getPublicKey().serialize().constData()); 369 | structure.set_localidentitykeyprivate(ourIdentityKey.getPrivateKey().serialize().constData()); 370 | sessionStructure.mutable_pendingkeyexchange()->CopyFrom(structure);*/ 371 | } 372 | 373 | int SessionState::getPendingKeyExchangeSequence() const 374 | { 375 | return sessionStructure.pendingkeyexchange().sequence(); 376 | } 377 | 378 | ECKeyPair SessionState::getPendingKeyExchangeBaseKey() const 379 | { 380 | ::std::string localbasekey = sessionStructure.pendingkeyexchange().localbasekey(); 381 | DjbECPublicKey publicKey = Curve::decodePoint(QByteArray(localbasekey.data(), localbasekey.length()), 0); 382 | ::std::string localbasekeyprivate = sessionStructure.pendingkeyexchange().localbasekeyprivate(); 383 | DjbECPrivateKey privateKey = Curve::decodePrivatePoint(QByteArray(localbasekeyprivate.data(), localbasekeyprivate.length())); 384 | 385 | return ECKeyPair(publicKey, privateKey); 386 | } 387 | 388 | ECKeyPair SessionState::getPendingKeyExchangeRatchetKey() const 389 | { 390 | ::std::string localratchetkey = sessionStructure.pendingkeyexchange().localratchetkey(); 391 | DjbECPublicKey publicKey = Curve::decodePoint(QByteArray(localratchetkey.data(), localratchetkey.length()), 0); 392 | ::std::string localratchetkeyprivate = sessionStructure.pendingkeyexchange().localratchetkeyprivate(); 393 | DjbECPrivateKey privateKey = Curve::decodePrivatePoint(QByteArray(localratchetkeyprivate.data(), localratchetkeyprivate.length())); 394 | 395 | return ECKeyPair(publicKey, privateKey); 396 | } 397 | 398 | IdentityKeyPair SessionState::getPendingKeyExchangeIdentityKey() const 399 | { 400 | IdentityKey publicKey(QByteArray(sessionStructure.pendingkeyexchange().localidentitykey().data(), 401 | sessionStructure.pendingkeyexchange().localidentitykey().length()), 0); 402 | 403 | DjbECPrivateKey privateKey = Curve::decodePrivatePoint(QByteArray(sessionStructure.pendingkeyexchange().localidentitykeyprivate().data(), 404 | sessionStructure.pendingkeyexchange().localidentitykeyprivate().length())); 405 | 406 | return IdentityKeyPair(publicKey, privateKey); 407 | } 408 | 409 | bool SessionState::hasPendingKeyExchange() const 410 | { 411 | return sessionStructure.has_pendingkeyexchange(); 412 | } 413 | 414 | void SessionState::setUnacknowledgedPreKeyMessage(int preKeyId, int signedPreKeyId, const DjbECPublicKey &baseKey) 415 | { 416 | sessionStructure.mutable_pendingprekey()->set_signedprekeyid(signedPreKeyId); 417 | QByteArray byteKey = baseKey.serialize(); 418 | sessionStructure.mutable_pendingprekey()->set_basekey(byteKey.constData(), byteKey.size()); 419 | 420 | if (preKeyId > -1) { 421 | sessionStructure.mutable_pendingprekey()->set_prekeyid(preKeyId); 422 | } 423 | } 424 | 425 | bool SessionState::hasUnacknowledgedPreKeyMessage() const 426 | { 427 | return sessionStructure.has_pendingprekey(); 428 | } 429 | 430 | UnacknowledgedPreKeyMessageItems SessionState::getUnacknowledgedPreKeyMessageItems() const 431 | { 432 | int preKeyId = -1; 433 | 434 | if (sessionStructure.pendingprekey().has_prekeyid()) { 435 | preKeyId = sessionStructure.pendingprekey().prekeyid(); 436 | } 437 | 438 | ::std::string basekey = sessionStructure.pendingprekey().basekey(); 439 | return UnacknowledgedPreKeyMessageItems(preKeyId, 440 | sessionStructure.pendingprekey().signedprekeyid(), 441 | Curve::decodePoint(QByteArray(basekey.data(), 442 | basekey.length()), 0)); 443 | } 444 | 445 | void SessionState::clearUnacknowledgedPreKeyMessage() 446 | { 447 | sessionStructure.clear_pendingprekey(); 448 | } 449 | 450 | void SessionState::setRemoteRegistrationId(int registrationId) 451 | { 452 | sessionStructure.set_remoteregistrationid(registrationId); 453 | } 454 | 455 | int SessionState::getRemoteRegistrationId() const 456 | { 457 | return sessionStructure.remoteregistrationid(); 458 | } 459 | 460 | void SessionState::setLocalRegistrationId(int registrationId) 461 | { 462 | sessionStructure.set_localregistrationid(registrationId); 463 | } 464 | 465 | int SessionState::getLocalRegistrationId() const 466 | { 467 | return sessionStructure.localregistrationid(); 468 | } 469 | 470 | QByteArray SessionState::serialize() const 471 | { 472 | ::std::string serialized = sessionStructure.SerializeAsString(); 473 | return QByteArray(serialized.data(), serialized.length()); 474 | } 475 | -------------------------------------------------------------------------------- /state/sessionstate.h: -------------------------------------------------------------------------------- 1 | #ifndef SESSIONSTATE_H 2 | #define SESSIONSTATE_H 3 | 4 | #include "LocalStorageProtocol.pb.h" 5 | #include "../identitykey.h" 6 | #include "../ratchet/rootkey.h" 7 | #include "../ratchet/chainkey.h" 8 | #include "../identitykeypair.h" 9 | #include "../ecc/djbec.h" 10 | 11 | class UnacknowledgedPreKeyMessageItems 12 | { 13 | public: 14 | UnacknowledgedPreKeyMessageItems(int preKeyId, int signedPreKeyId, const DjbECPublicKey &baseKey); 15 | int getPreKeyId() const; 16 | int getSignedPreKeyId() const; 17 | DjbECPublicKey getBaseKey() const; 18 | 19 | private: 20 | int preKeyId; 21 | int signedPreKeyId; 22 | DjbECPublicKey baseKey; 23 | }; 24 | 25 | class SessionState 26 | { 27 | public: 28 | static const int MAX_MESSAGE_KEYS = 2000; 29 | 30 | SessionState(); 31 | SessionState(const textsecure::SessionStructure &sessionSctucture); 32 | SessionState(const SessionState ©); 33 | 34 | textsecure::SessionStructure getStructure() const; 35 | QByteArray getAliceBaseKey() const; 36 | void setAliceBaseKey(const QByteArray &aliceBaseKey); 37 | void setSessionVersion(int version); 38 | int getSessionVersion() const; 39 | void setRemoteIdentityKey(const IdentityKey &identityKey); 40 | void setLocalIdentityKey(const IdentityKey &identityKey); 41 | bool hasRemoteIdentityKey() const; 42 | IdentityKey getRemoteIdentityKey() const; 43 | IdentityKey getLocalIdentityKey() const; 44 | int getPreviousCounter() const; 45 | void setPreviousCounter(int previousCounter); 46 | RootKey getRootKey() const; 47 | void setRootKey(const RootKey &rootKey); 48 | DjbECPublicKey getSenderRatchetKey() const; 49 | ECKeyPair getSenderRatchetKeyPair() const; 50 | bool hasReceiverChain(const DjbECPublicKey &senderEphemeral); 51 | bool hasSenderChain() const; 52 | int getReceiverChain(const DjbECPublicKey &senderEphemeral); 53 | ChainKey getReceiverChainKey(const DjbECPublicKey &senderEphemeral); 54 | void addReceiverChain(const DjbECPublicKey &senderRatchetKey, const ChainKey &chainKey); 55 | void setSenderChain(const ECKeyPair &senderRatchetKeyPair, const ChainKey &chainKey); 56 | ChainKey getSenderChainKey() const; 57 | void setSenderChainKey(const ChainKey &nextChainKey); 58 | bool hasMessageKeys(const DjbECPublicKey &senderEphemeral, uint counter); 59 | MessageKeys removeMessageKeys(const DjbECPublicKey &senderEphemeral, uint counter); 60 | void setMessageKeys(const DjbECPublicKey &senderEphemeral, const MessageKeys &messageKeys); 61 | void setReceiverChainKey(const DjbECPublicKey &senderEphemeral, const ChainKey &chainKey); 62 | void setPendingKeyExchange(int sequence, 63 | const ECKeyPair &ourBaseKey, 64 | const ECKeyPair &ourRatchetKey, 65 | const IdentityKeyPair &ourIdentityKey); 66 | int getPendingKeyExchangeSequence() const; 67 | ECKeyPair getPendingKeyExchangeBaseKey() const; 68 | ECKeyPair getPendingKeyExchangeRatchetKey() const; 69 | IdentityKeyPair getPendingKeyExchangeIdentityKey() const; 70 | bool hasPendingKeyExchange() const; 71 | void setUnacknowledgedPreKeyMessage(int preKeyId, int signedPreKeyId, const DjbECPublicKey &baseKey); 72 | bool hasUnacknowledgedPreKeyMessage() const; 73 | UnacknowledgedPreKeyMessageItems getUnacknowledgedPreKeyMessageItems() const; 74 | void clearUnacknowledgedPreKeyMessage(); 75 | void setRemoteRegistrationId(int registrationId); 76 | int getRemoteRegistrationId() const; 77 | void setLocalRegistrationId(int registrationId); 78 | int getLocalRegistrationId() const; 79 | QByteArray serialize() const; 80 | 81 | private: 82 | textsecure::SessionStructure sessionStructure; 83 | 84 | }; 85 | 86 | #endif // SESSIONSTATE_H 87 | -------------------------------------------------------------------------------- /state/sessionstore.h: -------------------------------------------------------------------------------- 1 | #ifndef SESSIONSTORE_H 2 | #define SESSIONSTORE_H 3 | 4 | #include "sessionrecord.h" 5 | 6 | #include "../axolotladdress.h" 7 | 8 | class SessionStore 9 | { 10 | public: 11 | virtual SessionRecord *loadSession(const AxolotlAddress &remoteAddress) = 0; 12 | virtual QList getSubDeviceSessions(const QString &name) = 0; 13 | virtual void storeSession(const AxolotlAddress &remoteAddress, SessionRecord *record) = 0; 14 | virtual bool containsSession(const AxolotlAddress &remoteAddressd) = 0; 15 | virtual void deleteSession(const AxolotlAddress &remoteAddress) = 0; 16 | virtual void deleteAllSessions(const QString &name) = 0; 17 | }; 18 | 19 | #endif // SESSIONSTORE_H 20 | -------------------------------------------------------------------------------- /state/signedprekeyrecord.cpp: -------------------------------------------------------------------------------- 1 | #include "signedprekeyrecord.h" 2 | #include "../ecc/curve.h" 3 | 4 | SignedPreKeyRecord::SignedPreKeyRecord(qulonglong id, long timestamp, const ECKeyPair &keyPair, const QByteArray &signature) 5 | { 6 | QByteArray bytePublic = keyPair.getPublicKey().serialize(); 7 | QByteArray bytePrivate = keyPair.getPrivateKey().serialize(); 8 | 9 | structure.set_id(id); 10 | structure.set_publickey(bytePublic.constData(), bytePublic.size()); 11 | structure.set_privatekey(bytePrivate.constData(), bytePrivate.size()); 12 | structure.set_signature(signature.constData(), signature.size()); 13 | structure.set_timestamp(timestamp); 14 | } 15 | 16 | SignedPreKeyRecord::SignedPreKeyRecord(const QByteArray &serialized) 17 | { 18 | structure.ParsePartialFromArray(serialized.constData(), serialized.size()); 19 | } 20 | 21 | qulonglong SignedPreKeyRecord::getId() const 22 | { 23 | return structure.id(); 24 | } 25 | 26 | long SignedPreKeyRecord::getTimestamp() const 27 | { 28 | return structure.timestamp(); 29 | } 30 | 31 | ECKeyPair SignedPreKeyRecord::getKeyPair() const 32 | { 33 | ::std::string publickey = structure.publickey(); 34 | QByteArray publicPoint(publickey.data(), publickey.length()); 35 | DjbECPublicKey publicKey = Curve::decodePoint(publicPoint, 0); 36 | 37 | ::std::string privatekey = structure.privatekey(); 38 | QByteArray privatePoint(privatekey.data(), privatekey.length()); 39 | DjbECPrivateKey privateKey = Curve::decodePrivatePoint(privatePoint); 40 | 41 | return ECKeyPair(publicKey, privateKey); 42 | } 43 | 44 | QByteArray SignedPreKeyRecord::getSignature() const 45 | { 46 | ::std::string signature = structure.signature(); 47 | return QByteArray(signature.data(), signature.length()); 48 | } 49 | 50 | QByteArray SignedPreKeyRecord::serialize() const 51 | { 52 | ::std::string serialized = structure.SerializeAsString(); 53 | return QByteArray(serialized.data(), serialized.length()); 54 | } 55 | -------------------------------------------------------------------------------- /state/signedprekeyrecord.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNEDPREKEYRECORD_H 2 | #define SIGNEDPREKEYRECORD_H 3 | 4 | #include "LocalStorageProtocol.pb.h" 5 | #include "../ecc/eckeypair.h" 6 | 7 | class SignedPreKeyRecord 8 | { 9 | public: 10 | SignedPreKeyRecord(qulonglong id, long timestamp, const ECKeyPair &keyPair, const QByteArray &signature); 11 | SignedPreKeyRecord(const QByteArray &serialized); 12 | 13 | qulonglong getId() const; 14 | long getTimestamp() const; 15 | ECKeyPair getKeyPair() const; 16 | QByteArray getSignature() const; 17 | QByteArray serialize() const; 18 | 19 | private: 20 | textsecure::SignedPreKeyRecordStructure structure; 21 | }; 22 | 23 | #endif // SIGNEDPREKEYRECORD_H 24 | -------------------------------------------------------------------------------- /state/signedprekeystore.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNEDPREKEYSTORE_H 2 | #define SIGNEDPREKEYSTORE_H 3 | 4 | #include "signedprekeyrecord.h" 5 | 6 | class SignedPreKeyStore 7 | { 8 | public: 9 | virtual SignedPreKeyRecord loadSignedPreKey(qulonglong signedPreKeyId) = 0; 10 | virtual QList loadSignedPreKeys() = 0; 11 | virtual void storeSignedPreKey(qulonglong signedPreKeyId, const SignedPreKeyRecord &record) = 0; 12 | virtual bool containsSignedPreKey(qulonglong signedPreKeyId) = 0; 13 | virtual void removeSignedPreKey(qulonglong signedPreKeyId) = 0; 14 | }; 15 | 16 | #endif // SIGNEDPREKEYSTORE_H 17 | -------------------------------------------------------------------------------- /untrustedidentityexception.h: -------------------------------------------------------------------------------- 1 | #ifndef UNTRUSTEDIDENTITYEXCEPTION_H 2 | #define UNTRUSTEDIDENTITYEXCEPTION_H 3 | 4 | #include "whisperexception.h" 5 | 6 | class UntrustedIdentityException : public WhisperException 7 | { 8 | public: 9 | UntrustedIdentityException(const QString &error) : WhisperException("UntrustedIdentityException", error) {} 10 | }; 11 | 12 | #endif // UNTRUSTEDIDENTITYEXCEPTION_H 13 | -------------------------------------------------------------------------------- /util/byteutil.cpp: -------------------------------------------------------------------------------- 1 | #include "byteutil.h" 2 | 3 | ByteUtil::ByteUtil() 4 | { 5 | } 6 | 7 | QByteArray ByteUtil::combine(const QList &items) 8 | { 9 | QByteArray result; 10 | foreach (const QByteArray &item, items) { 11 | result.append(item); 12 | } 13 | return result; 14 | } 15 | 16 | QList ByteUtil::split(const QByteArray &input, int firstLength, int secondLength, int thirdLength) 17 | { 18 | QList result; 19 | result.append(input.mid(0, firstLength)); 20 | result.append(input.mid(firstLength, secondLength)); 21 | if (thirdLength > -1) { 22 | result.append(input.mid(firstLength + secondLength, thirdLength)); 23 | } 24 | return result; 25 | } 26 | 27 | QByteArray ByteUtil::trim(const QByteArray &input, int length) 28 | { 29 | return input.mid(0, length); 30 | } 31 | 32 | qint8 ByteUtil::intsToByteHighAndLow(int highValue, int lowValue) 33 | { 34 | return (highValue << 4 | lowValue) & 0xFF; 35 | } 36 | 37 | int ByteUtil::highBitsToInt(qint8 input) 38 | { 39 | return (input & 0xFF) >> 4; 40 | } 41 | 42 | int ByteUtil::lowBitsToInt(qint8 input) 43 | { 44 | return input & 0xF; 45 | } 46 | 47 | int ByteUtil::intToByteArray(QByteArray &input, int offset, int value) 48 | { 49 | input[offset + 3] = (char)(value % 256); 50 | input[offset + 2] = (char)((value >> 8) % 256); 51 | input[offset + 1] = (char)((value >> 16) % 256); 52 | input[offset] = (char)((value >> 24) % 256); 53 | return 4; 54 | } 55 | -------------------------------------------------------------------------------- /util/byteutil.h: -------------------------------------------------------------------------------- 1 | #ifndef BYTEUTIL_H 2 | #define BYTEUTIL_H 3 | 4 | #include 5 | #include 6 | 7 | class ByteUtil 8 | { 9 | public: 10 | ByteUtil(); 11 | 12 | static QByteArray combine(const QList &items); 13 | static QList split(const QByteArray &input, int firstLength, int secondLength, int thirdLength = -1); 14 | static QByteArray trim(const QByteArray &input, int length); 15 | static qint8 intsToByteHighAndLow(int highValue, int lowValue); 16 | static int highBitsToInt(qint8 input); 17 | static int lowBitsToInt(qint8 input); 18 | static int intToByteArray(QByteArray &input, int offset, int value); 19 | }; 20 | 21 | #endif // BYTEUTIL_H 22 | -------------------------------------------------------------------------------- /util/keyhelper.cpp: -------------------------------------------------------------------------------- 1 | #include "keyhelper.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "../ecc/eckeypair.h" 8 | #include "../ecc/curve.h" 9 | 10 | quint64 KeyHelper::getRandomFFFF() 11 | { 12 | unsigned char buff1[2]; 13 | memset(buff1, 0, 2); 14 | RAND_bytes(buff1, 2); 15 | quint64 rand1 = ((unsigned int)buff1[1] << 8) + (unsigned int)buff1[0]; 16 | 17 | return rand1; 18 | } 19 | 20 | quint64 KeyHelper::getRandom7FFFFFFF() 21 | { 22 | quint64 rand2 = 0; 23 | for (int i = 0; i < 0x80; i++) { 24 | unsigned char buff0[3]; 25 | memset(buff0, 0, 3); 26 | RAND_bytes(buff0, 3); 27 | rand2 += ((unsigned int)buff0[2] << 16); 28 | rand2 += ((unsigned int)buff0[1] << 8); 29 | rand2 += (unsigned int)buff0[0]; 30 | } 31 | 32 | return rand2; 33 | } 34 | 35 | quint64 KeyHelper::getRandomFFFFFFFF() 36 | { 37 | unsigned char buff1[4]; 38 | memset(buff1, 0, 4); 39 | RAND_bytes(buff1, 4); 40 | quint64 rand1 = ((unsigned long)buff1[3] << 24) + ((unsigned int)buff1[2] << 16) 41 | + ((unsigned int)buff1[1] << 8) + (unsigned int)buff1[0]; 42 | 43 | return rand1; 44 | } 45 | 46 | QByteArray KeyHelper::getRandomBytes(int bytes) 47 | { 48 | QByteArray buff1(bytes, '\0'); 49 | RAND_bytes((unsigned char*)buff1.data(), bytes); 50 | return buff1; 51 | } 52 | 53 | IdentityKeyPair KeyHelper::generateIdentityKeyPair() 54 | { 55 | ECKeyPair keyPair = Curve::generateKeyPair(); 56 | IdentityKey publicKey(keyPair.getPublicKey()); 57 | IdentityKeyPair identityKeyPair(publicKey, keyPair.getPrivateKey()); 58 | return identityKeyPair; 59 | } 60 | 61 | quint64 KeyHelper::generateRegistrationId() 62 | { 63 | return getRandomFFFFFFFF(); 64 | } 65 | 66 | QList KeyHelper::generatePreKeys(qulonglong start, uint count) 67 | { 68 | QList results; 69 | for (uint i = 0; i < count; i++) { 70 | qulonglong preKeyId = ((start + i - 1) % (0xFFFFFE)) + 1; 71 | results.append(PreKeyRecord(preKeyId, Curve::generateKeyPair())); 72 | } 73 | return results; 74 | } 75 | 76 | SignedPreKeyRecord KeyHelper::generateSignedPreKey(const IdentityKeyPair &identityKeyPair, qulonglong signedPreKeyId) 77 | { 78 | ECKeyPair keyPair = Curve::generateKeyPair(); 79 | QByteArray signature = Curve::calculateSignature(identityKeyPair.getPrivateKey(), keyPair.getPublicKey().serialize()); 80 | return SignedPreKeyRecord(signedPreKeyId, QDateTime::currentMSecsSinceEpoch(), keyPair, signature); 81 | } 82 | 83 | ECKeyPair KeyHelper::generateSenderSigningKey() 84 | { 85 | return Curve::generateKeyPair(); 86 | } 87 | 88 | QByteArray KeyHelper::generateSenderKey() 89 | { 90 | unsigned char buff1[32]; 91 | memset(buff1, 0, 32); 92 | RAND_bytes(buff1, 32); 93 | 94 | QByteArray key = QByteArray::fromRawData((const char*)buff1, 32); 95 | return key; 96 | } 97 | 98 | unsigned long KeyHelper::generateSenderKeyId() 99 | { 100 | return getRandom7FFFFFFF(); 101 | } 102 | -------------------------------------------------------------------------------- /util/keyhelper.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYHELPER_H 2 | #define KEYHELPER_H 3 | 4 | #include "../identitykeypair.h" 5 | #include "../state/prekeyrecord.h" 6 | #include "../state/signedprekeyrecord.h" 7 | 8 | #include 9 | 10 | class KeyHelper 11 | { 12 | public: 13 | static quint64 getRandomFFFF(); 14 | static quint64 getRandom7FFFFFFF(); 15 | static quint64 getRandomFFFFFFFF(); 16 | static QByteArray getRandomBytes(int bytes); 17 | 18 | static IdentityKeyPair generateIdentityKeyPair(); 19 | static quint64 generateRegistrationId(); 20 | static QList generatePreKeys(qulonglong start, uint count); 21 | static SignedPreKeyRecord generateSignedPreKey(const IdentityKeyPair &identityKeyPair, qulonglong signedPreKeyId); 22 | static ECKeyPair generateSenderSigningKey(); 23 | static QByteArray generateSenderKey(); 24 | static unsigned long generateSenderKeyId(); 25 | }; 26 | 27 | #endif // KEYHELPER_H 28 | -------------------------------------------------------------------------------- /util/medium.h: -------------------------------------------------------------------------------- 1 | #ifndef MEDIUM_H 2 | #define MEDIUM_H 3 | 4 | class Medium { 5 | public: 6 | static const int MAX_VALUE = 0xFFFFFF; 7 | }; 8 | 9 | #endif // MEDIUM_H 10 | -------------------------------------------------------------------------------- /whisperexception.h: -------------------------------------------------------------------------------- 1 | #ifndef WHISPEREXCEPTION_H 2 | #define WHISPEREXCEPTION_H 3 | 4 | #include 5 | #include 6 | 7 | class WhisperException : public QException 8 | { 9 | public: 10 | WhisperException(const QString &type, const QString &error = QString("Unknown error")) throw() { 11 | _error = error; 12 | _type = type; 13 | } 14 | QString errorType() const { 15 | return _type; 16 | } 17 | QString errorMessage() const { 18 | return _error; 19 | } 20 | WhisperException(const WhisperException &source) { 21 | _error = source.errorMessage(); 22 | } 23 | virtual ~WhisperException() throw() {} 24 | 25 | void raise() const { throw *this; } 26 | WhisperException *clone() const { return new WhisperException(*this); } 27 | 28 | protected: 29 | QString _error; 30 | QString _type; 31 | }; 32 | 33 | #endif // WHISPEREXCEPTION_H 34 | --------------------------------------------------------------------------------