wmem: allow wmem_destroy_list to ignore a NULL list.
[metze/wireshark/wip.git] / wsutil / wsgcrypt.c
index b390059e7b91a2c7f2e72d076ddf1e1daa7cae43..eef943c2728a1a42e6be12961961c6a62eafa97d 100644 (file)
@@ -7,7 +7,7 @@
  * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 Gerald Combs
  *
- * SPDX-License-Identifier: GPL-2.0+
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "wsgcrypt.h"
@@ -144,6 +144,44 @@ out:
        return decr_len;
 }
 
+gcry_error_t
+hkdf_expand(int hashalgo, const guint8 *prk, guint prk_len, const guint8 *info, guint info_len,
+            guint8 *out, guint out_len)
+{
+       // Current maximum hash output size: 48 bytes for SHA-384.
+       guchar          lastoutput[48];
+       gcry_md_hd_t    h;
+       gcry_error_t    err;
+       const guint     hash_len = gcry_md_get_algo_dlen(hashalgo);
+
+       /* Some sanity checks */
+       if (!(out_len > 0 && out_len <= 255 * hash_len) ||
+           !(hash_len > 0 && hash_len <= sizeof(lastoutput))) {
+               return GPG_ERR_INV_ARG;
+       }
+
+       err = gcry_md_open(&h, hashalgo, GCRY_MD_FLAG_HMAC);
+       if (err) {
+               return err;
+       }
+
+       for (guint offset = 0; offset < out_len; offset += hash_len) {
+               gcry_md_reset(h);
+               gcry_md_setkey(h, prk, prk_len);                    /* Set PRK */
+               if (offset > 0) {
+                       gcry_md_write(h, lastoutput, hash_len);     /* T(1..N) */
+               }
+               gcry_md_write(h, info, info_len);                   /* info */
+               gcry_md_putc(h, (guint8) (offset / hash_len + 1));  /* constant 0x01..N */
+
+               memcpy(lastoutput, gcry_md_read(h, hashalgo), hash_len);
+               memcpy(out + offset, lastoutput, MIN(hash_len, out_len - offset));
+       }
+
+       gcry_md_close(h);
+       return 0;
+}
+
 /*
  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
  *