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