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