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.hashes; 018 019import static org.jivesoftware.smack.util.StringUtils.encodeHex; 020import static org.jivesoftware.smack.util.StringUtils.toUtf8Bytes; 021import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.BLAKE2B160; 022import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.BLAKE2B256; 023import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.BLAKE2B384; 024import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.BLAKE2B512; 025import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.MD5; 026import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA3_224; 027import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA3_256; 028import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA3_384; 029import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA3_512; 030import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_1; 031import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_224; 032import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_256; 033import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_384; 034import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_512; 035 036import java.security.MessageDigest; 037import java.security.NoSuchAlgorithmException; 038import java.security.NoSuchProviderException; 039import java.security.Security; 040import java.util.Arrays; 041import java.util.Collections; 042import java.util.List; 043import java.util.WeakHashMap; 044 045import org.jivesoftware.smack.Manager; 046import org.jivesoftware.smack.XMPPConnection; 047 048import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 049import org.jivesoftware.smackx.hashes.element.HashElement; 050 051import org.bouncycastle.jce.provider.BouncyCastleProvider; 052 053/** 054 * Manager that can be used to determine support for hash functions. By default the Manager announces support for 055 * XEP-0300, as well as for the recommended set of hash algorithms. Those contain SHA256, SHA384, SHA512, SHA3-256, 056 * SHA3-384, SHA3-512, BLAKE2B256, BLAKE2B384 and BLAKE2B512. Those algorithms got recommended here: 057 * <a href="https://xmpp.org/extensions/xep-0300.html#recommendations">https://xmpp.org/extensions/xep-0300.html#recommendations</a>. 058 */ 059public final class HashManager extends Manager { 060 061 static { 062 Security.addProvider(new BouncyCastleProvider()); 063 } 064 public static final String PROVIDER = "BC"; 065 066 public static final String PREFIX_NS_ALGO = "urn:xmpp:hash-function-text-names:"; 067 068 public enum NAMESPACE { 069 V1 ("urn:xmpp:hashes:1"), 070 V2 ("urn:xmpp:hashes:2"); 071 072 final String name; 073 074 NAMESPACE(String name) { 075 this.name = name; 076 } 077 078 @Override 079 public String toString() { 080 return this.name; 081 } 082 } 083 084 public static final List<ALGORITHM> RECOMMENDED = Collections.unmodifiableList(Arrays.asList( 085 SHA_256, SHA_384, SHA_512, 086 SHA3_256, SHA3_384, SHA3_512, 087 BLAKE2B256, BLAKE2B384, BLAKE2B512)); 088 089 private static final WeakHashMap<XMPPConnection, HashManager> INSTANCES = new WeakHashMap<>(); 090 091 /** 092 * Constructor of the HashManager. 093 * 094 * @param connection connection 095 */ 096 private HashManager(XMPPConnection connection) { 097 super(connection); 098 ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection); 099 sdm.addFeature(NAMESPACE.V2.toString()); 100 addAlgorithmsToFeatures(RECOMMENDED); 101 } 102 103 public static HashElement calculateHashElement(ALGORITHM algorithm, byte[] data) { 104 return new HashElement(algorithm, hash(algorithm, data)); 105 } 106 107 public static HashElement assembleHashElement(ALGORITHM algorithm, byte[] hash) { 108 return new HashElement(algorithm, hash); 109 } 110 111 /** 112 * Announce support for the given list of algorithms. 113 * @param algorithms 114 */ 115 public void addAlgorithmsToFeatures(List<ALGORITHM> algorithms) { 116 ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection()); 117 for (ALGORITHM algo : algorithms) { 118 sdm.addFeature(asFeature(algo)); 119 } 120 } 121 122 /** 123 * Get an instance of the HashManager for the given connection. 124 * @param connection 125 * @return the manager for the given connection. 126 */ 127 public static synchronized HashManager getInstanceFor(XMPPConnection connection) { 128 HashManager hashManager = INSTANCES.get(connection); 129 if (hashManager == null) { 130 hashManager = new HashManager(connection); 131 INSTANCES.put(connection, hashManager); 132 } 133 return hashManager; 134 } 135 136 /** 137 * Return the feature name of the given algorithm. 138 * @param algorithm eg. 'SHA_1' 139 * @return feature name (eg. urn:xmpp:hash-function-text-names:sha-1') 140 */ 141 public static String asFeature(ALGORITHM algorithm) { 142 return PREFIX_NS_ALGO + algorithm.toString(); 143 } 144 145 enum AlgorithmRecommendation { 146 unknown, 147 must_not, 148 should_not, 149 should, 150 must, 151 } 152 153 public enum ALGORITHM { 154 MD5 ("md5", AlgorithmRecommendation.must_not), 155 SHA_1 ("sha-1", AlgorithmRecommendation.should_not), 156 SHA_224 ("sha-224", AlgorithmRecommendation.unknown), 157 SHA_256 ("sha-256", AlgorithmRecommendation.must), 158 SHA_384 ("sha-384", AlgorithmRecommendation.unknown), 159 SHA_512 ("sha-512", AlgorithmRecommendation.should), 160 SHA3_224 ("sha3-224", AlgorithmRecommendation.unknown), 161 SHA3_256 ("sha3-256", AlgorithmRecommendation.must), 162 SHA3_384 ("sha3-384", AlgorithmRecommendation.unknown), 163 SHA3_512 ("sha3-512", AlgorithmRecommendation.should), 164 BLAKE2B160("id-blake2b160", AlgorithmRecommendation.unknown), 165 BLAKE2B256("id-blake2b256", AlgorithmRecommendation.must), 166 BLAKE2B384("id-blake2b384", AlgorithmRecommendation.unknown), 167 BLAKE2B512("id-blake2b512", AlgorithmRecommendation.should); 168 169 private final String name; 170 private final AlgorithmRecommendation recommendation; 171 172 ALGORITHM(String name, AlgorithmRecommendation recommendation) { 173 this.name = name; 174 this.recommendation = recommendation; 175 } 176 177 /** 178 * Return the name of the algorithm as it is used in the XEP. 179 * @return name. 180 */ 181 @Override 182 public String toString() { 183 return this.name; 184 } 185 186 public AlgorithmRecommendation getRecommendation() { 187 return recommendation; 188 } 189 190 /** 191 * Compensational method for static 'valueOf' function. 192 * 193 * @param s 194 * @return the algorithm for the given string. 195 * @throws IllegalArgumentException if no algorithm for the given string is known. 196 */ 197 public static ALGORITHM valueOfName(String s) { 198 for (ALGORITHM a : ALGORITHM.values()) { 199 if (a.toString().equals(s)) { 200 return a; 201 } 202 } 203 throw new IllegalArgumentException("No ALGORITHM enum with this name (" + s + ") found."); 204 } 205 } 206 207 /** 208 * Calculate the hash sum of data using algorithm. 209 * 210 * @param algorithm the algorithm to use. 211 * @param data the data to calculate the hash for. 212 * @return the hash value produced by the given algorithm for the given data. 213 */ 214 public static byte[] hash(ALGORITHM algorithm, byte[] data) { 215 return getMessageDigest(algorithm).digest(data); 216 } 217 218 public static byte[] hash(ALGORITHM algorithm, String data) { 219 return hash(algorithm, toUtf8Bytes(data)); 220 } 221 222 public static MessageDigest getMessageDigest(ALGORITHM algorithm) { 223 MessageDigest md; 224 try { 225 switch (algorithm) { 226 case MD5: 227 md = MessageDigest.getInstance("MD5", PROVIDER); 228 break; 229 case SHA_1: 230 md = MessageDigest.getInstance("SHA-1", PROVIDER); 231 break; 232 case SHA_224: 233 md = MessageDigest.getInstance("SHA-224", PROVIDER); 234 break; 235 case SHA_256: 236 md = MessageDigest.getInstance("SHA-256", PROVIDER); 237 break; 238 case SHA_384: 239 md = MessageDigest.getInstance("SHA-384", PROVIDER); 240 break; 241 case SHA_512: 242 md = MessageDigest.getInstance("SHA-512", PROVIDER); 243 break; 244 case SHA3_224: 245 md = MessageDigest.getInstance("SHA3-224", PROVIDER); 246 break; 247 case SHA3_256: 248 md = MessageDigest.getInstance("SHA3-256", PROVIDER); 249 break; 250 case SHA3_384: 251 md = MessageDigest.getInstance("SHA3-384", PROVIDER); 252 break; 253 case SHA3_512: 254 md = MessageDigest.getInstance("SHA3-512", PROVIDER); 255 break; 256 case BLAKE2B160: 257 md = MessageDigest.getInstance("BLAKE2b-160", PROVIDER); 258 break; 259 case BLAKE2B256: 260 md = MessageDigest.getInstance("BLAKE2b-256", PROVIDER); 261 break; 262 case BLAKE2B384: 263 md = MessageDigest.getInstance("BLAKE2b-384", PROVIDER); 264 break; 265 case BLAKE2B512: 266 md = MessageDigest.getInstance("BLAKE2b-512", PROVIDER); 267 break; 268 default: 269 throw new AssertionError("Invalid enum value: " + algorithm); 270 } 271 return md; 272 } catch (NoSuchAlgorithmException | NoSuchProviderException e) { 273 throw new AssertionError(e); 274 } 275 } 276 277 public static byte[] md5(byte[] data) { 278 return getMessageDigest(MD5).digest(data); 279 } 280 281 public static byte[] md5(String data) { 282 return md5(toUtf8Bytes(data)); 283 } 284 285 public static String md5HexString(byte[] data) { 286 return encodeHex(md5(data)); 287 } 288 289 public static String md5HexString(String data) { 290 return encodeHex(md5(data)); 291 } 292 293 public static byte[] sha_1(byte[] data) { 294 return getMessageDigest(SHA_1).digest(data); 295 } 296 297 public static byte[] sha_1(String data) { 298 return sha_1(toUtf8Bytes(data)); 299 } 300 301 public static String sha_1HexString(byte[] data) { 302 return encodeHex(sha_1(data)); 303 } 304 305 public static String sha_1HexString(String data) { 306 return encodeHex(sha_1(data)); 307 } 308 309 public static byte[] sha_224(byte[] data) { 310 return getMessageDigest(SHA_224).digest(data); 311 } 312 313 public static byte[] sha_224(String data) { 314 return sha_224(toUtf8Bytes(data)); 315 } 316 317 public static String sha_224HexString(byte[] data) { 318 return encodeHex(sha_224(data)); 319 } 320 321 public static String sha_224HexString(String data) { 322 return encodeHex(sha_224(data)); 323 } 324 325 public static byte[] sha_256(byte[] data) { 326 return getMessageDigest(SHA_256).digest(data); 327 } 328 329 public static byte[] sha_256(String data) { 330 return sha_256(toUtf8Bytes(data)); 331 } 332 333 public static String sha_256HexString(byte[] data) { 334 return encodeHex(sha_256(data)); 335 } 336 337 public static String sha_256HexString(String data) { 338 return encodeHex(sha_256(data)); 339 } 340 341 public static byte[] sha_384(byte[] data) { 342 return getMessageDigest(SHA_384).digest(data); 343 } 344 345 public static byte[] sha_384(String data) { 346 return sha_384(toUtf8Bytes(data)); 347 } 348 349 public static String sha_384HexString(byte[] data) { 350 return encodeHex(sha_384(data)); 351 } 352 353 public static String sha_384HexString(String data) { 354 return encodeHex(sha_384(data)); 355 } 356 357 public static byte[] sha_512(byte[] data) { 358 return getMessageDigest(SHA_512).digest(data); 359 } 360 361 public static byte[] sha_512(String data) { 362 return sha_512(toUtf8Bytes(data)); 363 } 364 365 public static String sha_512HexString(byte[] data) { 366 return encodeHex(sha_512(data)); 367 } 368 369 public static String sha_512HexString(String data) { 370 return encodeHex(sha_512(data)); 371 } 372 373 public static byte[] sha3_224(byte[] data) { 374 return getMessageDigest(SHA3_224).digest(data); 375 } 376 377 public static byte[] sha3_224(String data) { 378 return sha3_224(toUtf8Bytes(data)); 379 } 380 381 public static String sha3_224HexString(byte[] data) { 382 return encodeHex(sha3_224(data)); 383 } 384 385 public static String sha3_224HexString(String data) { 386 return encodeHex(sha3_224(data)); 387 } 388 389 public static byte[] sha3_256(byte[] data) { 390 return getMessageDigest(SHA3_256).digest(data); 391 } 392 393 public static byte[] sha3_256(String data) { 394 return sha3_256(toUtf8Bytes(data)); 395 } 396 397 public static String sha3_256HexString(byte[] data) { 398 return encodeHex(sha3_256(data)); 399 } 400 401 public static String sha3_256HexString(String data) { 402 return encodeHex(sha3_256(data)); 403 } 404 405 public static byte[] sha3_384(byte[] data) { 406 return getMessageDigest(SHA3_384).digest(data); 407 } 408 409 public static byte[] sha3_384(String data) { 410 return sha3_384(toUtf8Bytes(data)); 411 } 412 413 public static String sha3_384HexString(byte[] data) { 414 return encodeHex(sha3_384(data)); 415 } 416 417 public static String sha3_384HexString(String data) { 418 return encodeHex(sha3_384(data)); 419 } 420 421 public static byte[] sha3_512(byte[] data) { 422 return getMessageDigest(SHA3_512).digest(data); 423 } 424 425 public static byte[] sha3_512(String data) { 426 return sha3_512(toUtf8Bytes(data)); 427 } 428 429 public static String sha3_512HexString(byte[] data) { 430 return encodeHex(sha3_512(data)); 431 } 432 433 public static String sha3_512HexString(String data) { 434 return encodeHex(sha3_512(data)); 435 } 436 437 public static byte[] blake2b160(byte[] data) { 438 return getMessageDigest(BLAKE2B160).digest(data); 439 } 440 441 public static byte[] blake2b160(String data) { 442 return blake2b160(toUtf8Bytes(data)); 443 } 444 445 public static String blake2b160HexString(byte[] data) { 446 return encodeHex(blake2b160(data)); 447 } 448 449 public static String blake2b160HexString(String data) { 450 return encodeHex(blake2b160(data)); 451 } 452 453 public static byte[] blake2b256(byte[] data) { 454 return getMessageDigest(BLAKE2B256).digest(data); 455 } 456 457 public static byte[] blake2b256(String data) { 458 return blake2b256(toUtf8Bytes(data)); 459 } 460 461 public static String blake2b256HexString(byte[] data) { 462 return encodeHex(blake2b256(data)); 463 } 464 465 public static String blake2b256HexString(String data) { 466 return encodeHex(blake2b256(data)); 467 } 468 469 public static byte[] blake2b384(byte[] data) { 470 return getMessageDigest(BLAKE2B384).digest(data); 471 } 472 473 public static byte[] blake2b384(String data) { 474 return blake2b384(toUtf8Bytes(data)); 475 } 476 477 public static String blake2b384HexString(byte[] data) { 478 return encodeHex(blake2b384(data)); 479 } 480 481 public static String blake2b384HexString(String data) { 482 return encodeHex(blake2b384(data)); 483 } 484 485 public static byte[] blake2b512(byte[] data) { 486 return getMessageDigest(BLAKE2B512).digest(data); 487 } 488 489 public static byte[] blake2b512(String data) { 490 return blake2b512(toUtf8Bytes(data)); 491 } 492 493 public static String blake2b512HexString(byte[] data) { 494 return encodeHex(blake2b512(data)); 495 } 496 497 public static String blake2b512HexString(String data) { 498 return encodeHex(blake2b512(data)); 499 } 500 501}