File 0003-Add-argon2-kdf-from-libgcrypt.patch of Package grub2

From 03bebb951ea770dda2445de578da6cc7afef05c8 Mon Sep 17 00:00:00 2001
From: Michael Chang <mchang@suse.com>
Date: Mon, 5 Jun 2023 17:14:37 +0800
Subject: [PATCH 3/9] Add argon2 kdf from libgcrypt

---
 Makefile.utilargon2.def                       |   1 +
 grub-core/Makefile.argon2.def                 |   1 +
 .../lib/libgcrypt-argon2/cipher/blake2.c      |   3 -
 .../libgcrypt-argon2/cipher/kdf-internal.h    |  43 +
 grub-core/lib/libgcrypt-argon2/cipher/kdf.c   | 732 ++++++++++++++++++
 grub-core/lib/libgcrypt-argon2/src/gcrypt.h   |  38 +-
 include/grub/crypto110.h                      |   2 +
 include/grub/gcrypt/gpg-error.h               |  11 +
 8 files changed, 827 insertions(+), 4 deletions(-)
 create mode 100644 grub-core/lib/libgcrypt-argon2/cipher/kdf-internal.h
 create mode 100644 grub-core/lib/libgcrypt-argon2/cipher/kdf.c

diff --git a/Makefile.utilargon2.def b/Makefile.utilargon2.def
index 2ee7d6513..a7954a674 100644
--- a/Makefile.utilargon2.def
+++ b/Makefile.utilargon2.def
@@ -6,4 +6,5 @@ library = {
   cppflags = '$(CPPFLAGS_ARGON2)';
 
   common = grub-core/lib/libgcrypt-argon2/cipher/blake2.c;
+  common = grub-core/lib/libgcrypt-argon2/cipher/kdf.c;
 };
diff --git a/grub-core/Makefile.argon2.def b/grub-core/Makefile.argon2.def
index e9f493785..6276be51b 100644
--- a/grub-core/Makefile.argon2.def
+++ b/grub-core/Makefile.argon2.def
@@ -3,6 +3,7 @@ AutoGen definitions Makefile.tpl;
 module = {
   name = gcry_blake2;
   common = lib/libgcrypt-argon2/cipher/blake2.c;
+  common = lib/libgcrypt-argon2/cipher/kdf.c;
   cflags = '$(CFLAGS_GCRY)';
   cppflags = '$(CPPFLAGS_ARGON2)';
 };
diff --git a/grub-core/lib/libgcrypt-argon2/cipher/blake2.c b/grub-core/lib/libgcrypt-argon2/cipher/blake2.c
index 0345b65df..35a7bf4b6 100644
--- a/grub-core/lib/libgcrypt-argon2/cipher/blake2.c
+++ b/grub-core/lib/libgcrypt-argon2/cipher/blake2.c
@@ -22,9 +22,6 @@
  * Copyright 2012, Samuel Neves <sneves@dei.uc.pt>
  */
 
-#include <grub/dl.h>
-GRUB_MOD_LICENSE ("GPLv3+");
-
 #include "g10lib.h"
 #include "bithelp.h"
 #include "cipher.h"
