/** * Build a new session from a {@link org.whispersystems.libaxolotl.state.PreKeyBundle} retrieved from * a server. * * @param preKey A PreKey for the destination recipient, retrieved from a server. * @throws InvalidKeyException when the {@link org.whispersystems.libaxolotl.state.PreKeyBundle} is * badly formatted. * @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the sender's * {@link IdentityKey} is not * trusted. */ public void Process(PreKeyBundle preKey) { lock (SessionCipher.SESSION_LOCK) { if (!identityKeyStore.IsTrustedIdentity(remoteAddress.GetName(), preKey.GetIdentityKey())) { throw new UntrustedIdentityException(remoteAddress.GetName(), preKey.GetIdentityKey()); } if (preKey.GetSignedPreKey() != null && !Curve.VerifySignature(preKey.GetIdentityKey().GetPublicKey(), preKey.GetSignedPreKey().Serialize(), preKey.GetSignedPreKeySignature())) { throw new InvalidKeyException("Invalid Signature on Device Key!"); } if (preKey.GetSignedPreKey() == null && preKey.GetPreKey() == null) { throw new InvalidKeyException("Both Signed and Unsigned Prekeys are Absent!"); } bool supportsV3 = preKey.GetSignedPreKey() != null; SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); ECKeyPair ourBaseKey = Curve.GenerateKeyPair(); ECPublicKey theirSignedPreKey = supportsV3 ? preKey.GetSignedPreKey() : preKey.GetPreKey(); ECPublicKey test = preKey.GetPreKey(); // TODO: Cleanup May <ECPublicKey> theirOneTimePreKey = (test == null) ? May <ECPublicKey> .NoValue : new May <ECPublicKey>(test); May <uint> theirOneTimePreKeyId = theirOneTimePreKey.HasValue ? new May <uint>(preKey.GetPreKeyId()) : May <uint> .NoValue; AliceAxolotlParameters.Builder parameters = AliceAxolotlParameters.NewBuilder(); parameters.SetOurBaseKey(ourBaseKey) .SetOurIdentityKey(identityKeyStore.GetIdentityKeyPair()) .SetTheirIdentityKey(preKey.GetIdentityKey()) .SetTheirSignedPreKey(theirSignedPreKey) .SetTheirRatchetKey(theirSignedPreKey) .SetTheirOneTimePreKey(supportsV3 ? theirOneTimePreKey : May <ECPublicKey> .NoValue); if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), supportsV3 ? (uint)3 : 2, parameters.Create()); sessionRecord.GetSessionState().SetUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.GetSignedPreKeyId(), ourBaseKey.GetPublicKey()); sessionRecord.GetSessionState().SetLocalRegistrationId(identityKeyStore.GetLocalRegistrationId()); sessionRecord.GetSessionState().SetRemoteRegistrationId(preKey.GetRegistrationId()); sessionRecord.GetSessionState().SetAliceBaseKey(ourBaseKey.GetPublicKey().Serialize()); sessionStore.StoreSession(remoteAddress, sessionRecord); identityKeyStore.SaveIdentity(remoteAddress.GetName(), preKey.GetIdentityKey()); } }
/// <summary> /// Build a new session from a {@link org.whispersystems.libsignal.state.PreKeyBundle} retrieved from /// a server. /// </summary> /// @param preKey A PreKey for the destination recipient, retrieved from a server. /// @throws InvalidKeyException when the {@link org.whispersystems.libsignal.state.PreKeyBundle} is /// badly formatted. /// @throws org.whispersystems.libsignal.UntrustedIdentityException when the sender's /// {@link IdentityKey} is not /// trusted. /// public void Process(PreKeyBundle preKey) { lock (SessionCipher.SessionLock) { if (!_identityKeyStore.IsTrustedIdentity(_remoteAddress, preKey.GetIdentityKey(), Direction.Sending)) { throw new UntrustedIdentityException(_remoteAddress.Name, preKey.GetIdentityKey()); } if (preKey.GetSignedPreKey() != null && !Curve.VerifySignature(preKey.GetIdentityKey().GetPublicKey(), preKey.GetSignedPreKey().Serialize(), preKey.GetSignedPreKeySignature())) { throw new InvalidKeyException("Invalid signature on device key!"); } if (preKey.GetSignedPreKey() == null) { throw new InvalidKeyException("No signed prekey!"); } SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); EcKeyPair ourBaseKey = Curve.GenerateKeyPair(); IEcPublicKey theirSignedPreKey = preKey.GetSignedPreKey(); IEcPublicKey test = preKey.GetPreKey(); May <IEcPublicKey> theirOneTimePreKey = (test == null) ? May <IEcPublicKey> .NoValue : new May <IEcPublicKey>(test); May <uint> theirOneTimePreKeyId = theirOneTimePreKey.HasValue ? new May <uint>(preKey.GetPreKeyId()) : May <uint> .NoValue; AliceSignalProtocolParameters.Builder parameters = AliceSignalProtocolParameters.NewBuilder(); parameters.SetOurBaseKey(ourBaseKey) .SetOurIdentityKey(_identityKeyStore.GetIdentityKeyPair()) .SetTheirIdentityKey(preKey.GetIdentityKey()) .SetTheirSignedPreKey(theirSignedPreKey) .SetTheirRatchetKey(theirSignedPreKey) .SetTheirOneTimePreKey(theirOneTimePreKey); if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), parameters.Create()); sessionRecord.GetSessionState().SetUnacknowledgedPreKeyMessage(theirOneTimePreKeyId, preKey.GetSignedPreKeyId(), ourBaseKey.GetPublicKey()); sessionRecord.GetSessionState().SetLocalRegistrationId(_identityKeyStore.GetLocalRegistrationId()); sessionRecord.GetSessionState().SetRemoteRegistrationId(preKey.GetRegistrationId()); sessionRecord.GetSessionState().SetAliceBaseKey(ourBaseKey.GetPublicKey().Serialize()); _identityKeyStore.SaveIdentity(_remoteAddress, preKey.GetIdentityKey()); _sessionStore.StoreSession(_remoteAddress, sessionRecord); } }
/** * Initiate a new session by sending an initial KeyExchangeMessage to the recipient. * * @return the KeyExchangeMessage to deliver. */ public KeyExchangeMessage Process() { lock (SessionCipher.SESSION_LOCK) { try { uint sequence = KeyHelper.GetRandomSequence(65534) + 1; uint flags = KeyExchangeMessage.INITIATE_FLAG; ECKeyPair baseKey = Curve.GenerateKeyPair(); ECKeyPair ratchetKey = Curve.GenerateKeyPair(); IdentityKeyPair identityKey = identityKeyStore.GetIdentityKeyPair(); byte[] baseKeySignature = Curve.CalculateSignature(identityKey.GetPrivateKey(), baseKey.GetPublicKey().Serialize()); SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); sessionRecord.GetSessionState().SetPendingKeyExchange(sequence, baseKey, ratchetKey, identityKey); sessionStore.StoreSession(remoteAddress, sessionRecord); return(new KeyExchangeMessage(2, sequence, flags, baseKey.GetPublicKey(), baseKeySignature, ratchetKey.GetPublicKey(), identityKey.GetPublicKey())); } catch (InvalidKeyException e) { throw new Exception(e.Message); } } }
/** * Encrypt a message. * * @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple. * @return A ciphertext message encrypted to the recipient+device tuple. */ public CiphertextMessage Encrypt(byte[] paddedMessage) { lock (SessionLock) { SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); SessionState sessionState = sessionRecord.GetSessionState(); ChainKey chainKey = sessionState.GetSenderChainKey(); MessageKeys messageKeys = chainKey.GetMessageKeys(); IEcPublicKey senderEphemeral = sessionState.GetSenderRatchetKey(); uint previousCounter = sessionState.GetPreviousCounter(); uint sessionVersion = sessionState.GetSessionVersion(); byte[] ciphertextBody = GetCiphertext(sessionVersion, messageKeys, paddedMessage); CiphertextMessage ciphertextMessage = new SignalMessage(sessionVersion, messageKeys.GetMacKey(), senderEphemeral, chainKey.GetIndex(), previousCounter, ciphertextBody, sessionState.GetLocalIdentityKey(), sessionState.GetRemoteIdentityKey()); if (sessionState.HasUnacknowledgedPreKeyMessage()) { SessionState.UnacknowledgedPreKeyMessageItems items = sessionState.GetUnacknowledgedPreKeyMessageItems(); uint localRegistrationId = sessionState.GetLocalRegistrationId(); ciphertextMessage = new PreKeySignalMessage(sessionVersion, localRegistrationId, items.GetPreKeyId(), items.GetSignedPreKeyId(), items.GetBaseKey(), sessionState.GetLocalIdentityKey(), (SignalMessage)ciphertextMessage); } sessionState.SetSenderChainKey(chainKey.GetNextChainKey()); _sessionStore.StoreSession(_remoteAddress, sessionRecord); return(ciphertextMessage); } }
private May <uint> ProcessV3(SessionRecord sessionRecord, PreKeyWhisperMessage message) { if (sessionRecord.HasSessionState(message.GetMessageVersion(), message.GetBaseKey().Serialize())) { return(May <uint> .NoValue); } SignedPreKeyRecord signedPreKeyRecord = signedPreKeyStore.LoadSignedPreKey(message.GetSignedPreKeyId()); ECKeyPair ourSignedPreKey = signedPreKeyRecord.GetKeyPair(); BobAxolotlParameters.Builder parameters = BobAxolotlParameters.NewBuilder(); parameters.SetTheirBaseKey(message.GetBaseKey()) .SetTheirIdentityKey(message.GetIdentityKey()) .SetOurIdentityKey(identityKeyStore.GetIdentityKeyPair()) .SetOurSignedPreKey(ourSignedPreKey) .SetOurRatchetKey(ourSignedPreKey); if (message.GetPreKeyId().HasValue) { parameters.SetOurOneTimePreKey(new May <ECKeyPair>(preKeyStore.LoadPreKey(message.GetPreKeyId().ForceGetValue()).GetKeyPair())); } else { parameters.SetOurOneTimePreKey(May <ECKeyPair> .NoValue); } if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), message.GetMessageVersion(), parameters.Create()); sessionRecord.GetSessionState().SetLocalRegistrationId(identityKeyStore.GetLocalRegistrationId()); sessionRecord.GetSessionState().SetRemoteRegistrationId(message.GetRegistrationId()); sessionRecord.GetSessionState().SetAliceBaseKey(message.GetBaseKey().Serialize()); if (message.GetPreKeyId().HasValue&& message.GetPreKeyId().ForceGetValue() != Medium.MAX_VALUE) { return(message.GetPreKeyId()); } else { return(May <uint> .NoValue); } }
private May <uint> ProcessV3(SessionRecord sessionRecord, PreKeySignalMessage message) { if (sessionRecord.HasSessionState(message.GetMessageVersion(), message.GetBaseKey().Serialize())) { Debug.WriteLine("We've already setup a session for this V3 message, letting bundled message fall through..."); return(May <uint> .NoValue); } EcKeyPair ourSignedPreKey = _signedPreKeyStore.LoadSignedPreKey(message.GetSignedPreKeyId()).GetKeyPair(); BobSignalProtocolParameters.Builder parameters = BobSignalProtocolParameters.NewBuilder(); parameters.SetTheirBaseKey(message.GetBaseKey()) .SetTheirIdentityKey(message.GetIdentityKey()) .SetOurIdentityKey(_identityKeyStore.GetIdentityKeyPair()) .SetOurSignedPreKey(ourSignedPreKey) .SetOurRatchetKey(ourSignedPreKey); if (message.GetPreKeyId().HasValue) { parameters.SetOurOneTimePreKey(new May <EcKeyPair>(_preKeyStore.LoadPreKey(message.GetPreKeyId().ForceGetValue()).GetKeyPair())); } else { parameters.SetOurOneTimePreKey(May <EcKeyPair> .NoValue); } if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), parameters.Create()); sessionRecord.GetSessionState().SetLocalRegistrationId(_identityKeyStore.GetLocalRegistrationId()); sessionRecord.GetSessionState().SetRemoteRegistrationId(message.GetRegistrationId()); sessionRecord.GetSessionState().SetAliceBaseKey(message.GetBaseKey().Serialize()); if (message.GetPreKeyId().HasValue) { return(message.GetPreKeyId()); } else { return(May <uint> .NoValue); } }
public uint GetRemoteRegistrationId() { lock (SessionLock) { SessionRecord record = _sessionStore.LoadSession(_remoteAddress); return(record.GetSessionState().GetRemoteRegistrationId()); } }
public void TestBasicSessionV3() { SessionRecord aliceSessionRecord = new SessionRecord(); SessionRecord bobSessionRecord = new SessionRecord(); InitializeSessionsV3(aliceSessionRecord.GetSessionState(), bobSessionRecord.GetSessionState()); RunInteraction(aliceSessionRecord, bobSessionRecord); }
public uint GetRemoteRegistrationId() { lock (SESSION_LOCK) { SessionRecord record = sessionStore.LoadSession(remoteAddress); return(record.GetSessionState().GetRemoteRegistrationId()); } }
private May <uint> ProcessV2(SessionRecord sessionRecord, PreKeyWhisperMessage message) { if (!message.GetPreKeyId().HasValue) { throw new InvalidKeyIdException("V2 message requires one time prekey id!"); } if (!preKeyStore.ContainsPreKey(message.GetPreKeyId().ForceGetValue()) && sessionStore.ContainsSession(remoteAddress)) { return(May <uint> .NoValue); } ECKeyPair ourPreKey = preKeyStore.LoadPreKey(message.GetPreKeyId().ForceGetValue()).GetKeyPair(); BobAxolotlParameters.Builder parameters = BobAxolotlParameters.NewBuilder(); parameters.SetOurIdentityKey(identityKeyStore.GetIdentityKeyPair()) .SetOurSignedPreKey(ourPreKey) .SetOurRatchetKey(ourPreKey) .SetOurOneTimePreKey(May <ECKeyPair> .NoValue) //absent .SetTheirIdentityKey(message.GetIdentityKey()) .SetTheirBaseKey(message.GetBaseKey()); if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), message.GetMessageVersion(), parameters.Create()); sessionRecord.GetSessionState().SetLocalRegistrationId(identityKeyStore.GetLocalRegistrationId()); sessionRecord.GetSessionState().SetRemoteRegistrationId(message.GetRegistrationId()); sessionRecord.GetSessionState().SetAliceBaseKey(message.GetBaseKey().Serialize()); if (message.GetPreKeyId().ForceGetValue() != Medium.MAX_VALUE) { return(message.GetPreKeyId()); } else { return(May <uint> .NoValue); // May.absent(); } }
public uint GetSessionVersion() { lock (SESSION_LOCK) { if (!sessionStore.ContainsSession(remoteAddress)) { throw new Exception($"No session for {remoteAddress}!"); } SessionRecord record = sessionStore.LoadSession(remoteAddress); return(record.GetSessionState().GetSessionVersion()); } }
public uint GetSessionVersion() { lock (SessionLock) { if (!_sessionStore.ContainsSession(_remoteAddress)) { throw new Exception($"No session for {_remoteAddress}!"); // IllegalState } SessionRecord record = _sessionStore.LoadSession(_remoteAddress); return(record.GetSessionState().GetSessionVersion()); } }
private KeyExchangeMessage ProcessInitiate(KeyExchangeMessage message) { uint flags = KeyExchangeMessage.RESPONSE_FLAG; SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); if (message.GetVersion() >= 3 && !Curve.VerifySignature(message.GetIdentityKey().GetPublicKey(), message.GetBaseKey().Serialize(), message.GetBaseKeySignature())) { throw new InvalidKeyException("Bad signature!"); } SymmetricAxolotlParameters.Builder builder = SymmetricAxolotlParameters.NewBuilder(); if (!sessionRecord.GetSessionState().HasPendingKeyExchange()) { builder.SetOurIdentityKey(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(message.GetBaseKey()) .SetTheirRatchetKey(message.GetRatchetKey()) .SetTheirIdentityKey(message.GetIdentityKey()); SymmetricAxolotlParameters parameters = builder.Create(); if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), Math.Min(message.GetMaxVersion(), CipherTextMessage.CURRENT_VERSION), parameters); sessionStore.StoreSession(remoteAddress, sessionRecord); identityKeyStore.SaveIdentity(remoteAddress.GetName(), message.GetIdentityKey()); byte[] baseKeySignature = Curve.CalculateSignature(parameters.GetOurIdentityKey().GetPrivateKey(), parameters.GetOurBaseKey().GetPublicKey().Serialize()); return(new KeyExchangeMessage(sessionRecord.GetSessionState().GetSessionVersion(), message.GetSequence(), flags, parameters.GetOurBaseKey().GetPublicKey(), baseKeySignature, parameters.GetOurRatchetKey().GetPublicKey(), parameters.GetOurIdentityKey().GetPublicKey())); }
/** * Decrypt a message. * * @param ciphertext The {@link SignalMessage} to decrypt. * @param callback A callback that is triggered after decryption is complete, * but before the updated session state has been committed to the session * DB. This allows some implementations to store the committed plaintext * to a DB first, in case they are concerned with a crash happening between * the time the session state is updated but before they're able to store * the plaintext to disk. * * @return The plaintext. * @throws InvalidMessageException if the input is not valid ciphertext. * @throws DuplicateMessageException if the input is a message that has already been received. * @throws LegacyMessageException if the input is a message formatted by a protocol version that * is no longer supported. * @throws NoSessionException if there is no established session for this contact. */ public byte[] Decrypt(SignalMessage ciphertext, IDecryptionCallback callback) { lock (SessionLock) { if (!_sessionStore.ContainsSession(_remoteAddress)) { throw new NoSessionException($"No session for: {_remoteAddress}"); } SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); byte[] plaintext = Decrypt(sessionRecord, ciphertext); if (!_identityKeyStore.IsTrustedIdentity(_remoteAddress, sessionRecord.GetSessionState().GetRemoteIdentityKey(), Direction.Receiving)) { throw new UntrustedIdentityException(_remoteAddress.Name, sessionRecord.GetSessionState().GetRemoteIdentityKey()); } callback.HandlePlaintext(plaintext); _sessionStore.StoreSession(_remoteAddress, sessionRecord); return(plaintext); } }
private void ProcessResponse(KeyExchangeMessage message) { SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); SessionState sessionState = sessionRecord.GetSessionState(); bool hasPendingKeyExchange = sessionState.HasPendingKeyExchange(); bool isSimultaneousInitiateResponse = message.IsResponseForSimultaneousInitiate(); if (!hasPendingKeyExchange || sessionState.GetPendingKeyExchangeSequence() != message.GetSequence()) { //Log.w(TAG, "No matching sequence for response. Is simultaneous initiate response: " + isSimultaneousInitiateResponse); if (!isSimultaneousInitiateResponse) { throw new StaleKeyExchangeException(); } else { return; } } SymmetricAxolotlParameters.Builder parameters = SymmetricAxolotlParameters.NewBuilder(); parameters.SetOurBaseKey(sessionRecord.GetSessionState().GetPendingKeyExchangeBaseKey()) .SetOurRatchetKey(sessionRecord.GetSessionState().GetPendingKeyExchangeRatchetKey()) .SetOurIdentityKey(sessionRecord.GetSessionState().GetPendingKeyExchangeIdentityKey()) .SetTheirBaseKey(message.GetBaseKey()) .SetTheirRatchetKey(message.GetRatchetKey()) .SetTheirIdentityKey(message.GetIdentityKey()); if (!sessionRecord.IsFresh()) { sessionRecord.ArchiveCurrentState(); } RatchetingSession.InitializeSession(sessionRecord.GetSessionState(), Math.Min(message.GetMaxVersion(), CipherTextMessage.CURRENT_VERSION), parameters.Create()); if (sessionRecord.GetSessionState().GetSessionVersion() >= 3 && !Curve.VerifySignature(message.GetIdentityKey().GetPublicKey(), message.GetBaseKey().Serialize(), message.GetBaseKeySignature())) { throw new InvalidKeyException("Base key signature doesn't match!"); } sessionStore.StoreSession(remoteAddress, sessionRecord); identityKeyStore.SaveIdentity(remoteAddress.GetName(), message.GetIdentityKey()); }
private byte[] Decrypt(SessionRecord sessionRecord, WhisperMessage ciphertext) { lock (SESSION_LOCK) { IEnumerator <SessionState> previousStates = sessionRecord.GetPreviousSessionStates().GetEnumerator(); 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); } while (previousStates.MoveNext()) { try { SessionState promotedState = new SessionState(previousStates.Current); byte[] plaintext = Decrypt(promotedState, ciphertext); sessionRecord.GetPreviousSessionStates().Remove(previousStates.Current); sessionRecord.PromoteState(promotedState); return(plaintext); } catch (InvalidMessageException e) { exceptions.AddLast(e); } } throw new InvalidMessageException("No valid sessions.", exceptions); } }
private byte[] Decrypt(SessionRecord sessionRecord, SignalMessage ciphertext) { lock (SessionLock) { 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); } }
/** * Decrypt a message. * * @param ciphertext The {@link PreKeySignalMessage} to decrypt. * @param callback A callback that is triggered after decryption is complete, * but before the updated session state has been committed to the session * DB. This allows some implementations to store the committed plaintext * to a DB first, in case they are concerned with a crash happening between * the time the session state is updated but before they're able to store * the plaintext to disk. * * @return The plaintext. * @throws InvalidMessageException if the input is not valid ciphertext. * @throws DuplicateMessageException if the input is a message that has already been received. * @throws LegacyMessageException if the input is a message formatted by a protocol version that * is no longer supported. * @throws InvalidKeyIdException when there is no local {@link org.whispersystems.libsignal.state.PreKeyRecord} * that corresponds to the PreKey ID in the message. * @throws InvalidKeyException when the message is formatted incorrectly. * @throws UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted. */ public byte[] Decrypt(PreKeySignalMessage ciphertext, IDecryptionCallback callback) { lock (SessionLock) { SessionRecord sessionRecord = _sessionStore.LoadSession(_remoteAddress); May <uint> unsignedPreKeyId = _sessionBuilder.Process(sessionRecord, ciphertext); byte[] plaintext = Decrypt(sessionRecord, ciphertext.GetSignalMessage()); _identityKeyStore.SaveIdentity(_remoteAddress, sessionRecord.GetSessionState().GetRemoteIdentityKey()); callback.HandlePlaintext(plaintext); _sessionStore.StoreSession(_remoteAddress, sessionRecord); if (unsignedPreKeyId.HasValue) { _preKeyStore.RemovePreKey(unsignedPreKeyId.ForceGetValue()); } return(plaintext); } }
public void TestMessageKeyLimits() { SessionRecord aliceSessionRecord = new SessionRecord(); SessionRecord bobSessionRecord = new SessionRecord(); InitializeSessionsV3(aliceSessionRecord.GetSessionState(), bobSessionRecord.GetSessionState()); ISignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); ISignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); aliceStore.StoreSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord); bobStore.StoreSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord); SessionCipher aliceCipher = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1)); SessionCipher bobCipher = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1)); List <CiphertextMessage> inflight = new List <CiphertextMessage>(); for (int i = 0; i < 2010; i++) { inflight.Add(aliceCipher.Encrypt(Encoding.UTF8.GetBytes("you've never been so hungry, you've never been so cold"))); } bobCipher.Decrypt(new SignalMessage(inflight[1000].Serialize())); bobCipher.Decrypt(new SignalMessage(inflight[inflight.Count - 1].Serialize())); try { bobCipher.Decrypt(new SignalMessage(inflight[0].Serialize())); throw new Exception("Should have failed!"); } catch (DuplicateMessageException) { // good } }