HashManager.java

  1. /**
  2.  *
  3.  * Copyright © 2017 Paul Schaub
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.hashes;

  18. import static org.jivesoftware.smack.util.StringUtils.encodeHex;
  19. import static org.jivesoftware.smack.util.StringUtils.toUtf8Bytes;
  20. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.BLAKE2B160;
  21. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.BLAKE2B256;
  22. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.BLAKE2B384;
  23. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.BLAKE2B512;
  24. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.MD5;
  25. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA3_224;
  26. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA3_256;
  27. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA3_384;
  28. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA3_512;
  29. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_1;
  30. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_224;
  31. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_256;
  32. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_384;
  33. import static org.jivesoftware.smackx.hashes.HashManager.ALGORITHM.SHA_512;

  34. import java.security.MessageDigest;
  35. import java.security.NoSuchAlgorithmException;
  36. import java.security.NoSuchProviderException;
  37. import java.security.Security;
  38. import java.util.Arrays;
  39. import java.util.Collections;
  40. import java.util.List;
  41. import java.util.WeakHashMap;

  42. import org.jivesoftware.smack.Manager;
  43. import org.jivesoftware.smack.XMPPConnection;

  44. import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
  45. import org.jivesoftware.smackx.hashes.element.HashElement;

  46. import org.bouncycastle.jce.provider.BouncyCastleProvider;

  47. /**
  48.  * Manager that can be used to determine support for hash functions. By default the Manager announces support for
  49.  * XEP-0300, as well as for the recommended set of hash algorithms. Those contain SHA256, SHA384, SHA512, SHA3-256,
  50.  * SHA3-384, SHA3-512, BLAKE2B256, BLAKE2B384 and BLAKE2B512. Those algorithms got recommended here:
  51.  * <a href="https://xmpp.org/extensions/xep-0300.html#recommendations">https://xmpp.org/extensions/xep-0300.html#recommendations</a>.
  52.  */
  53. public final class HashManager extends Manager {

  54.     static {
  55.         Security.addProvider(new BouncyCastleProvider());
  56.     }
  57.     public static final String PROVIDER = "BC";

  58.     public static final String PREFIX_NS_ALGO = "urn:xmpp:hash-function-text-names:";

  59.     public enum NAMESPACE {
  60.         V1 ("urn:xmpp:hashes:1"),
  61.         V2 ("urn:xmpp:hashes:2");

  62.         final String name;

  63.         NAMESPACE(String name) {
  64.             this.name = name;
  65.         }

  66.         @Override
  67.         public String toString() {
  68.             return this.name;
  69.         }
  70.     }

  71.     public static final List<ALGORITHM> RECOMMENDED = Collections.unmodifiableList(Arrays.asList(
  72.             SHA_256, SHA_384, SHA_512,
  73.             SHA3_256, SHA3_384, SHA3_512,
  74.             BLAKE2B256, BLAKE2B384, BLAKE2B512));

  75.     private static final WeakHashMap<XMPPConnection, HashManager> INSTANCES = new WeakHashMap<>();

  76.     /**
  77.      * Constructor of the HashManager.
  78.      *
  79.      * @param connection connection
  80.      */
  81.     private HashManager(XMPPConnection connection) {
  82.         super(connection);
  83.         ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
  84.         sdm.addFeature(NAMESPACE.V2.toString());
  85.         addAlgorithmsToFeatures(RECOMMENDED);
  86.     }

  87.     public static HashElement calculateHashElement(ALGORITHM algorithm, byte[] data) {
  88.         return new HashElement(algorithm, hash(algorithm, data));
  89.     }

  90.     public static HashElement assembleHashElement(ALGORITHM algorithm, byte[] hash) {
  91.         return new HashElement(algorithm, hash);
  92.     }

  93.     /**
  94.      * Announce support for the given list of algorithms.
  95.      * @param algorithms
  96.      */
  97.     public void addAlgorithmsToFeatures(List<ALGORITHM> algorithms) {
  98.         ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
  99.         for (ALGORITHM algo : algorithms) {
  100.             sdm.addFeature(asFeature(algo));
  101.         }
  102.     }

  103.     /**
  104.      * Get an instance of the HashManager for the  given connection.
  105.      * @param connection
  106.      * @return the manager for the given connection.
  107.      */
  108.     public static synchronized HashManager getInstanceFor(XMPPConnection connection) {
  109.         HashManager hashManager = INSTANCES.get(connection);
  110.         if (hashManager == null) {
  111.             hashManager = new HashManager(connection);
  112.             INSTANCES.put(connection, hashManager);
  113.         }
  114.         return hashManager;
  115.     }

  116.     /**
  117.      * Return the feature name of the given algorithm.
  118.      * @param algorithm eg. 'SHA_1'
  119.      * @return feature name (eg. urn:xmpp:hash-function-text-names:sha-1')
  120.      */
  121.     public static String asFeature(ALGORITHM algorithm) {
  122.         return PREFIX_NS_ALGO + algorithm.toString();
  123.     }

  124.     enum AlgorithmRecommendation {
  125.         unknown,
  126.         must_not,
  127.         should_not,
  128.         should,
  129.         must,
  130.     }

  131.     public enum ALGORITHM {
  132.         MD5 ("md5", AlgorithmRecommendation.must_not),
  133.         SHA_1 ("sha-1", AlgorithmRecommendation.should_not),
  134.         SHA_224 ("sha-224", AlgorithmRecommendation.unknown),
  135.         SHA_256 ("sha-256", AlgorithmRecommendation.must),
  136.         SHA_384 ("sha-384", AlgorithmRecommendation.unknown),
  137.         SHA_512 ("sha-512", AlgorithmRecommendation.should),
  138.         SHA3_224 ("sha3-224", AlgorithmRecommendation.unknown),
  139.         SHA3_256 ("sha3-256", AlgorithmRecommendation.must),
  140.         SHA3_384 ("sha3-384", AlgorithmRecommendation.unknown),
  141.         SHA3_512 ("sha3-512", AlgorithmRecommendation.should),
  142.         BLAKE2B160("id-blake2b160", AlgorithmRecommendation.unknown),
  143.         BLAKE2B256("id-blake2b256", AlgorithmRecommendation.must),
  144.         BLAKE2B384("id-blake2b384", AlgorithmRecommendation.unknown),
  145.         BLAKE2B512("id-blake2b512", AlgorithmRecommendation.should);

  146.         private final String name;
  147.         private final AlgorithmRecommendation recommendation;

  148.         ALGORITHM(String name, AlgorithmRecommendation recommendation) {
  149.             this.name = name;
  150.             this.recommendation = recommendation;
  151.         }

  152.         /**
  153.          * Return the name of the algorithm as it is used in the XEP.
  154.          * @return name.
  155.          */
  156.         @Override
  157.         public String toString() {
  158.             return this.name;
  159.         }

  160.         public AlgorithmRecommendation getRecommendation() {
  161.             return recommendation;
  162.         }

  163.         /**
  164.          * Compensational method for static 'valueOf' function.
  165.          *
  166.          * @param s
  167.          * @return the algorithm for the given string.
  168.          * @throws IllegalArgumentException if no algorithm for the given string is known.
  169.          */
  170.         public static ALGORITHM valueOfName(String s) {
  171.             for (ALGORITHM a : ALGORITHM.values()) {
  172.                 if (a.toString().equals(s)) {
  173.                     return a;
  174.                 }
  175.             }
  176.             throw new IllegalArgumentException("No ALGORITHM enum with this name (" + s + ") found.");
  177.         }
  178.     }

  179.     /**
  180.      * Calculate the hash sum of data using algorithm.
  181.      *
  182.      * @param algorithm the algorithm to use.
  183.      * @param data the data to calculate the hash for.
  184.      * @return the hash value produced by the given algorithm for the given data.
  185.      */
  186.     public static byte[] hash(ALGORITHM algorithm, byte[] data) {
  187.         return getMessageDigest(algorithm).digest(data);
  188.     }

  189.     public static byte[] hash(ALGORITHM algorithm, String data) {
  190.         return hash(algorithm, toUtf8Bytes(data));
  191.     }

  192.     public static MessageDigest getMessageDigest(ALGORITHM algorithm) {
  193.         MessageDigest md;
  194.         try {
  195.             switch (algorithm) {
  196.                 case MD5:
  197.                     md = MessageDigest.getInstance("MD5", PROVIDER);
  198.                     break;
  199.                 case SHA_1:
  200.                     md = MessageDigest.getInstance("SHA-1", PROVIDER);
  201.                     break;
  202.                 case SHA_224:
  203.                     md = MessageDigest.getInstance("SHA-224", PROVIDER);
  204.                     break;
  205.                 case SHA_256:
  206.                     md = MessageDigest.getInstance("SHA-256", PROVIDER);
  207.                     break;
  208.                 case SHA_384:
  209.                     md = MessageDigest.getInstance("SHA-384", PROVIDER);
  210.                     break;
  211.                 case SHA_512:
  212.                     md = MessageDigest.getInstance("SHA-512", PROVIDER);
  213.                     break;
  214.                 case SHA3_224:
  215.                     md = MessageDigest.getInstance("SHA3-224", PROVIDER);
  216.                     break;
  217.                 case SHA3_256:
  218.                     md = MessageDigest.getInstance("SHA3-256", PROVIDER);
  219.                     break;
  220.                 case SHA3_384:
  221.                     md = MessageDigest.getInstance("SHA3-384", PROVIDER);
  222.                     break;
  223.                 case SHA3_512:
  224.                     md = MessageDigest.getInstance("SHA3-512", PROVIDER);
  225.                     break;
  226.                 case BLAKE2B160:
  227.                     md = MessageDigest.getInstance("BLAKE2b-160", PROVIDER);
  228.                     break;
  229.                 case BLAKE2B256:
  230.                     md = MessageDigest.getInstance("BLAKE2b-256", PROVIDER);
  231.                     break;
  232.                 case BLAKE2B384:
  233.                     md = MessageDigest.getInstance("BLAKE2b-384", PROVIDER);
  234.                     break;
  235.                 case BLAKE2B512:
  236.                     md = MessageDigest.getInstance("BLAKE2b-512", PROVIDER);
  237.                     break;
  238.                 default:
  239.                     throw new AssertionError("Invalid enum value: " + algorithm);
  240.             }
  241.             return md;
  242.         } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
  243.             throw new AssertionError(e);
  244.         }
  245.     }

  246.     public static byte[] md5(byte[] data) {
  247.         return getMessageDigest(MD5).digest(data);
  248.     }

  249.     public static byte[] md5(String data) {
  250.         return md5(toUtf8Bytes(data));
  251.     }

  252.     public static String md5HexString(byte[] data) {
  253.         return encodeHex(md5(data));
  254.     }

  255.     public static String md5HexString(String data) {
  256.         return encodeHex(md5(data));
  257.     }

  258.     public static byte[] sha_1(byte[] data) {
  259.         return getMessageDigest(SHA_1).digest(data);
  260.     }

  261.     public static byte[] sha_1(String data) {
  262.         return sha_1(toUtf8Bytes(data));
  263.     }

  264.     public static String sha_1HexString(byte[] data) {
  265.         return encodeHex(sha_1(data));
  266.     }

  267.     public static String sha_1HexString(String data) {
  268.         return encodeHex(sha_1(data));
  269.     }

  270.     public static byte[] sha_224(byte[] data) {
  271.         return getMessageDigest(SHA_224).digest(data);
  272.     }

  273.     public static byte[] sha_224(String data) {
  274.         return sha_224(toUtf8Bytes(data));
  275.     }

  276.     public static String sha_224HexString(byte[] data) {
  277.         return encodeHex(sha_224(data));
  278.     }

  279.     public static String sha_224HexString(String data) {
  280.         return encodeHex(sha_224(data));
  281.     }

  282.     public static byte[] sha_256(byte[] data) {
  283.         return getMessageDigest(SHA_256).digest(data);
  284.     }

  285.     public static byte[] sha_256(String data) {
  286.         return sha_256(toUtf8Bytes(data));
  287.     }

  288.     public static String sha_256HexString(byte[] data) {
  289.         return encodeHex(sha_256(data));
  290.     }

  291.     public static String sha_256HexString(String data) {
  292.         return encodeHex(sha_256(data));
  293.     }

  294.     public static byte[] sha_384(byte[] data) {
  295.         return getMessageDigest(SHA_384).digest(data);
  296.     }

  297.     public static byte[] sha_384(String data) {
  298.         return sha_384(toUtf8Bytes(data));
  299.     }

  300.     public static String sha_384HexString(byte[] data) {
  301.         return encodeHex(sha_384(data));
  302.     }

  303.     public static String sha_384HexString(String data) {
  304.         return encodeHex(sha_384(data));
  305.     }

  306.     public static byte[] sha_512(byte[] data) {
  307.         return getMessageDigest(SHA_512).digest(data);
  308.     }

  309.     public static byte[] sha_512(String data) {
  310.         return sha_512(toUtf8Bytes(data));
  311.     }

  312.     public static String sha_512HexString(byte[] data) {
  313.         return encodeHex(sha_512(data));
  314.     }

  315.     public static String sha_512HexString(String data) {
  316.         return encodeHex(sha_512(data));
  317.     }

  318.     public static byte[] sha3_224(byte[] data) {
  319.         return getMessageDigest(SHA3_224).digest(data);
  320.     }

  321.     public static byte[] sha3_224(String data) {
  322.         return sha3_224(toUtf8Bytes(data));
  323.     }

  324.     public static String sha3_224HexString(byte[] data) {
  325.         return encodeHex(sha3_224(data));
  326.     }

  327.     public static String sha3_224HexString(String data) {
  328.         return encodeHex(sha3_224(data));
  329.     }

  330.     public static byte[] sha3_256(byte[] data) {
  331.         return getMessageDigest(SHA3_256).digest(data);
  332.     }

  333.     public static byte[] sha3_256(String data) {
  334.         return sha3_256(toUtf8Bytes(data));
  335.     }

  336.     public static String sha3_256HexString(byte[] data) {
  337.         return encodeHex(sha3_256(data));
  338.     }

  339.     public static String sha3_256HexString(String data) {
  340.         return encodeHex(sha3_256(data));
  341.     }

  342.     public static byte[] sha3_384(byte[] data) {
  343.         return getMessageDigest(SHA3_384).digest(data);
  344.     }

  345.     public static byte[] sha3_384(String data) {
  346.         return sha3_384(toUtf8Bytes(data));
  347.     }

  348.     public static String sha3_384HexString(byte[] data) {
  349.         return encodeHex(sha3_384(data));
  350.     }

  351.     public static String sha3_384HexString(String data) {
  352.         return encodeHex(sha3_384(data));
  353.     }

  354.     public static byte[] sha3_512(byte[] data) {
  355.         return getMessageDigest(SHA3_512).digest(data);
  356.     }

  357.     public static byte[] sha3_512(String data) {
  358.         return sha3_512(toUtf8Bytes(data));
  359.     }

  360.     public static String sha3_512HexString(byte[] data) {
  361.         return encodeHex(sha3_512(data));
  362.     }

  363.     public static String sha3_512HexString(String data) {
  364.         return encodeHex(sha3_512(data));
  365.     }

  366.     public static byte[] blake2b160(byte[] data) {
  367.         return getMessageDigest(BLAKE2B160).digest(data);
  368.     }

  369.     public static byte[] blake2b160(String data) {
  370.         return blake2b160(toUtf8Bytes(data));
  371.     }

  372.     public static String blake2b160HexString(byte[] data) {
  373.         return encodeHex(blake2b160(data));
  374.     }

  375.     public static String blake2b160HexString(String data) {
  376.         return encodeHex(blake2b160(data));
  377.     }

  378.     public static byte[] blake2b256(byte[] data) {
  379.         return getMessageDigest(BLAKE2B256).digest(data);
  380.     }

  381.     public static byte[] blake2b256(String data) {
  382.         return blake2b256(toUtf8Bytes(data));
  383.     }

  384.     public static String blake2b256HexString(byte[] data) {
  385.         return encodeHex(blake2b256(data));
  386.     }

  387.     public static String blake2b256HexString(String data) {
  388.         return encodeHex(blake2b256(data));
  389.     }

  390.     public static byte[] blake2b384(byte[] data) {
  391.         return getMessageDigest(BLAKE2B384).digest(data);
  392.     }

  393.     public static byte[] blake2b384(String data) {
  394.         return blake2b384(toUtf8Bytes(data));
  395.     }

  396.     public static String blake2b384HexString(byte[] data) {
  397.         return encodeHex(blake2b384(data));
  398.     }

  399.     public static  String blake2b384HexString(String data) {
  400.         return encodeHex(blake2b384(data));
  401.     }

  402.     public static byte[] blake2b512(byte[] data) {
  403.         return getMessageDigest(BLAKE2B512).digest(data);
  404.     }

  405.     public static byte[] blake2b512(String data) {
  406.         return blake2b512(toUtf8Bytes(data));
  407.     }

  408.     public static String blake2b512HexString(byte[] data) {
  409.         return encodeHex(blake2b512(data));
  410.     }

  411.     public static String blake2b512HexString(String data) {
  412.         return encodeHex(blake2b512(data));
  413.     }

  414. }