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.util;
018
019import java.io.IOException;
020import java.util.HashMap;
021import java.util.Map;
022import java.util.TreeMap;
023import java.util.logging.Level;
024import java.util.logging.Logger;
025
026import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
027import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
028import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
029import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
030
031/**
032 * Class that is used to convert bytes to keys and vice versa.
033 *
034 * @param <T_IdKeyPair> IdentityKeyPair class
035 * @param <T_IdKey>     IdentityKey class
036 * @param <T_PreKey>    PreKey class
037 * @param <T_SigPreKey> SignedPreKey class
038 * @param <T_Sess>      Session class
039 * @param <T_ECPub>     Elliptic Curve PublicKey class
040 * @param <T_Bundle>    Bundle class
041 * @author Paul Schaub
042 */
043@SuppressWarnings("InconsistentCapitalization")
044public abstract class OmemoKeyUtil<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_ECPub, T_Bundle> {
045    private static final Logger LOGGER = Logger.getLogger(OmemoKeyUtil.class.getName());
046
047    public final Bundle BUNDLE = new Bundle();
048
049    /**
050     * Bundle related methods.
051     */
052    public class Bundle {
053
054        /**
055         * Extract an IdentityKey from a OmemoBundleElement.
056         *
057         * @param bundle OmemoBundleElement
058         * @return identityKey of the bundle
059         *
060         * @throws CorruptedOmemoKeyException if the key is damaged/malformed
061         */
062        public T_IdKey identityKey(OmemoBundleElement bundle) throws CorruptedOmemoKeyException {
063            return identityKeyFromBytes(bundle.getIdentityKey());
064        }
065
066        /**
067         * Extract a signedPreKey from an OmemoBundleElement.
068         *
069         * @param bundle OmemoBundleElement
070         * @return signed preKey
071         *
072         * @throws CorruptedOmemoKeyException if the key is damaged/malformed
073         */
074        public T_ECPub signedPreKeyPublic(OmemoBundleElement bundle) throws CorruptedOmemoKeyException {
075            return signedPreKeyPublicFromBytes(bundle.getSignedPreKey());
076        }
077
078        /**
079         * Extract the id of the transported signedPreKey from the bundle.
080         *
081         * @param bundle OmemoBundleElement
082         * @return id of the signed preKey
083         */
084        public int signedPreKeyId(OmemoBundleElement bundle) {
085            return bundle.getSignedPreKeyId();
086        }
087
088        /**
089         * Extract the signature of the signedPreKey in the bundle as a byte array.
090         *
091         * @param bundle OmemoBundleElement
092         * @return signature on the signed preKey
093         */
094        public byte[] signedPreKeySignature(OmemoBundleElement bundle) {
095            return bundle.getSignedPreKeySignature();
096        }
097
098        /**
099         * Extract the preKey with id 'keyId' from the bundle.
100         *
101         * @param bundle OmemoBundleElement
102         * @param keyId  id of the preKey
103         * @return the preKey
104         *
105         * @throws CorruptedOmemoKeyException when the key cannot be parsed from bytes
106         */
107        public T_ECPub preKeyPublic(OmemoBundleElement bundle, int keyId) throws CorruptedOmemoKeyException {
108            return preKeyPublicFromBytes(bundle.getPreKey(keyId));
109        }
110
111        /**
112         * Break up the OmemoBundleElement into a list of crypto-lib specific bundles (T_PreKey).
113         * In case of the signal library, we break the OmemoBundleElement in ~100 PreKeyBundles (one for every transported
114         * preKey).
115         *
116         * @param bundle  OmemoBundleElement containing multiple PreKeys
117         * @param contact Contact that the bundle belongs to
118         * @return a HashMap with one T_Bundle per preKey and the preKeyId as key
119         *
120         * @throws CorruptedOmemoKeyException when one of the keys cannot be parsed
121         */
122        public HashMap<Integer, T_Bundle> bundles(OmemoBundleElement bundle, OmemoDevice contact) throws CorruptedOmemoKeyException {
123            HashMap<Integer, T_Bundle> bundles = new HashMap<>();
124            for (int deviceId : bundle.getPreKeys().keySet()) {
125                try {
126                    bundles.put(deviceId, bundleFromOmemoBundle(bundle, contact, deviceId));
127                } catch (CorruptedOmemoKeyException e) {
128                    LOGGER.log(Level.INFO, "Cannot parse PreKeyBundle: " + e.getMessage());
129                }
130            }
131            if (bundles.size() == 0) {
132                throw new CorruptedOmemoKeyException("Bundle contained no valid preKeys.");
133            }
134            return bundles;
135        }
136    }
137
138    /**
139     * Deserialize an identityKeyPair from a byte array.
140     *
141     * @param data byte array
142     * @return IdentityKeyPair (T_IdKeyPair)
143     *
144     * @throws CorruptedOmemoKeyException if the key is damaged of malformed
145     */
146    public abstract T_IdKeyPair identityKeyPairFromBytes(byte[] data) throws CorruptedOmemoKeyException;
147
148    /**
149     * Deserialize an identityKey from a byte array.
150     *
151     * @param data byte array
152     * @return identityKey (T_IdKey)
153     *
154     * @throws CorruptedOmemoKeyException if the key is damaged or malformed
155     */
156    public abstract T_IdKey identityKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException;
157
158    /**
159     * Serialize an identityKey into bytes.
160     *
161     * @param identityKey idKey
162     * @return byte array representation of the identity key.
163     */
164    public abstract byte[] identityKeyToBytes(T_IdKey identityKey);
165
166    /**
167     * Deserialize an elliptic curve public key from bytes.
168     *
169     * @param data bytes
170     * @return elliptic curve public key (T_ECPub)
171     *
172     * @throws CorruptedOmemoKeyException if the key is damaged or malformed
173     */
174    public abstract T_ECPub ellipticCurvePublicKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException;
175
176    /**
177     * Deserialize a public preKey from bytes.
178     *
179     * @param data preKey as bytes
180     * @return deserialized preKey
181     *
182     * @throws CorruptedOmemoKeyException if the key is damaged or malformed
183     */
184    public T_ECPub preKeyPublicFromBytes(byte[] data) throws CorruptedOmemoKeyException {
185        return ellipticCurvePublicKeyFromBytes(data);
186    }
187
188    /**
189     * Serialize a preKey into a byte array.
190     *
191     * @param preKey preKey
192     * @return byte[]
193     */
194    public abstract byte[] preKeyToBytes(T_PreKey preKey);
195
196    /**
197     * Deserialize a preKey from a byte array.
198     *
199     * @param bytes byte array
200     * @return deserialized preKey
201     *
202     * @throws IOException when something goes wrong
203     */
204    public abstract T_PreKey preKeyFromBytes(byte[] bytes) throws IOException;
205
206    /**
207     * Generate 'count' new PreKeys beginning with id 'startId'.
208     * These preKeys are published and can be used by contacts to establish sessions with us.
209     *
210     * @param startId start id
211     * @param count   how many keys do we want to generate
212     * @return Map of new preKeys
213     */
214    public abstract TreeMap<Integer, T_PreKey> generateOmemoPreKeys(int startId, int count);
215
216    /**
217     * Generate a new signed preKey.
218     *
219     * @param identityKeyPair identityKeyPair used to sign the preKey
220     * @param signedPreKeyId  id that the preKey will have
221     * @return deserialized signed preKey
222     *
223     * @throws CorruptedOmemoKeyException when the identityKeyPair is invalid
224     */
225    public abstract T_SigPreKey generateOmemoSignedPreKey(T_IdKeyPair identityKeyPair, int signedPreKeyId) throws CorruptedOmemoKeyException;
226
227
228    /**
229     * Deserialize a public signedPreKey from bytes.
230     *
231     * @param data bytes
232     * @return deserialized signed preKey
233     *
234     * @throws CorruptedOmemoKeyException if the key is damaged or malformed
235     */
236    public T_ECPub signedPreKeyPublicFromBytes(byte[] data) throws CorruptedOmemoKeyException {
237        return ellipticCurvePublicKeyFromBytes(data);
238    }
239
240    /**
241     * Deserialize a signedPreKey from a byte array.
242     *
243     * @param data byte array
244     * @return deserialized signed preKey
245     *
246     * @throws IOException when something goes wrong
247     */
248    public abstract T_SigPreKey signedPreKeyFromBytes(byte[] data) throws IOException;
249
250    /**
251     * Serialize a signedPreKey into a byte array.
252     *
253     * @param sigPreKey signedPreKey
254     * @return byte array
255     */
256    public abstract byte[] signedPreKeyToBytes(T_SigPreKey sigPreKey);
257
258    /**
259     * Build a crypto-lib specific PreKeyBundle (T_Bundle) using a PreKey from the OmemoBundleElement 'bundle'.
260     * The PreKeyBundle will contain the identityKey, signedPreKey and signature, as well as a preKey
261     * from the OmemoBundleElement.
262     *
263     * @param bundle  OmemoBundleElement
264     * @param contact Contact that the bundle belongs to
265     * @param keyId   id of the preKey that will be selected from the OmemoBundleElement and that the PreKeyBundle will contain
266     * @return PreKeyBundle (T_PreKey)
267     *
268     * @throws CorruptedOmemoKeyException if some key is damaged or malformed
269     */
270    public abstract T_Bundle bundleFromOmemoBundle(OmemoBundleElement bundle, OmemoDevice contact, int keyId) throws CorruptedOmemoKeyException;
271
272    /**
273     * Extract the signature from a signedPreKey.
274     *
275     * @param signedPreKey signedPreKey
276     * @return signature as byte array
277     */
278    public abstract byte[] signedPreKeySignatureFromKey(T_SigPreKey signedPreKey);
279
280    /**
281     * Generate a new IdentityKeyPair. We should always have only one pair and usually keep this for a long time.
282     *
283     * @return deserialized identity key pair
284     */
285    public abstract T_IdKeyPair generateOmemoIdentityKeyPair();
286
287    /**
288     * return the id of the given signedPreKey.
289     *
290     * @param signedPreKey key
291     * @return id of the key
292     */
293    public abstract int signedPreKeyIdFromKey(T_SigPreKey signedPreKey);
294
295    /**
296     * serialize an identityKeyPair into bytes.
297     *
298     * @param identityKeyPair identityKeyPair
299     * @return byte array
300     */
301    public abstract byte[] identityKeyPairToBytes(T_IdKeyPair identityKeyPair);
302
303    /**
304     * Extract the public identityKey from an identityKeyPair.
305     *
306     * @param pair keyPair
307     * @return public key of the pair
308     */
309    public abstract T_IdKey identityKeyFromPair(T_IdKeyPair pair);
310
311    /**
312     * Prepare an identityKey for transport in an OmemoBundleElement (serialize it).
313     *
314     * @param identityKey identityKey that will be transported
315     * @return key as byte array
316     */
317    public abstract byte[] identityKeyForBundle(T_IdKey identityKey);
318
319    /**
320     * Prepare an elliptic curve preKey for transport in an OmemoBundleElement.
321     *
322     * @param preKey key
323     * @return key as byte array
324     */
325    public abstract byte[] preKeyPublicKeyForBundle(T_ECPub preKey);
326
327    /**
328     * Prepare a preKey for transport in an OmemoBundleElement.
329     *
330     * @param preKey preKey
331     * @return key as byte array
332     */
333    public abstract byte[] preKeyForBundle(T_PreKey preKey);
334
335    /**
336     * Prepare a whole bunche of preKeys for transport.
337     *
338     * @param preKeyHashMap HashMap of preKeys
339     * @return HashMap of byte arrays but with the same keyIds as key
340     */
341    public HashMap<Integer, byte[]> preKeyPublicKeysForBundle(TreeMap<Integer, T_PreKey> preKeyHashMap) {
342        HashMap<Integer, byte[]> out = new HashMap<>();
343        for (Map.Entry<Integer, T_PreKey> e : preKeyHashMap.entrySet()) {
344            out.put(e.getKey(), preKeyForBundle(e.getValue()));
345        }
346        return out;
347    }
348
349    /**
350     * Prepare a public signedPreKey for transport in a bundle.
351     *
352     * @param signedPreKey signedPreKey
353     * @return signedPreKey as byte array
354     */
355    public abstract byte[] signedPreKeyPublicForBundle(T_SigPreKey signedPreKey);
356
357    /**
358     * Return the fingerprint of an identityKey.
359     *
360     * @param identityKey identityKey
361     * @return fingerprint of the key
362     */
363    public abstract OmemoFingerprint getFingerprintOfIdentityKey(T_IdKey identityKey);
364
365    /**
366     * Returns the fingerprint of the public key of an identityKeyPair.
367     * @param identityKeyPair IdentityKeyPair.
368     * @return fingerprint of the public key.
369     */
370    public abstract OmemoFingerprint getFingerprintOfIdentityKeyPair(T_IdKeyPair identityKeyPair);
371
372    /**
373     * Deserialize a raw OMEMO Session from bytes.
374     *
375     * @param data bytes
376     * @return raw OMEMO Session
377     *
378     * @throws IOException when something goes wrong
379     */
380    public abstract T_Sess rawSessionFromBytes(byte[] data) throws IOException;
381
382    /**
383     * Serialize a raw OMEMO session into a byte array.
384     *
385     * @param session raw session
386     * @return byte array
387     */
388    public abstract byte[] rawSessionToBytes(T_Sess session);
389
390    /**
391     * Add integers modulo MAX_VALUE.
392     *
393     * @param value base integer
394     * @param added value that is added to the base value
395     * @return (value plus added) modulo Integer.MAX_VALUE
396     */
397    public static int addInBounds(int value, int added) {
398        int avail = Integer.MAX_VALUE - value;
399        if (avail < added) {
400            return added - avail;
401        } else {
402            return value + added;
403        }
404    }
405}