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 Map<Integer, T_Bundle> bundles(OmemoBundleElement bundle, OmemoDevice contact) throws CorruptedOmemoKeyException {
123            Map<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    // We use TreeMap.lastKey()
215    @SuppressWarnings("NonApiType")
216    public abstract TreeMap<Integer, T_PreKey> generateOmemoPreKeys(int startId, int count);
217
218    /**
219     * Generate a new signed preKey.
220     *
221     * @param identityKeyPair identityKeyPair used to sign the preKey
222     * @param signedPreKeyId  id that the preKey will have
223     * @return deserialized signed preKey
224     *
225     * @throws CorruptedOmemoKeyException when the identityKeyPair is invalid
226     */
227    public abstract T_SigPreKey generateOmemoSignedPreKey(T_IdKeyPair identityKeyPair, int signedPreKeyId) throws CorruptedOmemoKeyException;
228
229
230    /**
231     * Deserialize a public signedPreKey from bytes.
232     *
233     * @param data bytes
234     * @return deserialized signed preKey
235     *
236     * @throws CorruptedOmemoKeyException if the key is damaged or malformed
237     */
238    public T_ECPub signedPreKeyPublicFromBytes(byte[] data) throws CorruptedOmemoKeyException {
239        return ellipticCurvePublicKeyFromBytes(data);
240    }
241
242    /**
243     * Deserialize a signedPreKey from a byte array.
244     *
245     * @param data byte array
246     * @return deserialized signed preKey
247     *
248     * @throws IOException when something goes wrong
249     */
250    public abstract T_SigPreKey signedPreKeyFromBytes(byte[] data) throws IOException;
251
252    /**
253     * Serialize a signedPreKey into a byte array.
254     *
255     * @param sigPreKey signedPreKey
256     * @return byte array
257     */
258    public abstract byte[] signedPreKeyToBytes(T_SigPreKey sigPreKey);
259
260    /**
261     * Build a crypto-lib specific PreKeyBundle (T_Bundle) using a PreKey from the OmemoBundleElement 'bundle'.
262     * The PreKeyBundle will contain the identityKey, signedPreKey and signature, as well as a preKey
263     * from the OmemoBundleElement.
264     *
265     * @param bundle  OmemoBundleElement
266     * @param contact Contact that the bundle belongs to
267     * @param keyId   id of the preKey that will be selected from the OmemoBundleElement and that the PreKeyBundle will contain
268     * @return PreKeyBundle (T_PreKey)
269     *
270     * @throws CorruptedOmemoKeyException if some key is damaged or malformed
271     */
272    public abstract T_Bundle bundleFromOmemoBundle(OmemoBundleElement bundle, OmemoDevice contact, int keyId) throws CorruptedOmemoKeyException;
273
274    /**
275     * Extract the signature from a signedPreKey.
276     *
277     * @param signedPreKey signedPreKey
278     * @return signature as byte array
279     */
280    public abstract byte[] signedPreKeySignatureFromKey(T_SigPreKey signedPreKey);
281
282    /**
283     * Generate a new IdentityKeyPair. We should always have only one pair and usually keep this for a long time.
284     *
285     * @return deserialized identity key pair
286     */
287    public abstract T_IdKeyPair generateOmemoIdentityKeyPair();
288
289    /**
290     * return the id of the given signedPreKey.
291     *
292     * @param signedPreKey key
293     * @return id of the key
294     */
295    public abstract int signedPreKeyIdFromKey(T_SigPreKey signedPreKey);
296
297    /**
298     * serialize an identityKeyPair into bytes.
299     *
300     * @param identityKeyPair identityKeyPair
301     * @return byte array
302     */
303    public abstract byte[] identityKeyPairToBytes(T_IdKeyPair identityKeyPair);
304
305    /**
306     * Extract the public identityKey from an identityKeyPair.
307     *
308     * @param pair keyPair
309     * @return public key of the pair
310     */
311    public abstract T_IdKey identityKeyFromPair(T_IdKeyPair pair);
312
313    /**
314     * Prepare an identityKey for transport in an OmemoBundleElement (serialize it).
315     *
316     * @param identityKey identityKey that will be transported
317     * @return key as byte array
318     */
319    public abstract byte[] identityKeyForBundle(T_IdKey identityKey);
320
321    /**
322     * Prepare an elliptic curve preKey for transport in an OmemoBundleElement.
323     *
324     * @param preKey key
325     * @return key as byte array
326     */
327    public abstract byte[] preKeyPublicKeyForBundle(T_ECPub preKey);
328
329    /**
330     * Prepare a preKey for transport in an OmemoBundleElement.
331     *
332     * @param preKey preKey
333     * @return key as byte array
334     */
335    public abstract byte[] preKeyForBundle(T_PreKey preKey);
336
337    /**
338     * Prepare a whole bunche of preKeys for transport.
339     *
340     * @param preKeyHashMap HashMap of preKeys
341     * @return HashMap of byte arrays but with the same keyIds as key
342     */
343    public Map<Integer, byte[]> preKeyPublicKeysForBundle(Map<Integer, T_PreKey> preKeyHashMap) {
344        Map<Integer, byte[]> out = new HashMap<>();
345        for (Map.Entry<Integer, T_PreKey> e : preKeyHashMap.entrySet()) {
346            out.put(e.getKey(), preKeyForBundle(e.getValue()));
347        }
348        return out;
349    }
350
351    /**
352     * Prepare a public signedPreKey for transport in a bundle.
353     *
354     * @param signedPreKey signedPreKey
355     * @return signedPreKey as byte array
356     */
357    public abstract byte[] signedPreKeyPublicForBundle(T_SigPreKey signedPreKey);
358
359    /**
360     * Return the fingerprint of an identityKey.
361     *
362     * @param identityKey identityKey
363     * @return fingerprint of the key
364     */
365    public abstract OmemoFingerprint getFingerprintOfIdentityKey(T_IdKey identityKey);
366
367    /**
368     * Returns the fingerprint of the public key of an identityKeyPair.
369     * @param identityKeyPair IdentityKeyPair.
370     * @return fingerprint of the public key.
371     */
372    public abstract OmemoFingerprint getFingerprintOfIdentityKeyPair(T_IdKeyPair identityKeyPair);
373
374    /**
375     * Deserialize a raw OMEMO Session from bytes.
376     *
377     * @param data bytes
378     * @return raw OMEMO Session
379     *
380     * @throws IOException when something goes wrong
381     */
382    public abstract T_Sess rawSessionFromBytes(byte[] data) throws IOException;
383
384    /**
385     * Serialize a raw OMEMO session into a byte array.
386     *
387     * @param session raw session
388     * @return byte array
389     */
390    public abstract byte[] rawSessionToBytes(T_Sess session);
391
392    /**
393     * Add integers modulo MAX_VALUE.
394     *
395     * @param value base integer
396     * @param added value that is added to the base value
397     * @return (value plus added) modulo Integer.MAX_VALUE
398     */
399    public static int addInBounds(int value, int added) {
400        int avail = Integer.MAX_VALUE - value;
401        if (avail < added) {
402            return added - avail;
403        } else {
404            return value + added;
405        }
406    }
407}