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