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}