package org.thoughtcrime.SMP.crypto.SMP; import android.util.Log; import com.google.protobuf.InvalidProtocolBufferException; import org.whispersystems.libaxolotl.AxolotlAddress; 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.SessionCipher; import org.whispersystems.libaxolotl.UntrustedIdentityException; import org.whispersystems.libaxolotl.protocol.CiphertextMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.protocol.WhisperMessage; import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.textsecure.api.crypto.TextSecureCipher; import org.whispersystems.textsecure.api.messages.TextSecureAttachmentPointer; import org.whispersystems.textsecure.api.messages.TextSecureEnvelope; import org.whispersystems.textsecure.api.messages.TextSecureGroup; import org.whispersystems.textsecure.api.messages.TextSecureSyncContext; import org.whispersystems.textsecure.api.push.TextSecureAddress; import org.whispersystems.textsecure.internal.push.OutgoingPushMessage; import org.whispersystems.textsecure.internal.push.PushMessageProtos; import org.whispersystems.textsecure.internal.push.PushTransportDetails; import org.whispersystems.textsecure.internal.util.Base64; import java.util.Iterator; import java.util.LinkedList; import java.util.List; /** * Created by ludwig on 09/07/15. */ public class TextSecureSMPCipher extends TextSecureCipher { private static final String TAG = TextSecureSMPCipher.class.getSimpleName(); private final AxolotlStore axolotlStore; private final TextSecureAddress localAddress; public TextSecureSMPCipher (TextSecureAddress localAddress, AxolotlStore axolotlStore) { super(localAddress, axolotlStore); this.axolotlStore = axolotlStore; this.localAddress = localAddress; } public OutgoingPushMessage encrypt(AxolotlAddress destination, byte[] unpaddedMessage) { SessionCipher sessionCipher = new SessionCipher(this.axolotlStore, destination); PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); CiphertextMessage message = sessionCipher.encrypt(transportDetails.getPaddedMessageBody(unpaddedMessage)); int remoteRegistrationId = sessionCipher.getRemoteRegistrationId(); String body = Base64.encodeBytes(message.serialize()); byte type; switch(message.getType()) { case 2: type = 1; break; case 3: type = 3; break; default: throw new AssertionError("Bad type: " + message.getType()); } return new OutgoingPushMessage(type, destination.getDeviceId(), remoteRegistrationId, body); } public TextSecureSMPMessage decrypt(TextSecureEnvelope envelope) throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException, NoSessionException { try { AxolotlAddress e = new AxolotlAddress(envelope.getSource(), envelope.getSourceDevice()); SessionCipher sessionCipher = new SessionCipher(this.axolotlStore, e); byte[] paddedMessage; if(envelope.isPreKeyWhisperMessage()) { paddedMessage = sessionCipher.decrypt(new PreKeyWhisperMessage(envelope.getMessage())); } else { if(!envelope.isWhisperMessage()) { throw new InvalidMessageException("Unknown type: " + envelope.getType()); } paddedMessage = sessionCipher.decrypt(new WhisperMessage(envelope.getMessage())); } PushTransportDetails transportDetails = new PushTransportDetails(sessionCipher.getSessionVersion()); PushMessageProtos.PushMessageContent content = PushMessageProtos.PushMessageContent.parseFrom(transportDetails.getStrippedPaddingMessageBody(paddedMessage)); return this.createTextSecureSMPMessage(envelope, content); } catch (InvalidProtocolBufferException var7) { throw new InvalidMessageException(var7); } } private TextSecureSMPMessage createTextSecureSMPMessage(TextSecureEnvelope envelope, PushMessageProtos.PushMessageContent content) { TextSecureGroup groupInfo = this.createGroupInfo(envelope, content); TextSecureSyncContext syncContext = this.createSyncContext(envelope, content); //TODO: needs proper flags boolean smp = content.hasSync(); boolean smpSync = true; Log.d(TAG, "createTextSecureSMPMessage hasSync: " + content.hasSync()); LinkedList attachments = new LinkedList(); boolean endSession = (content.getFlags() & 1) != 0; Iterator var7 = content.getAttachmentsList().iterator(); while(var7.hasNext()) { PushMessageProtos.PushMessageContent.AttachmentPointer pointer = (PushMessageProtos.PushMessageContent.AttachmentPointer)var7.next(); attachments.add(new TextSecureAttachmentPointer(pointer.getId(), pointer.getContentType(), pointer.getKey().toByteArray(), envelope.getRelay())); } return new TextSecureSMPMessage(envelope.getTimestamp(), groupInfo, attachments, content.getBody(), syncContext, endSession, smp, smpSync); } private TextSecureSyncContext createSyncContext(TextSecureEnvelope envelope, PushMessageProtos.PushMessageContent content) { return !content.hasSync()?null:(!envelope.getSource().equals(this.localAddress.getNumber())?null:new TextSecureSyncContext(content.getSync().getDestination(), content.getSync().getTimestamp())); } private TextSecureGroup createGroupInfo(TextSecureEnvelope envelope, PushMessageProtos.PushMessageContent content) { if(!content.hasGroup()) { return null; } else { TextSecureGroup.Type type; switch(content.getGroup().getType()) { case DELIVER: type = TextSecureGroup.Type.DELIVER; break; case UPDATE: type = TextSecureGroup.Type.UPDATE; break; case QUIT: type = TextSecureGroup.Type.QUIT; break; default: type = TextSecureGroup.Type.UNKNOWN; } if(content.getGroup().getType() != org.whispersystems.textsecure.internal.push.PushMessageProtos.PushMessageContent.GroupContext.Type.DELIVER) { String name = null; List members = null; TextSecureAttachmentPointer avatar = null; if(content.getGroup().hasName()) { name = content.getGroup().getName(); } if(content.getGroup().getMembersCount() > 0) { members = content.getGroup().getMembersList(); } if(content.getGroup().hasAvatar()) { avatar = new TextSecureAttachmentPointer(content.getGroup().getAvatar().getId(), content.getGroup().getAvatar().getContentType(), content.getGroup().getAvatar().getKey().toByteArray(), envelope.getRelay()); } return new TextSecureGroup(type, content.getGroup().getId().toByteArray(), name, members, avatar); } else { return new TextSecureGroup(content.getGroup().getId().toByteArray()); } } } }