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