public static void initializeSession(SessionState sessionState, uint sessionVersion, SymmetricAxolotlParameters parameters) { if (isAlice(parameters.getOurBaseKey().getPublicKey(), parameters.getTheirBaseKey())) { AliceAxolotlParameters.Builder aliceParameters = AliceAxolotlParameters.newBuilder(); aliceParameters.setOurBaseKey(parameters.getOurBaseKey()) .setOurIdentityKey(parameters.getOurIdentityKey()) .setTheirRatchetKey(parameters.getTheirRatchetKey()) .setTheirIdentityKey(parameters.getTheirIdentityKey()) .setTheirSignedPreKey(parameters.getTheirBaseKey()) .setTheirOneTimePreKey(May<ECPublicKey>.NoValue); RatchetingSession.initializeSession(sessionState, sessionVersion, aliceParameters.create()); } else { BobAxolotlParameters.Builder bobParameters = BobAxolotlParameters.newBuilder(); bobParameters.setOurIdentityKey(parameters.getOurIdentityKey()) .setOurRatchetKey(parameters.getOurRatchetKey()) .setOurSignedPreKey(parameters.getOurBaseKey()) .setOurOneTimePreKey(May<ECKeyPair>.NoValue) .setTheirBaseKey(parameters.getTheirBaseKey()) .setTheirIdentityKey(parameters.getTheirIdentityKey()); RatchetingSession.initializeSession(sessionState, sessionVersion, bobParameters.create()); } }
public SessionRecord(byte[] serialized) { RecordStructure record = RecordStructure.ParseFrom(serialized); this.sessionState = new SessionState(record.CurrentSession); this.fresh = false; foreach (SessionStructure previousStructure in record.PreviousSessionsList) { previousStates.AddLast(new SessionState(previousStructure)); // add -> AddLast (java) } }
public static void initializeSession(SessionState sessionState, uint sessionVersion, AliceAxolotlParameters parameters) { try { sessionState.setSessionVersion(sessionVersion); sessionState.setRemoteIdentityKey(parameters.getTheirIdentityKey()); sessionState.setLocalIdentityKey(parameters.getOurIdentityKey().getPublicKey()); ECKeyPair sendingRatchetKey = Curve.generateKeyPair(); MemoryStream secrets = new MemoryStream(); if (sessionVersion >= 3) { byte[] discontinuityBytes = getDiscontinuityBytes(); secrets.Write(discontinuityBytes, 0, discontinuityBytes.Length); } byte[] agree1 = Curve.calculateAgreement(parameters.getTheirSignedPreKey(), parameters.getOurIdentityKey().getPrivateKey()); byte[] agree2 = Curve.calculateAgreement(parameters.getTheirIdentityKey().getPublicKey(), parameters.getOurBaseKey().getPrivateKey()); byte[] agree3 = Curve.calculateAgreement(parameters.getTheirSignedPreKey(), parameters.getOurBaseKey().getPrivateKey()); secrets.Write(agree1, 0, agree1.Length); secrets.Write(agree2, 0, agree2.Length); secrets.Write(agree3, 0, agree3.Length); if (sessionVersion >= 3 && parameters.getTheirOneTimePreKey().HasValue) { byte[] agree4 = Curve.calculateAgreement(parameters.getTheirOneTimePreKey().ForceGetValue(), parameters.getOurBaseKey().getPrivateKey()); secrets.Write(agree4, 0, agree4.Length); } DerivedKeys derivedKeys = calculateDerivedKeys(sessionVersion, secrets.ToArray()); Pair<RootKey, ChainKey> sendingChain = derivedKeys.getRootKey().createChain(parameters.getTheirRatchetKey(), sendingRatchetKey); sessionState.addReceiverChain(parameters.getTheirRatchetKey(), derivedKeys.getChainKey()); sessionState.setSenderChain(sendingRatchetKey, sendingChain.second()); sessionState.setRootKey(sendingChain.first()); } catch (IOException e) { throw new Exception(e.Message); } }
private MessageKeys getOrCreateMessageKeys(SessionState sessionState, ECPublicKey theirEphemeral, ChainKey chainKey, uint counter) { if (chainKey.getIndex() > counter) { if (sessionState.hasMessageKeys(theirEphemeral, counter)) { return sessionState.removeMessageKeys(theirEphemeral, counter); } else { throw new DuplicateMessageException($"Received message with old counter: {chainKey.getIndex()} , {counter}"); } } //Avoiding a uint overflow uint chainKeyIndex = chainKey.getIndex(); if ((counter > chainKeyIndex) && (counter - chainKeyIndex > 2000)) { throw new InvalidMessageException("Over 2000 messages into the future!"); } while (chainKey.getIndex() < counter) { MessageKeys messageKeys = chainKey.getMessageKeys(); sessionState.setMessageKeys(theirEphemeral, messageKeys); chainKey = chainKey.getNextChainKey(); } sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey()); return chainKey.getMessageKeys(); }
private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral) { try { if (sessionState.hasReceiverChain(theirEphemeral)) { return sessionState.getReceiverChainKey(theirEphemeral); } else { RootKey rootKey = sessionState.getRootKey(); ECKeyPair ourEphemeral = sessionState.getSenderRatchetKeyPair(); Pair<RootKey, ChainKey> receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral); ECKeyPair ourNewEphemeral = Curve.generateKeyPair(); Pair<RootKey, ChainKey> senderChain = receiverChain.first().createChain(theirEphemeral, ourNewEphemeral); sessionState.setRootKey(senderChain.first()); sessionState.addReceiverChain(theirEphemeral, receiverChain.second()); sessionState.setPreviousCounter(Math.Max(sessionState.getSenderChainKey().getIndex() - 1, 0)); sessionState.setSenderChain(ourNewEphemeral, senderChain.second()); return receiverChain.second(); } } catch (InvalidKeyException e) { throw new InvalidMessageException(e); } }
private byte[] decrypt(SessionState sessionState, WhisperMessage ciphertextMessage) { if (!sessionState.hasSenderChain()) { throw new InvalidMessageException("Uninitialized session!"); } if (ciphertextMessage.getMessageVersion() != sessionState.getSessionVersion()) { throw new InvalidMessageException($"Message version {ciphertextMessage.getMessageVersion()}, but session version {sessionState.getSessionVersion()}"); } uint messageVersion = ciphertextMessage.getMessageVersion(); ECPublicKey theirEphemeral = ciphertextMessage.getSenderRatchetKey(); uint counter = ciphertextMessage.getCounter(); ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral); MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral, chainKey, counter); /* ciphertextMessage.verifyMac(messageVersion, sessionState.getRemoteIdentityKey(), sessionState.getLocalIdentityKey(), messageKeys.getMacKey());*/ byte[] plaintext = getPlaintext(messageVersion, messageKeys, ciphertextMessage.getBody()); sessionState.clearUnacknowledgedPreKeyMessage(); return plaintext; }
private byte[] decrypt(SessionRecord sessionRecord, WhisperMessage ciphertext) { lock (SESSION_LOCK) { IEnumerator<SessionState> previousStates = sessionRecord.getPreviousSessionStates().GetEnumerator(); //iterator LinkedList<Exception> exceptions = new LinkedList<Exception>(); try { SessionState sessionState = new SessionState(sessionRecord.getSessionState()); byte[] plaintext = decrypt(sessionState, ciphertext); sessionRecord.setState(sessionState); return plaintext; } catch (InvalidMessageException e) { exceptions.AddLast(e); // add (java default behavioir addlast) } while (previousStates.MoveNext()) //hasNext(); { try { SessionState promotedState = new SessionState(previousStates.Current); //.next() byte[] plaintext = decrypt(promotedState, ciphertext); sessionRecord.getPreviousSessionStates().Remove(previousStates.Current); // previousStates.remove() sessionRecord.promoteState(promotedState); return plaintext; } catch (InvalidMessageException e) { exceptions.AddLast(e); } } throw new InvalidMessageException("No valid sessions.", exceptions); } }
public SessionRecord(SessionState sessionState) { this.sessionState = sessionState; this.fresh = false; }
public void setState(SessionState sessionState) { this.sessionState = sessionState; }
public void promoteState(SessionState promotedState) { this.previousStates.AddFirst(sessionState); this.sessionState = promotedState; if (previousStates.Count > ARCHIVED_STATES_MAX_LENGTH) { previousStates.RemoveLast(); } }
public SessionState(SessionState copy) { this.sessionStructure = copy.sessionStructure.ToBuilder().Build(); }
public void testRatchetingSessionAsAlice() { byte[] bobPublic = { 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 }; byte[] bobIdentityPublic = { 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 }; byte[] aliceBasePublic = { 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 }; byte[] aliceBasePrivate = { 0x11, 0xae, 0x7c, 0x64, 0xd1, 0xe6, 0x1c, 0xd5, 0x96, 0xb7, 0x6a, 0x0d, 0xb5, 0x01, 0x26, 0x73, 0x39, 0x1c, 0xae, 0x66, 0xed, 0xbf, 0xcf, 0x07, 0x3b, 0x4d, 0xa8, 0x05, 0x16, 0xa4, 0x74, 0x49 }; byte[] aliceEphemeralPublic = { 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 }; byte[] aliceEphemeralPrivate = { 0xd1, 0xba, 0x38, 0xce, 0xa9, 0x17, 0x43, 0xd3, 0x39, 0x39, 0xc3, 0x3c, 0x84, 0x98, 0x65, 0x09, 0x28, 0x01, 0x61, 0xb8, 0xb6, 0x0f, 0xc7, 0x87, 0x0c, 0x59, 0x9c, 0x1d, 0x46, 0x20, 0x12, 0x48 }; byte[] aliceIdentityPublic = { 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 }; byte[] aliceIdentityPrivate = { 0x90, 0x40, 0xf0, 0xd4, 0xe0, 0x9c, 0xf3, 0x8f, 0x6d, 0xc7, 0xc1, 0x37, 0x79, 0xc9, 0x08, 0xc0, 0x15, 0xa1, 0xda, 0x4f, 0xa7, 0x87, 0x37, 0xa0, 0x80, 0xeb, 0x0a, 0x6f, 0x4f, 0x5f, 0x8f, 0x58 }; byte[] receiverChain = { 0xd2, 0x2f, 0xd5, 0x6d, 0x3f, 0xec, 0x81, 0x9c, 0xf4, 0xc3, 0xd5, 0x0c, 0x56, 0xed, 0xfb, 0x1c, 0x28, 0x0a, 0x1b, 0x31, 0x96, 0x45, 0x37, 0xf1, 0xd1, 0x61, 0xe1, 0xc9, 0x31, 0x48, 0xe3, 0x6b }; IdentityKey bobIdentityKey = new IdentityKey(bobIdentityPublic, 0); ECPublicKey bobEphemeralPublicKey = Curve.decodePoint(bobPublic, 0); ECPublicKey bobBasePublicKey = bobEphemeralPublicKey; ECPublicKey aliceBasePublicKey = Curve.decodePoint(aliceBasePublic, 0); ECPrivateKey aliceBasePrivateKey = Curve.decodePrivatePoint(aliceBasePrivate); ECKeyPair aliceBaseKey = new ECKeyPair(aliceBasePublicKey, aliceBasePrivateKey); ECPublicKey aliceEphemeralPublicKey = Curve.decodePoint(aliceEphemeralPublic, 0); ECPrivateKey aliceEphemeralPrivateKey = Curve.decodePrivatePoint(aliceEphemeralPrivate); ECKeyPair aliceEphemeralKey = new ECKeyPair(aliceEphemeralPublicKey, aliceEphemeralPrivateKey); IdentityKey aliceIdentityPublicKey = new IdentityKey(aliceIdentityPublic, 0); ECPrivateKey aliceIdentityPrivateKey = Curve.decodePrivatePoint(aliceIdentityPrivate); IdentityKeyPair aliceIdentityKey = new IdentityKeyPair(aliceIdentityPublicKey, aliceIdentityPrivateKey); SessionState session = new SessionState(); AliceAxolotlParameters parameters = AliceAxolotlParameters.newBuilder() .setOurBaseKey(aliceBaseKey) .setOurIdentityKey(aliceIdentityKey) .setTheirIdentityKey(bobIdentityKey) .setTheirSignedPreKey(bobBasePublicKey) .setTheirRatchetKey(bobEphemeralPublicKey) .setTheirOneTimePreKey(May<ECPublicKey>.NoValue) .create(); RatchetingSession.initializeSession(session, 2, parameters); Assert.AreEqual(session.getLocalIdentityKey(), aliceIdentityKey.getPublicKey()); Assert.AreEqual(session.getRemoteIdentityKey(), bobIdentityKey); CollectionAssert.AreEqual(session.getReceiverChainKey(bobEphemeralPublicKey).getKey(), receiverChain); }