OmemoBundleElement.java

  1. /**
  2.  *
  3.  * Copyright 2017 Paul Schaub
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.omemo.element;

  18. import java.util.HashMap;
  19. import java.util.Map;

  20. import org.jivesoftware.smack.packet.ExtensionElement;
  21. import org.jivesoftware.smack.util.StringUtils;
  22. import org.jivesoftware.smack.util.XmlStringBuilder;
  23. import org.jivesoftware.smack.util.stringencoder.Base64;

  24. /**
  25.  * Class that represents an OMEMO Bundle element.
  26.  *
  27.  * @author Paul Schaub
  28.  */
  29. public abstract class OmemoBundleElement implements ExtensionElement {

  30.     public static final String BUNDLE = "bundle";
  31.     public static final String SIGNED_PRE_KEY_PUB = "signedPreKeyPublic";
  32.     public static final String SIGNED_PRE_KEY_ID = "signedPreKeyId";
  33.     public static final String SIGNED_PRE_KEY_SIG = "signedPreKeySignature";
  34.     public static final String IDENTITY_KEY = "identityKey";
  35.     public static final String PRE_KEYS = "prekeys";
  36.     public static final String PRE_KEY_PUB = "preKeyPublic";
  37.     public static final String PRE_KEY_ID = "preKeyId";

  38.     private final int signedPreKeyId;
  39.     private final String signedPreKeyB64;
  40.     private byte[] signedPreKey;
  41.     private final String signedPreKeySignatureB64;
  42.     private byte[] signedPreKeySignature;
  43.     private final String identityKeyB64;
  44.     private byte[] identityKey;
  45.     private final HashMap<Integer, String> preKeysB64;
  46.     private HashMap<Integer, byte[]> preKeys;

  47.     /**
  48.      * Constructor to create a Bundle Element from base64 Strings.
  49.      *
  50.      * @param signedPreKeyId id
  51.      * @param signedPreKeyB64 base64 encoded signedPreKey
  52.      * @param signedPreKeySigB64 base64 encoded signedPreKeySignature
  53.      * @param identityKeyB64 base64 encoded identityKey
  54.      * @param preKeysB64 HashMap of base64 encoded preKeys
  55.      */
  56.     public OmemoBundleElement(int signedPreKeyId, String signedPreKeyB64, String signedPreKeySigB64, String identityKeyB64, HashMap<Integer, String> preKeysB64) {
  57.         if (signedPreKeyId < 0) {
  58.             throw new IllegalArgumentException("signedPreKeyId MUST be greater than or equal to 0.");
  59.         }
  60.         this.signedPreKeyId = signedPreKeyId;
  61.         this.signedPreKeyB64 = StringUtils.requireNotNullNorEmpty(signedPreKeyB64, "signedPreKeyB64 MUST NOT be null nor empty.");
  62.         this.signedPreKeySignatureB64 = StringUtils.requireNotNullNorEmpty(signedPreKeySigB64, "signedPreKeySigB64 MUST NOT be null nor empty.");
  63.         this.identityKeyB64 = StringUtils.requireNotNullNorEmpty(identityKeyB64, "identityKeyB64 MUST NOT be null nor empty.");

  64.         if (preKeysB64 == null || preKeysB64.isEmpty()) {
  65.             throw new IllegalArgumentException("PreKeys MUST NOT be null nor empty.");
  66.         }
  67.         this.preKeysB64 = preKeysB64;
  68.     }

  69.     /**
  70.      * Constructor to create a Bundle Element from decoded byte arrays.
  71.      *
  72.      * @param signedPreKeyId id
  73.      * @param signedPreKey signedPreKey
  74.      * @param signedPreKeySig signedPreKeySignature
  75.      * @param identityKey identityKey
  76.      * @param preKeys HashMap of preKeys
  77.      */
  78.     public OmemoBundleElement(int signedPreKeyId, byte[] signedPreKey, byte[] signedPreKeySig, byte[] identityKey, HashMap<Integer, byte[]> preKeys) {
  79.         this(signedPreKeyId,
  80.                 signedPreKey != null ? Base64.encodeToString(signedPreKey) : null,
  81.                 signedPreKeySig != null ? Base64.encodeToString(signedPreKeySig) : null,
  82.                 identityKey != null ? Base64.encodeToString(identityKey) : null,
  83.                 base64EncodePreKeys(preKeys));
  84.         this.signedPreKey = signedPreKey;
  85.         this.signedPreKeySignature = signedPreKeySig;
  86.         this.identityKey = identityKey;
  87.         this.preKeys = preKeys;
  88.     }

  89.     private static HashMap<Integer, String> base64EncodePreKeys(HashMap<Integer, byte[]> preKeys) {
  90.         if (preKeys == null) {
  91.             return null;
  92.         }

  93.         HashMap<Integer, String> converted = new HashMap<>();
  94.         for (Integer id : preKeys.keySet()) {
  95.             converted.put(id, Base64.encodeToString(preKeys.get(id)));
  96.         }
  97.         return converted;
  98.     }

  99.     /**
  100.      * Return the signedPreKey of the OmemoBundleElement.
  101.      *
  102.      * @return signedPreKey as byte array
  103.      */
  104.     public byte[] getSignedPreKey() {
  105.         if (signedPreKey == null) {
  106.             signedPreKey = Base64.decode(signedPreKeyB64);
  107.         }
  108.         return this.signedPreKey.clone();
  109.     }

  110.     /**
  111.      * Return the id of the signedPreKey in the bundle.
  112.      *
  113.      * @return id of signedPreKey
  114.      */
  115.     public int getSignedPreKeyId() {
  116.         return this.signedPreKeyId;
  117.     }

  118.     /**
  119.      * Get the signature of the signedPreKey.
  120.      *
  121.      * @return signature as byte array
  122.      */
  123.     public byte[] getSignedPreKeySignature() {
  124.         if (signedPreKeySignature == null) {
  125.             signedPreKeySignature = Base64.decode(signedPreKeySignatureB64);
  126.         }
  127.         return signedPreKeySignature.clone();
  128.     }

  129.     /**
  130.      * Return the public identityKey of the bundles owner.
  131.      * This can be used to check the signedPreKeys signature.
  132.      * The fingerprint of this key is, what the user has to verify.
  133.      *
  134.      * @return public identityKey as byte array
  135.      */
  136.     public byte[] getIdentityKey() {
  137.         if (identityKey == null) {
  138.             identityKey = Base64.decode(identityKeyB64);
  139.         }
  140.         return this.identityKey.clone();
  141.     }

  142.     /**
  143.      * Return the HashMap of preKeys in the bundle.
  144.      * The map uses the preKeys ids as key and the preKeys as value.
  145.      *
  146.      * @return preKeys Pre-Keys contained in the bundle
  147.      */
  148.     public HashMap<Integer, byte[]> getPreKeys() {
  149.         if (preKeys == null) {
  150.             preKeys = new HashMap<>();
  151.             for (int id : preKeysB64.keySet()) {
  152.                 preKeys.put(id, Base64.decode(preKeysB64.get(id)));
  153.             }
  154.         }
  155.         return this.preKeys;
  156.     }

  157.     /**
  158.      * Return a single preKey from the map.
  159.      *
  160.      * @param id id of the preKey
  161.      * @return the preKey
  162.      */
  163.     public byte[] getPreKey(int id) {
  164.         return getPreKeys().get(id);
  165.     }

  166.     @Override
  167.     public String getElementName() {
  168.         return BUNDLE;
  169.     }

  170.     @Override
  171.     public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
  172.         XmlStringBuilder sb = new XmlStringBuilder(this, enclosingNamespace).rightAngleBracket();

  173.         sb.halfOpenElement(SIGNED_PRE_KEY_PUB).attribute(SIGNED_PRE_KEY_ID, signedPreKeyId).rightAngleBracket()
  174.                 .append(signedPreKeyB64).closeElement(SIGNED_PRE_KEY_PUB);

  175.         sb.openElement(SIGNED_PRE_KEY_SIG).append(signedPreKeySignatureB64).closeElement(SIGNED_PRE_KEY_SIG);

  176.         sb.openElement(IDENTITY_KEY).append(identityKeyB64).closeElement(IDENTITY_KEY);

  177.         sb.openElement(PRE_KEYS);
  178.         for (Map.Entry<Integer, String> p : this.preKeysB64.entrySet()) {
  179.             sb.halfOpenElement(PRE_KEY_PUB).attribute(PRE_KEY_ID, p.getKey()).rightAngleBracket()
  180.                     .append(p.getValue()).closeElement(PRE_KEY_PUB);
  181.         }
  182.         sb.closeElement(PRE_KEYS);

  183.         sb.closeElement(this);
  184.         return sb;
  185.     }

  186.     @Override
  187.     public String toString() {
  188.         StringBuilder sb = new StringBuilder("OmemoBundleElement[\n");
  189.         sb.append(SIGNED_PRE_KEY_PUB).append(' ').append(SIGNED_PRE_KEY_ID).append('=').append(signedPreKeyId)
  190.                 .append(':').append(signedPreKeyB64).append('\n')
  191.                 .append(SIGNED_PRE_KEY_SIG).append(": ").append(signedPreKeySignatureB64).append('\n')
  192.                 .append(IDENTITY_KEY).append(": ").append(identityKeyB64).append('\n')
  193.                 .append(PRE_KEYS).append(" (").append(preKeysB64.size()).append(")\n");
  194.         for (Map.Entry<Integer, String> e : preKeysB64.entrySet()) {
  195.             sb.append(PRE_KEY_PUB).append(' ').append(PRE_KEY_ID).append('=').append(e.getKey()).append(": ").append(e.getValue()).append('\n');
  196.         }
  197.         sb.append(']');
  198.         return sb.toString();
  199.     }

  200.     @Override
  201.     public boolean equals(Object other) {
  202.         if (!(other instanceof OmemoBundleElement)) {
  203.             return false;
  204.         }

  205.         OmemoBundleElement otherOmemoBundleElement = (OmemoBundleElement) other;
  206.         return toXML().toString().equals(otherOmemoBundleElement.toXML().toString());
  207.     }

  208.     @Override
  209.     public int hashCode() {
  210.         return toXML().toString().hashCode();
  211.     }
  212. }