diff --git a/grub-core/lib/libgcrypt-argon2/cipher/kdf-internal.h b/grub-core/lib/libgcrypt-argon2/cipher/kdf-internal.h
new file mode 100644
index 000000000..9e9a432e5
--- /dev/null
+++ b/grub-core/lib/libgcrypt-argon2/cipher/kdf-internal.h
@@ -0,0 +1,43 @@
+/* kdf-internal.h  - Internal defs for kdf.c
+ * Copyright (C) 2013 g10 Code GmbH
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GCRY_KDF_INTERNAL_H
+#define GCRY_KDF_INTERNAL_H
+
+/*-- kdf.c --*/
+gpg_err_code_t
+_gcry_kdf_pkdf2 (const void *passphrase, size_t passphraselen,
+                 int hashalgo,
+                 const void *salt, size_t saltlen,
+                 unsigned long iterations,
+                 size_t keysize, void *keybuffer);
+
+/*-- scrypt.c --*/
+gcry_err_code_t
+_gcry_kdf_scrypt (const unsigned char *passwd, size_t passwdlen,
+                  int algo, int subalgo,
+                  const unsigned char *salt, size_t saltlen,
+                  unsigned long iterations,
+                  size_t dklen, unsigned char *dk);
+
+/*-- blake2.c --*/
+gcry_err_code_t
+blake2b_vl_hash (const void *in, size_t inlen, size_t outputlen, void *output);
+
+#endif /*GCRY_KDF_INTERNAL_H*/
diff --git a/grub-core/lib/libgcrypt-argon2/cipher/kdf.c b/grub-core/lib/libgcrypt-argon2/cipher/kdf.c
new file mode 100644
index 000000000..8f24b99b5
--- /dev/null
+++ b/grub-core/lib/libgcrypt-argon2/cipher/kdf.c
@@ -0,0 +1,732 @@
+/* kdf.c  - Key Derivation Functions
+ * Copyright (C) 1998, 2008, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 g10 Code GmbH
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#include "g10lib.h"
+#define xtrymalloc(a)    gcry_malloc ((a))
+#define xfree(a)         gcry_free ((a))
+#include "cipher.h"
+#include "kdf-internal.h"
+
+extern gcry_md_spec_t _gcry_digest_spec_blake2b_512;
+
+
+#include "bufhelp.h"
+
+typedef struct argon2_context *argon2_ctx_t;
+
+/* Per thread data for Argon2.  */
+struct argon2_thread_data {
+  argon2_ctx_t a;
+  unsigned int pass;
+  unsigned int slice;
+  unsigned int lane;
+};
+
+/* Argon2 context */
+struct argon2_context {
+  int algo;
+  int hash_type;
+
+  unsigned int outlen;
+
+  const unsigned char *password;
+  size_t passwordlen;
+
+  const unsigned char *salt;
+  size_t saltlen;
+
+  const unsigned char *key;
+  size_t keylen;
+
+  const unsigned char *ad;
+  size_t adlen;
+
+  unsigned int m_cost;
+
+  unsigned int passes;
+  unsigned int memory_blocks;
+  unsigned int segment_length;
+  unsigned int lane_length;
+  unsigned int lanes;
+
+  u64 *block;
+  struct argon2_thread_data *thread_data;
+
+  unsigned char out[1];  /* In future, we may use flexible array member.  */
+};
+
+#define ARGON2_VERSION 0x13
+
+#define ARGON2_WORDS_IN_BLOCK (1024/8)
+
+static void
+xor_block (u64 *dst, const u64 *src)
+{
+  int i;
+
+  for (i = 0; i < ARGON2_WORDS_IN_BLOCK; i++)
+    dst[i] ^= src[i];
+}
+
+static void
+beswap64_block (u64 *dst)
+{
+#ifdef WORDS_BIGENDIAN
+  int i;
+
+  /* Swap a block in big-endian 64-bit word into one in
+     little-endian.  */
+  for (i = 0; i < ARGON2_WORDS_IN_BLOCK; i++)
+    dst[i] = _gcry_bswap64 (dst[i]);
+#else
+  /* Nothing to do.  */
+  (void)dst;
+#endif
+}
+
+
+static gpg_err_code_t
+argon2_fill_first_blocks (argon2_ctx_t a)
+{
+  unsigned char h0_01_i[72];
+  unsigned char buf[10][4];
+  gcry_buffer_t iov[8];
+  unsigned int iov_count = 0;
+  int i;
+
+  /* Generate H0.  */
+  buf_put_le32 (buf[0], a->lanes);
+  buf_put_le32 (buf[1], a->outlen);
+  buf_put_le32 (buf[2], a->m_cost);
+  buf_put_le32 (buf[3], a->passes);
+  buf_put_le32 (buf[4], ARGON2_VERSION);
+  buf_put_le32 (buf[5], a->hash_type);
+  buf_put_le32 (buf[6], a->passwordlen);
+  iov[iov_count].data = buf[0];
+  iov[iov_count].len = 4 * 7;
+  iov[iov_count].off = 0;
+  iov_count++;
+  iov[iov_count].data = (void *)a->password;
+  iov[iov_count].len = a->passwordlen;
+  iov[iov_count].off = 0;
+  iov_count++;
+
+  buf_put_le32 (buf[7], a->saltlen);
+  iov[iov_count].data = buf[7];
+  iov[iov_count].len = 4;
+  iov[iov_count].off = 0;
+  iov_count++;
+  iov[iov_count].data = (void *)a->salt;
+  iov[iov_count].len = a->saltlen;
+  iov[iov_count].off = 0;
+  iov_count++;
+
+  buf_put_le32 (buf[8], a->keylen);
+  iov[iov_count].data = buf[8];
+  iov[iov_count].len = 4;
+  iov[iov_count].off = 0;
+  iov_count++;
+  if (a->key)
+    {
+      iov[iov_count].data = (void *)a->key;
+      iov[iov_count].len = a->keylen;
+      iov[iov_count].off = 0;
+      iov_count++;
+    }
+
+  buf_put_le32 (buf[9], a->adlen);
+  iov[iov_count].data = buf[9];
+  iov[iov_count].len = 4;
+  iov[iov_count].off = 0;
+  iov_count++;
+  if (a->ad)
+    {
+      iov[iov_count].data = (void *)a->ad;
+      iov[iov_count].len = a->adlen;
+      iov[iov_count].off = 0;
+      iov_count++;
+    }
+
+  _gcry_digest_spec_blake2b_512.hash_buffers (h0_01_i, 64, iov, iov_count);
+
+  for (i = 0; i < a->lanes; i++)
+    {
+      memset (h0_01_i+64, 0, 4);
+      buf_put_le32 (h0_01_i+64+4, i);
+      blake2b_vl_hash (h0_01_i, 72, 1024,
+                       &a->block[i*a->lane_length*ARGON2_WORDS_IN_BLOCK]);
+      beswap64_block (&a->block[i*a->lane_length*ARGON2_WORDS_IN_BLOCK]);
+
+      buf_put_le32 (h0_01_i+64, 1);
+      blake2b_vl_hash (h0_01_i, 72, 1024,
+                       &a->block[(i*a->lane_length+1)*ARGON2_WORDS_IN_BLOCK]);
+      beswap64_block (&a->block[(i*a->lane_length+1)*ARGON2_WORDS_IN_BLOCK]);
+    }
+  return 0;
+}
+
+static gpg_err_code_t
+argon2_init (argon2_ctx_t a, unsigned int parallelism,
+             unsigned int m_cost, unsigned int t_cost)
+{
+  gpg_err_code_t ec = 0;
+  unsigned int memory_blocks;
+  unsigned int segment_length;
+  void *block;
+  struct argon2_thread_data *thread_data;
+
+  memory_blocks = m_cost;
+  if (memory_blocks < 8 * parallelism)
+    memory_blocks = 8 * parallelism;
+
+  segment_length = memory_blocks / (parallelism * 4);
+  memory_blocks = segment_length * parallelism * 4;
+
+  a->passes = t_cost;
+  a->memory_blocks = memory_blocks;
+  a->segment_length = segment_length;
+  a->lane_length = segment_length * 4;
+  a->lanes = parallelism;
+
+  a->block = NULL;
+  a->thread_data = NULL;
+
+  block = xtrymalloc (1024 * memory_blocks);
+  if (!block)
+    {
+      ec = gpg_err_code_from_errno (grub_errno);
+      return ec;
+    }
+  memset (block, 0, 1024 * memory_blocks);
+
+  thread_data = xtrymalloc (a->lanes * sizeof (struct argon2_thread_data));
+  if (!thread_data)
+    {
+      ec = gpg_err_code_from_errno (grub_errno);
+      xfree (block);
+      return ec;
+    }
+
+  memset (thread_data, 0, a->lanes * sizeof (struct argon2_thread_data));
+
+  a->block = block;
+  a->thread_data = thread_data;
+  return 0;
+}
+
+
+static u64 fBlaMka (u64 x, u64 y)
+{
+  const u64 m = U64_C(0xFFFFFFFF);
+  return x + y + 2 * (x & m) * (y & m);
+}
+
+static u64 rotr64 (u64 w, unsigned int c)
+{
+  return (w >> c) | (w << (64 - c));
+}
+
+#define G(a, b, c, d)                                                          \
+    do {                                                                       \
+        a = fBlaMka(a, b);                                                     \
+        d = rotr64(d ^ a, 32);                                                 \
+        c = fBlaMka(c, d);                                                     \
+        b = rotr64(b ^ c, 24);                                                 \
+        a = fBlaMka(a, b);                                                     \
+        d = rotr64(d ^ a, 16);                                                 \
+        c = fBlaMka(c, d);                                                     \
+        b = rotr64(b ^ c, 63);                                                 \
+    } while ((void)0, 0)
+
+#define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11,   \
+                           v12, v13, v14, v15)                                 \
+    do {                                                                       \
+        G(v0, v4, v8, v12);                                                    \
+        G(v1, v5, v9, v13);                                                    \
+        G(v2, v6, v10, v14);                                                   \
+        G(v3, v7, v11, v15);                                                   \
+        G(v0, v5, v10, v15);                                                   \
+        G(v1, v6, v11, v12);                                                   \
+        G(v2, v7, v8, v13);                                                    \
+        G(v3, v4, v9, v14);                                                    \
+    } while ((void)0, 0)
+
+static void
+fill_block (const u64 *prev_block, const u64 *ref_block, u64 *curr_block,
+            int with_xor)
+{
+  u64 block_r[ARGON2_WORDS_IN_BLOCK];
+  u64 block_tmp[ARGON2_WORDS_IN_BLOCK];
+  int i;
+
+  memcpy (block_r, ref_block, 1024);
+  if (prev_block)
+    xor_block (block_r, prev_block);
+  memcpy (block_tmp, block_r, 1024);
+
+  if (with_xor)
+    xor_block (block_tmp, curr_block);
+
+  for (i = 0; i < 8; ++i)
+    BLAKE2_ROUND_NOMSG
+      (block_r[16 * i],      block_r[16 * i + 1],  block_r[16 * i + 2],
+       block_r[16 * i + 3],  block_r[16 * i + 4],  block_r[16 * i + 5],
+       block_r[16 * i + 6],  block_r[16 * i + 7],  block_r[16 * i + 8],
+       block_r[16 * i + 9],  block_r[16 * i + 10], block_r[16 * i + 11],
+       block_r[16 * i + 12], block_r[16 * i + 13], block_r[16 * i + 14],
+       block_r[16 * i + 15]);
+
+  for (i = 0; i < 8; i++)
+    BLAKE2_ROUND_NOMSG
+      (block_r[2 * i],      block_r[2 * i + 1],  block_r[2 * i + 16],
+       block_r[2 * i + 17], block_r[2 * i + 32], block_r[2 * i + 33],
+       block_r[2 * i + 48], block_r[2 * i + 49], block_r[2 * i + 64],
+       block_r[2 * i + 65], block_r[2 * i + 80], block_r[2 * i + 81],
+       block_r[2 * i + 96], block_r[2 * i + 97], block_r[2 * i + 112],
+       block_r[2 * i + 113]);
+
+  memcpy (curr_block, block_tmp, 1024);
+  xor_block (curr_block, block_r);
+}
+
+static void
+pseudo_random_generate (u64 *random_block, u64 *input_block)
+{
+  input_block[6]++;
+  fill_block (NULL, input_block, random_block, 0);
+  fill_block (NULL, random_block, random_block, 0);
+}
+
+static u32
+index_alpha (argon2_ctx_t a, const struct argon2_thread_data *t,
+             int segment_index, u32 random, int same_lane)
+{
+  u32 reference_area_size;
+  u64 relative_position;
+  u32 start_position;
+
+  if (t->pass == 0)
+    {
+      if (t->slice == 0)
+        reference_area_size = segment_index - 1;
+      else
+        {
+          if (same_lane)
+            reference_area_size = t->slice * a->segment_length
+              + segment_index - 1;
+          else
+            reference_area_size = t->slice * a->segment_length +
+              ((segment_index == 0) ? -1 : 0);
+        }
+    }
+  else
+    {
+      if (same_lane)
+        reference_area_size = a->lane_length
+          - a->segment_length + segment_index - 1;
+      else
+        reference_area_size = a->lane_length
+          - a->segment_length + ((segment_index == 0) ? -1 : 0);
+    }
+
+  relative_position = (random * (u64)random) >> 32;
+  relative_position = reference_area_size - 1 -
+    ((reference_area_size * relative_position) >> 32);
+
+  if (t->pass == 0)
+    start_position = 0;
+  else
+    start_position = (t->slice == 4 - 1)
+      ? 0
+      : (t->slice + 1) * a->segment_length;
+
+  return (start_position + relative_position) % a->lane_length;
+}
+
+static void
+argon2_compute_segment (void *priv)
+{
+  const struct argon2_thread_data *t = (const struct argon2_thread_data *)priv;
+  argon2_ctx_t a = t->a;
+  int i;
+  int prev_offset, curr_offset;
+  u32 ref_index, ref_lane;
+  u64 input_block[1024/sizeof (u64)];
+  u64 address_block[1024/sizeof (u64)];
+  u64 *random_block = NULL;
+
+  if (a->hash_type == GCRY_KDF_ARGON2I
+      || (a->hash_type == GCRY_KDF_ARGON2ID && t->pass == 0 && t->slice < 2))
+    {
+      memset (input_block, 0, 1024);
+      input_block[0] = t->pass;
+      input_block[1] = t->lane;
+      input_block[2] = t->slice;
+      input_block[3] = a->memory_blocks;
+      input_block[4] = a->passes;
+      input_block[5] = a->hash_type;
+      random_block = address_block;
+    }
+
+  if (t->pass == 0 && t->slice == 0)
+    {
+      if (random_block)
+        pseudo_random_generate (random_block, input_block);
+      i = 2;
+    }
+  else
+    i = 0;
+
+  curr_offset = t->lane * a->lane_length + t->slice * a->segment_length + i;
+  if ((curr_offset % a->lane_length))
+    prev_offset = curr_offset - 1;
+  else
+    prev_offset = curr_offset + a->lane_length - 1;
+
+  for (; i < a->segment_length; i++, curr_offset++, prev_offset++)
+    {
+      u64 *ref_block, *curr_block;
+      u64 rand64;
+
+      if ((curr_offset % a->lane_length) == 1)
+        prev_offset = curr_offset - 1;
+
+      if (random_block)
+        {
+          if ((i % (1024/sizeof (u64))) == 0)
+            pseudo_random_generate (random_block, input_block);
+
+          rand64 = random_block[(i% (1024/sizeof (u64)))];
+        }
+      else
+        rand64 = a->block[prev_offset*ARGON2_WORDS_IN_BLOCK];
+
+      if (t->pass == 0 && t->slice == 0)
+        ref_lane = t->lane;
+      else
+        ref_lane = (rand64 >> 32) % a->lanes;
+
+      ref_index = index_alpha (a, t, i, (rand64 & 0xffffffff),
+                               ref_lane == t->lane);
+      ref_block =
+        &a->block[(a->lane_length * ref_lane + ref_index)* ARGON2_WORDS_IN_BLOCK];
+
+      curr_block = &a->block[curr_offset * ARGON2_WORDS_IN_BLOCK];
+      fill_block (&a->block[prev_offset * ARGON2_WORDS_IN_BLOCK], ref_block,
+                  curr_block, t->pass != 0);
+    }
+}
+
+
+static gpg_err_code_t
+argon2_compute (argon2_ctx_t a, const struct gcry_kdf_thread_ops *ops)
+{
+  gpg_err_code_t ec;
+  unsigned int r;
+  unsigned int s;
+  unsigned int l;
+  int ret;
+
+  ec = argon2_fill_first_blocks (a);
+  if (ec)
+    return ec;
+
+  for (r = 0; r < a->passes; r++)
+    for (s = 0; s < 4; s++)
+      {
+        for (l = 0; l < a->lanes; l++)
+          {
+            struct argon2_thread_data *thread_data;
+
+            /* launch a thread.  */
+            thread_data = &a->thread_data[l];
+            thread_data->a = a;
+            thread_data->pass = r;
+            thread_data->slice = s;
+            thread_data->lane = l;
+
+            if (ops)
+	      {
+		ret = ops->dispatch_job (ops->jobs_context,
+					 argon2_compute_segment, thread_data);
+		if (ret < 0)
+		  return GPG_ERR_CANCELED;
+	      }
+            else
+              argon2_compute_segment (thread_data);
+          }
+
+        if (ops)
+	  {
+	    ret = ops->wait_all_jobs (ops->jobs_context);
+	    if (ret < 0)
+	      return GPG_ERR_CANCELED;
+	  }
+      }
+
+  return 0;
+}
+
+
+static gpg_err_code_t
+argon2_final (argon2_ctx_t a, size_t resultlen, void *result)
+{
+  int i;
+
+  if (resultlen != a->outlen)
+    return GPG_ERR_INV_VALUE;
+
+  memset (a->block, 0, 1024);
+  for (i = 0; i < a->lanes; i++)
+    {
+      u64 *last_block;
+
+      last_block = &a->block[(a->lane_length * i + (a->lane_length - 1))
+                             * ARGON2_WORDS_IN_BLOCK];
+      xor_block (a->block, last_block);
+    }
+
+  beswap64_block (a->block);
+  blake2b_vl_hash (a->block, 1024, a->outlen, result);
+  return 0;
+}
+
+static void
+argon2_close (argon2_ctx_t a)
+{
+  size_t n;
+
+  n = offsetof (struct argon2_context, out) + a->outlen;
+
+  if (a->block)
+    {
+      wipememory (a->block, 1024 * a->memory_blocks);
+      xfree (a->block);
+    }
+
+  if (a->thread_data)
+    xfree (a->thread_data);
+
+  wipememory (a, n);
+  xfree (a);
+}
+
+static gpg_err_code_t
+argon2_open (gcry_kdf_hd_t *hd, int subalgo,
+             const unsigned long *param, unsigned int paramlen,
+             const void *password, size_t passwordlen,
+             const void *salt, size_t saltlen,
+             const void *key, size_t keylen,
+             const void *ad, size_t adlen)
+{
+  int hash_type;
+  unsigned int taglen;
+  unsigned int t_cost;
+  unsigned int m_cost;
+  unsigned int parallelism = 1;
+  argon2_ctx_t a;
+  gpg_err_code_t ec;
+  size_t n;
+
+  if (subalgo != GCRY_KDF_ARGON2D
+      && subalgo != GCRY_KDF_ARGON2I
+      && subalgo != GCRY_KDF_ARGON2ID)
+    return GPG_ERR_INV_VALUE;
+  else
+    hash_type = subalgo;
+
+  /* param : [ tag_length, t_cost, m_cost, parallelism ] */
+  if (paramlen < 3 || paramlen > 4)
+    return GPG_ERR_INV_VALUE;
+  else
+    {
+      taglen = (unsigned int)param[0];
+      t_cost = (unsigned int)param[1];
+      m_cost = (unsigned int)param[2];
+      if (paramlen >= 4)
+        parallelism = (unsigned int)param[3];
+    }
+
+  if (parallelism == 0)
+    return GPG_ERR_INV_VALUE;
+
+  n = offsetof (struct argon2_context, out) + taglen;
+  a = xtrymalloc (n);
+  if (!a)
+    return gpg_err_code_from_errno (grub_errno);
+
+  a->algo = GCRY_KDF_ARGON2;
+  a->hash_type = hash_type;
+
+  a->outlen = taglen;
+
+  a->password = password;
+  a->passwordlen = passwordlen;
+  a->salt = salt;
+  a->saltlen = saltlen;
+  a->key = key;
+  a->keylen = keylen;
+  a->ad = ad;
+  a->adlen = adlen;
+
+  a->m_cost = m_cost;
+
+  a->block = NULL;
+  a->thread_data = NULL;
+
+  ec = argon2_init (a, parallelism, m_cost, t_cost);
+  if (ec)
+    {
+      xfree (a);
+      return ec;
+    }
+
+  *hd = (void *)a;
+  return 0;
+}
+
+
+static gpg_err_code_t
+balloon_open (gcry_kdf_hd_t *hd, int subalgo,
+              const unsigned long *param, unsigned int paramlen,
+              const void *passphrase, size_t passphraselen,
+              const void *salt, size_t saltlen)
+{
+  /*
+   * It should have space_cost and time_cost.
+   * Optionally, for parallelised version, it has parallelism.
+   */
+  if (paramlen != 2 && paramlen != 3)
+    return GPG_ERR_INV_VALUE;
+
+  (void)param;
+  (void)subalgo;
+  (void)passphrase;
+  (void)passphraselen;
+  (void)salt;
+  (void)saltlen;
+  *hd = NULL;
+  return GPG_ERR_NOT_IMPLEMENTED;
+}
+
+
+struct gcry_kdf_handle {
+  int algo;
+  /* And algo specific parts come.  */
+};
+
+gpg_err_code_t
+gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo,
+                const unsigned long *param, unsigned int paramlen,
+                const void *passphrase, size_t passphraselen,
+                const void *salt, size_t saltlen,
+                const void *key, size_t keylen,
+                const void *ad, size_t adlen)
+{
+  gpg_err_code_t ec;
+
+  switch (algo)
+    {
+    case GCRY_KDF_ARGON2:
+      if (!passphraselen || !saltlen)
+        ec = GPG_ERR_INV_VALUE;
+      else
+        ec = argon2_open (hd, subalgo, param, paramlen,
+                          passphrase, passphraselen, salt, saltlen,
+                          key, keylen, ad, adlen);
+      break;
+
+    case GCRY_KDF_BALLOON:
+      if (!passphraselen || !saltlen)
+        ec = GPG_ERR_INV_VALUE;
+      else
+        {
+          (void)key;
+          (void)keylen;
+          (void)ad;
+          (void)adlen;
+          ec = balloon_open (hd, subalgo, param, paramlen,
+                             passphrase, passphraselen, salt, saltlen);
+        }
+      break;
+
+    default:
+      ec = GPG_ERR_UNKNOWN_ALGORITHM;
+      break;
+    }
+
+  return ec;
+}
+
+gpg_err_code_t
+gcry_kdf_compute (gcry_kdf_hd_t h, const struct gcry_kdf_thread_ops *ops)
+{
+  gpg_err_code_t ec;
+
+  switch (h->algo)
+    {
+    case GCRY_KDF_ARGON2:
+      ec = argon2_compute ((argon2_ctx_t)(void *)h, ops);
+      break;
+
+    default:
+      ec = GPG_ERR_UNKNOWN_ALGORITHM;
+      break;
+    }
+
+  return ec;
+}
+
+
+gpg_err_code_t
+gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result)
+{
+  gpg_err_code_t ec;
+
+  switch (h->algo)
+    {
+    case GCRY_KDF_ARGON2:
+      ec = argon2_final ((argon2_ctx_t)(void *)h, resultlen, result);
+      break;
+
+    default:
+      ec = GPG_ERR_UNKNOWN_ALGORITHM;
+      break;
+    }
+
+  return ec;
+}
+
+void
+gcry_kdf_close (gcry_kdf_hd_t h)
+{
+  switch (h->algo)
+    {
+    case GCRY_KDF_ARGON2:
+      argon2_close ((argon2_ctx_t)(void *)h);
+      break;
+
+    default:
+      break;
+    }
+}
diff --git a/grub-core/lib/libgcrypt-argon2/src/gcrypt.h b/grub-core/lib/libgcrypt-argon2/src/gcrypt.h
index cd1bc03a4..270eab3d2 100644
--- a/grub-core/lib/libgcrypt-argon2/src/gcrypt.h
+++ b/grub-core/lib/libgcrypt-argon2/src/gcrypt.h
@@ -1511,7 +1511,17 @@ enum gcry_kdf_algos
     GCRY_KDF_SALTED_S2K = 17,
     GCRY_KDF_ITERSALTED_S2K = 19,
     GCRY_KDF_PBKDF1 = 33,
