/// <summary> /// Decrypt a SenderKey group message. /// </summary> /// <param name="senderKeyMessageBytes">The received ciphertext.</param> /// <param name="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.</param> /// <returns>Plaintext</returns> /// <exception cref="LegacyMessageException"></exception> /// <exception cref="InvalidMessageException"></exception> /// <exception cref="DuplicateMessageException"></exception> /// <exception cref="NoSessionException"></exception> public byte[] decrypt(byte[] senderKeyMessageBytes, DecryptionCallback callback) { lock (LOCK) { try { SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); if (record.isEmpty()) { throw new NoSessionException("No sender key for: " + senderKeyId); } SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyMessageBytes); SenderKeyState senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()); senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()); SenderMessageKey senderKey = getSenderKey(senderKeyState, senderKeyMessage.getIteration()); byte[] plaintext = getPlainText(senderKey.getIv(), senderKey.getCipherKey(), senderKeyMessage.getCipherText()); callback.handlePlaintext(plaintext); senderKeyStore.storeSenderKey(senderKeyId, record); return(plaintext); } catch (Exception e) when(e is InvalidKeyException || e is InvalidKeyIdException) { throw new InvalidMessageException(e); } } }
/** * Decrypt a message. * * @param ciphertext The {@link WhisperMessage} 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(WhisperMessage ciphertext, DecryptionCallback callback) { lock (SESSION_LOCK) { if (!sessionStore.containsSession(remoteAddress)) { throw new NoSessionException($"No session for: {remoteAddress}"); } SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); byte[] plaintext = decrypt(sessionRecord, ciphertext); callback.handlePlaintext(plaintext); sessionStore.storeSession(remoteAddress, sessionRecord); return(plaintext); } }
/** * Decrypt a message. * * @param ciphertext The {@link PreKeyWhisperMessage} 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.libaxolotl.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(PreKeyWhisperMessage ciphertext, DecryptionCallback callback) { lock (SESSION_LOCK) { SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress); May <uint> unsignedPreKeyId = sessionBuilder.process(sessionRecord, ciphertext); byte[] plaintext = decrypt(sessionRecord, ciphertext.getWhisperMessage()); callback.handlePlaintext(plaintext); sessionStore.storeSession(remoteAddress, sessionRecord); if (unsignedPreKeyId.HasValue) { preKeyStore.removePreKey(unsignedPreKeyId.ForceGetValue()); } return(plaintext); } }
/** * 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, DecryptionCallback callback) { lock (SESSION_LOCK) { 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); } }
/** * 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, DecryptionCallback callback) { lock (SESSION_LOCK) { 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); } }
/** * Decrypt a message. * * @param ciphertext The {@link WhisperMessage} 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(WhisperMessage ciphertext, DecryptionCallback callback) { lock (SESSION_LOCK) { if (!sessionStore.ContainsSession(remoteAddress)) { throw new NoSessionException($"No session for: {remoteAddress}"); } SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); byte[] plaintext = decrypt(sessionRecord, ciphertext); callback.handlePlaintext(plaintext); sessionStore.StoreSession(remoteAddress, sessionRecord); return plaintext; } }
/** * Decrypt a message. * * @param ciphertext The {@link PreKeyWhisperMessage} 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.libaxolotl.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(PreKeyWhisperMessage ciphertext, DecryptionCallback callback) { lock (SESSION_LOCK) { SessionRecord sessionRecord = sessionStore.LoadSession(remoteAddress); May<uint> unsignedPreKeyId = sessionBuilder.process(sessionRecord, ciphertext); byte[] plaintext = decrypt(sessionRecord, ciphertext.getWhisperMessage()); callback.handlePlaintext(plaintext); sessionStore.StoreSession(remoteAddress, sessionRecord); if (unsignedPreKeyId.HasValue) { preKeyStore.RemovePreKey(unsignedPreKeyId.ForceGetValue()); } return plaintext; } }
/** * Decrypt a SenderKey group message. * * @param senderKeyMessageBytes The received ciphertext. * @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 Plaintext * @throws LegacyMessageException * @throws InvalidMessageException * @throws DuplicateMessageException */ public byte[] decrypt(byte[] senderKeyMessageBytes, DecryptionCallback callback) { lock (LOCK) { try { SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); if (record.isEmpty()) { throw new NoSessionException("No sender key for: " + senderKeyId); } SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyMessageBytes); SenderKeyState senderKeyState = record.getSenderKeyState(senderKeyMessage.getKeyId()); senderKeyMessage.verifySignature(senderKeyState.getSigningKeyPublic()); SenderMessageKey senderKey = getSenderKey(senderKeyState, senderKeyMessage.getIteration()); byte[] plaintext = getPlainText(senderKey.getIv(), senderKey.getCipherKey(), senderKeyMessage.getCipherText()); callback.handlePlaintext(plaintext); senderKeyStore.storeSenderKey(senderKeyId, record); return plaintext; } catch (InvalidKeyException e) { throw new InvalidMessageException(e); } catch (InvalidKeyIdException e) { throw new InvalidMessageException(e); } } }