python-axolotl-0.2.3/0000755000175000017500000000000013471043614014756 5ustar tarektarek00000000000000python-axolotl-0.2.3/PKG-INFO0000644000175000017500000000175313471043614016061 0ustar tarektarek00000000000000Metadata-Version: 1.1 Name: python-axolotl Version: 0.2.3 Summary: Python port of libaxolotl-android, originally written by Moxie Marlinspik Home-page: https://github.com/tgalal/python-axolotl Author: Tarek Galal Author-email: tare2.galal@gmail.com License: GPLv3 License Download-URL: https://github.com/tgalal/python-axolotl/releases Description: UNKNOWN Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) Classifier: Natural Language :: English Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Security :: Cryptography Classifier: Topic :: Software Development :: Libraries :: Python Modules python-axolotl-0.2.3/README.md0000644000175000017500000000615313471043576016251 0ustar tarektarek00000000000000This is a python port of [libsignal-protocol-java](https://github.com/WhisperSystems/libaxolotl-android) originally written by [Moxie Marlinspike](https://github.com/moxie0) Overview from original author's: > This is a ratcheting forward secrecy protocol that works in synchronous and asynchronous messaging environments. # Installation The library has some dependencies which are automatically pulled and installed if you use the instructions below for your OS - [protobuf 3.0+](https://github.com/google/protobuf/) - [cryptography](https://cryptography.io) - [python-axolotl-curve25519](https://github.com/tgalal/python-axolotl-curve25519) ## Linux You need to have python headers installed, usually through installing a package called `python-dev`, then as superuser run: ``` python setup.py install ``` ## Windows - Install [mingw](http://www.mingw.org/) compiler - Add mingw to your PATH - In PYTHONPATH\Lib\distutils create a file called distutils.cfg and add these lines: ``` [build] compiler=mingw32 ``` - Install gcc: ```mingw-get.exe install gcc``` - Install [zlib](http://www.zlib.net/) - ```python setup.py install``` # Usage This python port is done in an almost 1:1 mapping to the original java code. Therefore any original documentation for the java code can be easily mapped and used with this python port. ## Install time At install time, a libaxolotl client needs to generate its identity keys, registration id, and prekeys. ```python identityKeyPair = KeyHelper.generateIdentityKeyPair() registrationId = KeyHelper.generateRegistrationId() preKeys = KeyHelper.generatePreKeys(startId, 100) lastResortKey = KeyHelper.generateLastResortKey() signedPreKey = KeyHelper.generateSignedPreKey(identityKeyPair, 5) #Store identityKeyPair somewhere durable and safe. #Store registrationId somewhere durable and safe. #Store preKeys in PreKeyStore. #Store signed prekey in SignedPreKeyStore. ``` ## Building a session A libaxolotl client needs to implement four interfaces: IdentityKeyStore, PreKeyStore, SignedPreKeyStore, and SessionStore. These will manage loading and storing of identity, prekeys, signed prekeys, and session state. Once those are implemented, building a session is fairly straightforward: ```python sessionStore = MySessionStore() preKeyStore = MyPreKeyStore() signedPreKeyStore = MySignedPreKeyStore() identityStore = MyIdentityKeyStore() # Instantiate a SessionBuilder for a remote recipientId + deviceId tuple. sessionBuilder = SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, identityStore, recipientId, deviceId) # Build a session with a PreKey retrieved from the server. sessionBuilder.process(retrievedPreKey) sessionCipher = SessionCipher(sessionStore, recipientId, deviceId) message = sessionCipher.encrypt("Hello world!") deliver(message.serialize()) ``` # Examples python-axolotl is actively used in [yowsup](https://github.com/tgalal/yowsup) to support E2E encryption of WhatsApp # License Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html python-axolotl-0.2.3/axolotl/0000755000175000017500000000000013471043614016440 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/__init__.py0000644000175000017500000000023613471043576020561 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- __version__ = '0.2.3' __author__ = 'Tarek Galal' __email__ = 'tare2.galal@gmail.com' __license__ = 'GPLv3' __status__ = 'Production' python-axolotl-0.2.3/axolotl/axolotladdress.py0000644000175000017500000000115513471043576022053 0ustar tarektarek00000000000000class AxolotlAddress(object): def __init__(self, name, deviceId): self.name = name self.deviceId = deviceId def getName(self): return self.name def getDeviceId(self): return self.deviceId def __str__(self): return "%s;%s" % (self.name, self.deviceId) def __eq__(self, other): if other is None: return False if other.__class__ != AxolotlAddress: return False return self.name == other.getName() and self.deviceId == other.getDeviceId() def __hash__(self): return hash(self.name) ^ self.deviceId python-axolotl-0.2.3/axolotl/duplicatemessagexception.py0000644000175000017500000000011713471043576024111 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class DuplicateMessageException(Exception): pass python-axolotl-0.2.3/axolotl/ecc/0000755000175000017500000000000013471043614017172 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/ecc/__init__.py0000644000175000017500000000000013471043576021300 0ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/ecc/curve.py0000644000175000017500000000572013471043576020703 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import os from .eckeypair import ECKeyPair from ..invalidkeyexception import InvalidKeyException import axolotl_curve25519 as _curve class Curve: DJB_TYPE = 5 # always DJB curve25519 keys @staticmethod def generatePrivateKey(): rand = os.urandom(32) return _curve.generatePrivateKey(rand) @staticmethod def generatePublicKey(privateKey): return _curve.generatePublicKey(privateKey) @staticmethod def generateKeyPair(): from .djbec import DjbECPublicKey, DjbECPrivateKey privateKey = Curve.generatePrivateKey() publicKey = Curve.generatePublicKey(privateKey) return ECKeyPair(DjbECPublicKey(publicKey), DjbECPrivateKey(privateKey)) @staticmethod def decodePoint(_bytes, offset=0): type = _bytes[0] # byte appears to be automatically converted to an integer?? if type == Curve.DJB_TYPE: from .djbec import DjbECPublicKey type = _bytes[offset] & 0xFF if type != Curve.DJB_TYPE: raise InvalidKeyException("Unknown key type: %s " % type) keyBytes = _bytes[offset+1:][:32] return DjbECPublicKey(bytes(keyBytes)) else: raise InvalidKeyException("Unknown key type: %s" % type) @staticmethod def decodePrivatePoint(_bytes): from .djbec import DjbECPrivateKey return DjbECPrivateKey(bytes(_bytes)) @staticmethod def calculateAgreement(publicKey, privateKey): """ :type publicKey: ECPublicKey :type privateKey: ECPrivateKey """ if publicKey.getType() != privateKey.getType(): raise InvalidKeyException("Public and private keys must be of the same type!") if publicKey.getType() == Curve.DJB_TYPE: return _curve.calculateAgreement(privateKey.getPrivateKey(), publicKey.getPublicKey()) else: raise InvalidKeyException("Unknown type: %s" % publicKey.getType()) @staticmethod def verifySignature(ecPublicSigningKey, message, signature): """ :type ecPublicSigningKey: ECPublicKey :type message: bytearray :type signature: bytearray """ if ecPublicSigningKey.getType() == Curve.DJB_TYPE: result = _curve.verifySignature(ecPublicSigningKey.getPublicKey(), message, signature) return result == 0 else: raise InvalidKeyException("Unknown type: %s" % ecPublicSigningKey.getType()) @staticmethod def calculateSignature(privateSigningKey, message): """ :type privateSigningKey: ECPrivateKey :type message: bytearray """ if privateSigningKey.getType() == Curve.DJB_TYPE: rand = os.urandom(64) res = _curve.calculateSignature(rand, privateSigningKey.getPrivateKey(), message) return res else: raise InvalidKeyException("Unknown type: %s" % privateSigningKey.getType()) python-axolotl-0.2.3/axolotl/ecc/djbec.py0000644000175000017500000000271713471043576020631 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import struct from .ec import ECPublicKey, ECPrivateKey from ..util.byteutil import ByteUtil class DjbECPublicKey(ECPublicKey): def __init__(self, publicKey): self.publicKey = publicKey def serialize(self): from .curve import Curve combined = ByteUtil.combine([Curve.DJB_TYPE], self.publicKey) return bytes(combined) def getType(self): from .curve import Curve return Curve.DJB_TYPE def getPublicKey(self): return self.publicKey def __eq__(self, other): return self.publicKey == other.getPublicKey() def __lt__(self, other): myVal = struct.unpack(">8i", self.publicKey) otherVal = struct.unpack(">8i", other.getPublicKey()) return myVal < otherVal def __cmp__(self, other): myVal = struct.unpack(">8i", self.publicKey) otherVal = struct.unpack(">8i", other.getPublicKey()) if myVal < otherVal: return -1 elif myVal == otherVal: return 0 else: return 1 class DjbECPrivateKey(ECPrivateKey): def __init__(self, privateKey): self.privateKey = privateKey def getType(self): from .curve import Curve return Curve.DJB_TYPE def getPrivateKey(self): return self.privateKey def serialize(self): return self.privateKey def __eq__(self, other): return self.privateKey == other.getPrivateKey() python-axolotl-0.2.3/axolotl/ecc/ec.py0000644000175000017500000000065213471043576020145 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import abc class ECPublicKey(object): __metaclass__ = abc.ABCMeta KEY_SIZE = 33 @abc.abstractmethod def serialize(self): pass @abc.abstractmethod def getType(self): pass class ECPrivateKey(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def serialize(self): pass @abc.abstractmethod def getType(self): pass python-axolotl-0.2.3/axolotl/ecc/eckeypair.py0000644000175000017500000000060013471043576021523 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class ECKeyPair(): def __init__(self, publicKey, privateKey): """ :type publicKey: ECPublicKey :type privateKey: ECPrivateKey """ self.publicKey = publicKey self.privateKey = privateKey def getPrivateKey(self): return self.privateKey def getPublicKey(self): return self.publicKey python-axolotl-0.2.3/axolotl/groups/0000755000175000017500000000000013471043614017757 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/groups/__init__.py0000644000175000017500000000003013471043576022070 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- python-axolotl-0.2.3/axolotl/groups/groupcipher.py0000644000175000017500000001042613471043576022672 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..invalidkeyidexception import InvalidKeyIdException from ..invalidkeyexception import InvalidKeyException from ..invalidmessageexception import InvalidMessageException from ..duplicatemessagexception import DuplicateMessageException from ..nosessionexception import NoSessionException from ..protocol.senderkeymessage import SenderKeyMessage from ..sessioncipher import AESCipher from ..groups.state.senderkeystore import SenderKeyStore class GroupCipher: def __init__(self, senderKeyStore, senderKeyName): """ :type senderKeyStore: SenderKeyStore :type senderKeyName: SenderKeyName """ self.senderKeyStore = senderKeyStore self.senderKeyName = senderKeyName def encrypt(self, paddedPlaintext): """ :type paddedPlaintext: bytes """ try: record = self.senderKeyStore.loadSenderKey(self.senderKeyName) senderKeyState = record.getSenderKeyState() senderKey = senderKeyState.getSenderChainKey().getSenderMessageKey() ciphertext = self.getCipherText(senderKey.getIv(), senderKey.getCipherKey(), paddedPlaintext) senderKeyMessage = SenderKeyMessage(senderKeyState.getKeyId(), senderKey.getIteration(), ciphertext, senderKeyState.getSigningKeyPrivate()) senderKeyState.setSenderChainKey(senderKeyState.getSenderChainKey().getNext()) self.senderKeyStore.storeSenderKey(self.senderKeyName, record) return senderKeyMessage.serialize() except InvalidKeyIdException as e: raise NoSessionException(e) def decrypt(self, senderKeyMessageBytes): """ :type senderKeyMessageBytes: bytearray """ try: record = self.senderKeyStore.loadSenderKey(self.senderKeyName) if record.isEmpty(): raise NoSessionException("No sender key for: %s" % self.senderKeyName) senderKeyMessage = SenderKeyMessage(serialized = bytes(senderKeyMessageBytes)) senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()) senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()) senderKey = self.getSenderKey(senderKeyState, senderKeyMessage.getIteration()) plaintext = self.getPlainText(senderKey.getIv(), senderKey.getCipherKey(), senderKeyMessage.getCipherText()) self.senderKeyStore.storeSenderKey(self.senderKeyName, record) return plaintext except (InvalidKeyException, InvalidKeyIdException) as e: raise InvalidMessageException(e) def getSenderKey(self, senderKeyState, iteration): senderChainKey = senderKeyState.getSenderChainKey() if senderChainKey.getIteration() > iteration: if senderKeyState.hasSenderMessageKey(iteration): return senderKeyState.removeSenderMessageKey(iteration) else: raise DuplicateMessageException("Received message with old counter: %s, %s" % (senderChainKey.getIteration(), iteration)) if senderChainKey.getIteration() - iteration > 2000: raise InvalidMessageException("Over 2000 messages into the future!") while senderChainKey.getIteration() < iteration: senderKeyState.addSenderMessageKey(senderChainKey.getSenderMessageKey()) senderChainKey = senderChainKey.getNext() senderKeyState.setSenderChainKey(senderChainKey.getNext()) return senderChainKey.getSenderMessageKey() def getPlainText(self, iv, key, ciphertext): """ :type iv: bytearray :type key: bytearray :type ciphertext: bytearray """ try: cipher = AESCipher(key, iv) plaintext = cipher.decrypt(ciphertext) return plaintext except Exception as e: raise InvalidMessageException(e) def getCipherText(self, iv, key, plaintext): """ :type iv: bytearray :type key: bytearray :type plaintext: bytearray """ cipher = AESCipher(key, iv) return cipher.encrypt(plaintext) python-axolotl-0.2.3/axolotl/groups/groupsessionbuilder.py0000644000175000017500000000420113471043576024444 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..protocol.senderkeydistributionmessage import SenderKeyDistributionMessage from ..invalidkeyidexception import InvalidKeyIdException from ..invalidkeyexception import InvalidKeyException from ..util.keyhelper import KeyHelper class GroupSessionBuilder: def __init__(self, senderKeyStore): self.senderKeyStore = senderKeyStore def process(self, senderKeyName, senderKeyDistributionMessage): """ :type senderKeyName: SenderKeyName :type senderKeyDistributionMessage: SenderKeyDistributionMessage """ senderKeyRecord = self.senderKeyStore.loadSenderKey(senderKeyName) senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(), senderKeyDistributionMessage.getIteration(), senderKeyDistributionMessage.getChainKey(), senderKeyDistributionMessage.getSignatureKey()) self.senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord) def create(self, senderKeyName): """ :type senderKeyName: SenderKeyName """ try: senderKeyRecord = self.senderKeyStore.loadSenderKey(senderKeyName); if senderKeyRecord.isEmpty() : senderKeyRecord.setSenderKeyState(KeyHelper.generateSenderKeyId(), 0, KeyHelper.generateSenderKey(), KeyHelper.generateSenderSigningKey()); self.senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); state = senderKeyRecord.getSenderKeyState(); return SenderKeyDistributionMessage(state.getKeyId(), state.getSenderChainKey().getIteration(), state.getSenderChainKey().getSeed(), state.getSigningKeyPublic()); except (InvalidKeyException, InvalidKeyIdException) as e: raise AssertionError(e) python-axolotl-0.2.3/axolotl/groups/ratchet/0000755000175000017500000000000013471043614021411 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/groups/ratchet/__init__.py0000644000175000017500000000003013471043576023522 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- python-axolotl-0.2.3/axolotl/groups/ratchet/senderchainkey.py0000644000175000017500000000167013471043576024772 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import hashlib import hmac from .sendermessagekey import SenderMessageKey class SenderChainKey: MESSAGE_KEY_SEED = bytearray([0x01]) CHAIN_KEY_SEED = bytearray([0x02]) def __init__(self, iteration, chainKey): """ :type iteration: int :type chainKey: bytearray """ self.iteration = iteration self.chainKey = chainKey def getIteration(self): return self.iteration def getSenderMessageKey(self): return SenderMessageKey(self.iteration, self.getDerivative(self.__class__.MESSAGE_KEY_SEED, self.chainKey)) def getNext(self): return SenderChainKey(self.iteration + 1, self.getDerivative(self.__class__.CHAIN_KEY_SEED, self.chainKey)) def getSeed(self): return self.chainKey def getDerivative(self, seed, key): mac = hmac.new(bytes(key), bytes(seed), digestmod=hashlib.sha256) return mac.digest() python-axolotl-0.2.3/axolotl/groups/ratchet/sendermessagekey.py0000644000175000017500000000130613471043576025330 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ...kdf.hkdfv3 import HKDFv3 from ...util.byteutil import ByteUtil class SenderMessageKey: def __init__(self, iteration, seed): """ :type iteration: int :type seed: bytearray """ derivative = HKDFv3().deriveSecrets(seed, "WhisperGroup".encode(), 48) parts = ByteUtil.split(derivative, 16, 32) self.iteration = iteration self.seed = seed self.iv = parts[0] self.cipherKey = parts[1] def getIteration(self): return self.iteration def getIv(self): return self.iv def getCipherKey(self): return self.cipherKey def getSeed(self): return self.seed python-axolotl-0.2.3/axolotl/groups/senderkeyname.py0000644000175000017500000000125113471043576023171 0ustar tarektarek00000000000000class SenderKeyName(object): def __init__(self, groupId, senderAxolotlAddress): self.groupId = groupId self.sender = senderAxolotlAddress def getGroupId(self): return self.groupId def getSender(self): return self.sender def serialize(self): return "%s::%s::%s" % (self.groupId, self.sender.getName(), self.sender.getDeviceId()) def __eq__(self, other): if other is None: return False if other.__class__ != SenderKeyName: return False return self.groupId == other.getGroupId() and self.sender == other.getSender() def __hash__(self): return hash(self.groupId) ^ hash(self.sender) python-axolotl-0.2.3/axolotl/groups/state/0000755000175000017500000000000013471043614021077 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/groups/state/__init__.py0000644000175000017500000000003013471043576023210 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- python-axolotl-0.2.3/axolotl/groups/state/senderkeyrecord.py0000644000175000017500000000404413471043576024652 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ...state.storageprotos_pb2 import SenderKeyRecordStructure from .senderkeystate import SenderKeyState from ...invalidkeyidexception import InvalidKeyIdException class SenderKeyRecord: def __init__(self, serialized=None): self.senderKeyStates = [] if serialized: senderKeyRecordStructure = SenderKeyRecordStructure() senderKeyRecordStructure.ParseFromString(serialized) for structure in senderKeyRecordStructure.senderKeyStates: self.senderKeyStates.append(SenderKeyState(senderKeyStateStructure=structure)) def isEmpty(self): return len(self.senderKeyStates) == 0 def getSenderKeyState(self, keyId=None): if keyId is None: if len(self.senderKeyStates): return self.senderKeyStates[0] else: raise InvalidKeyIdException("No key state in record") else: for state in self.senderKeyStates: if state.getKeyId() == keyId: return state raise InvalidKeyIdException("No keys for: %s" % keyId) def addSenderKeyState(self, id, iteration, chainKey, signatureKey): """ :type id: int :type iteration: int :type chainKey: bytearray :type signatureKey: ECPublicKey """ self.senderKeyStates.append(SenderKeyState(id, iteration, chainKey, signatureKey)) def setSenderKeyState(self, id, iteration, chainKey, signatureKey): """ :type id: int :type iteration: int :type chainKey: bytearray :type signatureKey: ECKeyPair """ del self.senderKeyStates[:] self.senderKeyStates.append(SenderKeyState(id, iteration, chainKey, signatureKeyPair=signatureKey)) def serialize(self): recordStructure = SenderKeyRecordStructure() for senderKeyState in self.senderKeyStates: recordStructure.senderKeyStates.extend([senderKeyState.getStructure()]) return recordStructure.SerializeToString()python-axolotl-0.2.3/axolotl/groups/state/senderkeystate.py0000644000175000017500000000764113471043576024522 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ...state import storageprotos_pb2 as storageprotos from ..ratchet.senderchainkey import SenderChainKey from ..ratchet.sendermessagekey import SenderMessageKey from ...ecc.curve import Curve class SenderKeyState: def __init__(self, id=None, iteration=None, chainKey=None, signatureKeyPublic=None, signatureKeyPrivate=None, signatureKeyPair=None, senderKeyStateStructure=None): """ :type id: int :type iteration: int :type chainKey: bytearray :type signatureKeyPublic: ECPublicKey :type signatureKeyPrivate: ECPrivateKey :type signatureKeyPair: ECKeyPair :type senderKeyStateStructure: SenderKeyStateStructure """ assert (bool(id) and bool(iteration) and bool(chainKey)) or \ (bool(senderKeyStateStructure) ^ bool(signatureKeyPublic or signatureKeyPair)) or \ (bool(signatureKeyPublic) ^ bool(signatureKeyPair)), "Missing required arguments" if senderKeyStateStructure: self.senderKeyStateStructure = senderKeyStateStructure else: if signatureKeyPair: signatureKeyPublic = signatureKeyPair.getPublicKey() signatureKeyPrivate = signatureKeyPair.getPrivateKey() self.senderKeyStateStructure = storageprotos.SenderKeyStateStructure() senderChainKeyStructure = self.senderKeyStateStructure.SenderChainKey() senderChainKeyStructure.iteration = iteration senderChainKeyStructure.seed = chainKey self.senderKeyStateStructure.senderChainKey.MergeFrom(senderChainKeyStructure) signingKeyStructure = self.senderKeyStateStructure.SenderSigningKey() signingKeyStructure.public = signatureKeyPublic.serialize() if signatureKeyPrivate: signingKeyStructure.private = signatureKeyPrivate.serialize() self.senderKeyStateStructure.senderKeyId = id self.senderChainKey = senderChainKeyStructure self.senderKeyStateStructure.senderSigningKey.CopyFrom(signingKeyStructure) def getKeyId(self): return self.senderKeyStateStructure.senderKeyId def getSenderChainKey(self): return SenderChainKey(self.senderKeyStateStructure.senderChainKey.iteration, bytearray(self.senderKeyStateStructure.senderChainKey.seed)) def setSenderChainKey(self, chainKey): self.senderKeyStateStructure.senderChainKey.iteration = chainKey.getIteration() self.senderKeyStateStructure.senderChainKey.seed = chainKey.getSeed() def getSigningKeyPublic(self): return Curve.decodePoint(bytearray(self.senderKeyStateStructure.senderSigningKey.public), 0) def getSigningKeyPrivate(self): return Curve.decodePrivatePoint(self.senderKeyStateStructure.senderSigningKey.private) def hasSenderMessageKey(self, iteration): for senderMessageKey in self.senderKeyStateStructure.senderMessageKeys: if senderMessageKey.iteration == iteration: return True return False def addSenderMessageKey(self, senderMessageKey): smk = self.senderKeyStateStructure.SenderMessageKey() smk.iteration = senderMessageKey.iteration smk.seed = senderMessageKey.seed self.senderKeyStateStructure.senderMessageKeys.extend([smk]) def removeSenderMessageKey(self, iteration): keys = self.senderKeyStateStructure.senderMessageKeys result = None for i in range(0, len(keys)): senderMessageKey = keys[i] if senderMessageKey.iteration == iteration: result = senderMessageKey del keys[i] break if result is not None: return SenderMessageKey(result.iteration, bytearray(result.seed)) return None def getStructure(self): return self.senderKeyStateStructure python-axolotl-0.2.3/axolotl/groups/state/senderkeystore.py0000644000175000017500000000063113471043576024526 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import abc class SenderKeyStore(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def storeSenderKey(self, senderKeyId, senderKeyRecord): """ :type senderKeyId: str :type senderKeyRecord: SenderKeyRecord """ @abc.abstractmethod def loadSenderKey(self, senderKeyId): """ :type senderKeyId: str """ python-axolotl-0.2.3/axolotl/identitykey.py0000644000175000017500000000117413471043576021366 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from .ecc.curve import Curve class IdentityKey: def __init__(self, ecPubKeyOrBytes, offset=None): if offset is None: self.publicKey = ecPubKeyOrBytes else: self.publicKey = Curve.decodePoint(bytearray(ecPubKeyOrBytes), offset) def getPublicKey(self): return self.publicKey def serialize(self): return self.publicKey.serialize() def get_fingerprint(self): raise Exception("IMPL ME") def __eq__(self, other): return self.publicKey == other.getPublicKey() def hashCode(self): raise Exception("IMPL ME") python-axolotl-0.2.3/axolotl/identitykeypair.py0000644000175000017500000000177513471043576022251 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from .state.storageprotos_pb2 import IdentityKeyPairStructure from .identitykey import IdentityKey from .ecc.curve import Curve class IdentityKeyPair: def __init__(self, identityKeyPublicKey=None, ecPrivateKey=None, serialized=None): if serialized: structure = IdentityKeyPairStructure() structure.ParseFromString(serialized) self.publicKey = IdentityKey(bytearray(structure.publicKey), offset=0) self.privateKey = Curve.decodePrivatePoint(bytearray(structure.privateKey)) else: self.publicKey = identityKeyPublicKey self.privateKey = ecPrivateKey def getPublicKey(self): return self.publicKey def getPrivateKey(self): return self.privateKey def serialize(self): structure = IdentityKeyPairStructure() structure.publicKey = self.publicKey.serialize() structure.privateKey = self.privateKey.serialize() return structure.SerializeToString() python-axolotl-0.2.3/axolotl/invalidkeyexception.py0000644000175000017500000000011113471043576023070 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class InvalidKeyException(Exception): pass python-axolotl-0.2.3/axolotl/invalidkeyidexception.py0000644000175000017500000000011313471043576023407 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class InvalidKeyIdException(Exception): pass python-axolotl-0.2.3/axolotl/invalidmessageexception.py0000644000175000017500000000036613471043576023740 0ustar tarektarek00000000000000# -*- cofing: utf-8 -*- class InvalidMessageException(Exception): def __init__(self, message, exceptions=None): if exceptions: message += str(exceptions[0]) super(InvalidMessageException, self).__init__(message) python-axolotl-0.2.3/axolotl/invalidversionexception.py0000644000175000017500000000011513471043576023771 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class InvalidVersionException(Exception): pass python-axolotl-0.2.3/axolotl/kdf/0000755000175000017500000000000013471043614017204 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/kdf/__init__.py0000644000175000017500000000005613471043576021325 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- __author__ = 'tarek' python-axolotl-0.2.3/axolotl/kdf/derivedmessagesecrets.py0000644000175000017500000000125613471043576024151 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..util.byteutil import ByteUtil class DerivedMessageSecrets: SIZE = 80 CIPHER_KEY_LENGTH = 32 MAC_KEY_LENGTH = 32 IV_LENGTH = 16 def __init__(self, okm): keys = ByteUtil.split(okm, self.__class__.CIPHER_KEY_LENGTH, self.__class__.MAC_KEY_LENGTH, self.__class__.IV_LENGTH) self.cipherKey = keys[0] # AES self.macKey = keys[1] # sha256 self.iv = keys[2] def getCipherKey(self): return self.cipherKey def getMacKey(self): return self.macKey def getIv(self): return self.iv python-axolotl-0.2.3/axolotl/kdf/derivedrootsecrets.py0000644000175000017500000000054013471043576023503 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..util.byteutil import ByteUtil class DerivedRootSecrets: SIZE = 64 def __init__(self, okm): keys = ByteUtil.split(okm, 32, 32) self.rootKey = keys[0] self.chainKey = keys[1] def getRootKey(self): return self.rootKey def getChainKey(self): return self.chainKey python-axolotl-0.2.3/axolotl/kdf/hkdf.py0000644000175000017500000000345513471043576020510 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import abc import hmac import hashlib import math class HKDF(object): __metaclass__ = abc.ABCMeta HASH_OUTPUT_SIZE = 32 @staticmethod def createFor(messageVersion): from .hkdfv2 import HKDFv2 from .hkdfv3 import HKDFv3 if messageVersion == 2: return HKDFv2() elif messageVersion == 3: return HKDFv3() else: raise AssertionError("Unknown version: %s " % messageVersion) def deriveSecrets(self, inputKeyMaterial, info, outputLength, salt=None): salt = salt or bytearray(self.__class__.HASH_OUTPUT_SIZE) prk = self.extract(salt, inputKeyMaterial) return self.expand(prk, info, outputLength) def extract(self, salt, inputKeyMaterial): mac = hmac.new(bytes(salt), digestmod=hashlib.sha256) mac.update(bytes(inputKeyMaterial)) return mac.digest() def expand(self, prk, info, outputSize): iterations = int(math.ceil(float(outputSize) / float(self.__class__.HASH_OUTPUT_SIZE))) mixin = bytearray() results = bytearray() remainingBytes = outputSize for i in range(self.getIterationStartOffset(), iterations + self.getIterationStartOffset()): mac = hmac.new(prk, digestmod=hashlib.sha256) mac.update(bytes(mixin)) if info is not None: mac.update(bytes(info)) updateChr = chr(i % 256) mac.update(updateChr.encode()) stepResult = mac.digest() stepSize = min(remainingBytes, len(stepResult)) results.extend(stepResult[:stepSize]) mixin = stepResult remainingBytes -= stepSize return bytes(results) @abc.abstractmethod def getIterationStartOffset(self): return 0 python-axolotl-0.2.3/axolotl/kdf/hkdfv2.py0000644000175000017500000000017613471043576020755 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from .hkdf import HKDF class HKDFv2(HKDF): def getIterationStartOffset(self): return 0 python-axolotl-0.2.3/axolotl/kdf/hkdfv3.py0000644000175000017500000000017713471043576020757 0ustar tarektarek00000000000000# -*- codsing: utf-8 -*- from .hkdf import HKDF class HKDFv3(HKDF): def getIterationStartOffset(self): return 1 python-axolotl-0.2.3/axolotl/kdf/messagekeys.py0000644000175000017500000000065513471043576022113 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class MessageKeys: def __init__(self, cipherKey, macKey, iv, counter): self.cipherKey = cipherKey self.macKey = macKey self.iv = iv self.counter = counter def getCipherKey(self): return self.cipherKey def getMacKey(self): return self.macKey def getIv(self): return self.iv def getCounter(self): return self.counter python-axolotl-0.2.3/axolotl/legacymessageexception.py0000644000175000017500000000011413471043576023545 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class LegacyMessageException(Exception): pass python-axolotl-0.2.3/axolotl/nosessionexception.py0000644000175000017500000000011013471043576022750 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class NoSessionException(Exception): pass python-axolotl-0.2.3/axolotl/protocol/0000755000175000017500000000000013471043614020301 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/protocol/__init__.py0000644000175000017500000000005613471043576022422 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- __author__ = 'tarek' python-axolotl-0.2.3/axolotl/protocol/ciphertextmessage.py0000644000175000017500000000102013471043576024377 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import abc class CiphertextMessage(object): __metaclass__ = abc.ABCMeta UNSUPPORTED_VERSION = 1 CURRENT_VERSION = 3 WHISPER_TYPE = 2 PREKEY_TYPE = 3 SENDERKEY_TYPE = 4 SENDERKEY_DISTRIBUTION_TYPE = 5 # This should be the worst case (worse than V2). So not always accurate, but good enough for padding. ENCRYPTED_MESSAGE_OVERHEAD = 53 @abc.abstractmethod def serialize(self): return @abc.abstractmethod def getType(self): return python-axolotl-0.2.3/axolotl/protocol/keyexchangemessage.py0000644000175000017500000001070113471043576024521 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from .ciphertextmessage import CiphertextMessage from ..util.byteutil import ByteUtil from . import whisperprotos_pb2 as whisperprotos from ..legacymessageexception import LegacyMessageException from ..invalidversionexception import InvalidVersionException from ..invalidmessageexception import InvalidMessageException from ..invalidkeyexception import InvalidKeyException from ..ecc.curve import Curve from ..identitykey import IdentityKey class KeyExchangeMessage: INITIATE_FLAG = 0x01 RESPONSE_FLAG = 0X02 SIMULTAENOUS_INITIATE_FLAG = 0x04 def __init__(self, messageVersion=None, sequence=None, flags=None, baseKey=None, baseKeySignature=None, ratchetKey=None, identityKey=None, serialized=None): """ :type messageVersion: int :type sequence: int :type flags:int :type baseKey: ECPublicKey :type baseKeySignature: bytearray :type ratchetKey: ECPublicKey :type identityKey: IdentityKey :type serialized: bytearray """ if serialized: try: parts = ByteUtil.split(serialized, 1, len(serialized) - 1) self.version = ByteUtil.highBitsToInt(parts[0][0]) self.supportedVersion = ByteUtil.lowBitsToInt(parts[0][0]) if self.version < CiphertextMessage.CURRENT_VERSION: raise LegacyMessageException("Unsupportmessageed legacy version: %s" % self.version) if self.version > CiphertextMessage.CURRENT_VERSION: raise InvalidVersionException("Unkown version: %s" % self.version) message = whisperprotos.KeyExchangeMessage() message.ParseFromString(bytes(parts[1])) if (not message.HasField("id") or not message.HasField("baseKey") or not message.HasField("ratchetKey") or not message.HasField("identityKey") or not message.HasField("baseKeySignature")): raise InvalidMessageException("Some required fields are missing!") self.sequence = message.id >> 5 self.flags = message.id & 0x1f self.serialized = serialized self.baseKey = Curve.decodePoint(bytearray(message.baseKey), 0) self.baseKeySignature = message.baseKeySignature self.ratchetKey = Curve.decodePoint(bytearray(message.ratchetKey), 0) self.identityKey = IdentityKey(message.identityKey, 0) except InvalidKeyException as e: raise InvalidMessageException(e) else: self.supportedVersion = CiphertextMessage.CURRENT_VERSION self.version = messageVersion self.sequence = sequence self.flags = flags self.baseKey = baseKey self.baseKeySignature = baseKeySignature self.ratchetKey = ratchetKey self.identityKey = identityKey version = [ByteUtil.intsToByteHighAndLow(self.version, self.supportedVersion)] keyExchangeMessage = whisperprotos.KeyExchangeMessage() keyExchangeMessage.id = (self.sequence << 5) | self.flags keyExchangeMessage.baseKey = baseKey.serialize() keyExchangeMessage.ratchetKey = ratchetKey.serialize() keyExchangeMessage.identityKey = identityKey.serialize() if messageVersion >= 3: keyExchangeMessage.baseKeySignature = baseKeySignature self.serialized = ByteUtil.combine(version, keyExchangeMessage.SerializeToString()) def getVersion(self): return self.version def getBaseKey(self): return self.baseKey def getBaseKeySignature(self): return self.baseKeySignature def getRatchetKey(self): return self.ratchetKey def getIdentityKey(self): return self.identityKey def hasIdentityKey(self): return True def getMaxVersion(self): return self.supportedVersion def isResponse(self): return ((self.flags & self.__class__.RESPONSE_FLAG) != 0) def isInitiate(self): return (self.flags & self.__class__.INITIATE_FLAG) != 0 def isResponseForSimultaneousInitiate(self): return (self.flags & self.__class__.SIMULTAENOUS_INITIATE_FLAG) != 0 def getFlags(self): return self.flags def getSequence(self): return self.sequence def serialize(self): return self.serialized python-axolotl-0.2.3/axolotl/protocol/prekeywhispermessage.py0000644000175000017500000000776213471043576025144 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from google.protobuf.message import DecodeError from .ciphertextmessage import CiphertextMessage from ..util.byteutil import ByteUtil from ..ecc.curve import Curve from ..identitykey import IdentityKey from .whispermessage import WhisperMessage from ..invalidversionexception import InvalidVersionException from ..invalidmessageexception import InvalidMessageException from ..legacymessageexception import LegacyMessageException from ..invalidkeyexception import InvalidKeyException from . import whisperprotos_pb2 as whisperprotos class PreKeyWhisperMessage(CiphertextMessage): def __init__(self, messageVersion=None, registrationId=None, preKeyId=None, signedPreKeyId=None, ecPublicBaseKey=None, identityKey=None, whisperMessage=None, serialized=None): if serialized: try: self.version = ByteUtil.highBitsToInt(serialized[0]) if self.version > CiphertextMessage.CURRENT_VERSION: raise InvalidVersionException("Unknown version %s" % self.version) if self.version < CiphertextMessage.CURRENT_VERSION: raise LegacyMessageException("Legacy version: %s" % self.version) preKeyWhisperMessage = whisperprotos.PreKeyWhisperMessage() preKeyWhisperMessage.ParseFromString(serialized[1:]) if preKeyWhisperMessage.signedPreKeyId is None or \ not preKeyWhisperMessage.baseKey or \ not preKeyWhisperMessage.identityKey or \ not preKeyWhisperMessage.message: raise InvalidMessageException("Incomplete message") self.serialized = serialized self.registrationId = preKeyWhisperMessage.registrationId self.preKeyId = preKeyWhisperMessage.preKeyId if preKeyWhisperMessage.signedPreKeyId is not None: self.signedPreKeyId = preKeyWhisperMessage.signedPreKeyId else: self.signedPreKeyId = -1 self.baseKey = Curve.decodePoint(bytearray(preKeyWhisperMessage.baseKey), 0) self.identityKey = IdentityKey(Curve.decodePoint(bytearray(preKeyWhisperMessage.identityKey), 0)) self.message = WhisperMessage(serialized=preKeyWhisperMessage.message) except (InvalidKeyException, LegacyMessageException, DecodeError) as e: raise InvalidMessageException(e) else: self.version = messageVersion self.registrationId = registrationId self.preKeyId = preKeyId self.signedPreKeyId = signedPreKeyId self.baseKey = ecPublicBaseKey self.identityKey = identityKey self.message = whisperMessage builder = whisperprotos.PreKeyWhisperMessage() builder.signedPreKeyId = signedPreKeyId builder.baseKey = ecPublicBaseKey.serialize() builder.identityKey = identityKey.serialize() builder.message = whisperMessage.serialize() builder.registrationId = registrationId if preKeyId is not None: builder.preKeyId = preKeyId versionBytes = ByteUtil.intsToByteHighAndLow(self.version, self.__class__.CURRENT_VERSION) messageBytes = builder.SerializeToString() self.serialized = bytes(ByteUtil.combine(versionBytes, messageBytes)) def getMessageVersion(self): return self.version def getIdentityKey(self): return self.identityKey def getRegistrationId(self): return self.registrationId def getPreKeyId(self): return self.preKeyId def getSignedPreKeyId(self): return self.signedPreKeyId def getBaseKey(self): return self.baseKey def getWhisperMessage(self): return self.message def serialize(self): return self.serialized def getType(self): return CiphertextMessage.PREKEY_TYPE python-axolotl-0.2.3/axolotl/protocol/senderkeydistributionmessage.py0000644000175000017500000000632613471043576026667 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from .ciphertextmessage import CiphertextMessage from . import whisperprotos_pb2 as whisperprotos from ..util.byteutil import ByteUtil from ..legacymessageexception import LegacyMessageException from ..invalidmessageexception import InvalidMessageException from ..invalidkeyexception import InvalidKeyException from ..ecc.curve import Curve class SenderKeyDistributionMessage(CiphertextMessage): def __init__(self, id=None, iteration=None, chainKey=None, signatureKey=None, serialized=None): """ :type id: int :type iteration: int :type chainKey: bytearray :type signatureKey: ECPublicKey """ assert bool(id is not None and iteration is not None and chainKey is not None and signatureKey is not None)\ ^ bool(serialized),\ "Either pass arguments or serialized data" if serialized: try: messageParts = ByteUtil.split(serialized, 1, len(serialized)- 1) version = messageParts[0][0] message = messageParts[1] if ByteUtil.highBitsToInt(version) < 3: raise LegacyMessageException("Legacy message: %s" % ByteUtil.highBitsToInt(version)) if ByteUtil.highBitsToInt(version) > self.__class__.CURRENT_VERSION: raise InvalidMessageException("Unknown version: %s" % ByteUtil.highBitsToInt(version)) distributionMessage = whisperprotos.SenderKeyDistributionMessage() distributionMessage.ParseFromString(message) if distributionMessage.id is None or distributionMessage.iteration is None\ or distributionMessage.chainKey is None or distributionMessage.signingKey is None: raise InvalidMessageException("Incomplete message") self.serialized = serialized self.id = distributionMessage.id self.iteration = distributionMessage.iteration self.chainKey = distributionMessage.chainKey self.signatureKey = Curve.decodePoint(bytearray(distributionMessage.signingKey), 0) except Exception as e: raise InvalidMessageException(e) else: version = [ByteUtil.intsToByteHighAndLow(self.__class__.CURRENT_VERSION, self.__class__.CURRENT_VERSION)] self.id = id self.iteration = iteration self.chainKey = chainKey self.signatureKey = signatureKey message = whisperprotos.SenderKeyDistributionMessage() message.id = id message.iteration = iteration message.chainKey= bytes(chainKey) message.signingKey = signatureKey.serialize() message = message.SerializeToString() self.serialized = bytes(ByteUtil.combine(version, message)) def serialize(self): return self.serialized def getType(self): return self.__class__.SENDERKEY_DISTRIBUTION_TYPE def getIteration(self): return self.iteration def getChainKey(self): return self.chainKey def getSignatureKey(self): return self.signatureKey def getId(self): return self.idpython-axolotl-0.2.3/axolotl/protocol/senderkeymessage.py0000644000175000017500000000777113471043576024234 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from .ciphertextmessage import CiphertextMessage from ..util.byteutil import ByteUtil from ..legacymessageexception import LegacyMessageException from ..invalidmessageexception import InvalidMessageException from ..invalidkeyexception import InvalidKeyException from ..ecc.curve import Curve from . import whisperprotos_pb2 as whisperprotos class SenderKeyMessage(CiphertextMessage): SIGNATURE_LENGTH = 64 def __init__(self, keyId=None, iteration=None, ciphertext=None, signatureKey=None, serialized=None): assert bool(keyId is not None and iteration is not None and ciphertext is not None and signatureKey is not None) ^ bool(serialized), "Either pass arguments or serialized data" if serialized: try: messageParts = ByteUtil.split(serialized, 1, len(serialized) - 1 - self.__class__.SIGNATURE_LENGTH, self.__class__.SIGNATURE_LENGTH) version = messageParts[0][0] message = messageParts[1] signature = messageParts[2] if ByteUtil.highBitsToInt(version) < 3: raise LegacyMessageException("Legacy message: %s" % ByteUtil.highBitsToInt(version)) if ByteUtil.highBitsToInt(version) > self.__class__.CURRENT_VERSION: raise InvalidMessageException("Unknown version: %s" % ByteUtil.highBitsToInt(version)) senderKeyMessage = whisperprotos.SenderKeyMessage() senderKeyMessage.ParseFromString(message) if senderKeyMessage.id is None or senderKeyMessage.iteration is None or \ senderKeyMessage.ciphertext is None: raise InvalidMessageException("Incomplete message") self.serialized = serialized self.messageVersion = ByteUtil.highBitsToInt(version) self.keyId = senderKeyMessage.id self.iteration = senderKeyMessage.iteration self.ciphertext = senderKeyMessage.ciphertext except Exception as e: raise InvalidMessageException(e) else: version = [ByteUtil.intsToByteHighAndLow(self.__class__.CURRENT_VERSION, self.__class__.CURRENT_VERSION)] message = whisperprotos.SenderKeyMessage() message.id = keyId message.iteration = iteration message.ciphertext = ciphertext message = message.SerializeToString() signature = self.getSignature(signatureKey, bytes(ByteUtil.combine(version, message))) self.serialized = bytes(ByteUtil.combine(version, message, signature)) self.messageVersion = self.__class__.CURRENT_VERSION self.keyId = keyId self.iteration = iteration self.ciphertext = ciphertext def getKeyId(self): return self.keyId def getIteration(self): return self.iteration def getCipherText(self): return self.ciphertext def verifySignature(self, signatureKey): """ :type signatureKey: ECPublicKey """ try: parts = ByteUtil.split(self.serialized, len(self.serialized) - self.__class__.SIGNATURE_LENGTH, self.__class__.SIGNATURE_LENGTH) if not Curve.verifySignature(signatureKey, parts[0], parts[1]): raise InvalidMessageException("Invalid signature!") except InvalidKeyException as e: raise InvalidMessageException(e) def getSignature(self, signatureKey, serialized): """ :type signatureKey: ECPrivateKey :type serialized: bytearray """ try: return Curve.calculateSignature(signatureKey, serialized) except InvalidKeyException as e: raise AssertionError(e) def serialize(self): return self.serialized def getType(self): return CiphertextMessage.SENDERKEY_TYPEpython-axolotl-0.2.3/axolotl/protocol/whispermessage.py0000644000175000017500000001134513471043576023714 0ustar tarektarek00000000000000# -*- cosing: utf-8 -*- import hmac import hashlib from .ciphertextmessage import CiphertextMessage from ..util.byteutil import ByteUtil from ..ecc.curve import Curve from . import whisperprotos_pb2 as whisperprotos from ..legacymessageexception import LegacyMessageException from ..invalidmessageexception import InvalidMessageException from ..invalidkeyexception import InvalidKeyException class WhisperMessage(CiphertextMessage): MAC_LENGTH = 8 def __init__(self, messageVersion=None, macKey=None, ECPublicKey_senderRatchetKey=None, counter=None, previousCounter=None, ciphertext=None, senderIdentityKey=None, receiverIdentityKey=None, serialized=None): self.serialized = "" if serialized: try: assert type(serialized) in (str, bytes), "Expected serialized %s, got %s" % (str, type(serialized)) messageParts = ByteUtil.split(serialized, 1, len(serialized) - 1 - WhisperMessage.MAC_LENGTH, WhisperMessage.MAC_LENGTH) version = messageParts[0][0] message = messageParts[1] mac = messageParts[2] if ByteUtil.highBitsToInt(version) <= self.__class__.UNSUPPORTED_VERSION: raise LegacyMessageException("Legacy message %s" % ByteUtil.highBitsToInt(version)) if ByteUtil.highBitsToInt(version) > self.__class__.CURRENT_VERSION: raise InvalidMessageException("Unknown version: %s" % ByteUtil.highBitsToInt(version)) whisperMessage = whisperprotos.WhisperMessage() whisperMessage.ParseFromString(message) if not whisperMessage.ciphertext or whisperMessage.counter is None or not whisperMessage.ratchetKey: raise InvalidMessageException("Incomplete message") self.serialized = serialized self.senderRatchetKey = Curve.decodePoint(bytearray(whisperMessage.ratchetKey), 0) self.messageVersion = ByteUtil.highBitsToInt(version) self.counter = whisperMessage.counter self.previousCounter = whisperMessage.previousCounter self.ciphertext = whisperMessage.ciphertext except InvalidKeyException as e: raise InvalidMessageException(e) else: version = ByteUtil.intsToByteHighAndLow(messageVersion, self.__class__.CURRENT_VERSION) message = whisperprotos.WhisperMessage() message.ratchetKey = ECPublicKey_senderRatchetKey.serialize() message.counter = counter message.previousCounter = previousCounter message.ciphertext = ciphertext message = message.SerializeToString() mac = self.getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, ByteUtil.combine(version, message)) self.serialized = bytes(ByteUtil.combine(version, message, mac)) self.senderRatchetKey = ECPublicKey_senderRatchetKey self.counter = counter self.previousCounter = previousCounter self.ciphertext = ciphertext self.messageVersion = messageVersion def getSenderRatchetKey(self): return self.senderRatchetKey def getMessageVersion(self): return self.messageVersion def getCounter(self): return self.counter def getBody(self): return self.ciphertext def verifyMac(self, messageVersion, senderIdentityKey, receiverIdentityKey, macKey): parts = ByteUtil.split(self.serialized, len(self.serialized) - self.__class__.MAC_LENGTH, self.__class__.MAC_LENGTH) ourMac = self.getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, parts[0]) theirMac = parts[1] if ourMac != theirMac: raise InvalidMessageException("Bad Mac!") def getMac(self, messageVersion, senderIdentityKey, receiverIdentityKey, macKey, serialized): mac = hmac.new(macKey, digestmod=hashlib.sha256) if messageVersion >= 3: mac.update(senderIdentityKey.getPublicKey().serialize()) mac.update(receiverIdentityKey.getPublicKey().serialize()) mac.update(bytes(serialized)) fullMac = mac.digest() return ByteUtil.trim(fullMac, self.__class__.MAC_LENGTH) def serialize(self): return self.serialized def getType(self): return CiphertextMessage.WHISPER_TYPE def isLegacy(self, message): return message is not None and \ len(message) >= 1 and \ ByteUtil.highBitsToInt(message[0]) <= CiphertextMessage.UNSUPPORTED_VERSION python-axolotl-0.2.3/axolotl/protocol/whisperprotos_pb2.py0000644000175000017500000003252113471043576024360 0ustar tarektarek00000000000000# Generated by the protocol buffer compiler. DO NOT EDIT! # source: WhisperTextProtocol.proto import sys _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='WhisperTextProtocol.proto', package='textsecure', serialized_pb=_b('\n\x19WhisperTextProtocol.proto\x12\ntextsecure\"b\n\x0eWhisperMessage\x12\x12\n\nratchetKey\x18\x01 \x01(\x0c\x12\x0f\n\x07\x63ounter\x18\x02 \x01(\r\x12\x17\n\x0fpreviousCounter\x18\x03 \x01(\r\x12\x12\n\nciphertext\x18\x04 \x01(\x0c\"\x8f\x01\n\x14PreKeyWhisperMessage\x12\x16\n\x0eregistrationId\x18\x05 \x01(\r\x12\x10\n\x08preKeyId\x18\x01 \x01(\r\x12\x16\n\x0esignedPreKeyId\x18\x06 \x01(\r\x12\x0f\n\x07\x62\x61seKey\x18\x02 \x01(\x0c\x12\x13\n\x0bidentityKey\x18\x03 \x01(\x0c\x12\x0f\n\x07message\x18\x04 \x01(\x0c\"t\n\x12KeyExchangeMessage\x12\n\n\x02id\x18\x01 \x01(\r\x12\x0f\n\x07\x62\x61seKey\x18\x02 \x01(\x0c\x12\x12\n\nratchetKey\x18\x03 \x01(\x0c\x12\x13\n\x0bidentityKey\x18\x04 \x01(\x0c\x12\x18\n\x10\x62\x61seKeySignature\x18\x05 \x01(\x0c\"E\n\x10SenderKeyMessage\x12\n\n\x02id\x18\x01 \x01(\r\x12\x11\n\titeration\x18\x02 \x01(\r\x12\x12\n\nciphertext\x18\x03 \x01(\x0c\"c\n\x1cSenderKeyDistributionMessage\x12\n\n\x02id\x18\x01 \x01(\r\x12\x11\n\titeration\x18\x02 \x01(\r\x12\x10\n\x08\x63hainKey\x18\x03 \x01(\x0c\x12\x12\n\nsigningKey\x18\x04 \x01(\x0c\x42\x37\n&org.whispersystems.libaxolotl.protocolB\rWhisperProtos') ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) _WHISPERMESSAGE = _descriptor.Descriptor( name='WhisperMessage', full_name='textsecure.WhisperMessage', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='ratchetKey', full_name='textsecure.WhisperMessage.ratchetKey', index=0, number=1, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='counter', full_name='textsecure.WhisperMessage.counter', index=1, number=2, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='previousCounter', full_name='textsecure.WhisperMessage.previousCounter', index=2, number=3, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='ciphertext', full_name='textsecure.WhisperMessage.ciphertext', index=3, number=4, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=41, serialized_end=139, ) _PREKEYWHISPERMESSAGE = _descriptor.Descriptor( name='PreKeyWhisperMessage', full_name='textsecure.PreKeyWhisperMessage', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='registrationId', full_name='textsecure.PreKeyWhisperMessage.registrationId', index=0, number=5, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='preKeyId', full_name='textsecure.PreKeyWhisperMessage.preKeyId', index=1, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='signedPreKeyId', full_name='textsecure.PreKeyWhisperMessage.signedPreKeyId', index=2, number=6, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='baseKey', full_name='textsecure.PreKeyWhisperMessage.baseKey', index=3, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='identityKey', full_name='textsecure.PreKeyWhisperMessage.identityKey', index=4, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='message', full_name='textsecure.PreKeyWhisperMessage.message', index=5, number=4, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=142, serialized_end=285, ) _KEYEXCHANGEMESSAGE = _descriptor.Descriptor( name='KeyExchangeMessage', full_name='textsecure.KeyExchangeMessage', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='textsecure.KeyExchangeMessage.id', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='baseKey', full_name='textsecure.KeyExchangeMessage.baseKey', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='ratchetKey', full_name='textsecure.KeyExchangeMessage.ratchetKey', index=2, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='identityKey', full_name='textsecure.KeyExchangeMessage.identityKey', index=3, number=4, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='baseKeySignature', full_name='textsecure.KeyExchangeMessage.baseKeySignature', index=4, number=5, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=287, serialized_end=403, ) _SENDERKEYMESSAGE = _descriptor.Descriptor( name='SenderKeyMessage', full_name='textsecure.SenderKeyMessage', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='textsecure.SenderKeyMessage.id', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='iteration', full_name='textsecure.SenderKeyMessage.iteration', index=1, number=2, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='ciphertext', full_name='textsecure.SenderKeyMessage.ciphertext', index=2, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=405, serialized_end=474, ) _SENDERKEYDISTRIBUTIONMESSAGE = _descriptor.Descriptor( name='SenderKeyDistributionMessage', full_name='textsecure.SenderKeyDistributionMessage', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='textsecure.SenderKeyDistributionMessage.id', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='iteration', full_name='textsecure.SenderKeyDistributionMessage.iteration', index=1, number=2, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='chainKey', full_name='textsecure.SenderKeyDistributionMessage.chainKey', index=2, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='signingKey', full_name='textsecure.SenderKeyDistributionMessage.signingKey', index=3, number=4, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=476, serialized_end=575, ) DESCRIPTOR.message_types_by_name['WhisperMessage'] = _WHISPERMESSAGE DESCRIPTOR.message_types_by_name['PreKeyWhisperMessage'] = _PREKEYWHISPERMESSAGE DESCRIPTOR.message_types_by_name['KeyExchangeMessage'] = _KEYEXCHANGEMESSAGE DESCRIPTOR.message_types_by_name['SenderKeyMessage'] = _SENDERKEYMESSAGE DESCRIPTOR.message_types_by_name['SenderKeyDistributionMessage'] = _SENDERKEYDISTRIBUTIONMESSAGE WhisperMessage = _reflection.GeneratedProtocolMessageType('WhisperMessage', (_message.Message,), dict( DESCRIPTOR = _WHISPERMESSAGE, __module__ = 'WhisperTextProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.WhisperMessage) )) _sym_db.RegisterMessage(WhisperMessage) PreKeyWhisperMessage = _reflection.GeneratedProtocolMessageType('PreKeyWhisperMessage', (_message.Message,), dict( DESCRIPTOR = _PREKEYWHISPERMESSAGE, __module__ = 'WhisperTextProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.PreKeyWhisperMessage) )) _sym_db.RegisterMessage(PreKeyWhisperMessage) KeyExchangeMessage = _reflection.GeneratedProtocolMessageType('KeyExchangeMessage', (_message.Message,), dict( DESCRIPTOR = _KEYEXCHANGEMESSAGE, __module__ = 'WhisperTextProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.KeyExchangeMessage) )) _sym_db.RegisterMessage(KeyExchangeMessage) SenderKeyMessage = _reflection.GeneratedProtocolMessageType('SenderKeyMessage', (_message.Message,), dict( DESCRIPTOR = _SENDERKEYMESSAGE, __module__ = 'WhisperTextProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SenderKeyMessage) )) _sym_db.RegisterMessage(SenderKeyMessage) SenderKeyDistributionMessage = _reflection.GeneratedProtocolMessageType('SenderKeyDistributionMessage', (_message.Message,), dict( DESCRIPTOR = _SENDERKEYDISTRIBUTIONMESSAGE, __module__ = 'WhisperTextProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SenderKeyDistributionMessage) )) _sym_db.RegisterMessage(SenderKeyDistributionMessage) DESCRIPTOR.has_options = True DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n&org.whispersystems.libaxolotl.protocolB\rWhisperProtos')) # @@protoc_insertion_point(module_scope) python-axolotl-0.2.3/axolotl/ratchet/0000755000175000017500000000000013471043614020072 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/ratchet/__init__.py0000644000175000017500000000003013471043576022203 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- python-axolotl-0.2.3/axolotl/ratchet/aliceaxolotlparameters.py0000644000175000017500000000541213471043576025221 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class AliceAxolotlParameters: def __init__(self, ourIdentityKey, ourBaseKey, theirIdentityKey, theirSignedPreKey, theirRatchetKey, theirOneTimePreKey): """ :type ourBaseKey: ECKeyPair :type theirSignedPreKey: ECKeyPair :type ourIdentityKey: IdentityKeyPair :type theirOneTimePreKey: ECPublicKey :type theirRatchetKey: ECKeyPair :type theirIdentityKey: IdentityKey """ self.ourBaseKey = ourBaseKey self.ourIdentityKey = ourIdentityKey self.theirSignedPreKey = theirSignedPreKey self.theirRatchetKey = theirRatchetKey self.theirIdentityKey = theirIdentityKey self.theirOneTimePreKey = theirOneTimePreKey if ourBaseKey is None or ourIdentityKey is None or theirSignedPreKey is None \ or theirRatchetKey is None or theirIdentityKey is None or theirSignedPreKey is None: raise ValueError("Null value!") def getOurIdentityKey(self): return self.ourIdentityKey def getOurBaseKey(self): return self.ourBaseKey def getTheirIdentityKey(self): return self.theirIdentityKey def getTheirSignedPreKey(self): return self.theirSignedPreKey def getTheirOneTimePreKey(self): return self.theirOneTimePreKey def getTheirRatchetKey(self): return self.theirRatchetKey @staticmethod def newBuilder(): return AliceAxolotlParameters.Builder() class Builder: def __init__(self): self.ourIdentityKey = None self.ourBaseKey = None self.theirIdentityKey = None self.theirSignedPreKey = None self.theirRatchetKey = None self.theirOneTimePreKey = None def setOurIdentityKey(self, ourIdentityKey): self.ourIdentityKey = ourIdentityKey return self def setOurBaseKey(self, ourBaseKey): self.ourBaseKey = ourBaseKey return self def setTheirRatchetKey(self, theirRatchetKey): self.theirRatchetKey = theirRatchetKey return self def setTheirIdentityKey(self, theirIdentityKey): self.theirIdentityKey = theirIdentityKey return self def setTheirSignedPreKey(self, theirSignedPreKey): self.theirSignedPreKey = theirSignedPreKey return self def setTheirOneTimePreKey(self, theirOneTimePreKey): self.theirOneTimePreKey = theirOneTimePreKey return self def create(self): return AliceAxolotlParameters(self.ourIdentityKey, self.ourBaseKey, self.theirIdentityKey, self.theirSignedPreKey, self.theirRatchetKey, self.theirOneTimePreKey) python-axolotl-0.2.3/axolotl/ratchet/bobaxolotlparamaters.py0000644000175000017500000000526313471043576024706 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class BobAxolotlParameters: def __init__(self, ourIdentityKey, ourSignedPreKey, ourRatchetKey, ourOneTimePreKey, theirIdentityKey, theirBaseKey): """ :type ourIdentityKey: IdentityKeyPair :type ourSignedPreKey: ECKeyPair :type ourRatchetKey: ECKeyPair :type ourOneTimePreKey: ECKeyPair :type theirIdentityKey: IdentityKey :type theirBaseKey: ECPublicKey """ self.ourIdentityKey = ourIdentityKey self.ourSignedPreKey = ourSignedPreKey self.ourRatchetKey = ourRatchetKey self.ourOneTimePreKey = ourOneTimePreKey self.theirIdentityKey = theirIdentityKey self.theirBaseKey = theirBaseKey if ourIdentityKey is None or ourSignedPreKey is None or ourRatchetKey is None \ or theirIdentityKey is None or theirBaseKey is None: raise ValueError("Null value!") def getOurIdentityKey(self): return self.ourIdentityKey def getOurSignedPreKey(self): return self.ourSignedPreKey def getOurOneTimePreKey(self): return self.ourOneTimePreKey def getTheirIdentityKey(self): return self.theirIdentityKey def getTheirBaseKey(self): return self.theirBaseKey def getOurRatchetKey(self): return self.ourRatchetKey @staticmethod def newBuilder(): return BobAxolotlParameters.Builder() class Builder: def __init__(self): self.ourIdentityKey = None self.ourSignedPreKey = None self.ourOneTimePreKey = None self.ourRatchetKey = None self.theirIdentityKey = None self.theirBaseKey = None def setOurIdentityKey(self, ourIdentityKey): self.ourIdentityKey = ourIdentityKey return self def setOurSignedPreKey(self, ourSignedPreKey): self.ourSignedPreKey = ourSignedPreKey return self def setOurOneTimePreKey(self, ourOneTimePreKey): self.ourOneTimePreKey = ourOneTimePreKey return self def setOurRatchetKey(self, ourRatchetKey): self.ourRatchetKey = ourRatchetKey return self def setTheirIdentityKey(self, theirIdentityKey): self.theirIdentityKey = theirIdentityKey return self def setTheirBaseKey(self, theirBaseKey): self.theirBaseKey = theirBaseKey return self def create(self): return BobAxolotlParameters(self.ourIdentityKey, self.ourSignedPreKey, self.ourRatchetKey, self.ourOneTimePreKey, self.theirIdentityKey, self.theirBaseKey) python-axolotl-0.2.3/axolotl/ratchet/chainkey.py0000644000175000017500000000246013471043576022250 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import hmac import hashlib from ..kdf.derivedmessagesecrets import DerivedMessageSecrets from ..kdf.messagekeys import MessageKeys class ChainKey: MESSAGE_KEY_SEED = bytearray([0x01]) CHAIN_KEY_SEED = bytearray([0x02]) def __init__(self, kdf, key, index): self.kdf = kdf self.key = key self.index = index def getKey(self): return self.key def getIndex(self): return self.index def getNextChainKey(self): nextKey = self.getBaseMaterial(self.__class__.CHAIN_KEY_SEED) return ChainKey(self.kdf, nextKey, self.index + 1) def getMessageKeys(self): inputKeyMaterial = self.getBaseMaterial(self.__class__.MESSAGE_KEY_SEED) keyMaterialBytes = self.kdf.deriveSecrets(inputKeyMaterial, bytearray("WhisperMessageKeys".encode()), DerivedMessageSecrets.SIZE) keyMaterial = DerivedMessageSecrets(keyMaterialBytes) return MessageKeys(keyMaterial.getCipherKey(), keyMaterial.getMacKey(), keyMaterial.getIv(), self.index) def getBaseMaterial(self, seedBytes): mac = hmac.new(bytes(self.key), digestmod=hashlib.sha256) mac.update(bytes(seedBytes)) return mac.digest() python-axolotl-0.2.3/axolotl/ratchet/ratchetingsession.py0000644000175000017500000001404513471043576024213 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..ecc.curve import Curve from .bobaxolotlparamaters import BobAxolotlParameters from .aliceaxolotlparameters import AliceAxolotlParameters from ..kdf.hkdfv3 import HKDFv3 from ..util.byteutil import ByteUtil from .rootkey import RootKey from .chainkey import ChainKey from ..protocol.ciphertextmessage import CiphertextMessage class RatchetingSession: @staticmethod def initializeSession(sessionState, parameters): """ :type sessionState: SessionState :type parameters: SymmetricAxolotlParameters """ if RatchetingSession.isAlice(parameters.getOurBaseKey().getPublicKey(), parameters.getTheirBaseKey()): aliceParameters = AliceAxolotlParameters.newBuilder() aliceParameters.setOurBaseKey(parameters.getOurBaseKey()) \ .setOurIdentityKey(parameters.getOurIdentityKey()) \ .setTheirRatchetKey(parameters.getTheirRatchetKey()) \ .setTheirIdentityKey(parameters.getTheirIdentityKey()) \ .setTheirSignedPreKey(parameters.getTheirBaseKey()) \ .setTheirOneTimePreKey(None) RatchetingSession.initializeSessionAsAlice(sessionState, aliceParameters.create()) else: bobParameters = BobAxolotlParameters.newBuilder() bobParameters.setOurIdentityKey(parameters.getOurIdentityKey()) \ .setOurRatchetKey(parameters.getOurRatchetKey()) \ .setOurSignedPreKey(parameters.getOurBaseKey()) \ .setOurOneTimePreKey(None) \ .setTheirBaseKey(parameters.getTheirBaseKey()) \ .setTheirIdentityKey(parameters.getTheirIdentityKey()) RatchetingSession.initializeSessionAsBob(sessionState, bobParameters.create()) @staticmethod def initializeSessionAsAlice(sessionState, parameters): """ :type sessionState: SessionState :type parameters: AliceAxolotlParameters """ sessionState.setSessionVersion(CiphertextMessage.CURRENT_VERSION) sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()) sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()) sendingRatchetKey = Curve.generateKeyPair() secrets = bytearray() secrets.extend(RatchetingSession.getDiscontinuityBytes()) secrets.extend(Curve.calculateAgreement(parameters.getTheirSignedPreKey(), parameters.getOurIdentityKey().getPrivateKey())) secrets.extend(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), parameters.getOurBaseKey().getPrivateKey())) secrets.extend(Curve.calculateAgreement(parameters.getTheirSignedPreKey(), parameters.getOurBaseKey().getPrivateKey())) if parameters.getTheirOneTimePreKey() is not None: secrets.extend(Curve.calculateAgreement(parameters.getTheirOneTimePreKey(), parameters.getOurBaseKey().getPrivateKey())) derivedKeys = RatchetingSession.calculateDerivedKeys(secrets) sendingChain = derivedKeys.getRootKey().createChain(parameters.getTheirRatchetKey(), sendingRatchetKey) sessionState.addReceiverChain(parameters.getTheirRatchetKey(), derivedKeys.getChainKey()) sessionState.setSenderChain(sendingRatchetKey, sendingChain[1]) sessionState.setRootKey(sendingChain[0]) @staticmethod def initializeSessionAsBob(sessionState, parameters): """ :type sessionState: SessionState :type parameters: BobAxolotlParameters """ sessionState.setSessionVersion(CiphertextMessage.CURRENT_VERSION) sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()) sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()) secrets = bytearray() secrets.extend(RatchetingSession.getDiscontinuityBytes()) secrets.extend(Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), parameters.getOurSignedPreKey().getPrivateKey())) secrets.extend(Curve.calculateAgreement(parameters.getTheirBaseKey(), parameters.getOurIdentityKey().getPrivateKey())) secrets.extend(Curve.calculateAgreement(parameters.getTheirBaseKey(), parameters.getOurSignedPreKey().getPrivateKey())) if parameters.getOurOneTimePreKey() is not None: secrets.extend(Curve.calculateAgreement(parameters.getTheirBaseKey(), parameters.getOurOneTimePreKey().getPrivateKey())) derivedKeys = RatchetingSession.calculateDerivedKeys(secrets) sessionState.setSenderChain(parameters.getOurRatchetKey(), derivedKeys.getChainKey()) sessionState.setRootKey(derivedKeys.getRootKey()) @staticmethod def getDiscontinuityBytes(): return bytearray([0xFF] * 32) @staticmethod def calculateDerivedKeys(masterSecret): kdf = HKDFv3() derivedSecretBytes = kdf.deriveSecrets(masterSecret, bytearray("WhisperText".encode()), 64) derivedSecrets = ByteUtil.split(derivedSecretBytes, 32, 32) return RatchetingSession.DerivedKeys(RootKey(kdf, derivedSecrets[0]), ChainKey(kdf, derivedSecrets[1], 0)) @staticmethod def isAlice(ourKey, theirKey): """ :type ourKey: ECPublicKey :type theirKey: ECPublicKey """ return ourKey < theirKey class DerivedKeys: def __init__(self, rootKey, chainKey): """ :type rootKey: RootKey :type chainKey: ChainKey """ self.rootKey = rootKey self.chainKey = chainKey def getRootKey(self): return self.rootKey def getChainKey(self): return self.chainKey python-axolotl-0.2.3/axolotl/ratchet/rootkey.py0000644000175000017500000000200313471043576022142 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..ecc.curve import Curve from ..kdf.derivedrootsecrets import DerivedRootSecrets from .chainkey import ChainKey class RootKey: def __init__(self, kdf, key): self.kdf = kdf self.key = key def getKeyBytes(self): return self.key def createChain(self, ECPublicKey_theirRatchetKey, ECKeyPair_ourRatchetKey): sharedSecret = Curve.calculateAgreement(ECPublicKey_theirRatchetKey, ECKeyPair_ourRatchetKey.getPrivateKey()) derivedSecretBytes = self.kdf.deriveSecrets(sharedSecret, "WhisperRatchet".encode(), DerivedRootSecrets.SIZE, salt=self.key) derivedSecrets = DerivedRootSecrets(derivedSecretBytes) newRootKey = RootKey(self.kdf, derivedSecrets.getRootKey()) newChainKey = ChainKey(self.kdf, derivedSecrets.getChainKey(), 0) return (newRootKey, newChainKey) python-axolotl-0.2.3/axolotl/ratchet/symmetricaxolotlparameters.py0000644000175000017500000000523113471043576026157 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class SymmetricAxolotlParameters: def __init__(self, ourBaseKey, ourRatchetKey, ourIdentityKey, theirBaseKey, theirRatchetKey, theirIdentityKey): """ :type ourBaseKey: ECKeyPair :type ourRatchetKey: ECKeyPair :type ourIdentityKey: IdentityKeyPair :type theirBaseKey: ECPublicKey :type theirRatchetKey: ECKeyPair :type theirIdentityKey: IdentityKey """ self.ourBaseKey = ourBaseKey self.ourIdentityKey = ourIdentityKey self.ourRatchetKey = ourRatchetKey self.theirRatchetKey = theirRatchetKey self.theirIdentityKey = theirIdentityKey self.theirBaseKey = theirBaseKey if ourBaseKey is None or ourIdentityKey is None or ourRatchetKey is None \ or theirRatchetKey is None or theirIdentityKey is None or theirBaseKey is None: raise ValueError("Null value!") def getOurBaseKey(self): return self.ourBaseKey def getOurIdentityKey(self): return self.ourIdentityKey def getTheirRatchetKey(self): return self.theirRatchetKey def getTheirIdentityKey(self): return self.theirIdentityKey def getTheirBaseKey(self): return self.theirBaseKey def getOurRatchetKey(self): return self.ourRatchetKey @staticmethod def newBuilder(): return SymmetricAxolotlParameters.Builder() class Builder: def __init__(self): self.ourIdentityKey = None self.ourBaseKey = None self.ourRatchetKey = None self.theirRatchetKey = None self.theirIdentityKey = None self.theirBaseKey = None def setOurIdentityKey(self, ourIdentityKey): self.ourIdentityKey = ourIdentityKey return self def setOurBaseKey(self, ourBaseKey): self.ourBaseKey = ourBaseKey return self def setOurRatchetKey(self, ourRatchetKey): self.ourRatchetKey = ourRatchetKey return self def setTheirRatchetKey(self, theirRatchetKey): self.theirRatchetKey = theirRatchetKey return self def setTheirIdentityKey(self, theirIdentityKey): self.theirIdentityKey = theirIdentityKey return self def setTheirBaseKey(self, theirBaseKey): self.theirBaseKey = theirBaseKey return self def create(self): return SymmetricAxolotlParameters(self.ourBaseKey, self.ourRatchetKey, self.ourIdentityKey, self.theirBaseKey, self.theirRatchetKey, self.theirIdentityKey) python-axolotl-0.2.3/axolotl/sessionbuilder.py0000644000175000017500000002735413471043576022066 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import logging from .ecc.curve import Curve from .ratchet.aliceaxolotlparameters import AliceAxolotlParameters from .ratchet.bobaxolotlparamaters import BobAxolotlParameters from .ratchet.symmetricaxolotlparameters import SymmetricAxolotlParameters from .ratchet.ratchetingsession import RatchetingSession from .invalidkeyexception import InvalidKeyException from .invalidkeyidexception import InvalidKeyIdException from .untrustedidentityexception import UntrustedIdentityException from .protocol.keyexchangemessage import KeyExchangeMessage from .protocol.ciphertextmessage import CiphertextMessage from .statekeyexchangeexception import StaleKeyExchangeException from .util.medium import Medium from .util.keyhelper import KeyHelper logger = logging.getLogger(__name__) class SessionBuilder: def __init__(self, sessionStore, preKeyStore, signedPreKeyStore, identityKeyStore, recepientId, deviceId): self.sessionStore = sessionStore self.preKeyStore = preKeyStore self.signedPreKeyStore = signedPreKeyStore self.identityKeyStore = identityKeyStore self.recipientId = recepientId self.deviceId = deviceId def process(self, sessionRecord, message): """ :param sessionRecord: :param message: :type message: PreKeyWhisperMessage """ theirIdentityKey = message.getIdentityKey() if not self.identityKeyStore.isTrustedIdentity(self.recipientId, theirIdentityKey): raise UntrustedIdentityException(self.recipientId, theirIdentityKey) unsignedPreKeyId = self.processV3(sessionRecord, message) self.identityKeyStore.saveIdentity(self.recipientId, theirIdentityKey) return unsignedPreKeyId def processV3(self, sessionRecord, message): """ :param sessionRecord: :param message: :type message: PreKeyWhisperMessage :return: """ if sessionRecord.hasSessionState(message.getMessageVersion(), message.getBaseKey().serialize()): logger.warn("We've already setup a session for this V3 message, letting bundled message fall through...") return None ourSignedPreKey = self.signedPreKeyStore.loadSignedPreKey(message.getSignedPreKeyId()).getKeyPair() parameters = BobAxolotlParameters.newBuilder() parameters.setTheirBaseKey(message.getBaseKey())\ .setTheirIdentityKey(message.getIdentityKey())\ .setOurIdentityKey(self.identityKeyStore.getIdentityKeyPair())\ .setOurSignedPreKey(ourSignedPreKey)\ .setOurRatchetKey(ourSignedPreKey) if message.getPreKeyId() is not None: parameters.setOurOneTimePreKey(self.preKeyStore.loadPreKey(message.getPreKeyId()).getKeyPair()) else: parameters.setOurOneTimePreKey(None) if not sessionRecord.isFresh(): sessionRecord.archiveCurrentState() RatchetingSession.initializeSessionAsBob(sessionRecord.getSessionState(), parameters.create()) sessionRecord.getSessionState().setLocalRegistrationId(self.identityKeyStore.getLocalRegistrationId()) sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()) sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize()) if message.getPreKeyId() is not None and message.getPreKeyId() != Medium.MAX_VALUE: return message.getPreKeyId() else: return None def processPreKeyBundle(self, preKey): """ :type preKey: PreKeyBundle """ if not self.identityKeyStore.isTrustedIdentity(self.recipientId, preKey.getIdentityKey()): raise UntrustedIdentityException(self.recipientId, preKey.getIdentityKey()) if preKey.getSignedPreKey() is not None and\ not Curve.verifySignature(preKey.getIdentityKey().getPublicKey(), preKey.getSignedPreKey().serialize(), preKey.getSignedPreKeySignature()): raise InvalidKeyException("Invalid signature on device key!") if preKey.getSignedPreKey() is None: raise InvalidKeyException("No signed prekey!!") sessionRecord = self.sessionStore.loadSession(self.recipientId, self.deviceId) ourBaseKey = Curve.generateKeyPair() theirSignedPreKey = preKey.getSignedPreKey() theirOneTimePreKey = preKey.getPreKey() theirOneTimePreKeyId = preKey.getPreKeyId() if theirOneTimePreKey is not None else None parameters = AliceAxolotlParameters.newBuilder() parameters.setOurBaseKey(ourBaseKey)\ .setOurIdentityKey(self.identityKeyStore.getIdentityKeyPair())\ .setTheirIdentityKey(preKey.getIdentityKey())\ .setTheirSignedPreKey(theirSignedPreKey)\ .setTheirRatchetKey(theirSignedPreKey)\ .setTheirOneTimePreKey(theirOneTimePreKey) if not sessionRecord.isFresh(): sessionRecord.archiveCurrentState() RatchetingSession.initializeSessionAsAlice(sessionRecord.getSessionState(), parameters.create()) sessionRecord.getSessionState().setUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.getSignedPreKeyId(), ourBaseKey.getPublicKey()) sessionRecord.getSessionState().setLocalRegistrationId(self.identityKeyStore.getLocalRegistrationId()) sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId()) sessionRecord.getSessionState().setAliceBaseKey(ourBaseKey.getPublicKey().serialize()) self.sessionStore.storeSession(self.recipientId, self.deviceId, sessionRecord) self.identityKeyStore.saveIdentity(self.recipientId, preKey.getIdentityKey()) def processKeyExchangeMessage(self, keyExchangeMessage): if not self.identityKeyStore.isTrustedIdentity(self.recipientId, keyExchangeMessage.getIdentityKey()): raise UntrustedIdentityException(self.recipientId, keyExchangeMessage.getIdentityKey()) responseMessage = None if keyExchangeMessage.isInitiate(): responseMessage = self.processInitiate(keyExchangeMessage) else: self.processResponse(keyExchangeMessage) return responseMessage def processInitiate(self, keyExchangeMessage): flags = KeyExchangeMessage.RESPONSE_FLAG sessionRecord = self.sessionStore.loadSession(self.recipientId, self.deviceId) if not Curve.verifySignature( keyExchangeMessage.getIdentityKey().getPublicKey(), keyExchangeMessage.getBaseKey().serialize(), keyExchangeMessage.getBaseKeySignature()): raise InvalidKeyException("Bad signature!") builder = SymmetricAxolotlParameters.newBuilder() if not sessionRecord.getSessionState().hasPendingKeyExchange(): builder.setOurIdentityKey(self.identityKeyStore.getIdentityKeyPair())\ .setOurBaseKey(Curve.generateKeyPair())\ .setOurRatchetKey(Curve.generateKeyPair()) else: builder.setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey())\ .setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey())\ .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey()) flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG builder.setTheirBaseKey(keyExchangeMessage.getBaseKey())\ .setTheirRatchetKey(keyExchangeMessage.getRatchetKey())\ .setTheirIdentityKey(keyExchangeMessage.getIdentityKey()) parameters = builder.create() if not sessionRecord.isFresh(): sessionRecord.archiveCurrentState() RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters) self.sessionStore.storeSession(self.recipientId, self.deviceId, sessionRecord) self.identityKeyStore.saveIdentity(self.recipientId, keyExchangeMessage.getIdentityKey()) baseKeySignature = Curve.calculateSignature(parameters.getOurIdentityKey().getPrivateKey(), parameters.getOurBaseKey().getPublicKey().serialize()) return KeyExchangeMessage(sessionRecord.getSessionState().getSessionVersion(), keyExchangeMessage.getSequence(), flags, parameters.getOurBaseKey().getPublicKey(), baseKeySignature, parameters.getOurRatchetKey().getPublicKey(), parameters.getOurIdentityKey().getPublicKey()) def processResponse(self, keyExchangeMessage): sessionRecord = self.sessionStore.loadSession(self.recipientId, self.deviceId) sessionState = sessionRecord.getSessionState() hasPendingKeyExchange = sessionState.hasPendingKeyExchange() isSimultaneousInitiateResponse = keyExchangeMessage.isResponseForSimultaneousInitiate() if not hasPendingKeyExchange \ or sessionState.getPendingKeyExchangeSequence() != keyExchangeMessage.getSequence(): logger.warn("No matching sequence for response. " "Is simultaneous initiate response: %s" % isSimultaneousInitiateResponse) if not isSimultaneousInitiateResponse: raise StaleKeyExchangeException() else: return parameters = SymmetricAxolotlParameters.newBuilder() parameters.setOurBaseKey(sessionRecord.getSessionState().getPendingKeyExchangeBaseKey())\ .setOurRatchetKey(sessionRecord.getSessionState().getPendingKeyExchangeRatchetKey())\ .setOurIdentityKey(sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey())\ .setTheirBaseKey(keyExchangeMessage.getBaseKey())\ .setTheirRatchetKey(keyExchangeMessage.getRatchetKey())\ .setTheirIdentityKey(keyExchangeMessage.getIdentityKey()) if not sessionRecord.isFresh(): sessionRecord.archiveCurrentState() RatchetingSession.initializeSession(sessionRecord.getSessionState(), parameters.create()) if not Curve.verifySignature( keyExchangeMessage.getIdentityKey().getPublicKey(), keyExchangeMessage.getBaseKey().serialize(), keyExchangeMessage.getBaseKeySignature()): raise InvalidKeyException("Base key signature doesn't match!") self.sessionStore.storeSession(self.recipientId, self.deviceId, sessionRecord) self.identityKeyStore.saveIdentity(self.recipientId, keyExchangeMessage.getIdentityKey()) def processInitKeyExchangeMessage(self): try: sequence = KeyHelper.getRandomSequence(65534) + 1 flags = KeyExchangeMessage.INITIATE_FLAG baseKey = Curve.generateKeyPair() ratchetKey = Curve.generateKeyPair() identityKey = self.identityKeyStore.getIdentityKeyPair() baseKeySignature = Curve.calculateSignature(identityKey.getPrivateKey(), baseKey.getPublicKey().serialize()) sessionRecord = self.sessionStore.loadSession(self.recipientId, self.deviceId) sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey) self.sessionStore.storeSession(self.recipientId, self.deviceId, sessionRecord) return KeyExchangeMessage(CiphertextMessage.CURRENT_VERSION, sequence, flags, baseKey.getPublicKey(), baseKeySignature, ratchetKey.getPublicKey(), identityKey.getPublicKey()) except InvalidKeyException as e: raise AssertionError(e) python-axolotl-0.2.3/axolotl/sessioncipher.py0000644000175000017500000002332213471043576021701 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import sys from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding from .ecc.curve import Curve from .sessionbuilder import SessionBuilder from .state.sessionstate import SessionState from .protocol.whispermessage import WhisperMessage from .protocol.prekeywhispermessage import PreKeyWhisperMessage from .nosessionexception import NoSessionException from .invalidmessageexception import InvalidMessageException from .duplicatemessagexception import DuplicateMessageException import logging logger = logging.getLogger(__name__) class SessionCipher: def __init__(self, sessionStore, preKeyStore, signedPreKeyStore, identityKeyStore, recepientId, deviceId): self.sessionStore = sessionStore self.preKeyStore = preKeyStore self.recipientId = recepientId self.deviceId = deviceId self.sessionBuilder = SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore, identityKeyStore, recepientId, deviceId) def encrypt(self, paddedMessage): """ :type paddedMessage: bytes """ sessionRecord = self.sessionStore.loadSession(self.recipientId, self.deviceId) sessionState = sessionRecord.getSessionState() chainKey = sessionState.getSenderChainKey() messageKeys = chainKey.getMessageKeys() senderEphemeral = sessionState.getSenderRatchetKey() previousCounter = sessionState.getPreviousCounter() sessionVersion = sessionState.getSessionVersion() ciphertextBody = self.getCiphertext(sessionVersion, messageKeys, paddedMessage) ciphertextMessage = WhisperMessage(sessionVersion, messageKeys.getMacKey(), senderEphemeral, chainKey.getIndex(), previousCounter, ciphertextBody, sessionState.getLocalIdentityKey(), sessionState.getRemoteIdentityKey()) if sessionState.hasUnacknowledgedPreKeyMessage(): items = sessionState.getUnacknowledgedPreKeyMessageItems() localRegistrationid = sessionState.getLocalRegistrationId() ciphertextMessage = PreKeyWhisperMessage(sessionVersion, localRegistrationid, items.getPreKeyId(), items.getSignedPreKeyId(), items.getBaseKey(), sessionState.getLocalIdentityKey(), ciphertextMessage) sessionState.setSenderChainKey(chainKey.getNextChainKey()) self.sessionStore.storeSession(self.recipientId, self.deviceId, sessionRecord) return ciphertextMessage def decryptMsg(self, ciphertext, textMsg=True): """ :type ciphertext: WhisperMessage :type textMsg: Bool set this to False if you are decrypting bytes instead of string """ if not self.sessionStore.containsSession(self.recipientId, self.deviceId): raise NoSessionException("No session for: %s, %s" % (self.recipientId, self.deviceId)) sessionRecord = self.sessionStore.loadSession(self.recipientId, self.deviceId) plaintext = self.decryptWithSessionRecord(sessionRecord, ciphertext) self.sessionStore.storeSession(self.recipientId, self.deviceId, sessionRecord) return plaintext def decryptPkmsg(self, ciphertext, textMsg=True): """ :type ciphertext: PreKeyWhisperMessage """ sessionRecord = self.sessionStore.loadSession(self.recipientId, self.deviceId) unsignedPreKeyId = self.sessionBuilder.process(sessionRecord, ciphertext) plaintext = self.decryptWithSessionRecord(sessionRecord, ciphertext.getWhisperMessage()) # callback.handlePlaintext(plaintext) self.sessionStore.storeSession(self.recipientId, self.deviceId, sessionRecord) if unsignedPreKeyId is not None: self.preKeyStore.removePreKey(unsignedPreKeyId) return plaintext def decryptWithSessionRecord(self, sessionRecord, cipherText): """ :type sessionRecord: SessionRecord :type cipherText: WhisperMessage """ previousStates = sessionRecord.getPreviousSessionStates() exceptions = [] try: sessionState = SessionState(sessionRecord.getSessionState()) plaintext = self.decryptWithSessionState(sessionState, cipherText) sessionRecord.setState(sessionState) return plaintext except InvalidMessageException as e: exceptions.append(e) for i in range(0, len(previousStates)): previousState = previousStates[i] try: promotedState = SessionState(previousState) plaintext = self.decryptWithSessionState(promotedState, cipherText) previousStates.pop(i) sessionRecord.promoteState(promotedState) return plaintext except InvalidMessageException as e: exceptions.append(e) raise InvalidMessageException("No valid sessions", exceptions) def decryptWithSessionState(self, sessionState, ciphertextMessage): if not sessionState.hasSenderChain(): raise InvalidMessageException("Uninitialized session!") if ciphertextMessage.getMessageVersion() != sessionState.getSessionVersion(): raise InvalidMessageException("Message version %s, but session version %s" % (ciphertextMessage.getMessageVersion, sessionState.getSessionVersion())) messageVersion = ciphertextMessage.getMessageVersion() theirEphemeral = ciphertextMessage.getSenderRatchetKey() counter = ciphertextMessage.getCounter() chainKey = self.getOrCreateChainKey(sessionState, theirEphemeral) messageKeys = self.getOrCreateMessageKeys(sessionState, theirEphemeral, chainKey, counter) ciphertextMessage.verifyMac(messageVersion, sessionState.getRemoteIdentityKey(), sessionState.getLocalIdentityKey(), messageKeys.getMacKey()) plaintext = self.getPlaintext(messageVersion, messageKeys, ciphertextMessage.getBody()) sessionState.clearUnacknowledgedPreKeyMessage() return plaintext def getOrCreateChainKey(self, sessionState, ECPublickKey_theirEphemeral): theirEphemeral = ECPublickKey_theirEphemeral if sessionState.hasReceiverChain(theirEphemeral): return sessionState.getReceiverChainKey(theirEphemeral) else: rootKey = sessionState.getRootKey() ourEphemeral = sessionState.getSenderRatchetKeyPair() receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral) ourNewEphemeral = Curve.generateKeyPair() senderChain = receiverChain[0].createChain(theirEphemeral, ourNewEphemeral) sessionState.setRootKey(senderChain[0]) sessionState.addReceiverChain(theirEphemeral, receiverChain[1]) sessionState.setPreviousCounter(max(sessionState.getSenderChainKey().getIndex() - 1, 0)) sessionState.setSenderChain(ourNewEphemeral, senderChain[1]) return receiverChain[1] def getOrCreateMessageKeys(self, sessionState, ECPublicKey_theirEphemeral, chainKey, counter): theirEphemeral = ECPublicKey_theirEphemeral if chainKey.getIndex() > counter: if sessionState.hasMessageKeys(theirEphemeral, counter): return sessionState.removeMessageKeys(theirEphemeral, counter) else: raise DuplicateMessageException("Received message with old counter: %s, %s" % (chainKey.getIndex(), counter)) if counter - chainKey.getIndex() > 2000: raise InvalidMessageException("Over 2000 messages into the future!") while chainKey.getIndex() < counter: messageKeys = chainKey.getMessageKeys() sessionState.setMessageKeys(theirEphemeral, messageKeys) chainKey = chainKey.getNextChainKey() sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey()) return chainKey.getMessageKeys() def getCiphertext(self, version, messageKeys, plainText): """ :type version: int :type messageKeys: MessageKeys :type plainText: bytearray """ cipher = self.getCipher(messageKeys.getCipherKey(), messageKeys.getIv()) return cipher.encrypt(plainText) def getPlaintext(self, version, messageKeys, cipherText): cipher = self.getCipher(messageKeys.getCipherKey(), messageKeys.getIv()) return cipher.decrypt(cipherText) def getCipher(self, key, iv): return AESCipher(key, iv) class AESCipher: def __init__(self, key, iv): self.key = key self.iv = iv self.cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) def encrypt(self, raw): padder = padding.PKCS7(128).padder() rawPadded = padder.update(raw) + padder.finalize() encryptor = self.cipher.encryptor() try: return encryptor.update(rawPadded) + encryptor.finalize() except ValueError: raise def decrypt(self, enc): decryptor = self.cipher.decryptor() decrypted = decryptor.update(enc) + decryptor.finalize() unpadder = padding.PKCS7(128).unpadder() return unpadder.update(decrypted) + unpadder.finalize() python-axolotl-0.2.3/axolotl/state/0000755000175000017500000000000013471043614017560 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/state/__init__.py0000644000175000017500000000003013471043576021671 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- python-axolotl-0.2.3/axolotl/state/axolotlstore.py0000644000175000017500000000050713471043576022702 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import abc from .identitykeystore import IdentityKeyStore from .prekeystore import PreKeyStore from .sessionstore import SessionStore from .signedprekeystore import SignedPreKeyStore class AxolotlStore(IdentityKeyStore, PreKeyStore, SignedPreKeyStore, SessionStore): __metaclass__ = abc.ABCMeta python-axolotl-0.2.3/axolotl/state/identitykeystore.py0000644000175000017500000000066713471043576023571 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import abc class IdentityKeyStore(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def getIdentityKeyPair(self): pass @abc.abstractmethod def getLocalRegistrationId(self): pass @abc.abstractmethod def saveIdentity(self, recepientId, identityKey): pass @abc.abstractmethod def isTrustedIdentity(self, recepientId, identityKey): pass python-axolotl-0.2.3/axolotl/state/prekeybundle.py0000644000175000017500000000217613471043576022640 0ustar tarektarek00000000000000# -*- cosing: utf-8 -*- class PreKeyBundle: def __init__(self, registrationId, deviceId, preKeyId, ECPublicKey_preKeyPublic, signedPreKeyId, ECPublicKey_signedPreKeyPublic, signedPreKeySignature, identityKey): self.registrationId = registrationId self.deviceId = deviceId self.preKeyId = preKeyId self.preKeyPublic = ECPublicKey_preKeyPublic self.signedPreKeyId = signedPreKeyId self.signedPreKeyPublic = ECPublicKey_signedPreKeyPublic self.signedPreKeySignature = signedPreKeySignature self.identityKey = identityKey def getDeviceId(self): return self.deviceId def getPreKeyId(self): return self.preKeyId def getPreKey(self): return self.preKeyPublic def getSignedPreKeyId(self): return self.signedPreKeyId def getSignedPreKey(self): return self.signedPreKeyPublic def getSignedPreKeySignature(self): return self.signedPreKeySignature def getIdentityKey(self): return self.identityKey def getRegistrationId(self): return self.registrationId python-axolotl-0.2.3/axolotl/state/prekeyrecord.py0000644000175000017500000000164613471043576022646 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from .storageprotos_pb2 import PreKeyRecordStructure from ..ecc.curve import Curve from ..ecc.eckeypair import ECKeyPair class PreKeyRecord: def __init__(self, _id=None, ecKeyPair=None, serialized=None): self.structure = PreKeyRecordStructure() if serialized: self.structure.ParseFromString(serialized) else: self.structure.id = _id self.structure.publicKey = ecKeyPair.getPublicKey().serialize() self.structure.privateKey = ecKeyPair.getPrivateKey().serialize() def getId(self): return self.structure.id def getKeyPair(self): publicKey = Curve.decodePoint(bytearray(self.structure.publicKey), 0) privateKey = Curve.decodePrivatePoint(bytearray(self.structure.privateKey)) return ECKeyPair(publicKey, privateKey) def serialize(self): return self.structure.SerializeToString() python-axolotl-0.2.3/axolotl/state/prekeystore.py0000644000175000017500000000063613471043576022522 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import abc class PreKeyStore(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def loadPreKey(self, preKeyId): pass @abc.abstractmethod def storePreKey(self, preKeyId, preKeyRecord): pass @abc.abstractmethod def containsPreKey(self, preKeyId): pass @abc.abstractmethod def removePreKey(self, preKeyId): pass python-axolotl-0.2.3/axolotl/state/sessionrecord.py0000644000175000017500000000433113471043576023024 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from . import storageprotos_pb2 as storageprotos from .sessionstate import SessionState class SessionRecord: ARCHIVED_STATES_MAX_LENGTH = 40 def __init__(self, sessionState=None, serialized=None): """ :type sessionState: SessionState :type serialized: str """ self.previousStates = [] if sessionState: self.sessionState = sessionState self.fresh = False elif serialized: record = storageprotos.RecordStructure() record.ParseFromString(serialized) self.sessionState = SessionState(record.currentSession) self.fresh = False for previousStructure in record.previousSessions: self.previousStates.append(SessionState(previousStructure)) else: self.fresh = True self.sessionState = SessionState() def hasSessionState(self, version, aliceBaseKey): if self.sessionState.getSessionVersion() == version and aliceBaseKey == self.sessionState.getAliceBaseKey(): return True for state in self.previousStates: if state.getSessionVersion() == version and aliceBaseKey == state.getAliceBaseKey(): return True return False def getSessionState(self): return self.sessionState def getPreviousSessionStates(self): return self.previousStates def isFresh(self): return self.fresh def archiveCurrentState(self): self.promoteState(SessionState()) def promoteState(self, promotedState): self.previousStates.insert(0, self.sessionState) self.sessionState = promotedState if len(self.previousStates) > self.__class__.ARCHIVED_STATES_MAX_LENGTH: self.previousStates.pop() def setState(self, sessionState): self.sessionState = sessionState def serialize(self): previousStructures = [previousState.getStructure() for previousState in self.previousStates] record = storageprotos.RecordStructure() record.currentSession.MergeFrom(self.sessionState.getStructure()) record.previousSessions.extend(previousStructures) return record.SerializeToString() python-axolotl-0.2.3/axolotl/state/sessionstate.py0000644000175000017500000002756213471043576022701 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from . import storageprotos_pb2 as storageprotos from ..identitykeypair import IdentityKey, IdentityKeyPair from ..ratchet.rootkey import RootKey from ..kdf.hkdf import HKDF from ..ecc.curve import Curve from ..ecc.eckeypair import ECKeyPair from ..ratchet.chainkey import ChainKey from ..kdf.messagekeys import MessageKeys class SessionState: def __init__(self, session=None): if session is None: self.sessionStructure = storageprotos.SessionStructure() elif session.__class__ == SessionState: self.sessionStructure = storageprotos.SessionStructure() self.sessionStructure.CopyFrom(session.sessionStructure) else: self.sessionStructure = session def getStructure(self): return self.sessionStructure def getAliceBaseKey(self): return self.sessionStructure.aliceBaseKey def setAliceBaseKey(self, aliceBaseKey): self.sessionStructure.aliceBaseKey = aliceBaseKey def setSessionVersion(self, version): self.sessionStructure.sessionVersion = version def getSessionVersion(self): sessionVersion = self.sessionStructure.sessionVersion return 2 if sessionVersion == 0 else sessionVersion def setRemoteIdentityKey(self, identityKey): self.sessionStructure.remoteIdentityPublic = identityKey.serialize() def setLocalIdentityKey(self, identityKey): self.sessionStructure.localIdentityPublic = identityKey.serialize() def getRemoteIdentityKey(self): if self.sessionStructure.remoteIdentityPublic is None: return None return IdentityKey(self.sessionStructure.remoteIdentityPublic, 0) def getLocalIdentityKey(self): return IdentityKey(self.sessionStructure.localIdentityPublic, 0) def getPreviousCounter(self): return self.sessionStructure.previousCounter def setPreviousCounter(self, previousCounter): self.sessionStructure.previousCounter = previousCounter def getRootKey(self): return RootKey(HKDF.createFor(self.getSessionVersion()), self.sessionStructure.rootKey) def setRootKey(self, rootKey): self.sessionStructure.rootKey = rootKey.getKeyBytes() def getSenderRatchetKey(self): return Curve.decodePoint(bytearray(self.sessionStructure.senderChain.senderRatchetKey), 0) def getSenderRatchetKeyPair(self): publicKey = self.getSenderRatchetKey() privateKey = Curve.decodePrivatePoint(self.sessionStructure.senderChain.senderRatchetKeyPrivate) return ECKeyPair(publicKey, privateKey) def hasReceiverChain(self, ECPublickKey_senderEphemeral): return self.getReceiverChain(ECPublickKey_senderEphemeral) is not None def hasSenderChain(self): return self.sessionStructure.HasField("senderChain") def getReceiverChain(self, ECPublickKey_senderEphemeral): receiverChains = self.sessionStructure.receiverChains index = 0 for receiverChain in receiverChains: chainSenderRatchetKey = Curve.decodePoint(bytearray(receiverChain.senderRatchetKey), 0) if chainSenderRatchetKey == ECPublickKey_senderEphemeral: return (receiverChain, index) index += 1 def getReceiverChainKey(self, ECPublicKey_senderEphemeral): receiverChainAndIndex = self.getReceiverChain(ECPublicKey_senderEphemeral) receiverChain = receiverChainAndIndex[0] if receiverChain is None: return None return ChainKey(HKDF.createFor(self.getSessionVersion()), receiverChain.chainKey.key, receiverChain.chainKey.index) def addReceiverChain(self, ECPublickKey_senderRatchetKey, chainKey): senderRatchetKey = ECPublickKey_senderRatchetKey chain = storageprotos.SessionStructure.Chain() chain.senderRatchetKey = senderRatchetKey.serialize() chain.chainKey.key = chainKey.getKey() chain.chainKey.index = chainKey.getIndex() self.sessionStructure.receiverChains.extend([chain]) if len(self.sessionStructure.receiverChains) > 5: del self.sessionStructure.receiverChains[0] def setSenderChain(self, ECKeyPair_senderRatchetKeyPair, chainKey): senderRatchetKeyPair = ECKeyPair_senderRatchetKeyPair # TODO: This is never used, maybe a bug? senderChain = storageprotos.SessionStructure.Chain() self.sessionStructure.senderChain.senderRatchetKey = senderRatchetKeyPair.getPublicKey().serialize() self.sessionStructure.senderChain.senderRatchetKeyPrivate = senderRatchetKeyPair.getPrivateKey().serialize() self.sessionStructure.senderChain.chainKey.key = chainKey.key self.sessionStructure.senderChain.chainKey.index = chainKey.index def getSenderChainKey(self): chainKeyStructure = self.sessionStructure.senderChain.chainKey return ChainKey(HKDF.createFor(self.getSessionVersion()), chainKeyStructure.key, chainKeyStructure.index) def setSenderChainKey(self, ChainKey_nextChainKey): nextChainKey = ChainKey_nextChainKey self.sessionStructure.senderChain.chainKey.key = nextChainKey.getKey() self.sessionStructure.senderChain.chainKey.index = nextChainKey.getIndex() def hasMessageKeys(self, ECPublickKey_senderEphemeral, counter): senderEphemeral = ECPublickKey_senderEphemeral chainAndIndex = self.getReceiverChain(senderEphemeral) chain = chainAndIndex[0] if chain is None: return False messageKeyList = chain.messageKeys for messageKey in messageKeyList: if messageKey.index == counter: return True return False def removeMessageKeys(self, ECPublicKey_senderEphemeral, counter): senderEphemeral = ECPublicKey_senderEphemeral chainAndIndex = self.getReceiverChain(senderEphemeral) chain = chainAndIndex[0] if chain is None: return None messageKeyList = chain.messageKeys result = None for i in range(0, len(messageKeyList)): messageKey = messageKeyList[i] if messageKey.index == counter: result = MessageKeys(messageKey.cipherKey, messageKey.macKey, messageKey.iv, messageKey.index) del messageKeyList[i] break self.sessionStructure.receiverChains[chainAndIndex[1]].CopyFrom(chain) return result def setMessageKeys(self, ECPublicKey_senderEphemeral, messageKeys): senderEphemeral = ECPublicKey_senderEphemeral chainAndIndex = self.getReceiverChain(senderEphemeral) chain = chainAndIndex[0] messageKeyStructure = chain.messageKeys.add() # storageprotos.SessionStructure.Chain.MessageKey() messageKeyStructure.cipherKey = messageKeys.getCipherKey() messageKeyStructure.macKey = messageKeys.getMacKey() messageKeyStructure.index = messageKeys.getCounter() messageKeyStructure.iv = messageKeys.getIv() # chain.messageKeys.append(messageKeyStructure) self.sessionStructure.receiverChains[chainAndIndex[1]].CopyFrom(chain) def setReceiverChainKey(self, ECPublicKey_senderEphemeral, chainKey): senderEphemeral = ECPublicKey_senderEphemeral chainAndIndex = self.getReceiverChain(senderEphemeral) chain = chainAndIndex[0] chain.chainKey.key = chainKey.getKey() chain.chainKey.index = chainKey.getIndex() # self.sessionStructure.receiverChains[chainAndIndex[1]].ClearField() self.sessionStructure.receiverChains[chainAndIndex[1]].CopyFrom(chain) def setPendingKeyExchange(self, sequence, ourBaseKey, ourRatchetKey, ourIdentityKey): """ :type sequence: int :type ourBaseKey: ECKeyPair :type ourRatchetKey: ECKeyPair :type ourIdentityKey: IdentityKeyPair """ structure = self.sessionStructure.PendingKeyExchange() structure.sequence = sequence structure.localBaseKey = ourBaseKey.getPublicKey().serialize() structure.localBaseKeyPrivate = ourBaseKey.getPrivateKey().serialize() structure.localRatchetKey = ourRatchetKey.getPublicKey().serialize() structure.localRatchetKeyPrivate = ourRatchetKey.getPrivateKey().serialize() structure.localIdentityKey = ourIdentityKey.getPublicKey().serialize() structure.localIdentityKeyPrivate = ourIdentityKey.getPrivateKey().serialize() self.sessionStructure.pendingKeyExchange.MergeFrom(structure) def getPendingKeyExchangeSequence(self): return self.sessionStructure.pendingKeyExchange.sequence def getPendingKeyExchangeBaseKey(self): publicKey = Curve.decodePoint(bytearray(self.sessionStructure.pendingKeyExchange.localBaseKey), 0) privateKey = Curve.decodePrivatePoint(self.sessionStructure.pendingKeyExchange.localBaseKeyPrivate) return ECKeyPair(publicKey, privateKey) def getPendingKeyExchangeRatchetKey(self): publicKey = Curve.decodePoint(bytearray(self.sessionStructure.pendingKeyExchange.localRatchetKey), 0) privateKey = Curve.decodePrivatePoint(self.sessionStructure.pendingKeyExchange.localRatchetKeyPrivate) return ECKeyPair(publicKey, privateKey) def getPendingKeyExchangeIdentityKey(self): publicKey = IdentityKey(bytearray(self.sessionStructure.pendingKeyExchange.localIdentityKey), 0) privateKey = Curve.decodePrivatePoint(self.sessionStructure.pendingKeyExchange.localIdentityKeyPrivate) return IdentityKeyPair(publicKey, privateKey) def hasPendingKeyExchange(self): return self.sessionStructure.HasField("pendingKeyExchange") def setUnacknowledgedPreKeyMessage(self, preKeyId, signedPreKeyId, baseKey): """ :type preKeyId: int :type signedPreKeyId: int :type baseKey: ECPublicKey """ self.sessionStructure.pendingPreKey.signedPreKeyId = signedPreKeyId self.sessionStructure.pendingPreKey.baseKey = baseKey.serialize() if preKeyId is not None: self.sessionStructure.pendingPreKey.preKeyId = preKeyId def hasUnacknowledgedPreKeyMessage(self): return self.sessionStructure.HasField("pendingPreKey") def getUnacknowledgedPreKeyMessageItems(self): preKeyId = None if self.sessionStructure.pendingPreKey.HasField("preKeyId"): preKeyId = self.sessionStructure.pendingPreKey.preKeyId return SessionState.UnacknowledgedPreKeyMessageItems(preKeyId, self.sessionStructure.pendingPreKey.signedPreKeyId, Curve.decodePoint(bytearray(self.sessionStructure.pendingPreKey.baseKey), 0)) def clearUnacknowledgedPreKeyMessage(self): self.sessionStructure.ClearField("pendingPreKey") def setRemoteRegistrationId(self, registrationId): self.sessionStructure.remoteRegistrationId = registrationId def getRemoteRegistrationId(self, registrationId): return self.sessionStructure.remoteRegistrationId def setLocalRegistrationId(self, registrationId): self.sessionStructure.localRegistrationId = registrationId def getLocalRegistrationId(self): return self.sessionStructure.localRegistrationId def serialize(self): return self.sessionStructure.SerializeToString() class UnacknowledgedPreKeyMessageItems: def __init__(self, preKeyId, signedPreKeyId, baseKey): """ :type preKeyId: int :type signedPreKeyId: int :type baseKey: ECPublicKey """ self.preKeyId = preKeyId self.signedPreKeyId = signedPreKeyId self.baseKey = baseKey def getPreKeyId(self): return self.preKeyId def getSignedPreKeyId(self): return self.signedPreKeyId def getBaseKey(self): return self.baseKey python-axolotl-0.2.3/axolotl/state/sessionstore.py0000644000175000017500000000120313471043576022675 0ustar tarektarek00000000000000# -*- cosing: utf-8 -*- import abc class SessionStore(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def loadSession(self, recepientId, deviceId): pass @abc.abstractmethod def getSubDeviceSessions(self, recepientId): pass @abc.abstractmethod def storeSession(self, recepientId, deviceId, sessionRecord): pass @abc.abstractmethod def containsSession(self, recepientId, deviceId): pass @abc.abstractmethod def deleteSession(self, recepientId, deviceId): pass @abc.abstractmethod def deleteAllSessions(self, recepientId): pass python-axolotl-0.2.3/axolotl/state/signedprekeyrecord.py0000644000175000017500000000230513471043576024031 0ustar tarektarek00000000000000# -*- coding; utf-8 -*- from .storageprotos_pb2 import SignedPreKeyRecordStructure from ..ecc.curve import Curve from ..ecc.eckeypair import ECKeyPair class SignedPreKeyRecord: def __init__(self, _id=None, timestamp=None, ecKeyPair=None, signature=None, serialized=None): self.structure = SignedPreKeyRecordStructure() if serialized: self.structure.ParseFromString(serialized) else: self.structure.id = _id self.structure.publicKey = ecKeyPair.getPublicKey().serialize() self.structure.privateKey = ecKeyPair.getPrivateKey().serialize() self.structure.signature = signature self.structure.timestamp = timestamp def getId(self): return self.structure.id def getTimestamp(self): return self.structure.timestamp def getKeyPair(self): publicKey = Curve.decodePoint(bytearray(self.structure.publicKey), 0) privateKey = Curve.decodePrivatePoint(bytearray(self.structure.privateKey)) return ECKeyPair(publicKey, privateKey) def getSignature(self): return self.structure.signature def serialize(self): return self.structure.SerializeToString() python-axolotl-0.2.3/axolotl/state/signedprekeystore.py0000644000175000017500000000104113471043576023703 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import abc class SignedPreKeyStore(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def loadSignedPreKey(self, signedPreKeyId): pass @abc.abstractmethod def loadSignedPreKeys(self): pass @abc.abstractmethod def storeSignedPreKey(self, signedPreKeyId, signedPreKeyRecord): pass @abc.abstractmethod def containsSignedPreKey(self, signedPreKeyId): pass @abc.abstractmethod def removeSignedPreKey(self, signedPreKeyId): pass python-axolotl-0.2.3/axolotl/state/storageprotos_pb2.py0000644000175000017500000011323613471043576023625 0ustar tarektarek00000000000000# Generated by the protocol buffer compiler. DO NOT EDIT! # source: LocalStorageProtocol.proto import sys _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor.FileDescriptor( name='LocalStorageProtocol.proto', package='textsecure', serialized_pb=_b('\n\x1aLocalStorageProtocol.proto\x12\ntextsecure\"\xd3\x08\n\x10SessionStructure\x12\x16\n\x0esessionVersion\x18\x01 \x01(\r\x12\x1b\n\x13localIdentityPublic\x18\x02 \x01(\x0c\x12\x1c\n\x14remoteIdentityPublic\x18\x03 \x01(\x0c\x12\x0f\n\x07rootKey\x18\x04 \x01(\x0c\x12\x17\n\x0fpreviousCounter\x18\x05 \x01(\r\x12\x37\n\x0bsenderChain\x18\x06 \x01(\x0b\x32\".textsecure.SessionStructure.Chain\x12:\n\x0ereceiverChains\x18\x07 \x03(\x0b\x32\".textsecure.SessionStructure.Chain\x12K\n\x12pendingKeyExchange\x18\x08 \x01(\x0b\x32/.textsecure.SessionStructure.PendingKeyExchange\x12\x41\n\rpendingPreKey\x18\t \x01(\x0b\x32*.textsecure.SessionStructure.PendingPreKey\x12\x1c\n\x14remoteRegistrationId\x18\n \x01(\r\x12\x1b\n\x13localRegistrationId\x18\x0b \x01(\r\x12\x14\n\x0cneedsRefresh\x18\x0c \x01(\x08\x12\x14\n\x0c\x61liceBaseKey\x18\r \x01(\x0c\x1a\xb9\x02\n\x05\x43hain\x12\x18\n\x10senderRatchetKey\x18\x01 \x01(\x0c\x12\x1f\n\x17senderRatchetKeyPrivate\x18\x02 \x01(\x0c\x12=\n\x08\x63hainKey\x18\x03 \x01(\x0b\x32+.textsecure.SessionStructure.Chain.ChainKey\x12\x42\n\x0bmessageKeys\x18\x04 \x03(\x0b\x32-.textsecure.SessionStructure.Chain.MessageKey\x1a&\n\x08\x43hainKey\x12\r\n\x05index\x18\x01 \x01(\r\x12\x0b\n\x03key\x18\x02 \x01(\x0c\x1aJ\n\nMessageKey\x12\r\n\x05index\x18\x01 \x01(\r\x12\x11\n\tcipherKey\x18\x02 \x01(\x0c\x12\x0e\n\x06macKey\x18\x03 \x01(\x0c\x12\n\n\x02iv\x18\x04 \x01(\x0c\x1a\xcd\x01\n\x12PendingKeyExchange\x12\x10\n\x08sequence\x18\x01 \x01(\r\x12\x14\n\x0clocalBaseKey\x18\x02 \x01(\x0c\x12\x1b\n\x13localBaseKeyPrivate\x18\x03 \x01(\x0c\x12\x17\n\x0flocalRatchetKey\x18\x04 \x01(\x0c\x12\x1e\n\x16localRatchetKeyPrivate\x18\x05 \x01(\x0c\x12\x18\n\x10localIdentityKey\x18\x07 \x01(\x0c\x12\x1f\n\x17localIdentityKeyPrivate\x18\x08 \x01(\x0c\x1aJ\n\rPendingPreKey\x12\x10\n\x08preKeyId\x18\x01 \x01(\r\x12\x16\n\x0esignedPreKeyId\x18\x03 \x01(\x05\x12\x0f\n\x07\x62\x61seKey\x18\x02 \x01(\x0c\"\x7f\n\x0fRecordStructure\x12\x34\n\x0e\x63urrentSession\x18\x01 \x01(\x0b\x32\x1c.textsecure.SessionStructure\x12\x36\n\x10previousSessions\x18\x02 \x03(\x0b\x32\x1c.textsecure.SessionStructure\"J\n\x15PreKeyRecordStructure\x12\n\n\x02id\x18\x01 \x01(\r\x12\x11\n\tpublicKey\x18\x02 \x01(\x0c\x12\x12\n\nprivateKey\x18\x03 \x01(\x0c\"v\n\x1bSignedPreKeyRecordStructure\x12\n\n\x02id\x18\x01 \x01(\r\x12\x11\n\tpublicKey\x18\x02 \x01(\x0c\x12\x12\n\nprivateKey\x18\x03 \x01(\x0c\x12\x11\n\tsignature\x18\x04 \x01(\x0c\x12\x11\n\ttimestamp\x18\x05 \x01(\x06\"A\n\x18IdentityKeyPairStructure\x12\x11\n\tpublicKey\x18\x01 \x01(\x0c\x12\x12\n\nprivateKey\x18\x02 \x01(\x0c\"\xb8\x03\n\x17SenderKeyStateStructure\x12\x13\n\x0bsenderKeyId\x18\x01 \x01(\r\x12J\n\x0esenderChainKey\x18\x02 \x01(\x0b\x32\x32.textsecure.SenderKeyStateStructure.SenderChainKey\x12N\n\x10senderSigningKey\x18\x03 \x01(\x0b\x32\x34.textsecure.SenderKeyStateStructure.SenderSigningKey\x12O\n\x11senderMessageKeys\x18\x04 \x03(\x0b\x32\x34.textsecure.SenderKeyStateStructure.SenderMessageKey\x1a\x31\n\x0eSenderChainKey\x12\x11\n\titeration\x18\x01 \x01(\r\x12\x0c\n\x04seed\x18\x02 \x01(\x0c\x1a\x33\n\x10SenderMessageKey\x12\x11\n\titeration\x18\x01 \x01(\r\x12\x0c\n\x04seed\x18\x02 \x01(\x0c\x1a\x33\n\x10SenderSigningKey\x12\x0e\n\x06public\x18\x01 \x01(\x0c\x12\x0f\n\x07private\x18\x02 \x01(\x0c\"X\n\x18SenderKeyRecordStructure\x12<\n\x0fsenderKeyStates\x18\x01 \x03(\x0b\x32#.textsecure.SenderKeyStateStructureB4\n#org.whispersystems.libaxolotl.stateB\rStorageProtos') ) _sym_db.RegisterFileDescriptor(DESCRIPTOR) _SESSIONSTRUCTURE_CHAIN_CHAINKEY = _descriptor.Descriptor( name='ChainKey', full_name='textsecure.SessionStructure.Chain.ChainKey', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='index', full_name='textsecure.SessionStructure.Chain.ChainKey.index', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='key', full_name='textsecure.SessionStructure.Chain.ChainKey.key', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=752, serialized_end=790, ) _SESSIONSTRUCTURE_CHAIN_MESSAGEKEY = _descriptor.Descriptor( name='MessageKey', full_name='textsecure.SessionStructure.Chain.MessageKey', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='index', full_name='textsecure.SessionStructure.Chain.MessageKey.index', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='cipherKey', full_name='textsecure.SessionStructure.Chain.MessageKey.cipherKey', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='macKey', full_name='textsecure.SessionStructure.Chain.MessageKey.macKey', index=2, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='iv', full_name='textsecure.SessionStructure.Chain.MessageKey.iv', index=3, number=4, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=792, serialized_end=866, ) _SESSIONSTRUCTURE_CHAIN = _descriptor.Descriptor( name='Chain', full_name='textsecure.SessionStructure.Chain', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='senderRatchetKey', full_name='textsecure.SessionStructure.Chain.senderRatchetKey', index=0, number=1, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='senderRatchetKeyPrivate', full_name='textsecure.SessionStructure.Chain.senderRatchetKeyPrivate', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='chainKey', full_name='textsecure.SessionStructure.Chain.chainKey', index=2, number=3, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='messageKeys', full_name='textsecure.SessionStructure.Chain.messageKeys', index=3, number=4, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[_SESSIONSTRUCTURE_CHAIN_CHAINKEY, _SESSIONSTRUCTURE_CHAIN_MESSAGEKEY, ], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=553, serialized_end=866, ) _SESSIONSTRUCTURE_PENDINGKEYEXCHANGE = _descriptor.Descriptor( name='PendingKeyExchange', full_name='textsecure.SessionStructure.PendingKeyExchange', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='sequence', full_name='textsecure.SessionStructure.PendingKeyExchange.sequence', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='localBaseKey', full_name='textsecure.SessionStructure.PendingKeyExchange.localBaseKey', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='localBaseKeyPrivate', full_name='textsecure.SessionStructure.PendingKeyExchange.localBaseKeyPrivate', index=2, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='localRatchetKey', full_name='textsecure.SessionStructure.PendingKeyExchange.localRatchetKey', index=3, number=4, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='localRatchetKeyPrivate', full_name='textsecure.SessionStructure.PendingKeyExchange.localRatchetKeyPrivate', index=4, number=5, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='localIdentityKey', full_name='textsecure.SessionStructure.PendingKeyExchange.localIdentityKey', index=5, number=7, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='localIdentityKeyPrivate', full_name='textsecure.SessionStructure.PendingKeyExchange.localIdentityKeyPrivate', index=6, number=8, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=869, serialized_end=1074, ) _SESSIONSTRUCTURE_PENDINGPREKEY = _descriptor.Descriptor( name='PendingPreKey', full_name='textsecure.SessionStructure.PendingPreKey', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='preKeyId', full_name='textsecure.SessionStructure.PendingPreKey.preKeyId', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='signedPreKeyId', full_name='textsecure.SessionStructure.PendingPreKey.signedPreKeyId', index=1, number=3, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='baseKey', full_name='textsecure.SessionStructure.PendingPreKey.baseKey', index=2, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1076, serialized_end=1150, ) _SESSIONSTRUCTURE = _descriptor.Descriptor( name='SessionStructure', full_name='textsecure.SessionStructure', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='sessionVersion', full_name='textsecure.SessionStructure.sessionVersion', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='localIdentityPublic', full_name='textsecure.SessionStructure.localIdentityPublic', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='remoteIdentityPublic', full_name='textsecure.SessionStructure.remoteIdentityPublic', index=2, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='rootKey', full_name='textsecure.SessionStructure.rootKey', index=3, number=4, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='previousCounter', full_name='textsecure.SessionStructure.previousCounter', index=4, number=5, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='senderChain', full_name='textsecure.SessionStructure.senderChain', index=5, number=6, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='receiverChains', full_name='textsecure.SessionStructure.receiverChains', index=6, number=7, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='pendingKeyExchange', full_name='textsecure.SessionStructure.pendingKeyExchange', index=7, number=8, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='pendingPreKey', full_name='textsecure.SessionStructure.pendingPreKey', index=8, number=9, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='remoteRegistrationId', full_name='textsecure.SessionStructure.remoteRegistrationId', index=9, number=10, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='localRegistrationId', full_name='textsecure.SessionStructure.localRegistrationId', index=10, number=11, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='needsRefresh', full_name='textsecure.SessionStructure.needsRefresh', index=11, number=12, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='aliceBaseKey', full_name='textsecure.SessionStructure.aliceBaseKey', index=12, number=13, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[_SESSIONSTRUCTURE_CHAIN, _SESSIONSTRUCTURE_PENDINGKEYEXCHANGE, _SESSIONSTRUCTURE_PENDINGPREKEY, ], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=43, serialized_end=1150, ) _RECORDSTRUCTURE = _descriptor.Descriptor( name='RecordStructure', full_name='textsecure.RecordStructure', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='currentSession', full_name='textsecure.RecordStructure.currentSession', index=0, number=1, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='previousSessions', full_name='textsecure.RecordStructure.previousSessions', index=1, number=2, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1152, serialized_end=1279, ) _PREKEYRECORDSTRUCTURE = _descriptor.Descriptor( name='PreKeyRecordStructure', full_name='textsecure.PreKeyRecordStructure', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='textsecure.PreKeyRecordStructure.id', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='publicKey', full_name='textsecure.PreKeyRecordStructure.publicKey', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='privateKey', full_name='textsecure.PreKeyRecordStructure.privateKey', index=2, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1281, serialized_end=1355, ) _SIGNEDPREKEYRECORDSTRUCTURE = _descriptor.Descriptor( name='SignedPreKeyRecordStructure', full_name='textsecure.SignedPreKeyRecordStructure', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='id', full_name='textsecure.SignedPreKeyRecordStructure.id', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='publicKey', full_name='textsecure.SignedPreKeyRecordStructure.publicKey', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='privateKey', full_name='textsecure.SignedPreKeyRecordStructure.privateKey', index=2, number=3, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='signature', full_name='textsecure.SignedPreKeyRecordStructure.signature', index=3, number=4, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='timestamp', full_name='textsecure.SignedPreKeyRecordStructure.timestamp', index=4, number=5, type=6, cpp_type=4, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1357, serialized_end=1475, ) _IDENTITYKEYPAIRSTRUCTURE = _descriptor.Descriptor( name='IdentityKeyPairStructure', full_name='textsecure.IdentityKeyPairStructure', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='publicKey', full_name='textsecure.IdentityKeyPairStructure.publicKey', index=0, number=1, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='privateKey', full_name='textsecure.IdentityKeyPairStructure.privateKey', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1477, serialized_end=1542, ) _SENDERKEYSTATESTRUCTURE_SENDERCHAINKEY = _descriptor.Descriptor( name='SenderChainKey', full_name='textsecure.SenderKeyStateStructure.SenderChainKey', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='iteration', full_name='textsecure.SenderKeyStateStructure.SenderChainKey.iteration', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='seed', full_name='textsecure.SenderKeyStateStructure.SenderChainKey.seed', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1830, serialized_end=1879, ) _SENDERKEYSTATESTRUCTURE_SENDERMESSAGEKEY = _descriptor.Descriptor( name='SenderMessageKey', full_name='textsecure.SenderKeyStateStructure.SenderMessageKey', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='iteration', full_name='textsecure.SenderKeyStateStructure.SenderMessageKey.iteration', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='seed', full_name='textsecure.SenderKeyStateStructure.SenderMessageKey.seed', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1881, serialized_end=1932, ) _SENDERKEYSTATESTRUCTURE_SENDERSIGNINGKEY = _descriptor.Descriptor( name='SenderSigningKey', full_name='textsecure.SenderKeyStateStructure.SenderSigningKey', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='public', full_name='textsecure.SenderKeyStateStructure.SenderSigningKey.public', index=0, number=1, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='private', full_name='textsecure.SenderKeyStateStructure.SenderSigningKey.private', index=1, number=2, type=12, cpp_type=9, label=1, has_default_value=False, default_value=_b(""), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1934, serialized_end=1985, ) _SENDERKEYSTATESTRUCTURE = _descriptor.Descriptor( name='SenderKeyStateStructure', full_name='textsecure.SenderKeyStateStructure', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='senderKeyId', full_name='textsecure.SenderKeyStateStructure.senderKeyId', index=0, number=1, type=13, cpp_type=3, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='senderChainKey', full_name='textsecure.SenderKeyStateStructure.senderChainKey', index=1, number=2, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='senderSigningKey', full_name='textsecure.SenderKeyStateStructure.senderSigningKey', index=2, number=3, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='senderMessageKeys', full_name='textsecure.SenderKeyStateStructure.senderMessageKeys', index=3, number=4, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[_SENDERKEYSTATESTRUCTURE_SENDERCHAINKEY, _SENDERKEYSTATESTRUCTURE_SENDERMESSAGEKEY, _SENDERKEYSTATESTRUCTURE_SENDERSIGNINGKEY, ], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1545, serialized_end=1985, ) _SENDERKEYRECORDSTRUCTURE = _descriptor.Descriptor( name='SenderKeyRecordStructure', full_name='textsecure.SenderKeyRecordStructure', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='senderKeyStates', full_name='textsecure.SenderKeyRecordStructure.senderKeyStates', index=0, number=1, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, extension_ranges=[], oneofs=[ ], serialized_start=1987, serialized_end=2075, ) _SESSIONSTRUCTURE_CHAIN_CHAINKEY.containing_type = _SESSIONSTRUCTURE_CHAIN _SESSIONSTRUCTURE_CHAIN_MESSAGEKEY.containing_type = _SESSIONSTRUCTURE_CHAIN _SESSIONSTRUCTURE_CHAIN.fields_by_name['chainKey'].message_type = _SESSIONSTRUCTURE_CHAIN_CHAINKEY _SESSIONSTRUCTURE_CHAIN.fields_by_name['messageKeys'].message_type = _SESSIONSTRUCTURE_CHAIN_MESSAGEKEY _SESSIONSTRUCTURE_CHAIN.containing_type = _SESSIONSTRUCTURE _SESSIONSTRUCTURE_PENDINGKEYEXCHANGE.containing_type = _SESSIONSTRUCTURE _SESSIONSTRUCTURE_PENDINGPREKEY.containing_type = _SESSIONSTRUCTURE _SESSIONSTRUCTURE.fields_by_name['senderChain'].message_type = _SESSIONSTRUCTURE_CHAIN _SESSIONSTRUCTURE.fields_by_name['receiverChains'].message_type = _SESSIONSTRUCTURE_CHAIN _SESSIONSTRUCTURE.fields_by_name['pendingKeyExchange'].message_type = _SESSIONSTRUCTURE_PENDINGKEYEXCHANGE _SESSIONSTRUCTURE.fields_by_name['pendingPreKey'].message_type = _SESSIONSTRUCTURE_PENDINGPREKEY _RECORDSTRUCTURE.fields_by_name['currentSession'].message_type = _SESSIONSTRUCTURE _RECORDSTRUCTURE.fields_by_name['previousSessions'].message_type = _SESSIONSTRUCTURE _SENDERKEYSTATESTRUCTURE_SENDERCHAINKEY.containing_type = _SENDERKEYSTATESTRUCTURE _SENDERKEYSTATESTRUCTURE_SENDERMESSAGEKEY.containing_type = _SENDERKEYSTATESTRUCTURE _SENDERKEYSTATESTRUCTURE_SENDERSIGNINGKEY.containing_type = _SENDERKEYSTATESTRUCTURE _SENDERKEYSTATESTRUCTURE.fields_by_name['senderChainKey'].message_type = _SENDERKEYSTATESTRUCTURE_SENDERCHAINKEY _SENDERKEYSTATESTRUCTURE.fields_by_name['senderSigningKey'].message_type = _SENDERKEYSTATESTRUCTURE_SENDERSIGNINGKEY _SENDERKEYSTATESTRUCTURE.fields_by_name['senderMessageKeys'].message_type = _SENDERKEYSTATESTRUCTURE_SENDERMESSAGEKEY _SENDERKEYRECORDSTRUCTURE.fields_by_name['senderKeyStates'].message_type = _SENDERKEYSTATESTRUCTURE DESCRIPTOR.message_types_by_name['SessionStructure'] = _SESSIONSTRUCTURE DESCRIPTOR.message_types_by_name['RecordStructure'] = _RECORDSTRUCTURE DESCRIPTOR.message_types_by_name['PreKeyRecordStructure'] = _PREKEYRECORDSTRUCTURE DESCRIPTOR.message_types_by_name['SignedPreKeyRecordStructure'] = _SIGNEDPREKEYRECORDSTRUCTURE DESCRIPTOR.message_types_by_name['IdentityKeyPairStructure'] = _IDENTITYKEYPAIRSTRUCTURE DESCRIPTOR.message_types_by_name['SenderKeyStateStructure'] = _SENDERKEYSTATESTRUCTURE DESCRIPTOR.message_types_by_name['SenderKeyRecordStructure'] = _SENDERKEYRECORDSTRUCTURE SessionStructure = _reflection.GeneratedProtocolMessageType('SessionStructure', (_message.Message,), dict( Chain = _reflection.GeneratedProtocolMessageType('Chain', (_message.Message,), dict( ChainKey = _reflection.GeneratedProtocolMessageType('ChainKey', (_message.Message,), dict( DESCRIPTOR = _SESSIONSTRUCTURE_CHAIN_CHAINKEY, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain.ChainKey) )) , MessageKey = _reflection.GeneratedProtocolMessageType('MessageKey', (_message.Message,), dict( DESCRIPTOR = _SESSIONSTRUCTURE_CHAIN_MESSAGEKEY, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain.MessageKey) )) , DESCRIPTOR = _SESSIONSTRUCTURE_CHAIN, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SessionStructure.Chain) )) , PendingKeyExchange = _reflection.GeneratedProtocolMessageType('PendingKeyExchange', (_message.Message,), dict( DESCRIPTOR = _SESSIONSTRUCTURE_PENDINGKEYEXCHANGE, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SessionStructure.PendingKeyExchange) )) , PendingPreKey = _reflection.GeneratedProtocolMessageType('PendingPreKey', (_message.Message,), dict( DESCRIPTOR = _SESSIONSTRUCTURE_PENDINGPREKEY, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SessionStructure.PendingPreKey) )) , DESCRIPTOR = _SESSIONSTRUCTURE, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SessionStructure) )) _sym_db.RegisterMessage(SessionStructure) _sym_db.RegisterMessage(SessionStructure.Chain) _sym_db.RegisterMessage(SessionStructure.Chain.ChainKey) _sym_db.RegisterMessage(SessionStructure.Chain.MessageKey) _sym_db.RegisterMessage(SessionStructure.PendingKeyExchange) _sym_db.RegisterMessage(SessionStructure.PendingPreKey) RecordStructure = _reflection.GeneratedProtocolMessageType('RecordStructure', (_message.Message,), dict( DESCRIPTOR = _RECORDSTRUCTURE, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.RecordStructure) )) _sym_db.RegisterMessage(RecordStructure) PreKeyRecordStructure = _reflection.GeneratedProtocolMessageType('PreKeyRecordStructure', (_message.Message,), dict( DESCRIPTOR = _PREKEYRECORDSTRUCTURE, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.PreKeyRecordStructure) )) _sym_db.RegisterMessage(PreKeyRecordStructure) SignedPreKeyRecordStructure = _reflection.GeneratedProtocolMessageType('SignedPreKeyRecordStructure', (_message.Message,), dict( DESCRIPTOR = _SIGNEDPREKEYRECORDSTRUCTURE, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SignedPreKeyRecordStructure) )) _sym_db.RegisterMessage(SignedPreKeyRecordStructure) IdentityKeyPairStructure = _reflection.GeneratedProtocolMessageType('IdentityKeyPairStructure', (_message.Message,), dict( DESCRIPTOR = _IDENTITYKEYPAIRSTRUCTURE, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.IdentityKeyPairStructure) )) _sym_db.RegisterMessage(IdentityKeyPairStructure) SenderKeyStateStructure = _reflection.GeneratedProtocolMessageType('SenderKeyStateStructure', (_message.Message,), dict( SenderChainKey = _reflection.GeneratedProtocolMessageType('SenderChainKey', (_message.Message,), dict( DESCRIPTOR = _SENDERKEYSTATESTRUCTURE_SENDERCHAINKEY, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderChainKey) )) , SenderMessageKey = _reflection.GeneratedProtocolMessageType('SenderMessageKey', (_message.Message,), dict( DESCRIPTOR = _SENDERKEYSTATESTRUCTURE_SENDERMESSAGEKEY, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderMessageKey) )) , SenderSigningKey = _reflection.GeneratedProtocolMessageType('SenderSigningKey', (_message.Message,), dict( DESCRIPTOR = _SENDERKEYSTATESTRUCTURE_SENDERSIGNINGKEY, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure.SenderSigningKey) )) , DESCRIPTOR = _SENDERKEYSTATESTRUCTURE, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SenderKeyStateStructure) )) _sym_db.RegisterMessage(SenderKeyStateStructure) _sym_db.RegisterMessage(SenderKeyStateStructure.SenderChainKey) _sym_db.RegisterMessage(SenderKeyStateStructure.SenderMessageKey) _sym_db.RegisterMessage(SenderKeyStateStructure.SenderSigningKey) SenderKeyRecordStructure = _reflection.GeneratedProtocolMessageType('SenderKeyRecordStructure', (_message.Message,), dict( DESCRIPTOR = _SENDERKEYRECORDSTRUCTURE, __module__ = 'LocalStorageProtocol_pb2' # @@protoc_insertion_point(class_scope:textsecure.SenderKeyRecordStructure) )) _sym_db.RegisterMessage(SenderKeyRecordStructure) DESCRIPTOR.has_options = True DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('\n#org.whispersystems.libaxolotl.stateB\rStorageProtos')) # @@protoc_insertion_point(module_scope) python-axolotl-0.2.3/axolotl/statekeyexchangeexception.py0000644000175000017500000000011713471043576024273 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class StaleKeyExchangeException(Exception): pass python-axolotl-0.2.3/axolotl/tests/0000755000175000017500000000000013471043614017602 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/tests/__init__.py0000644000175000017500000000003013471043576021713 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- python-axolotl-0.2.3/axolotl/tests/groups/0000755000175000017500000000000013471043614021121 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/tests/groups/__init__.py0000644000175000017500000000003013471043576023232 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- python-axolotl-0.2.3/axolotl/tests/groups/inmemorysenderkeystore.py0000644000175000017500000000105613471043576026332 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ...groups.state.senderkeystore import SenderKeyStore from ...groups.state.senderkeyrecord import SenderKeyRecord class InMemorySenderKeyStore(SenderKeyStore): def __init__(self): self.store = {} def storeSenderKey(self, senderKeyName, senderKeyRecord): self.store[senderKeyName] = senderKeyRecord def loadSenderKey(self, senderKeyName): if senderKeyName in self.store: return SenderKeyRecord(serialized=self.store[senderKeyName].serialize()) return SenderKeyRecord() python-axolotl-0.2.3/axolotl/tests/groups/test_groupcipher.py0000644000175000017500000001321113471043576025066 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import unittest from .inmemorysenderkeystore import InMemorySenderKeyStore from ...groups.groupsessionbuilder import GroupSessionBuilder from ...util.keyhelper import KeyHelper from ...groups.groupcipher import GroupCipher from ...duplicatemessagexception import DuplicateMessageException from ...nosessionexception import NoSessionException from ...groups.senderkeyname import SenderKeyName from ...axolotladdress import AxolotlAddress from ...protocol.senderkeydistributionmessage import SenderKeyDistributionMessage SENDER_ADDRESS = AxolotlAddress("+14150001111", 1) GROUP_SENDER = SenderKeyName("nihilist history reading group", SENDER_ADDRESS); class GroupCipherTest(unittest.TestCase): def test_noSession(self): aliceStore = InMemorySenderKeyStore(); bobStore = InMemorySenderKeyStore(); aliceSessionBuilder = GroupSessionBuilder(aliceStore) bobSessionBuilder = GroupSessionBuilder(bobStore) aliceGroupCipher = GroupCipher(aliceStore, GROUP_SENDER) bobGroupCipher = GroupCipher(bobStore, GROUP_SENDER); sentAliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER); receivedAliceDistributionMessage = SenderKeyDistributionMessage(serialized = sentAliceDistributionMessage.serialize()); ciphertextFromAlice = aliceGroupCipher.encrypt(b"smert ze smert"); try: plaintextFromAlice = bobGroupCipher.decrypt(ciphertextFromAlice); raise AssertionError("Should be no session!"); except NoSessionException as e: pass def test_basicEncryptDecrypt(self): aliceStore = InMemorySenderKeyStore(); bobStore = InMemorySenderKeyStore(); aliceSessionBuilder = GroupSessionBuilder(aliceStore) bobSessionBuilder = GroupSessionBuilder(bobStore) aliceGroupCipher = GroupCipher(aliceStore, GROUP_SENDER) bobGroupCipher = GroupCipher(bobStore, GROUP_SENDER); sentAliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER); receivedAliceDistributionMessage = SenderKeyDistributionMessage(serialized = sentAliceDistributionMessage.serialize()); bobSessionBuilder.process(GROUP_SENDER, receivedAliceDistributionMessage) ciphertextFromAlice = aliceGroupCipher.encrypt(b"smert ze smert") plaintextFromAlice = bobGroupCipher.decrypt(ciphertextFromAlice) self.assertEqual(plaintextFromAlice, b"smert ze smert") def test_basicRatchet(self): aliceStore = InMemorySenderKeyStore() bobStore = InMemorySenderKeyStore() aliceSessionBuilder = GroupSessionBuilder(aliceStore) bobSessionBuilder = GroupSessionBuilder(bobStore) aliceGroupCipher = GroupCipher(aliceStore, "groupWithBobInIt") bobGroupCipher = GroupCipher(bobStore, "groupWithBobInIt::aliceUserName") aliceGroupCipher = GroupCipher(aliceStore, GROUP_SENDER) bobGroupCipher = GroupCipher(bobStore, GROUP_SENDER); sentAliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER); receivedAliceDistributionMessage = SenderKeyDistributionMessage(serialized = sentAliceDistributionMessage.serialize()); bobSessionBuilder.process(GROUP_SENDER, receivedAliceDistributionMessage) ciphertextFromAlice = aliceGroupCipher.encrypt(b"smert ze smert") ciphertextFromAlice2 = aliceGroupCipher.encrypt(b"smert ze smert2") ciphertextFromAlice3 = aliceGroupCipher.encrypt(b"smert ze smert3") plaintextFromAlice = bobGroupCipher.decrypt(ciphertextFromAlice) try: bobGroupCipher.decrypt(ciphertextFromAlice) raise AssertionError("Should have ratcheted forward!") except DuplicateMessageException as dme: # good pass plaintextFromAlice2 = bobGroupCipher.decrypt(ciphertextFromAlice2) plaintextFromAlice3 = bobGroupCipher.decrypt(ciphertextFromAlice3) self.assertEqual(plaintextFromAlice, b"smert ze smert") self.assertEqual(plaintextFromAlice2, b"smert ze smert2") self.assertEqual(plaintextFromAlice3, b"smert ze smert3") def test_outOfOrder(self): aliceStore = InMemorySenderKeyStore(); bobStore = InMemorySenderKeyStore(); aliceSessionBuilder = GroupSessionBuilder(aliceStore) bobSessionBuilder = GroupSessionBuilder(bobStore) aliceGroupCipher = GroupCipher(aliceStore, "groupWithBobInIt") bobGroupCipher = GroupCipher(bobStore, "groupWithBobInIt::aliceUserName") aliceGroupCipher = GroupCipher(aliceStore, GROUP_SENDER) bobGroupCipher = GroupCipher(bobStore, GROUP_SENDER); sentAliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER); receivedAliceDistributionMessage = SenderKeyDistributionMessage(serialized = sentAliceDistributionMessage.serialize()); bobSessionBuilder.process(GROUP_SENDER, receivedAliceDistributionMessage) ciphertexts = [] for i in range(0, 100): ciphertexts.append(aliceGroupCipher.encrypt(b"up the punks")) while len(ciphertexts) > 0: index = KeyHelper.getRandomSequence(2147483647) % len(ciphertexts) ciphertext = ciphertexts.pop(index) plaintext = bobGroupCipher.decrypt(ciphertext) self.assertEqual(plaintext, b"up the punks") def test_encryptNoSession(self): aliceStore = InMemorySenderKeyStore() aliceGroupCipher = GroupCipher(aliceStore, "groupWithBobInIt") try: aliceGroupCipher.encrypt(b"up the punks") raise AssertionError("Should have failed!") except NoSessionException as nse: # good passpython-axolotl-0.2.3/axolotl/tests/inmemoryaxolotlstore.py0000644000175000017500000000532713471043576024511 0ustar tarektarek00000000000000# -*- coding: utf8 -*- from ..state.axolotlstore import AxolotlStore from .inmemoryidentitykeystore import InMemoryIdentityKeyStore from .inmemoryprekeystore import InMemoryPreKeyStore from .inmemorysessionstore import InMemorySessionStore from .inmemorysignedprekeystore import InMemorySignedPreKeyStore class InMemoryAxolotlStore(AxolotlStore): def __init__(self): self.identityKeyStore = InMemoryIdentityKeyStore() self.preKeyStore = InMemoryPreKeyStore() self.signedPreKeyStore = InMemorySignedPreKeyStore() self.sessionStore = InMemorySessionStore() def getIdentityKeyPair(self): return self.identityKeyStore.getIdentityKeyPair() def getLocalRegistrationId(self): return self.identityKeyStore.getLocalRegistrationId() def saveIdentity(self, recepientId, identityKey): self.identityKeyStore.saveIdentity(recepientId, identityKey) def isTrustedIdentity(self, recepientId, identityKey): return self.identityKeyStore.isTrustedIdentity(recepientId, identityKey) def loadPreKey(self, preKeyId): return self.preKeyStore.loadPreKey(preKeyId) def storePreKey(self, preKeyId, preKeyRecord): self.preKeyStore.storePreKey(preKeyId, preKeyRecord) def containsPreKey(self, preKeyId): return self.preKeyStore.containsPreKey(preKeyId) def removePreKey(self, preKeyId): self.preKeyStore.removePreKey(preKeyId) def loadSession(self, recepientId, deviceId): return self.sessionStore.loadSession(recepientId, deviceId) def getSubDeviceSessions(self, recepientId): return self.sessionStore.getSubDeviceSessions(recepientId) def storeSession(self, recepientId, deviceId, sessionRecord): self.sessionStore.storeSession(recepientId, deviceId, sessionRecord) def containsSession(self, recepientId, deviceId): return self.sessionStore.containsSession(recepientId, deviceId) def deleteSession(self, recepientId, deviceId): self.sessionStore.deleteSession(recepientId, deviceId) def deleteAllSessions(self, recepientId): self.sessionStore.deleteAllSessions(recepientId) def loadSignedPreKey(self, signedPreKeyId): return self.signedPreKeyStore.loadSignedPreKey(signedPreKeyId) def loadSignedPreKeys(self): return self.signedPreKeyStore.loadSignedPreKeys() def storeSignedPreKey(self, signedPreKeyId, signedPreKeyRecord): self.signedPreKeyStore.storeSignedPreKey(signedPreKeyId, signedPreKeyRecord) def containsSignedPreKey(self, signedPreKeyId): return self.signedPreKeyStore.containsSignedPreKey(signedPreKeyId) def removeSignedPreKey(self, signedPreKeyId): return self.signedPreKeyStore.containsSignedPreKey() python-axolotl-0.2.3/axolotl/tests/inmemoryidentitykeystore.py0000644000175000017500000000210313471043576025356 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..state.identitykeystore import IdentityKeyStore from ..ecc.curve import Curve from ..identitykey import IdentityKey from ..util.keyhelper import KeyHelper from ..identitykeypair import IdentityKeyPair class InMemoryIdentityKeyStore(IdentityKeyStore): def __init__(self): self.trustedKeys = {} identityKeyPairKeys = Curve.generateKeyPair() self.identityKeyPair = IdentityKeyPair(IdentityKey(identityKeyPairKeys.getPublicKey()), identityKeyPairKeys.getPrivateKey()) self.localRegistrationId = KeyHelper.generateRegistrationId() def getIdentityKeyPair(self): return self.identityKeyPair def getLocalRegistrationId(self): return self.localRegistrationId def saveIdentity(self, recepientId, identityKey): self.trustedKeys[recepientId] = identityKey def isTrustedIdentity(self, recepientId, identityKey): if recepientId not in self.trustedKeys: return True return self.trustedKeys[recepientId] == identityKey python-axolotl-0.2.3/axolotl/tests/inmemoryprekeystore.py0000644000175000017500000000137413471043576024324 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..state.prekeystore import PreKeyStore from ..state.prekeyrecord import PreKeyRecord from ..invalidkeyidexception import InvalidKeyIdException class InMemoryPreKeyStore(PreKeyStore): def __init__(self): self.store = {} def loadPreKey(self, preKeyId): if preKeyId not in self.store: raise InvalidKeyIdException("No such prekeyRecord!") return PreKeyRecord(serialized=self.store[preKeyId]) def storePreKey(self, preKeyId, preKeyRecord): self.store[preKeyId] = preKeyRecord.serialize() def containsPreKey(self, preKeyId): return preKeyId in self.store def removePreKey(self, preKeyId): if preKeyId in self.store: del self.store[preKeyId] python-axolotl-0.2.3/axolotl/tests/inmemorysessionstore.py0000644000175000017500000000223713471043576024507 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..state.sessionstore import SessionStore from ..state.sessionrecord import SessionRecord class InMemorySessionStore(SessionStore): def __init__(self): self.sessions = {} def loadSession(self, recepientId, deviceId): if self.containsSession(recepientId, deviceId): return SessionRecord(serialized=self.sessions[(recepientId, deviceId)]) else: return SessionRecord() def getSubDeviceSessions(self, recepientId): deviceIds = [] for k in self.sessions.keys(): if k[0] == recepientId: deviceIds.append(k[1]) return deviceIds def storeSession(self, recepientId, deviceId, sessionRecord): self.sessions[(recepientId, deviceId)] = sessionRecord.serialize() def containsSession(self, recepientId, deviceId): return (recepientId, deviceId) in self.sessions def deleteSession(self, recepientId, deviceId): del self.sessions[(recepientId, deviceId)] def deleteAllSessions(self, recepientId): for k in self.sessions.keys(): if k[0] == recepientId: del self.sessions[k] python-axolotl-0.2.3/axolotl/tests/inmemorysignedprekeystore.py0000644000175000017500000000207013471043576025510 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- from ..state.signedprekeystore import SignedPreKeyStore from ..state.signedprekeyrecord import SignedPreKeyRecord from ..invalidkeyidexception import InvalidKeyIdException class InMemorySignedPreKeyStore(SignedPreKeyStore): def __init__(self): self.store = {} def loadSignedPreKey(self, signedPreKeyId): if signedPreKeyId not in self.store: raise InvalidKeyIdException("No such signedprekeyrecord! %s " % signedPreKeyId) return SignedPreKeyRecord(serialized=self.store[signedPreKeyId]) def loadSignedPreKeys(self): results = [] for serialized in self.store.values(): results.append(SignedPreKeyRecord(serialized=serialized)) return results def storeSignedPreKey(self, signedPreKeyId, signedPreKeyRecord): self.store[signedPreKeyId] = signedPreKeyRecord.serialize() def containsSignedPreKey(self, signedPreKeyId): return signedPreKeyId in self.store def removeSignedPreKey(self, signedPreKeyId): del self.store[signedPreKeyId] python-axolotl-0.2.3/axolotl/tests/kdf/0000755000175000017500000000000013471043614020346 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/tests/kdf/__init__.py0000644000175000017500000000005613471043576022467 0ustar tarektarek00000000000000# -*- cosing: utf-8 -*- __author__ = 'tarek' python-axolotl-0.2.3/axolotl/tests/kdf/test_hkdf.py0000644000175000017500000001116513471043576022706 0ustar tarektarek00000000000000# -*- coding ;utf-8 -*- import unittest from ...kdf.hkdf import HKDF class HKDFTest(unittest.TestCase): def test_vectorV3(self): ikm = bytearray([0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b]) salt = bytearray([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c]) info = bytearray([0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9]) okm = bytearray([0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65]) actualOutput = HKDF.createFor(3).deriveSecrets(ikm, info, 42, salt=salt) self.assertEqual(okm, actualOutput) def test_vectorLongV3(self): ikm = bytearray([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f]) salt = bytearray([0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf]) info = bytearray([0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff]) okm = bytearray([0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87]) actualOutput = HKDF.createFor(3).deriveSecrets(ikm, info, 82, salt=salt) self.assertEqual(okm, actualOutput) def test_vectorV3_(self): ikm = bytearray([0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b]) salt = bytearray([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c]) info = bytearray([0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9]) okm = bytearray([0x6e, 0xc2, 0x55, 0x6d, 0x5d, 0x7b, 0x1d, 0x81, 0xde, 0xe4, 0x22, 0x2a, 0xd7, 0x48, 0x36, 0x95, 0xdd, 0xc9, 0x8f, 0x4f, 0x5f, 0xab, 0xc0, 0xe0, 0x20, 0x5d, 0xc2, 0xef, 0x87, 0x52, 0xd4, 0x1e, 0x04, 0xe2, 0xe2, 0x11, 0x01, 0xc6, 0x8f, 0xf0, 0x93, 0x94, 0xb8, 0xad, 0x0b, 0xdc, 0xb9, 0x60, 0x9c, 0xd4, 0xee, 0x82, 0xac, 0x13, 0x19, 0x9b, 0x4a, 0xa9, 0xfd, 0xa8, 0x99, 0xda, 0xeb, 0xec]) actualOutput = HKDF.createFor(2).deriveSecrets(ikm, info, 64, salt=salt) self.assertEqual(okm, actualOutput) python-axolotl-0.2.3/axolotl/tests/ratchet/0000755000175000017500000000000013471043614021234 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/tests/ratchet/__init__.py0000644000175000017500000000005613471043576023355 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- __author__ = 'tarek' python-axolotl-0.2.3/axolotl/tests/ratchet/test_chainkey.py0000644000175000017500000000361513471043576024454 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import unittest from ...ratchet.chainkey import ChainKey from ...kdf.hkdf import HKDF class ChainKeyTest(unittest.TestCase): def test_chainKeyDerivationV2(self): seed = bytearray([0x8a, 0xb7, 0x2d, 0x6f, 0x4c, 0xc5, 0xac, 0x0d, 0x38, 0x7e, 0xaf, 0x46, 0x33, 0x78, 0xdd, 0xb2, 0x8e, 0xdd, 0x07, 0x38, 0x5b, 0x1c, 0xb0, 0x12, 0x50, 0xc7, 0x15, 0x98, 0x2e, 0x7a, 0xd4, 0x8f]) messageKey = bytearray([0x02, 0xa9, 0xaa, 0x6c, 0x7d, 0xbd, 0x64, 0xf9, 0xd3, 0xaa, 0x92, 0xf9, 0x2a, 0x27, 0x7b, 0xf5, 0x46, 0x09, 0xda, 0xdf, 0x0b, 0x00, 0x82, 0x8a, 0xcf, 0xc6, 0x1e, 0x3c, 0x72, 0x4b, 0x84, 0xa7]) macKey = bytearray([0xbf, 0xbe, 0x5e, 0xfb, 0x60, 0x30, 0x30, 0x52, 0x67, 0x42, 0xe3, 0xee, 0x89, 0xc7, 0x02, 0x4e, 0x88, 0x4e, 0x44, 0x0f, 0x1f, 0xf3, 0x76, 0xbb, 0x23, 0x17, 0xb2, 0xd6, 0x4d, 0xeb, 0x7c, 0x83]) nextChainKey = bytearray([0x28, 0xe8, 0xf8, 0xfe, 0xe5, 0x4b, 0x80, 0x1e, 0xef, 0x7c, 0x5c, 0xfb, 0x2f, 0x17, 0xf3, 0x2c, 0x7b, 0x33, 0x44, 0x85, 0xbb, 0xb7, 0x0f, 0xac, 0x6e, 0xc1, 0x03, 0x42, 0xa2, 0x46, 0xd1, 0x5d]) chainKey = ChainKey(HKDF.createFor(2), seed, 0) self.assertEqual(chainKey.getKey(), seed) self.assertEqual(chainKey.getMessageKeys().getCipherKey(), messageKey) self.assertEqual(chainKey.getMessageKeys().getMacKey(), macKey) self.assertEqual(chainKey.getNextChainKey().getKey(), nextChainKey) self.assertEqual(chainKey.getIndex(), 0) self.assertEqual(chainKey.getMessageKeys().getCounter(), 0) self.assertEqual(chainKey.getNextChainKey().getIndex(), 1) self.assertEqual(chainKey.getNextChainKey().getMessageKeys().getCounter(), 1) python-axolotl-0.2.3/axolotl/tests/ratchet/test_ratchetingsession.py0000644000175000017500000001212013471043576026404 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import unittest from ...identitykey import IdentityKey from ...identitykeypair import IdentityKeyPair from ...ecc.curve import Curve from ...ecc.eckeypair import ECKeyPair from ...ratchet.bobaxolotlparamaters import BobAxolotlParameters from ...state.sessionstate import SessionState from ...ratchet.ratchetingsession import RatchetingSession class RatchetingSessionTest(unittest.TestCase): def test_ratchetingSessionAsBob(self): bobPublic = bytearray([0x05, 0x2c, 0xb4, 0x97, 0x76, 0xb8, 0x77, 0x02, 0x05, 0x74, 0x5a, 0x3a, 0x6e, 0x24, 0xf5, 0x79, 0xcd, 0xb4, 0xba, 0x7a, 0x89, 0x04, 0x10, 0x05, 0x92, 0x8e, 0xbb, 0xad, 0xc9, 0xc0, 0x5a, 0xd4, 0x58]) bobPrivate = bytearray([0xa1, 0xca, 0xb4, 0x8f, 0x7c, 0x89, 0x3f, 0xaf, 0xa9, 0x88, 0x0a, 0x28, 0xc3, 0xb4, 0x99, 0x9d, 0x28, 0xd6, 0x32, 0x95, 0x62, 0xd2, 0x7a, 0x4e, 0xa4, 0xe2, 0x2e, 0x9f, 0xf1, 0xbd, 0xd6, 0x5a]) bobIdentityPublic = bytearray([0x05, 0xf1, 0xf4, 0x38, 0x74, 0xf6, 0x96, 0x69, 0x56, 0xc2, 0xdd, 0x47, 0x3f, 0x8f, 0xa1, 0x5a, 0xde, 0xb7, 0x1d, 0x1c, 0xb9, 0x91, 0xb2, 0x34, 0x16, 0x92, 0x32, 0x4c, 0xef, 0xb1, 0xc5, 0xe6, 0x26]) bobIdentityPrivate = bytearray([0x48, 0x75, 0xcc, 0x69, 0xdd, 0xf8, 0xea, 0x07, 0x19, 0xec, 0x94, 0x7d, 0x61, 0x08, 0x11, 0x35, 0x86, 0x8d, 0x5f, 0xd8, 0x01, 0xf0, 0x2c, 0x02, 0x25, 0xe5, 0x16, 0xdf, 0x21, 0x56, 0x60, 0x5e]) aliceBasePublic = bytearray([0x05, 0x47, 0x2d, 0x1f, 0xb1, 0xa9, 0x86, 0x2c, 0x3a, 0xf6, 0xbe, 0xac, 0xa8, 0x92, 0x02, 0x77, 0xe2, 0xb2, 0x6f, 0x4a, 0x79, 0x21, 0x3e, 0xc7, 0xc9, 0x06, 0xae, 0xb3, 0x5e, 0x03, 0xcf, 0x89, 0x50]) aliceEphemeralPublic = bytearray([0x05, 0x6c, 0x3e, 0x0d, 0x1f, 0x52, 0x02, 0x83, 0xef, 0xcc, 0x55, 0xfc, 0xa5, 0xe6, 0x70, 0x75, 0xb9, 0x04, 0x00, 0x7f, 0x18, 0x81, 0xd1, 0x51, 0xaf, 0x76, 0xdf, 0x18, 0xc5, 0x1d, 0x29, 0xd3, 0x4b]) aliceIdentityPublic = bytearray([0x05, 0xb4, 0xa8, 0x45, 0x56, 0x60, 0xad, 0xa6, 0x5b, 0x40, 0x10, 0x07, 0xf6, 0x15, 0xe6, 0x54, 0x04, 0x17, 0x46, 0x43, 0x2e, 0x33, 0x39, 0xc6, 0x87, 0x51, 0x49, 0xbc, 0xee, 0xfc, 0xb4, 0x2b, 0x4a]) bobSignedPreKeyPublic = bytearray([ 0x05, 0xac, 0x24, 0x8a, 0x8f, 0x26, 0x3b, 0xe6, 0x86, 0x35, 0x76, 0xeb, 0x03, 0x62, 0xe2, 0x8c, 0x82, 0x8f, 0x01, 0x07, 0xa3, 0x37, 0x9d, 0x34, 0xba, 0xb1, 0x58, 0x6b, 0xf8, 0xc7, 0x70, 0xcd, 0x67 ]) bobSignedPreKeyPrivate = bytearray([ 0x58, 0x39, 0x00, 0x13, 0x1f, 0xb7, 0x27, 0x99, 0x8b, 0x78, 0x03, 0xfe, 0x6a, 0xc2, 0x2c, 0xc5, 0x91, 0xf3, 0x42, 0xe4, 0xe4, 0x2a, 0x8c, 0x8d, 0x5d, 0x78, 0x19, 0x42, 0x09, 0xb8, 0xd2, 0x53]) senderChain = bytearray([ 0x97, 0x97, 0xca, 0xca, 0x53, 0xc9, 0x89, 0xbb, 0xe2, 0x29, 0xa4, 0x0c, 0xa7, 0x72, 0x70, 0x10, 0xeb, 0x26, 0x04, 0xfc, 0x14, 0x94, 0x5d, 0x77, 0x95, 0x8a, 0x0a, 0xed, 0xa0, 0x88, 0xb4, 0x4d]) bobIdentityKeyPublic = IdentityKey(bobIdentityPublic, 0) bobIdentityKeyPrivate = Curve.decodePrivatePoint(bobIdentityPrivate) bobIdentityKey = IdentityKeyPair(bobIdentityKeyPublic, bobIdentityKeyPrivate) bobEphemeralPublicKey = Curve.decodePoint(bobPublic, 0) bobEphemeralPrivateKey = Curve.decodePrivatePoint(bobPrivate) bobEphemeralKey = ECKeyPair(bobEphemeralPublicKey, bobEphemeralPrivateKey) bobBaseKey = bobEphemeralKey bobSignedPreKey = ECKeyPair(Curve.decodePoint(bobSignedPreKeyPublic, 0), Curve.decodePrivatePoint(bobSignedPreKeyPrivate)) aliceBasePublicKey = Curve.decodePoint(aliceBasePublic, 0) aliceEphemeralPublicKey = Curve.decodePoint(aliceEphemeralPublic, 0) aliceIdentityPublicKey = IdentityKey(aliceIdentityPublic, 0) parameters = BobAxolotlParameters.newBuilder() \ .setOurIdentityKey(bobIdentityKey) \ .setOurSignedPreKey(bobSignedPreKey) \ .setOurRatchetKey(bobEphemeralKey) \ .setOurOneTimePreKey(None) \ .setTheirIdentityKey(aliceIdentityPublicKey) \ .setTheirBaseKey(aliceBasePublicKey) \ .create() session = SessionState() RatchetingSession.initializeSessionAsBob(session, parameters) self.assertEqual(session.getLocalIdentityKey(), bobIdentityKey.getPublicKey()) self.assertEqual(session.getRemoteIdentityKey(), aliceIdentityPublicKey) self.assertEqual(session.getSenderChainKey().getKey(), senderChain) python-axolotl-0.2.3/axolotl/tests/ratchet/test_rootkey.py0000644000175000017500000000515413471043576024355 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import unittest from ...ecc.curve import Curve from ...ecc.eckeypair import ECKeyPair from ...ratchet.rootkey import RootKey from ...kdf.hkdf import HKDF class RootKeyTest(unittest.TestCase): def test_rootKeyDerivationV2(self): rootKeySeed = bytearray([0x7b, 0xa6, 0xde, 0xbc, 0x2b, 0xc1, 0xbb, 0xf9, 0x1a, 0xbb, 0xc1, 0x36, 0x74, 0x04, 0x17, 0x6c, 0xa6, 0x23, 0x09, 0x5b, 0x7e, 0xc6, 0x6b, 0x45, 0xf6, 0x02, 0xd9, 0x35, 0x38, 0x94, 0x2d, 0xcc]) alicePublic = bytearray([0x05, 0xee, 0x4f, 0xa6, 0xcd, 0xc0, 0x30, 0xdf, 0x49, 0xec, 0xd0, 0xba, 0x6c, 0xfc, 0xff, 0xb2, 0x33, 0xd3, 0x65, 0xa2, 0x7f, 0xad, 0xbe, 0xff, 0x77, 0xe9, 0x63, 0xfc, 0xb1, 0x62, 0x22, 0xe1, 0x3a]) alicePrivate = bytearray([0x21, 0x68, 0x22, 0xec, 0x67, 0xeb, 0x38, 0x04, 0x9e, 0xba, 0xe7, 0xb9, 0x39, 0xba, 0xea, 0xeb, 0xb1, 0x51, 0xbb, 0xb3, 0x2d, 0xb8, 0x0f, 0xd3, 0x89, 0x24, 0x5a, 0xc3, 0x7a, 0x94, 0x8e, 0x50]) bobPublic = bytearray([0x05, 0xab, 0xb8, 0xeb, 0x29, 0xcc, 0x80, 0xb4, 0x71, 0x09, 0xa2, 0x26, 0x5a, 0xbe, 0x97, 0x98, 0x48, 0x54, 0x06, 0xe3, 0x2d, 0xa2, 0x68, 0x93, 0x4a, 0x95, 0x55, 0xe8, 0x47, 0x57, 0x70, 0x8a, 0x30]) nextRoot = bytearray([0xb1, 0x14, 0xf5, 0xde, 0x28, 0x01, 0x19, 0x85, 0xe6, 0xeb, 0xa2, 0x5d, 0x50, 0xe7, 0xec, 0x41, 0xa9, 0xb0, 0x2f, 0x56, 0x93, 0xc5, 0xc7, 0x88, 0xa6, 0x3a, 0x06, 0xd2, 0x12, 0xa2, 0xf7, 0x31]) nextChain = bytearray([0x9d, 0x7d, 0x24, 0x69, 0xbc, 0x9a, 0xe5, 0x3e, 0xe9, 0x80, 0x5a, 0xa3, 0x26, 0x4d, 0x24, 0x99, 0xa3, 0xac, 0xe8, 0x0f, 0x4c, 0xca, 0xe2, 0xda, 0x13, 0x43, 0x0c, 0x5c, 0x55, 0xb5, 0xca, 0x5f]) alicePublicKey = Curve.decodePoint(alicePublic, 0) alicePrivateKey = Curve.decodePrivatePoint(alicePrivate) aliceKeyPair = ECKeyPair(alicePublicKey, alicePrivateKey) bobPublicKey = Curve.decodePoint(bobPublic, 0) rootKey = RootKey(HKDF.createFor(2), rootKeySeed) rootKeyChainKeyPair = rootKey.createChain(bobPublicKey, aliceKeyPair) nextRootKey = rootKeyChainKeyPair[0] nextChainKey = rootKeyChainKeyPair[1] self.assertEqual(rootKey.getKeyBytes(), rootKeySeed) self.assertEqual(nextRootKey.getKeyBytes(), nextRoot) self.assertEqual(nextChainKey.getKey(), nextChain) python-axolotl-0.2.3/axolotl/tests/test_sessionbuilder.py0000644000175000017500000004125613471043576024264 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import unittest import time import sys from ..invalidkeyexception import InvalidKeyException from ..sessionbuilder import SessionBuilder from ..sessioncipher import SessionCipher from ..ecc.curve import Curve from ..protocol.ciphertextmessage import CiphertextMessage from ..protocol.whispermessage import WhisperMessage from ..protocol.prekeywhispermessage import PreKeyWhisperMessage from ..state.prekeybundle import PreKeyBundle from ..tests.inmemoryaxolotlstore import InMemoryAxolotlStore from ..state.prekeyrecord import PreKeyRecord from ..state.signedprekeyrecord import SignedPreKeyRecord from ..tests.inmemoryidentitykeystore import InMemoryIdentityKeyStore from ..protocol.keyexchangemessage import KeyExchangeMessage from ..untrustedidentityexception import UntrustedIdentityException class SessionBuilderTest(unittest.TestCase): ALICE_RECIPIENT_ID = 5 BOB_RECIPIENT_ID = 2 def test_basicPreKeyV2(self): aliceStore = InMemoryAxolotlStore() aliceSessionBuilder = SessionBuilder(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) bobStore = InMemoryAxolotlStore() bobPreKeyPair = Curve.generateKeyPair() bobPreKey = PreKeyBundle(bobStore.getLocalRegistrationId(), 1, 31337, bobPreKeyPair.getPublicKey(), 0, None, None, bobStore.getIdentityKeyPair().getPublicKey()) try: aliceSessionBuilder.processPreKeyBundle(bobPreKey) raise AssertionError("Should fail with missing unsigned prekey!"); except InvalidKeyException: # good pass return def test_basicPreKeyV3(self): aliceStore = InMemoryAxolotlStore() aliceSessionBuilder = SessionBuilder(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) bobStore = InMemoryAxolotlStore() bobPreKeyPair = Curve.generateKeyPair() bobSignedPreKeyPair = Curve.generateKeyPair() bobSignedPreKeySignature = Curve.calculateSignature(bobStore.getIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()) bobPreKey = PreKeyBundle(bobStore.getLocalRegistrationId(), 1, 31337, bobPreKeyPair.getPublicKey(), 22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, bobStore.getIdentityKeyPair().getPublicKey()) aliceSessionBuilder.processPreKeyBundle(bobPreKey) self.assertTrue(aliceStore.containsSession(self.__class__.BOB_RECIPIENT_ID, 1)) self.assertTrue(aliceStore.loadSession(self.__class__.BOB_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3) originalMessage = b"L'homme est condamne a etre libre" aliceSessionCipher = SessionCipher(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) outgoingMessage = aliceSessionCipher.encrypt(originalMessage) self.assertTrue(outgoingMessage.getType() == CiphertextMessage.PREKEY_TYPE) incomingMessage = PreKeyWhisperMessage(serialized=outgoingMessage.serialize()) bobStore.storePreKey(31337, PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)) bobStore.storeSignedPreKey(22, SignedPreKeyRecord(22, int(time.time() * 1000), bobSignedPreKeyPair, bobSignedPreKeySignature)) bobSessionCipher = SessionCipher(bobStore, bobStore, bobStore, bobStore, self.__class__.ALICE_RECIPIENT_ID, 1) plaintext = bobSessionCipher.decryptPkmsg(incomingMessage) self.assertEqual(originalMessage, plaintext) # @@TODO: in callback assertion # self.assertFalse(bobStore.containsSession(self.__class__.ALICE_RECIPIENT_ID, 1)) self.assertTrue(bobStore.containsSession(self.__class__.ALICE_RECIPIENT_ID, 1)) self.assertTrue(bobStore.loadSession(self.__class__.ALICE_RECIPIENT_ID, 1).getSessionState().getSessionVersion() == 3) self.assertTrue(bobStore.loadSession(self.__class__.ALICE_RECIPIENT_ID, 1).getSessionState().getAliceBaseKey() is not None) self.assertEqual(originalMessage, plaintext) bobOutgoingMessage = bobSessionCipher.encrypt(originalMessage) self.assertTrue(bobOutgoingMessage.getType() == CiphertextMessage.WHISPER_TYPE) alicePlaintext = aliceSessionCipher.decryptMsg(WhisperMessage(serialized=bobOutgoingMessage.serialize())) self.assertEqual(alicePlaintext, originalMessage) self.runInteraction(aliceStore, bobStore) aliceStore = InMemoryAxolotlStore() aliceSessionBuilder = SessionBuilder(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) aliceSessionCipher = SessionCipher(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) bobPreKeyPair = Curve.generateKeyPair() bobSignedPreKeyPair = Curve.generateKeyPair() bobSignedPreKeySignature = Curve.calculateSignature(bobStore.getIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()) bobPreKey = PreKeyBundle(bobStore.getLocalRegistrationId(), 1, 31338, bobPreKeyPair.getPublicKey(), 23, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, bobStore.getIdentityKeyPair().getPublicKey()) bobStore.storePreKey(31338, PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair)) bobStore.storeSignedPreKey(23, SignedPreKeyRecord(23, int(time.time() * 1000), bobSignedPreKeyPair, bobSignedPreKeySignature)) aliceSessionBuilder.processPreKeyBundle(bobPreKey) outgoingMessage = aliceSessionCipher.encrypt(originalMessage) try: plaintext = bobSessionCipher.decryptPkmsg(PreKeyWhisperMessage(serialized=outgoingMessage)) raise AssertionError("shouldn't be trusted!") except Exception: bobStore.saveIdentity(self.__class__.ALICE_RECIPIENT_ID, PreKeyWhisperMessage(serialized=outgoingMessage.serialize()).getIdentityKey()) plaintext = bobSessionCipher.decryptPkmsg(PreKeyWhisperMessage(serialized=outgoingMessage.serialize())) self.assertEqual(plaintext, originalMessage) bobPreKey = PreKeyBundle(bobStore.getLocalRegistrationId(), 1, 31337, Curve.generateKeyPair().getPublicKey(), 23, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, aliceStore.getIdentityKeyPair().getPublicKey()) try: aliceSessionBuilder.process(bobPreKey) raise AssertionError("shouldn't be trusted!") except Exception: # good pass def test_badSignedPreKeySignature(self): aliceStore = InMemoryAxolotlStore() aliceSessionBuilder = SessionBuilder(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) bobIdentityKeyStore = InMemoryIdentityKeyStore() bobPreKeyPair = Curve.generateKeyPair() bobSignedPreKeyPair = Curve.generateKeyPair() bobSignedPreKeySignature = Curve.calculateSignature(bobIdentityKeyStore.getIdentityKeyPair().getPrivateKey(), bobSignedPreKeyPair.getPublicKey().serialize()) for i in range(0, len(bobSignedPreKeySignature) * 8): modifiedSignature = bytearray(bobSignedPreKeySignature[:]) modifiedSignature[int(i/8)] ^= 0x01 << (i % 8) bobPreKey = PreKeyBundle(bobIdentityKeyStore.getLocalRegistrationId(), 1, 31337, bobPreKeyPair.getPublicKey(), 22, bobSignedPreKeyPair.getPublicKey(), modifiedSignature, bobIdentityKeyStore.getIdentityKeyPair().getPublicKey()) try: aliceSessionBuilder.processPreKeyBundle(bobPreKey) except Exception: # good pass bobPreKey = PreKeyBundle(bobIdentityKeyStore.getLocalRegistrationId(), 1, 31337, bobPreKeyPair.getPublicKey(), 22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature, bobIdentityKeyStore.getIdentityKeyPair().getPublicKey()) aliceSessionBuilder.processPreKeyBundle(bobPreKey) def test_basicKeyExchange(self): aliceStore = InMemoryAxolotlStore() aliceSessionBuilder = SessionBuilder(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) bobStore = InMemoryAxolotlStore() bobSessionBuilder = SessionBuilder(bobStore, bobStore, bobStore, bobStore, self.__class__.ALICE_RECIPIENT_ID, 1) aliceKeyExchangeMessage = aliceSessionBuilder.processInitKeyExchangeMessage() self.assertTrue(aliceKeyExchangeMessage is not None) aliceKeyExchangeMessageBytes = aliceKeyExchangeMessage.serialize() bobKeyExchangeMessage = bobSessionBuilder.processKeyExchangeMessage(KeyExchangeMessage( serialized=aliceKeyExchangeMessageBytes)) self.assertTrue(bobKeyExchangeMessage is not None) bobKeyExchangeMessageBytes = bobKeyExchangeMessage.serialize() response = aliceSessionBuilder.processKeyExchangeMessage(KeyExchangeMessage( serialized=bobKeyExchangeMessageBytes)) self.assertTrue(response is None) self.assertTrue(aliceStore.containsSession(self.__class__.BOB_RECIPIENT_ID, 1)) self.assertTrue(bobStore.containsSession(self.__class__.ALICE_RECIPIENT_ID, 1)) self.runInteraction(aliceStore, bobStore) aliceStore = InMemoryAxolotlStore() aliceSessionBuilder = SessionBuilder(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) aliceKeyExchangeMessage = aliceSessionBuilder.processInitKeyExchangeMessage() try: bobKeyExchangeMessage = bobSessionBuilder.processKeyExchangeMessage(aliceKeyExchangeMessage) raise AssertionError("This identity shouldn't be trusted!") except UntrustedIdentityException: bobStore.saveIdentity(self.__class__.ALICE_RECIPIENT_ID, aliceKeyExchangeMessage.getIdentityKey()) bobKeyExchangeMessage = bobSessionBuilder.processKeyExchangeMessage(aliceKeyExchangeMessage) self.assertTrue(aliceSessionBuilder.processKeyExchangeMessage(bobKeyExchangeMessage) == None) self.runInteraction(aliceStore, bobStore) def runInteraction(self, aliceStore, bobStore): """ :type aliceStore: AxolotlStore :type bobStore: AxolotlStore """ aliceSessionCipher = SessionCipher(aliceStore, aliceStore, aliceStore, aliceStore, self.__class__.BOB_RECIPIENT_ID, 1) bobSessionCipher = SessionCipher(bobStore, bobStore, bobStore, bobStore, self.__class__.ALICE_RECIPIENT_ID, 1) originalMessage = b"smert ze smert" aliceMessage = aliceSessionCipher.encrypt(originalMessage) self.assertTrue(aliceMessage.getType() == CiphertextMessage.WHISPER_TYPE) plaintext = bobSessionCipher.decryptMsg(WhisperMessage(serialized=aliceMessage.serialize())) self.assertEqual(plaintext, originalMessage) bobMessage = bobSessionCipher.encrypt(originalMessage) self.assertTrue(bobMessage.getType() == CiphertextMessage.WHISPER_TYPE) plaintext = aliceSessionCipher.decryptMsg(WhisperMessage(serialized=bobMessage.serialize())) self.assertEqual(plaintext, originalMessage) for i in range(0, 10): loopingMessage = b"What do we mean by saying that existence precedes essence? " \ b"We mean that man first of all exists, encounters himself, " \ b"surges up in the world--and defines himself aftward. %d" % i aliceLoopingMessage = aliceSessionCipher.encrypt(loopingMessage) loopingPlaintext = bobSessionCipher.decryptMsg(WhisperMessage(serialized=aliceLoopingMessage.serialize())) self.assertEqual(loopingPlaintext, loopingMessage) for i in range(0, 10): loopingMessage = b"What do we mean by saying that existence precedes essence? " \ b"We mean that man first of all exists, encounters himself, " \ b"surges up in the world--and defines himself aftward. %d" % i bobLoopingMessage = bobSessionCipher.encrypt(loopingMessage) loopingPlaintext = aliceSessionCipher.decryptMsg(WhisperMessage(serialized=bobLoopingMessage.serialize())) self.assertEqual(loopingPlaintext, loopingMessage) aliceOutOfOrderMessages = [] for i in range(0, 10): loopingMessage = b"What do we mean by saying that existence precedes essence? " \ b"We mean that man first of all exists, encounters himself, " \ b"surges up in the world--and defines himself aftward. %d" % i aliceLoopingMessage = aliceSessionCipher.encrypt(loopingMessage) aliceOutOfOrderMessages.append((loopingMessage, aliceLoopingMessage)) for i in range(0, 10): loopingMessage = b"What do we mean by saying that existence precedes essence? " \ b"We mean that man first of all exists, encounters himself, " \ b"surges up in the world--and defines himself aftward. %d" % i aliceLoopingMessage = aliceSessionCipher.encrypt(loopingMessage) loopingPlaintext = bobSessionCipher.decryptMsg(WhisperMessage(serialized=aliceLoopingMessage.serialize())) self.assertEqual(loopingPlaintext, loopingMessage) for i in range(0, 10): loopingMessage = b"You can only desire based on what you know: %d" % i bobLoopingMessage = bobSessionCipher.encrypt(loopingMessage) loopingPlaintext = aliceSessionCipher.decryptMsg(WhisperMessage(serialized=bobLoopingMessage.serialize())) self.assertEqual(loopingPlaintext, loopingMessage) for aliceOutOfOrderMessage in aliceOutOfOrderMessages: outOfOrderPlaintext = bobSessionCipher.decryptMsg(WhisperMessage( serialized=aliceOutOfOrderMessage[1].serialize())) self.assertEqual(outOfOrderPlaintext, aliceOutOfOrderMessage[0]) python-axolotl-0.2.3/axolotl/tests/test_sessioncipher.py0000644000175000017500000001060513471043576024102 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import unittest from ..state.sessionrecord import SessionRecord from ..ecc.curve import Curve from ..identitykeypair import IdentityKeyPair, IdentityKey from ..ratchet.aliceaxolotlparameters import AliceAxolotlParameters from ..ratchet.bobaxolotlparamaters import BobAxolotlParameters from ..ratchet.ratchetingsession import RatchetingSession from ..tests.inmemoryaxolotlstore import InMemoryAxolotlStore from ..sessioncipher import SessionCipher from ..protocol.whispermessage import WhisperMessage class SessionCipherTest(unittest.TestCase): def test_basicSessionV3(self): aliceSessionRecord = SessionRecord() bobSessionRecord = SessionRecord() self.initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState()) self.runInteraction(aliceSessionRecord, bobSessionRecord) def runInteraction(self, aliceSessionRecord, bobSessionRecord): aliceStore = InMemoryAxolotlStore() bobStore = InMemoryAxolotlStore() aliceStore.storeSession(2, 1, aliceSessionRecord) bobStore.storeSession(3, 1, bobSessionRecord) aliceCipher = SessionCipher(aliceStore, aliceStore, aliceStore, aliceStore, 2, 1) bobCipher = SessionCipher(bobStore, bobStore, bobStore, bobStore, 3, 1) alicePlaintext = b"This is a plaintext message." message = aliceCipher.encrypt(alicePlaintext) bobPlaintext = bobCipher.decryptMsg(WhisperMessage(serialized=message.serialize())) self.assertEqual(alicePlaintext, bobPlaintext) bobReply = b"This is a message from Bob." reply = bobCipher.encrypt(bobReply) receivedReply = aliceCipher.decryptMsg(WhisperMessage(serialized=reply.serialize())) self.assertEqual(bobReply, receivedReply) alicePlaintext = b"ABCDEFGHIJKLMNOP" # ensure padding/unpadding properly applies on message of blocksize length message = aliceCipher.encrypt(alicePlaintext) bobPlaintext = bobCipher.decryptMsg(WhisperMessage(serialized=message.serialize())) self.assertEqual(alicePlaintext, bobPlaintext) aliceCiphertextMessages = [] alicePlaintextMessages = [] for i in range(0, 50): alicePlaintextMessages.append(b"aaaaaa %d" % i) aliceCiphertextMessages.append(aliceCipher.encrypt(b"aaaaaa %d" % i)) # shuffle(aliceCiphertextMessages) # shuffle(alicePlaintextMessages) for i in range(0, int(len(aliceCiphertextMessages)/2)): receivedPlaintext = bobCipher.decryptMsg(WhisperMessage(serialized=aliceCiphertextMessages[i].serialize())) self.assertEqual(receivedPlaintext, alicePlaintextMessages[i]) def initializeSessionsV3(self, aliceSessionState, bobSessionState): aliceIdentityKeyPair = Curve.generateKeyPair() aliceIdentityKey = IdentityKeyPair(IdentityKey(aliceIdentityKeyPair.getPublicKey()), aliceIdentityKeyPair.getPrivateKey()) aliceBaseKey = Curve.generateKeyPair() # aliceEphemeralKey = Curve.generateKeyPair() # alicePreKey = aliceBaseKey bobIdentityKeyPair = Curve.generateKeyPair() bobIdentityKey = IdentityKeyPair(IdentityKey(bobIdentityKeyPair.getPublicKey()), bobIdentityKeyPair.getPrivateKey()) bobBaseKey = Curve.generateKeyPair() bobEphemeralKey = bobBaseKey # bobPreKey = Curve.generateKeyPair() aliceParameters = AliceAxolotlParameters.newBuilder()\ .setOurBaseKey(aliceBaseKey)\ .setOurIdentityKey(aliceIdentityKey)\ .setTheirOneTimePreKey(None)\ .setTheirRatchetKey(bobEphemeralKey.getPublicKey())\ .setTheirSignedPreKey(bobBaseKey.getPublicKey())\ .setTheirIdentityKey(bobIdentityKey.getPublicKey())\ .create() bobParameters = BobAxolotlParameters.newBuilder()\ .setOurRatchetKey(bobEphemeralKey)\ .setOurSignedPreKey(bobBaseKey)\ .setOurOneTimePreKey(None)\ .setOurIdentityKey(bobIdentityKey)\ .setTheirIdentityKey(aliceIdentityKey.getPublicKey())\ .setTheirBaseKey(aliceBaseKey.getPublicKey())\ .create() RatchetingSession.initializeSessionAsAlice(aliceSessionState, aliceParameters) RatchetingSession.initializeSessionAsBob(bobSessionState, bobParameters) python-axolotl-0.2.3/axolotl/tests/test_sigs.py0000644000175000017500000001074513471043576022176 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import unittest from ..ecc.curve import Curve from ..util.keyhelper import KeyHelper class Curve25519Test(unittest.TestCase): def test_agreement(self): alicePublic = bytearray([0x05, 0x1b, 0xb7, 0x59, 0x66, 0xf2, 0xe9, 0x3a, 0x36, 0x91, 0xdf, 0xff, 0x94, 0x2b, 0xb2, 0xa4, 0x66, 0xa1, 0xc0, 0x8b, 0x8d, 0x78, 0xca, 0x3f, 0x4d, 0x6d, 0xf8, 0xb8, 0xbf, 0xa2, 0xe4, 0xee, 0x28]) alicePrivate = bytearray([0xc8, 0x06, 0x43, 0x9d, 0xc9, 0xd2, 0xc4, 0x76, 0xff, 0xed, 0x8f, 0x25, 0x80, 0xc0, 0x88, 0x8d, 0x58, 0xab, 0x40, 0x6b, 0xf7, 0xae, 0x36, 0x98, 0x87, 0x90, 0x21, 0xb9, 0x6b, 0xb4, 0xbf, 0x59]) bobPublic = bytearray([0x05, 0x65, 0x36, 0x14, 0x99, 0x3d, 0x2b, 0x15, 0xee, 0x9e, 0x5f, 0xd3, 0xd8, 0x6c, 0xe7, 0x19, 0xef, 0x4e, 0xc1, 0xda, 0xae, 0x18, 0x86, 0xa8, 0x7b, 0x3f, 0x5f, 0xa9, 0x56, 0x5a, 0x27, 0xa2, 0x2f]) bobPrivate = bytearray([0xb0, 0x3b, 0x34, 0xc3, 0x3a, 0x1c, 0x44, 0xf2, 0x25, 0xb6, 0x62, 0xd2, 0xbf, 0x48, 0x59, 0xb8, 0x13, 0x54, 0x11, 0xfa, 0x7b, 0x03, 0x86, 0xd4, 0x5f, 0xb7, 0x5d, 0xc5, 0xb9, 0x1b, 0x44, 0x66]) shared = bytearray([0x32, 0x5f, 0x23, 0x93, 0x28, 0x94, 0x1c, 0xed, 0x6e, 0x67, 0x3b, 0x86, 0xba, 0x41, 0x01, 0x74, 0x48, 0xe9, 0x9b, 0x64, 0x9a, 0x9c, 0x38, 0x06, 0xc1, 0xdd, 0x7c, 0xa4, 0xc4, 0x77, 0xe6, 0x29]) alicePublicKey = Curve.decodePoint(alicePublic, 0) alicePrivateKey = Curve.decodePrivatePoint(alicePrivate) bobPublicKey = Curve.decodePoint(bobPublic, 0) bobPrivateKey = Curve.decodePrivatePoint(bobPrivate) sharedOne = Curve.calculateAgreement(alicePublicKey, bobPrivateKey) sharedTwo = Curve.calculateAgreement(bobPublicKey, alicePrivateKey) self.assertEqual(sharedOne, shared) self.assertEqual(sharedTwo, shared) def test_randomAgreements(self): for i in range(0, 50): alice = Curve.generateKeyPair() bob = Curve.generateKeyPair() sharedAlice = Curve.calculateAgreement(bob.getPublicKey(), alice.getPrivateKey()) sharedBob = Curve.calculateAgreement(alice.getPublicKey(), bob.getPrivateKey()) self.assertEqual(sharedAlice, sharedBob) def test_gensig(self): identityKeyPair = KeyHelper.generateIdentityKeyPair() KeyHelper.generateSignedPreKey(identityKeyPair, 0) def test_signature(self): # aliceIdentityPrivate = bytearray([0xc0, 0x97, 0x24, 0x84, 0x12, 0xe5, 0x8b, 0xf0, 0x5d, 0xf4, 0x87, 0x96, # 0x82, 0x05, 0x13, 0x27, 0x94, 0x17, 0x8e, 0x36, 0x76, 0x37, 0xf5, 0x81, # 0x8f, 0x81, 0xe0, 0xe6, 0xce, 0x73, 0xe8, 0x65]) aliceIdentityPublic = bytearray([0x05, 0xab, 0x7e, 0x71, 0x7d, 0x4a, 0x16, 0x3b, 0x7d, 0x9a, 0x1d, 0x80, 0x71, 0xdf, 0xe9, 0xdc, 0xf8, 0xcd, 0xcd, 0x1c, 0xea, 0x33, 0x39, 0xb6, 0x35, 0x6b, 0xe8, 0x4d, 0x88, 0x7e, 0x32, 0x2c, 0x64]) aliceEphemeralPublic = bytearray([0x05, 0xed, 0xce, 0x9d, 0x9c, 0x41, 0x5c, 0xa7, 0x8c, 0xb7, 0x25, 0x2e, 0x72, 0xc2, 0xc4, 0xa5, 0x54, 0xd3, 0xeb, 0x29, 0x48, 0x5a, 0x0e, 0x1d, 0x50, 0x31, 0x18, 0xd1, 0xa8, 0x2d, 0x99, 0xfb, 0x4a]) aliceSignature = bytearray([0x5d, 0xe8, 0x8c, 0xa9, 0xa8, 0x9b, 0x4a, 0x11, 0x5d, 0xa7, 0x91, 0x09, 0xc6, 0x7c, 0x9c, 0x74, 0x64, 0xa3, 0xe4, 0x18, 0x02, 0x74, 0xf1, 0xcb, 0x8c, 0x63, 0xc2, 0x98, 0x4e, 0x28, 0x6d, 0xfb, 0xed, 0xe8, 0x2d, 0xeb, 0x9d, 0xcd, 0x9f, 0xae, 0x0b, 0xfb, 0xb8, 0x21, 0x56, 0x9b, 0x3d, 0x90, 0x01, 0xbd, 0x81, 0x30, 0xcd, 0x11, 0xd4, 0x86, 0xce, 0xf0, 0x47, 0xbd, 0x60, 0xb8, 0x6e, 0x88]) # alicePrivateKey = Curve.decodePrivatePoint(aliceIdentityPrivate) alicePublicKey = Curve.decodePoint(aliceIdentityPublic, 0) aliceEphemeral = Curve.decodePoint(aliceEphemeralPublic, 0) res = Curve.verifySignature(alicePublicKey, aliceEphemeral.serialize(), bytes(aliceSignature)) self.assertTrue(res) python-axolotl-0.2.3/axolotl/tests/util/0000755000175000017500000000000013471043614020557 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/tests/util/__init__.py0000644000175000017500000000005613471043576022700 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- __author__ = 'tarek' python-axolotl-0.2.3/axolotl/tests/util/test_byteutil.py0000644000175000017500000000147413471043576024046 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import unittest from ...util.byteutil import ByteUtil # from ...util.hexutil import HexUtil class ByteUtilTest(unittest.TestCase): def test_split(self): # okm = HexUtil.decodeHex('02a9aa6c7dbd64f9d3aa92f92a277bf54609dadf0b00' # '828acfc61e3c724b84a7bfbe5efb603030526742e3ee' # '89c7024e884e440f1ff376bb2317b2d64deb7c8322f4' # 'c5015d9d895849411ba1d793a827') data = [i for i in range(0, 80)] a_data = [i for i in range(0, 32)] b_data = [i for i in range(32, 64)] c_data = [i for i in range(64, 80)] a, b, c = ByteUtil.split(data, 32, 32, 16) self.assertEqual(a, a_data) self.assertEqual(b, b_data) self.assertEqual(c, c_data) python-axolotl-0.2.3/axolotl/untrustedidentityexception.py0000644000175000017500000000044113471043576024546 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class UntrustedIdentityException(Exception): def __init__(self, name, identityKey): self.name = name self.identityKey = identityKey def getName(self): return self.name def getIdentityKey(self): return self.identityKey python-axolotl-0.2.3/axolotl/util/0000755000175000017500000000000013471043614017415 5ustar tarektarek00000000000000python-axolotl-0.2.3/axolotl/util/__init__.py0000644000175000017500000000003013471043576021526 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- python-axolotl-0.2.3/axolotl/util/byteutil.py0000644000175000017500000000343313471043576021642 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class ByteUtil: @staticmethod def combine(*args): baos = bytearray() for a in args: if type(a) in (list, bytearray, str, bytes): baos.extend(a) else: baos.append(a) return baos # @staticmethod # def xsplit(inp, firstLength, secondLength, thirdLength = None): # parts = [] # parts.append(inp[:firstLength]) # parts.append(inp[len(parts[0]): secondLength + 1]) # if thirdLength: # parts.append(inp[len(parts[1]): thirdLength + 1]) # return parts @staticmethod def split(inp, firstLength, secondLength, thirdLength=None): parts = [] parts.append(inp[:firstLength]) parts.append(inp[firstLength:firstLength + secondLength]) if thirdLength is not None: parts.append(inp[firstLength + secondLength: firstLength + secondLength + thirdLength]) return parts @staticmethod def trim(inp, length): return inp[:length] @staticmethod def intsToByteHighAndLow(highValue, lowValue): highValue = ord(highValue) if type(highValue) is str else highValue lowValue = ord(lowValue) if type(lowValue) is str else lowValue return ((highValue << 4 | lowValue) & 0xFF) % 256 @staticmethod def highBitsToInt(value): bit = ord(value) if type(value) is str else value return (bit & 0xFF) >> 4 @staticmethod def lowBitsToInt(value): return value & 0xF @staticmethod def intToByteArray(_bytes, offset, value): _bytes[offset + 3] = value % 256 _bytes[offset + 2] = (value >> 8) % 256 _bytes[offset + 1] = (value >> 16) % 256 _bytes[offset] = (value >> 24) % 256 return 4 python-axolotl-0.2.3/axolotl/util/hexutil.py0000644000175000017500000000032513471043576021460 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import codecs decode_hex = codecs.getdecoder("hex_codec") class HexUtil: @staticmethod def decodeHex(hexString): result = decode_hex(hexString)[0] return result python-axolotl-0.2.3/axolotl/util/keyhelper.py0000644000175000017500000000647413471043576022001 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import time import binascii import os from random import SystemRandom from ..ecc.curve import Curve from ..identitykey import IdentityKey from ..identitykeypair import IdentityKeyPair from ..state.prekeyrecord import PreKeyRecord from ..state.signedprekeyrecord import SignedPreKeyRecord from .medium import Medium class KeyHelper: def __init__(self): pass @staticmethod def generateIdentityKeyPair(): """ Generate an identity key pair. Clients should only do this once, at install time. @return the generated IdentityKeyPair. """ keyPair = Curve.generateKeyPair() publicKey = IdentityKey(keyPair.getPublicKey()) serialized = '0a21056e8936e8367f768a7bba008ade7cf58407bdc7a6aae293e2c' \ 'b7c06668dcd7d5e12205011524f0c15467100dd603e0d6020f4d293' \ 'edfbcd82129b14a88791ac81365c' serialized = binascii.unhexlify(serialized.encode()) identityKeyPair = IdentityKeyPair(publicKey, keyPair.getPrivateKey()) return identityKeyPair # return IdentityKeyPair(serialized=serialized) @staticmethod def generateRegistrationId(extended_range=False): """ Generate a registration ID. Clients should only do this once, at install time. :param extended_range: By default (false), the generated registration ID is sized to require the minimal possible protobuf encoding overhead. Specify true if the caller needs the full range of MAX_INT at the cost of slightly higher encoding overhead. """ if extended_range: regId = KeyHelper.getRandomSequence(2147483646) + 1 else: regId = KeyHelper.getRandomSequence(16380) + 1 return regId @staticmethod def getRandomSequence(max): return SystemRandom().randrange(max) @staticmethod def generatePreKeys(start, count): """ Generate a list of PreKeys. Clients should do this at install time, and subsequently any time the list of PreKeys stored on the server runs low. PreKey IDs are shorts, so they will eventually be repeated. Clients should store PreKeys in a circular buffer, so that they are repeated as infrequently as possible. @param start The starting PreKey ID, inclusive. @param count The number of PreKeys to generate. @return the list of generated PreKeyRecords. """ results = [] start -= 1 for i in range(0, count): preKeyId = ((start + i) % (Medium.MAX_VALUE - 1)) + 1 results.append(PreKeyRecord(preKeyId, Curve.generateKeyPair())) return results @staticmethod def generateSignedPreKey(identityKeyPair, signedPreKeyId): keyPair = Curve.generateKeyPair() signature = Curve.calculateSignature(identityKeyPair.getPrivateKey(), keyPair.getPublicKey().serialize()) spk = SignedPreKeyRecord(signedPreKeyId, int(round(time.time() * 1000)), keyPair, signature) return spk @staticmethod def generateSenderSigningKey(): return Curve.generateKeyPair() @staticmethod def generateSenderKey(): return os.urandom(32) @staticmethod def generateSenderKeyId(): return KeyHelper.getRandomSequence(2147483647) python-axolotl-0.2.3/axolotl/util/medium.py0000644000175000017500000000010113471043576021246 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- class Medium: MAX_VALUE = 0xFFFFFF python-axolotl-0.2.3/python_axolotl.egg-info/0000755000175000017500000000000013471043614021533 5ustar tarektarek00000000000000python-axolotl-0.2.3/python_axolotl.egg-info/PKG-INFO0000644000175000017500000000175313471043613022635 0ustar tarektarek00000000000000Metadata-Version: 1.1 Name: python-axolotl Version: 0.2.3 Summary: Python port of libaxolotl-android, originally written by Moxie Marlinspik Home-page: https://github.com/tgalal/python-axolotl Author: Tarek Galal Author-email: tare2.galal@gmail.com License: GPLv3 License Download-URL: https://github.com/tgalal/python-axolotl/releases Description: UNKNOWN Platform: any Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) Classifier: Natural Language :: English Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Topic :: Security :: Cryptography Classifier: Topic :: Software Development :: Libraries :: Python Modules python-axolotl-0.2.3/python_axolotl.egg-info/SOURCES.txt0000644000175000017500000000610413471043614023420 0ustar tarektarek00000000000000README.md setup.py axolotl/__init__.py axolotl/axolotladdress.py axolotl/duplicatemessagexception.py axolotl/identitykey.py axolotl/identitykeypair.py axolotl/invalidkeyexception.py axolotl/invalidkeyidexception.py axolotl/invalidmessageexception.py axolotl/invalidversionexception.py axolotl/legacymessageexception.py axolotl/nosessionexception.py axolotl/sessionbuilder.py axolotl/sessioncipher.py axolotl/statekeyexchangeexception.py axolotl/untrustedidentityexception.py axolotl/ecc/__init__.py axolotl/ecc/curve.py axolotl/ecc/djbec.py axolotl/ecc/ec.py axolotl/ecc/eckeypair.py axolotl/groups/__init__.py axolotl/groups/groupcipher.py axolotl/groups/groupsessionbuilder.py axolotl/groups/senderkeyname.py axolotl/groups/ratchet/__init__.py axolotl/groups/ratchet/senderchainkey.py axolotl/groups/ratchet/sendermessagekey.py axolotl/groups/state/__init__.py axolotl/groups/state/senderkeyrecord.py axolotl/groups/state/senderkeystate.py axolotl/groups/state/senderkeystore.py axolotl/kdf/__init__.py axolotl/kdf/derivedmessagesecrets.py axolotl/kdf/derivedrootsecrets.py axolotl/kdf/hkdf.py axolotl/kdf/hkdfv2.py axolotl/kdf/hkdfv3.py axolotl/kdf/messagekeys.py axolotl/protocol/__init__.py axolotl/protocol/ciphertextmessage.py axolotl/protocol/keyexchangemessage.py axolotl/protocol/prekeywhispermessage.py axolotl/protocol/senderkeydistributionmessage.py axolotl/protocol/senderkeymessage.py axolotl/protocol/whispermessage.py axolotl/protocol/whisperprotos_pb2.py axolotl/ratchet/__init__.py axolotl/ratchet/aliceaxolotlparameters.py axolotl/ratchet/bobaxolotlparamaters.py axolotl/ratchet/chainkey.py axolotl/ratchet/ratchetingsession.py axolotl/ratchet/rootkey.py axolotl/ratchet/symmetricaxolotlparameters.py axolotl/state/__init__.py axolotl/state/axolotlstore.py axolotl/state/identitykeystore.py axolotl/state/prekeybundle.py axolotl/state/prekeyrecord.py axolotl/state/prekeystore.py axolotl/state/sessionrecord.py axolotl/state/sessionstate.py axolotl/state/sessionstore.py axolotl/state/signedprekeyrecord.py axolotl/state/signedprekeystore.py axolotl/state/storageprotos_pb2.py axolotl/tests/__init__.py axolotl/tests/inmemoryaxolotlstore.py axolotl/tests/inmemoryidentitykeystore.py axolotl/tests/inmemoryprekeystore.py axolotl/tests/inmemorysessionstore.py axolotl/tests/inmemorysignedprekeystore.py axolotl/tests/test_sessionbuilder.py axolotl/tests/test_sessioncipher.py axolotl/tests/test_sigs.py axolotl/tests/groups/__init__.py axolotl/tests/groups/inmemorysenderkeystore.py axolotl/tests/groups/test_groupcipher.py axolotl/tests/kdf/__init__.py axolotl/tests/kdf/test_hkdf.py axolotl/tests/ratchet/__init__.py axolotl/tests/ratchet/test_chainkey.py axolotl/tests/ratchet/test_ratchetingsession.py axolotl/tests/ratchet/test_rootkey.py axolotl/tests/util/__init__.py axolotl/tests/util/test_byteutil.py axolotl/util/__init__.py axolotl/util/byteutil.py axolotl/util/hexutil.py axolotl/util/keyhelper.py axolotl/util/medium.py python_axolotl.egg-info/PKG-INFO python_axolotl.egg-info/SOURCES.txt python_axolotl.egg-info/dependency_links.txt python_axolotl.egg-info/requires.txt python_axolotl.egg-info/top_level.txtpython-axolotl-0.2.3/python_axolotl.egg-info/dependency_links.txt0000644000175000017500000000000113471043613025600 0ustar tarektarek00000000000000 python-axolotl-0.2.3/python_axolotl.egg-info/requires.txt0000644000175000017500000000010113471043613024122 0ustar tarektarek00000000000000cryptography python-axolotl-curve25519>=0.4.1 protobuf>=3.0.0.b2 python-axolotl-0.2.3/python_axolotl.egg-info/top_level.txt0000644000175000017500000000001013471043613024253 0ustar tarektarek00000000000000axolotl python-axolotl-0.2.3/setup.cfg0000644000175000017500000000004613471043614016577 0ustar tarektarek00000000000000[egg_info] tag_build = tag_date = 0 python-axolotl-0.2.3/setup.py0000644000175000017500000000253313471043576016502 0ustar tarektarek00000000000000# -*- coding: utf-8 -*- import sys import axolotl from setuptools import find_packages, setup deps = ['cryptography', 'python-axolotl-curve25519>=0.4.1', 'protobuf>=3.0.0.b2'] setup( name='python-axolotl', version=axolotl.__version__ , packages= find_packages(), install_requires = deps, license='GPLv3 License', author='Tarek Galal', author_email='tare2.galal@gmail.com', description="Python port of libaxolotl-android, originally written by Moxie Marlinspik", url='https://github.com/tgalal/python-axolotl', download_url='https://github.com/tgalal/python-axolotl/releases', platforms='any', classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'Natural Language :: English', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Topic :: Security :: Cryptography', 'Topic :: Software Development :: Libraries :: Python Modules'] )