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}