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 static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL;
020
021import java.util.HashMap;
022import java.util.Map;
023
024import org.jivesoftware.smack.util.XmlStringBuilder;
025import org.jivesoftware.smack.util.stringencoder.Base64;
026
027/**
028 * OMEMO device bundle as described here:
029 * https://xmpp.org/extensions/xep-0384.html#usecases-announcing (Example 3).
030 *
031 * @author Paul Schaub
032 */
033public class OmemoBundleVAxolotlElement extends OmemoBundleElement {
034
035    private final int signedPreKeyId;
036    private final String signedPreKeyB64;
037    private byte[] signedPreKey;
038    private final String signedPreKeySignatureB64;
039    private byte[] signedPreKeySignature;
040    private final String identityKeyB64;
041    private byte[] identityKey;
042    private final HashMap<Integer, String> preKeysB64;
043    private HashMap<Integer, byte[]> preKeys;
044
045    /**
046     * Constructor to create a Bundle Element from base64 Strings.
047     *
048     * @param signedPreKeyId id
049     * @param signedPreKeyB64 base64 encoded signedPreKey
050     * @param signedPreKeySigB64 base64 encoded signedPreKeySignature
051     * @param identityKeyB64 base64 encoded identityKey
052     * @param preKeysB64 HashMap of base64 encoded preKeys
053     */
054    public OmemoBundleVAxolotlElement(int signedPreKeyId, String signedPreKeyB64, String signedPreKeySigB64, String identityKeyB64, HashMap<Integer, String> preKeysB64) {
055        this.signedPreKeyId = signedPreKeyId;
056        this.signedPreKeyB64 = signedPreKeyB64;
057        this.signedPreKeySignatureB64 = signedPreKeySigB64;
058        this.identityKeyB64 = identityKeyB64;
059        this.preKeysB64 = preKeysB64;
060    }
061
062    /**
063     * Constructor to create a Bundle Element from decoded byte arrays.
064     *
065     * @param signedPreKeyId id
066     * @param signedPreKey signedPreKey
067     * @param signedPreKeySig signedPreKeySignature
068     * @param identityKey identityKey
069     * @param preKeys HashMap of preKeys
070     */
071    public OmemoBundleVAxolotlElement(int signedPreKeyId, byte[] signedPreKey, byte[] signedPreKeySig, byte[] identityKey, HashMap<Integer, byte[]> preKeys) {
072        this.signedPreKeyId = signedPreKeyId;
073
074        this.signedPreKey = signedPreKey;
075        this.signedPreKeyB64 = Base64.encodeToString(signedPreKey);
076
077        this.signedPreKeySignature = signedPreKeySig;
078        this.signedPreKeySignatureB64 = Base64.encodeToString(signedPreKeySignature);
079
080        this.identityKey = identityKey;
081        this.identityKeyB64 = Base64.encodeToString(identityKey);
082
083        this.preKeys = preKeys;
084        this.preKeysB64 = new HashMap<>();
085        for (int id : preKeys.keySet()) {
086            preKeysB64.put(id, Base64.encodeToString(preKeys.get(id)));
087        }
088    }
089
090    /**
091     * Return the signedPreKey of the OmemoBundleElement.
092     *
093     * @return signedPreKey as byte array
094     */
095    public byte[] getSignedPreKey() {
096        if (signedPreKey == null) {
097            signedPreKey = Base64.decode(signedPreKeyB64);
098        }
099        return this.signedPreKey.clone();
100    }
101
102    /**
103     * Return the id of the signedPreKey in the bundle.
104     *
105     * @return id of signedPreKey
106     */
107    public int getSignedPreKeyId() {
108        return this.signedPreKeyId;
109    }
110
111    /**
112     * Get the signature of the signedPreKey.
113     *
114     * @return signature as byte array
115     */
116    public byte[] getSignedPreKeySignature() {
117        if (signedPreKeySignature == null) {
118            signedPreKeySignature = Base64.decode(signedPreKeySignatureB64);
119        }
120        return signedPreKeySignature.clone();
121    }
122
123    /**
124     * Return the public identityKey of the bundles owner.
125     * This can be used to check the signedPreKeys signature.
126     * The fingerprint of this key is, what the user has to verify.
127     *
128     * @return public identityKey as byte array
129     */
130    public byte[] getIdentityKey() {
131        if (identityKey == null) {
132            identityKey = Base64.decode(identityKeyB64);
133        }
134        return this.identityKey.clone();
135    }
136
137    /**
138     * Return the HashMap of preKeys in the bundle.
139     * The map uses the preKeys ids as key and the preKeys as value.
140     *
141     * @return preKeys
142     */
143    public HashMap<Integer, byte[]> getPreKeys() {
144        if (preKeys == null) {
145            preKeys = new HashMap<>();
146            for (int id : preKeysB64.keySet()) {
147                preKeys.put(id, Base64.decode(preKeysB64.get(id)));
148            }
149        }
150        return this.preKeys;
151    }
152
153    /**
154     * Return a single preKey from the map.
155     *
156     * @param id id of the preKey
157     * @return the preKey
158     */
159    public byte[] getPreKey(int id) {
160        return getPreKeys().get(id);
161    }
162
163    @Override
164    public String getElementName() {
165        return BUNDLE;
166    }
167
168    @Override
169    public XmlStringBuilder toXML(String enclosingNamespace) {
170        XmlStringBuilder sb = new XmlStringBuilder(this).rightAngleBracket();
171
172        sb.halfOpenElement(SIGNED_PRE_KEY_PUB).attribute(SIGNED_PRE_KEY_ID, signedPreKeyId).rightAngleBracket()
173                .append(signedPreKeyB64).closeElement(SIGNED_PRE_KEY_PUB);
174
175        sb.openElement(SIGNED_PRE_KEY_SIG).append(signedPreKeySignatureB64).closeElement(SIGNED_PRE_KEY_SIG);
176
177        sb.openElement(IDENTITY_KEY).append(identityKeyB64).closeElement(IDENTITY_KEY);
178
179        sb.openElement(PRE_KEYS);
180        for (Map.Entry<Integer, String> p : this.preKeysB64.entrySet()) {
181            sb.halfOpenElement(PRE_KEY_PUB).attribute(PRE_KEY_ID, p.getKey()).rightAngleBracket()
182                    .append(p.getValue()).closeElement(PRE_KEY_PUB);
183        }
184        sb.closeElement(PRE_KEYS);
185
186        sb.closeElement(this);
187        return sb;
188    }
189
190    @Override
191    public String getNamespace() {
192        return OMEMO_NAMESPACE_V_AXOLOTL;
193    }
194
195    @Override
196    public String toString() {
197        String out = "OmemoBundleElement[\n";
198        out += SIGNED_PRE_KEY_PUB + " " + SIGNED_PRE_KEY_ID + "=" + signedPreKeyId + ": " + signedPreKeyB64 + "\n";
199        out += SIGNED_PRE_KEY_SIG + ": " + signedPreKeySignatureB64 + "\n";
200        out += IDENTITY_KEY + ": " + identityKeyB64 + "\n";
201        out += PRE_KEYS + " (" + preKeysB64.size() + ")\n";
202        for (Map.Entry<Integer, String> e : preKeysB64.entrySet()) {
203            out += PRE_KEY_PUB + " " + PRE_KEY_ID + "=" + e.getKey() + ": " + e.getValue() + "\n";
204        }
205        return out;
206    }
207}