mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-28 20:45:17 +00:00
130 lines
6.4 KiB
Java
130 lines
6.4 KiB
Java
package org.thoughtcrime.securesms.crypto;
|
|
|
|
import android.content.Context;
|
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
|
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
|
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
|
|
import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
|
|
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
|
|
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
|
|
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
|
|
import org.thoughtcrime.securesms.sms.OutgoingPrekeyBundleMessage;
|
|
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
|
import org.thoughtcrime.securesms.sms.SmsTransportDetails;
|
|
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
|
import org.whispersystems.libaxolotl.InvalidKeyException;
|
|
import org.whispersystems.libaxolotl.InvalidKeyIdException;
|
|
import org.whispersystems.libaxolotl.InvalidMessageException;
|
|
import org.whispersystems.libaxolotl.InvalidVersionException;
|
|
import org.whispersystems.libaxolotl.LegacyMessageException;
|
|
import org.whispersystems.libaxolotl.NoSessionException;
|
|
import org.whispersystems.libaxolotl.SessionBuilder;
|
|
import org.whispersystems.libaxolotl.SessionCipher;
|
|
import org.whispersystems.libaxolotl.StaleKeyExchangeException;
|
|
import org.whispersystems.libaxolotl.UntrustedIdentityException;
|
|
import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
|
|
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
|
|
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
|
|
import org.whispersystems.libaxolotl.protocol.WhisperMessage;
|
|
import org.whispersystems.libaxolotl.state.AxolotlStore;
|
|
import org.whispersystems.textsecure.api.push.PushAddress;
|
|
|
|
import java.io.IOException;
|
|
|
|
public class SmsCipher {
|
|
|
|
private final SmsTransportDetails transportDetails = new SmsTransportDetails();
|
|
|
|
private final AxolotlStore axolotlStore;
|
|
|
|
public SmsCipher(AxolotlStore axolotlStore) {
|
|
this.axolotlStore = axolotlStore;
|
|
}
|
|
|
|
public IncomingTextMessage decrypt(Context context, IncomingTextMessage message)
|
|
throws LegacyMessageException, InvalidMessageException,
|
|
DuplicateMessageException, NoSessionException
|
|
{
|
|
try {
|
|
Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), false);
|
|
long recipientId = recipients.getPrimaryRecipient().getRecipientId();
|
|
byte[] decoded = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
|
|
WhisperMessage whisperMessage = new WhisperMessage(decoded);
|
|
SessionCipher sessionCipher = new SessionCipher(axolotlStore, recipientId, 1);
|
|
byte[] padded = sessionCipher.decrypt(whisperMessage);
|
|
byte[] plaintext = transportDetails.getStrippedPaddingMessageBody(padded);
|
|
|
|
if (message.isEndSession() && "TERMINATE".equals(new String(plaintext))) {
|
|
axolotlStore.deleteSession(recipientId, 1);
|
|
}
|
|
|
|
return message.withMessageBody(new String(plaintext));
|
|
} catch (RecipientFormattingException | IOException e) {
|
|
throw new InvalidMessageException(e);
|
|
}
|
|
}
|
|
|
|
public IncomingEncryptedMessage decrypt(Context context, IncomingPreKeyBundleMessage message)
|
|
throws InvalidVersionException, InvalidMessageException, DuplicateMessageException,
|
|
UntrustedIdentityException, LegacyMessageException
|
|
{
|
|
try {
|
|
Recipients recipients = RecipientFactory.getRecipientsFromString(context, message.getSender(), false);
|
|
byte[] decoded = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
|
|
PreKeyWhisperMessage preKeyMessage = new PreKeyWhisperMessage(decoded);
|
|
SessionCipher sessionCipher = new SessionCipher(axolotlStore, recipients.getPrimaryRecipient().getRecipientId(), 1);
|
|
byte[] padded = sessionCipher.decrypt(preKeyMessage);
|
|
byte[] plaintext = transportDetails.getStrippedPaddingMessageBody(padded);
|
|
|
|
return new IncomingEncryptedMessage(message, new String(plaintext));
|
|
} catch (RecipientFormattingException | IOException | InvalidKeyException | InvalidKeyIdException e) {
|
|
throw new InvalidMessageException(e);
|
|
}
|
|
}
|
|
|
|
public OutgoingTextMessage encrypt(OutgoingTextMessage message) throws NoSessionException {
|
|
byte[] paddedBody = transportDetails.getPaddedMessageBody(message.getMessageBody().getBytes());
|
|
long recipientId = message.getRecipients().getPrimaryRecipient().getRecipientId();
|
|
|
|
if (!axolotlStore.containsSession(recipientId, PushAddress.DEFAULT_DEVICE_ID)) {
|
|
throw new NoSessionException("No session for: " + recipientId);
|
|
}
|
|
|
|
SessionCipher cipher = new SessionCipher(axolotlStore, recipientId, PushAddress.DEFAULT_DEVICE_ID);
|
|
CiphertextMessage ciphertextMessage = cipher.encrypt(paddedBody);
|
|
String encodedCiphertext = new String(transportDetails.getEncodedMessage(ciphertextMessage.serialize()));
|
|
|
|
if (ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE) {
|
|
return new OutgoingPrekeyBundleMessage(message, encodedCiphertext);
|
|
} else {
|
|
return message.withBody(encodedCiphertext);
|
|
}
|
|
}
|
|
|
|
public OutgoingKeyExchangeMessage process(Context context, IncomingKeyExchangeMessage message)
|
|
throws UntrustedIdentityException, StaleKeyExchangeException,
|
|
InvalidVersionException, LegacyMessageException, InvalidMessageException
|
|
{
|
|
try {
|
|
Recipient recipient = RecipientFactory.getRecipientsFromString(context, message.getSender(), false).getPrimaryRecipient();
|
|
KeyExchangeMessage exchangeMessage = new KeyExchangeMessage(transportDetails.getDecodedMessage(message.getMessageBody().getBytes()));
|
|
SessionBuilder sessionBuilder = new SessionBuilder(axolotlStore, recipient.getRecipientId(), 1);
|
|
|
|
KeyExchangeMessage response = sessionBuilder.process(exchangeMessage);
|
|
|
|
if (response != null) {
|
|
byte[] serializedResponse = transportDetails.getEncodedMessage(response.serialize());
|
|
return new OutgoingKeyExchangeMessage(recipient, new String(serializedResponse));
|
|
} else {
|
|
return null;
|
|
}
|
|
} catch (RecipientFormattingException | IOException | InvalidKeyException e) {
|
|
throw new InvalidMessageException(e);
|
|
}
|
|
}
|
|
|
|
}
|