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.element;
018
019import java.util.ArrayList;
020
021import org.jivesoftware.smack.packet.ExtensionElement;
022import org.jivesoftware.smack.packet.NamedElement;
023import org.jivesoftware.smack.util.Objects;
024import org.jivesoftware.smack.util.XmlStringBuilder;
025import org.jivesoftware.smack.util.stringencoder.Base64;
026
027/**
028 * Class that represents a OmemoElement.
029 * TODO: Move functionality here.
030 *
031 * @author Paul Schaub
032 */
033public abstract class OmemoElement implements ExtensionElement {
034
035    public static final int TYPE_OMEMO_PREKEY_MESSAGE = 1;
036    public static final int TYPE_OMEMO_MESSAGE = 0;
037
038    public static final String ENCRYPTED = "encrypted";
039    public static final String HEADER = "header";
040    public static final String SID = "sid";
041    public static final String KEY = "key";
042    public static final String RID = "rid";
043    public static final String PREKEY = "prekey";
044    public static final String IV = "iv";
045    public static final String PAYLOAD = "payload";
046
047    protected final OmemoElement.OmemoHeader header;
048    protected final byte[] payload;
049
050    /**
051     * Create a new OmemoMessageElement from a header and a payload.
052     *
053     * @param header  header of the message
054     * @param payload payload
055     */
056    public OmemoElement(OmemoElement.OmemoHeader header, byte[] payload) {
057        this.header = Objects.requireNonNull(header);
058        this.payload = payload;
059    }
060
061    public OmemoElement.OmemoHeader getHeader() {
062        return header;
063    }
064
065    /**
066     * Return the payload of the message.
067     *
068     * @return payload
069     */
070    public byte[] getPayload() {
071        if (payload == null) {
072            return null;
073        }
074        return payload.clone();
075    }
076
077    public boolean isKeyTransportElement() {
078        return payload == null;
079    }
080
081    public boolean isMessageElement() {
082        return payload != null;
083    }
084
085    /**
086     * Header element of the message. The header contains information about the sender and the encrypted keys for
087     * the recipients, as well as the iv element for AES.
088     */
089    public static class OmemoHeader implements NamedElement {
090        private final int sid;
091        private final ArrayList<Key> keys;
092        private final byte[] iv;
093
094        public OmemoHeader(int sid, ArrayList<OmemoHeader.Key> keys, byte[] iv) {
095            this.sid = sid;
096            this.keys = keys;
097            this.iv = iv;
098        }
099
100        /**
101         * Return the deviceId of the sender of the message.
102         *
103         * @return senders id
104         */
105        public int getSid() {
106            return sid;
107        }
108
109        public ArrayList<OmemoHeader.Key> getKeys() {
110            return new ArrayList<>(keys);
111        }
112
113        public byte[] getIv() {
114            return iv != null ? iv.clone() : null;
115        }
116
117        @Override
118        public String getElementName() {
119            return HEADER;
120        }
121
122        @Override
123        public CharSequence toXML(String enclosingNamespace) {
124            XmlStringBuilder sb = new XmlStringBuilder(this);
125            sb.attribute(SID, getSid()).rightAngleBracket();
126
127            for (OmemoHeader.Key k : getKeys()) {
128                sb.element(k);
129            }
130
131            sb.openElement(IV).append(Base64.encodeToString(getIv())).closeElement(IV);
132
133            return sb.closeElement(this);
134        }
135
136        /**
137         * Small class to collect key (byte[]), its id and whether its a prekey or not.
138         */
139        public static class Key implements NamedElement {
140            final byte[] data;
141            final int id;
142            final boolean preKey;
143
144            public Key(byte[] data, int id) {
145                this.data = data;
146                this.id = id;
147                this.preKey = false;
148            }
149
150            public Key(byte[] data, int id, boolean preKey) {
151                this.data = data;
152                this.id = id;
153                this.preKey = preKey;
154            }
155
156            public int getId() {
157                return this.id;
158            }
159
160            public byte[] getData() {
161                return this.data;
162            }
163
164            public boolean isPreKey() {
165                return this.preKey;
166            }
167
168            @Override
169            public String toString() {
170                return Integer.toString(id);
171            }
172
173            @Override
174            public String getElementName() {
175                return KEY;
176            }
177
178            @Override
179            public CharSequence toXML(String enclosingNamespace) {
180                XmlStringBuilder sb = new XmlStringBuilder(this);
181
182                if (isPreKey()) {
183                    sb.attribute(PREKEY, true);
184                }
185
186                sb.attribute(RID, getId());
187                sb.rightAngleBracket();
188                sb.append(Base64.encodeToString(getData()));
189                sb.closeElement(this);
190                return sb;
191            }
192        }
193    }
194}