HashManager.java

  1. /**
  2.  *
  3.  * Copyright © 2017 Paul Schaub, 2019-2020 Florian Schmaus
  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.util.Arrays;
  37. import java.util.Collections;
  38. import java.util.List;
  39. import java.util.WeakHashMap;

  40. import org.jivesoftware.smack.Manager;
  41. import org.jivesoftware.smack.XMPPConnection;

  42. import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
  43. import org.jivesoftware.smackx.hashes.element.HashElement;

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

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

  52.     public enum NAMESPACE {
  53.         V1 ("urn:xmpp:hashes:1"),
  54.         V2 ("urn:xmpp:hashes:2");

  55.         final String name;

  56.         NAMESPACE(String name) {
  57.             this.name = name;
  58.         }

  59.         @Override
  60.         public String toString() {
  61.             return this.name;
  62.         }
  63.     }

  64.     public static final List<ALGORITHM> RECOMMENDED = Collections.unmodifiableList(Arrays.asList(
  65.             SHA_256, SHA_384, SHA_512,
  66.             SHA3_256, SHA3_384, SHA3_512,
  67.             BLAKE2B256, BLAKE2B384, BLAKE2B512));

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

  69.     /**
  70.      * Constructor of the HashManager.
  71.      *
  72.      * @param connection connection
  73.      */
  74.     private HashManager(XMPPConnection connection) {
  75.         super(connection);
  76.         ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection);
  77.         sdm.addFeature(NAMESPACE.V2.toString());
  78.         addAlgorithmsToFeatures(RECOMMENDED);
  79.     }

  80.     public static HashElement calculateHashElement(ALGORITHM algorithm, byte[] data) {
  81.         return new HashElement(algorithm, hash(algorithm, data));
  82.     }

  83.     public static HashElement assembleHashElement(ALGORITHM algorithm, byte[] hash) {
  84.         return new HashElement(algorithm, hash);
  85.     }

  86.     /**
  87.      * Announce support for the given list of algorithms.
  88.      * @param algorithms TODO javadoc me please
  89.      */
  90.     public void addAlgorithmsToFeatures(List<ALGORITHM> algorithms) {
  91.         ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
  92.         for (ALGORITHM algo : algorithms) {
  93.             sdm.addFeature(asFeature(algo));
  94.         }
  95.     }

  96.     /**
  97.      * Get an instance of the HashManager for the  given connection.
  98.      * @param connection TODO javadoc me please
  99.      * @return the manager for the given connection.
  100.      */
  101.     public static synchronized HashManager getInstanceFor(XMPPConnection connection) {
  102.         HashManager hashManager = INSTANCES.get(connection);
  103.         if (hashManager == null) {
  104.             hashManager = new HashManager(connection);
  105.             INSTANCES.put(connection, hashManager);
  106.         }
  107.         return hashManager;
  108.     }

  109.     /**
  110.      * Return the feature name of the given algorithm.
  111.      * @param algorithm eg. 'SHA_1'
  112.      * @return feature name (eg. urn:xmpp:hash-function-text-names:sha-1')
  113.      */
  114.     public static String asFeature(ALGORITHM algorithm) {
  115.         return PREFIX_NS_ALGO + algorithm.toString();
  116.     }

  117.     enum AlgorithmRecommendation {
  118.         unknown,
  119.         must_not,
  120.         should_not,
  121.         should,
  122.         must,
  123.     }

  124.     public enum ALGORITHM {
  125.         MD5 ("md5", AlgorithmRecommendation.must_not),
  126.         SHA_1 ("sha-1", AlgorithmRecommendation.should_not),
  127.         SHA_224 ("sha-224", AlgorithmRecommendation.unknown),
  128.         SHA_256 ("sha-256", AlgorithmRecommendation.must),
  129.         SHA_384 ("sha-384", AlgorithmRecommendation.unknown),
  130.         SHA_512 ("sha-512", AlgorithmRecommendation.should),
  131.         SHA3_224 ("sha3-224", AlgorithmRecommendation.unknown),
  132.         SHA3_256 ("sha3-256", AlgorithmRecommendation.must),
  133.         SHA3_384 ("sha3-384", AlgorithmRecommendation.unknown),
  134.         SHA3_512 ("sha3-512", AlgorithmRecommendation.should),
  135.         BLAKE2B160("id-blake2b160", AlgorithmRecommendation.unknown),
  136.         BLAKE2B256("id-blake2b256", AlgorithmRecommendation.must),
  137.         BLAKE2B384("id-blake2b384", AlgorithmRecommendation.unknown),
  138.         BLAKE2B512("id-blake2b512", AlgorithmRecommendation.should);

  139.         private final String name;
  140.         private final AlgorithmRecommendation recommendation;

  141.         ALGORITHM(String name, AlgorithmRecommendation recommendation) {
  142.             this.name = name;
  143.             this.recommendation = recommendation;
  144.         }

  145.         /**
  146.          * Return the name of the algorithm as it is used in the XEP.
  147.          * @return name.
  148.          */
  149.         @Override
  150.         public String toString() {
  151.             return this.name;
  152.         }

  153.         public AlgorithmRecommendation getRecommendation() {
  154.             return recommendation;
  155.         }

  156.         /**
  157.          * Compensational method for static 'valueOf' function.
  158.          *
  159.          * @param s TODO javadoc me please
  160.          * @return the algorithm for the given string.
  161.          * @throws IllegalArgumentException if no algorithm for the given string is known.
  162.          */
  163.         public static ALGORITHM valueOfName(String s) {
  164.             for (ALGORITHM a : ALGORITHM.values()) {
  165.                 if (a.toString().equals(s)) {
  166.                     return a;
  167.                 }
  168.             }
  169.             throw new IllegalArgumentException("No ALGORITHM enum with this name (" + s + ") found.");
  170.         }
  171.     }

  172.     /**
  173.      * Calculate the hash sum of data using algorithm.
  174.      *
  175.      * @param algorithm the algorithm to use.
  176.      * @param data the data to calculate the hash for.
  177.      * @return the hash value produced by the given algorithm for the given data.
  178.      */
  179.     public static byte[] hash(ALGORITHM algorithm, byte[] data) {
  180.         return getMessageDigest(algorithm).digest(data);
  181.     }

  182.     public static byte[] hash(ALGORITHM algorithm, String data) {
  183.         return hash(algorithm, toUtf8Bytes(data));
  184.     }

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

  239.     public static byte[] md5(byte[] data) {
  240.         return getMessageDigest(MD5).digest(data);
  241.     }

  242.     public static byte[] md5(String data) {
  243.         return md5(toUtf8Bytes(data));
  244.     }

  245.     public static String md5HexString(byte[] data) {
  246.         return encodeHex(md5(data));
  247.     }

  248.     public static String md5HexString(String data) {
  249.         return encodeHex(md5(data));
  250.     }

  251.     public static byte[] sha_1(byte[] data) {
  252.         return getMessageDigest(SHA_1).digest(data);
  253.     }

  254.     public static byte[] sha_1(String data) {
  255.         return sha_1(toUtf8Bytes(data));
  256.     }

  257.     public static String sha_1HexString(byte[] data) {
  258.         return encodeHex(sha_1(data));
  259.     }

  260.     public static String sha_1HexString(String data) {
  261.         return encodeHex(sha_1(data));
  262.     }

  263.     public static byte[] sha_224(byte[] data) {
  264.         return getMessageDigest(SHA_224).digest(data);
  265.     }

  266.     public static byte[] sha_224(String data) {
  267.         return sha_224(toUtf8Bytes(data));
  268.     }

  269.     public static String sha_224HexString(byte[] data) {
  270.         return encodeHex(sha_224(data));
  271.     }

  272.     public static String sha_224HexString(String data) {
  273.         return encodeHex(sha_224(data));
  274.     }

  275.     public static byte[] sha_256(byte[] data) {
  276.         return getMessageDigest(SHA_256).digest(data);
  277.     }

  278.     public static byte[] sha_256(String data) {
  279.         return sha_256(toUtf8Bytes(data));
  280.     }

  281.     public static String sha_256HexString(byte[] data) {
  282.         return encodeHex(sha_256(data));
  283.     }

  284.     public static String sha_256HexString(String data) {
  285.         return encodeHex(sha_256(data));
  286.     }

  287.     public static byte[] sha_384(byte[] data) {
  288.         return getMessageDigest(SHA_384).digest(data);
  289.     }

  290.     public static byte[] sha_384(String data) {
  291.         return sha_384(toUtf8Bytes(data));
  292.     }

  293.     public static String sha_384HexString(byte[] data) {
  294.         return encodeHex(sha_384(data));
  295.     }

  296.     public static String sha_384HexString(String data) {
  297.         return encodeHex(sha_384(data));
  298.     }

  299.     public static byte[] sha_512(byte[] data) {
  300.         return getMessageDigest(SHA_512).digest(data);
  301.     }

  302.     public static byte[] sha_512(String data) {
  303.         return sha_512(toUtf8Bytes(data));
  304.     }

  305.     public static String sha_512HexString(byte[] data) {
  306.         return encodeHex(sha_512(data));
  307.     }

  308.     public static String sha_512HexString(String data) {
  309.         return encodeHex(sha_512(data));
  310.     }

  311.     public static byte[] sha3_224(byte[] data) {
  312.         return getMessageDigest(SHA3_224).digest(data);
  313.     }

  314.     public static byte[] sha3_224(String data) {
  315.         return sha3_224(toUtf8Bytes(data));
  316.     }

  317.     public static String sha3_224HexString(byte[] data) {
  318.         return encodeHex(sha3_224(data));
  319.     }

  320.     public static String sha3_224HexString(String data) {
  321.         return encodeHex(sha3_224(data));
  322.     }

  323.     public static byte[] sha3_256(byte[] data) {
  324.         return getMessageDigest(SHA3_256).digest(data);
  325.     }

  326.     public static byte[] sha3_256(String data) {
  327.         return sha3_256(toUtf8Bytes(data));
  328.     }

  329.     public static String sha3_256HexString(byte[] data) {
  330.         return encodeHex(sha3_256(data));
  331.     }

  332.     public static String sha3_256HexString(String data) {
  333.         return encodeHex(sha3_256(data));
  334.     }

  335.     public static byte[] sha3_384(byte[] data) {
  336.         return getMessageDigest(SHA3_384).digest(data);
  337.     }

  338.     public static byte[] sha3_384(String data) {
  339.         return sha3_384(toUtf8Bytes(data));
  340.     }

  341.     public static String sha3_384HexString(byte[] data) {
  342.         return encodeHex(sha3_384(data));
  343.     }

  344.     public static String sha3_384HexString(String data) {
  345.         return encodeHex(sha3_384(data));
  346.     }

  347.     public static byte[] sha3_512(byte[] data) {
  348.         return getMessageDigest(SHA3_512).digest(data);
  349.     }

  350.     public static byte[] sha3_512(String data) {
  351.         return sha3_512(toUtf8Bytes(data));
  352.     }

  353.     public static String sha3_512HexString(byte[] data) {
  354.         return encodeHex(sha3_512(data));
  355.     }

  356.     public static String sha3_512HexString(String data) {
  357.         return encodeHex(sha3_512(data));
  358.     }

  359.     public static byte[] blake2b160(byte[] data) {
  360.         return getMessageDigest(BLAKE2B160).digest(data);
  361.     }

  362.     public static byte[] blake2b160(String data) {
  363.         return blake2b160(toUtf8Bytes(data));
  364.     }

  365.     public static String blake2b160HexString(byte[] data) {
  366.         return encodeHex(blake2b160(data));
  367.     }

  368.     public static String blake2b160HexString(String data) {
  369.         return encodeHex(blake2b160(data));
  370.     }

  371.     public static byte[] blake2b256(byte[] data) {
  372.         return getMessageDigest(BLAKE2B256).digest(data);
  373.     }

  374.     public static byte[] blake2b256(String data) {
  375.         return blake2b256(toUtf8Bytes(data));
  376.     }

  377.     public static String blake2b256HexString(byte[] data) {
  378.         return encodeHex(blake2b256(data));
  379.     }

  380.     public static String blake2b256HexString(String data) {
  381.         return encodeHex(blake2b256(data));
  382.     }

  383.     public static byte[] blake2b384(byte[] data) {
  384.         return getMessageDigest(BLAKE2B384).digest(data);
  385.     }

  386.     public static byte[] blake2b384(String data) {
  387.         return blake2b384(toUtf8Bytes(data));
  388.     }

  389.     public static String blake2b384HexString(byte[] data) {
  390.         return encodeHex(blake2b384(data));
  391.     }

  392.     public static  String blake2b384HexString(String data) {
  393.         return encodeHex(blake2b384(data));
  394.     }

  395.     public static byte[] blake2b512(byte[] data) {
  396.         return getMessageDigest(BLAKE2B512).digest(data);
  397.     }

  398.     public static byte[] blake2b512(String data) {
  399.         return blake2b512(toUtf8Bytes(data));
  400.     }

  401.     public static String blake2b512HexString(byte[] data) {
  402.         return encodeHex(blake2b512(data));
  403.     }

  404.     public static String blake2b512HexString(String data) {
  405.         return encodeHex(blake2b512(data));
  406.     }

  407. }