-    GCRY_KDF_PBKDF2 = 34
+    GCRY_KDF_PBKDF2 = 34,
+    GCRY_KDF_SCRYPT = 48,
+    GCRY_KDF_ARGON2   = 64,
+    GCRY_KDF_BALLOON  = 65
+  };
+
+enum gcry_kdf_subalgo_argon2
+  {
+    GCRY_KDF_ARGON2D  = 0,
+    GCRY_KDF_ARGON2I  = 1,
+    GCRY_KDF_ARGON2ID = 2
   };
 
 /* Derive a key from a passphrase.  */
@@ -1521,7 +1531,33 @@ gpg_error_t gcry_kdf_derive (const void *passphrase, size_t passphraselen,
                              unsigned long iterations,
                              size_t keysize, void *keybuffer);
 
+/* Another API to derive a key from a passphrase.  */
+typedef struct gcry_kdf_handle *gcry_kdf_hd_t;
 
+typedef void (*gcry_kdf_job_fn_t) (void *priv);
+typedef int (*gcry_kdf_dispatch_job_fn_t) (void *jobs_context,
+                                           gcry_kdf_job_fn_t job_fn,
+                                           void *job_priv);
+typedef int (*gcry_kdf_wait_all_jobs_fn_t) (void *jobs_context);
+
+/* Exposed structure for KDF computation to decouple thread functionality.  */
+typedef struct gcry_kdf_thread_ops
+{
+  void *jobs_context;
+  gcry_kdf_dispatch_job_fn_t dispatch_job;
+  gcry_kdf_wait_all_jobs_fn_t wait_all_jobs;
+} gcry_kdf_thread_ops_t;
+
+gcry_error_t gcry_kdf_open (gcry_kdf_hd_t *hd, int algo, int subalgo,
+                            const unsigned long *param, unsigned int paramlen,
+                            const void *passphrase, size_t passphraselen,
+                            const void *salt, size_t saltlen,
+                            const void *key, size_t keylen,
+                            const void *ad, size_t adlen);
+gcry_error_t gcry_kdf_compute (gcry_kdf_hd_t h,
+                               const gcry_kdf_thread_ops_t *ops);
+gcry_error_t gcry_kdf_final (gcry_kdf_hd_t h, size_t resultlen, void *result);
+void gcry_kdf_close (gcry_kdf_hd_t h);
 
 
 /************************************
diff --git a/include/grub/crypto110.h b/include/grub/crypto110.h
index 2a9038a70..ffbb6592a 100644
--- a/include/grub/crypto110.h
+++ b/include/grub/crypto110.h
@@ -65,6 +65,8 @@ typedef enum
     GPG_ERR_WRONG_PUBKEY_ALGO,
     GPG_ERR_OUT_OF_MEMORY,
     GPG_ERR_TOO_LARGE,
+    GPG_ERR_CANCELED,
+    GPG_ERR_UNKNOWN_ALGORITHM,
     GPG_ERR_ENOMEM
   } gpg_err_code_t;
 typedef gpg_err_code_t gpg_error_t;
diff --git a/include/grub/gcrypt/gpg-error.h b/include/grub/gcrypt/gpg-error.h
index 51b8e99a0..f1192c003 100644
--- a/include/grub/gcrypt/gpg-error.h
+++ b/include/grub/gcrypt/gpg-error.h
@@ -34,4 +34,15 @@ gpg_err_source (gpg_error_t err __attribute__ ((unused)))
 gcry_err_code_t
 gpg_error_from_syserror (void);
 
+/* Retrieve the error code for the system error ERR.  This returns
+ * GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped (report
+ * this). */
+static inline gpg_err_code_t
+gpg_err_code_from_errno (int err)
+{
+  (void)err;
+  return GPG_ERR_OUT_OF_MEMORY;
+}
+
+
 #endif
-- 
2.40.1

openSUSE Build Service is sponsored by