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}