001/**
002 *
003 * Copyright 2017 Paul Schaub
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.omemo;
018
019import static org.jivesoftware.smackx.omemo.util.OmemoConstants.BODY_OMEMO_HINT;
020import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO;
021import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL;
022
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.Set;
026
027import org.jivesoftware.smack.packet.Message;
028import org.jivesoftware.smack.packet.MessageBuilder;
029
030import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
031import org.jivesoftware.smackx.hints.element.StoreHint;
032import org.jivesoftware.smackx.omemo.element.OmemoElement;
033import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
034import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
035
036import org.jxmpp.jid.Jid;
037
038public class OmemoMessage {
039
040    private final OmemoElement element;
041    private final byte[] messageKey, iv;
042
043    OmemoMessage(OmemoElement element, byte[] key, byte[] iv) {
044        this.element = element;
045        this.messageKey = key;
046        this.iv = iv;
047    }
048
049    /**
050     * Return the original OmemoElement (<encrypted/>).
051     *
052     * @return omemoElement of the message
053     */
054    public OmemoElement getElement() {
055        return element;
056    }
057
058    /**
059     * Return the messageKey (or transported key in case of a KeyTransportMessage).
060     *
061     * @return encryption key that protects the message payload
062     */
063    public byte[] getKey() {
064        return messageKey.clone();
065    }
066
067    /**
068     * Return the initialization vector belonging to the key.
069     *
070     * @return initialization vector
071     */
072    public byte[] getIv() {
073        return iv.clone();
074    }
075
076    /**
077     * Outgoing OMEMO message.
078     */
079    public static class Sent extends OmemoMessage {
080        private final Set<OmemoDevice> intendedDevices = new HashSet<>();
081        private final HashMap<OmemoDevice, Throwable> skippedDevices = new HashMap<>();
082
083        /**
084         * Create a new outgoing OMEMO message.
085         *
086         * @param element OmemoElement
087         * @param key messageKey (or transported key)
088         * @param iv initialization vector belonging to key
089         * @param intendedDevices devices the client intended to encrypt the message for
090         * @param skippedDevices devices which were skipped during encryption process because encryption
091         *                       failed for some reason
092         */
093        Sent(OmemoElement element, byte[] key, byte[] iv, Set<OmemoDevice> intendedDevices, HashMap<OmemoDevice, Throwable> skippedDevices) {
094            super(element, key, iv);
095            this.intendedDevices.addAll(intendedDevices);
096            this.skippedDevices.putAll(skippedDevices);
097        }
098
099        /**
100         * Return a list of all devices the sender originally intended to encrypt the message for.
101         *
102         * @return list of intended recipients.
103         */
104        public Set<OmemoDevice> getIntendedDevices() {
105            return intendedDevices;
106        }
107
108        /**
109         * Return a map of all skipped recipients and the reasons for skipping.
110         *
111         * @return map of skipped recipients and reasons for that.
112         */
113        public HashMap<OmemoDevice, Throwable> getSkippedDevices() {
114            return skippedDevices;
115        }
116
117        /**
118         * Determine, if some recipients were skipped during encryption.
119         *
120         * @return true if recipients were skipped.
121         */
122        public boolean isMissingRecipients() {
123            return !getSkippedDevices().isEmpty();
124        }
125
126        /**
127         * Return the OmemoElement wrapped in a Message ready to be sent.
128         * The message is addressed to recipient, contains the OmemoElement
129         * as well as an optional clear text hint as body, a MAM storage hint
130         * and an EME hint about OMEMO encryption.
131         *
132         * @param messageBuilder a message builder which will be used to build the message.
133         * @param recipient recipient for the to-field of the message.
134         * @return the build message.
135         */
136        public Message buildMessage(MessageBuilder messageBuilder, Jid recipient) {
137            messageBuilder.ofType(Message.Type.chat).to(recipient);
138
139            messageBuilder.addExtension(getElement());
140
141            if (OmemoConfiguration.getAddOmemoHintBody()) {
142                messageBuilder.setBody(BODY_OMEMO_HINT);
143            }
144
145            StoreHint.set(messageBuilder);
146            messageBuilder.addExtension(new ExplicitMessageEncryptionElement(OMEMO_NAMESPACE_V_AXOLOTL, OMEMO));
147
148            return messageBuilder.build();
149        }
150    }
151
152    /**
153     * Incoming OMEMO message.
154     */
155    public static class Received extends OmemoMessage {
156        private final String message;
157        private final OmemoFingerprint sendersFingerprint;
158        private final OmemoDevice senderDevice;
159        private final boolean preKeyMessage;
160
161        /**
162         * Create a new incoming OMEMO message.
163         *
164         * @param element original OmemoElement
165         * @param key message key (or transported key)
166         * @param iv respective initialization vector
167         * @param body decrypted body
168         * @param sendersFingerprint OmemoFingerprint of the senders identityKey
169         * @param senderDevice OmemoDevice of the sender
170         * @param preKeyMessage if this was a preKeyMessage or not
171         */
172        Received(OmemoElement element, byte[] key, byte[] iv, String body, OmemoFingerprint sendersFingerprint, OmemoDevice senderDevice, boolean preKeyMessage) {
173            super(element, key, iv);
174            this.message = body;
175            this.sendersFingerprint = sendersFingerprint;
176            this.senderDevice = senderDevice;
177            this.preKeyMessage = preKeyMessage;
178        }
179
180        /**
181         * Return the decrypted body of the message.
182         *
183         * @return decrypted body
184         */
185        public String getBody() {
186            return message;
187        }
188
189        /**
190         * Return the fingerprint of the messages sender device.
191         *
192         * @return fingerprint of sender
193         */
194        public OmemoFingerprint getSendersFingerprint() {
195            return sendersFingerprint;
196        }
197
198        /**
199         * Return the OmemoDevice which sent the message.
200         *
201         * @return OMEMO device that sent the message.
202         */
203        public OmemoDevice getSenderDevice() {
204            return senderDevice;
205        }
206
207        /**
208         * Return true, if this message was sent as a preKeyMessage.
209         *
210         * @return preKeyMessage or not
211         */
212        boolean isPreKeyMessage() {
213            return preKeyMessage;
214        }
215
216        /**
217         * Return true, if the message was a KeyTransportMessage.
218         * A KeyTransportMessage is a OmemoMessage without a payload.
219         *
220         * @return keyTransportMessage?
221         */
222        public boolean isKeyTransportMessage() {
223            return message == null;
224        }
225    }
226}