grub-lakitu: UPSTREAM: VERIFIERS: verify framework.

(cherry picked from commit 09bd17faaabcfc61ec0509ede7e0ad2c6c0e0ba5)
(from phcoder/verifiers branch of git://git.savannah.gnu.org/grub.git)

BUG=b:69569602
TEST=TBD

Change-Id: Ie0e15d42f416f1e70feb6f43ecd5a574cbeb1751
Reviewed-on: https://chromium-review.googlesource.com/945878
Reviewed-by: Edward Jee <edjee@google.com>
Commit-Queue: Edward Jee <edjee@google.com>
Tested-by: Edward Jee <edjee@google.com>
Trybot-Ready: Edward Jee <edjee@google.com>
diff --git a/grub-lakitu/grub-core/Makefile.core.def b/grub-lakitu/grub-core/Makefile.core.def
index 1d86bd2..16c4d0e 100644
--- a/grub-lakitu/grub-core/Makefile.core.def
+++ b/grub-lakitu/grub-core/Makefile.core.def
@@ -900,6 +900,11 @@
 };
 
 module = {
+  name = verify_helper;
+  common = commands/verify_helper.c;
+};
+
+module = {
   name = hdparm;
   common = commands/hdparm.c;
   common = lib/hexdump.c;
diff --git a/grub-lakitu/grub-core/commands/verify.c b/grub-lakitu/grub-core/commands/verify.c
index 3616318..d5d7c0f 100644
--- a/grub-lakitu/grub-core/commands/verify.c
+++ b/grub-lakitu/grub-core/commands/verify.c
@@ -30,16 +30,10 @@
 #include <grub/env.h>
 #include <grub/kernel.h>
 #include <grub/extcmd.h>
+#include <grub/verify.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
-struct grub_verified
-{
-  grub_file_t file;
-  void *buf;
-};
-typedef struct grub_verified *grub_verified_t;
-
 enum
   {
     OPTION_SKIP_SIG = 0
@@ -445,22 +439,26 @@
   return ret;
 }
 
+struct grub_pubkey_context
+{
+  grub_file_t sig;
+  struct signature_v4_header v4;
+  grub_uint8_t v;
+  const gcry_md_spec_t *hash;
+  void *hash_context;
+};
+
 static grub_err_t
-grub_verify_signature_real (char *buf, grub_size_t size,
-			    grub_file_t f, grub_file_t sig,
-			    struct grub_public_key *pkey)
+grub_verify_signature_init (struct grub_pubkey_context *ctxt, grub_file_t sig)
 {
   grub_size_t len;
-  grub_uint8_t v;
   grub_uint8_t h;
   grub_uint8_t t;
-  grub_uint8_t pk;
-  const gcry_md_spec_t *hash;
-  struct signature_v4_header v4;
   grub_err_t err;
-  grub_size_t i;
-  gcry_mpi_t mpis[10];
   grub_uint8_t type = 0;
+  grub_uint8_t pk;
+
+  grub_memset (ctxt, 0, sizeof (*ctxt));
 
   err = read_packet_header (sig, &type, &len);
   if (err)
@@ -469,18 +467,18 @@
   if (type != 0x2)
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
 
-  if (grub_file_read (sig, &v, sizeof (v)) != sizeof (v))
+  if (grub_file_read (sig, &ctxt->v, sizeof (ctxt->v)) != sizeof (ctxt->v))
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
 
-  if (v != 4)
+  if (ctxt->v != 4)
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
 
-  if (grub_file_read (sig, &v4, sizeof (v4)) != sizeof (v4))
+  if (grub_file_read (sig, &ctxt->v4, sizeof (ctxt->v4)) != sizeof (ctxt->v4))
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
 
-  h = v4.hash;
-  t = v4.type;
-  pk = v4.pkeyalgo;
+  h = ctxt->v4.hash;
+  t = ctxt->v4.type;
+  pk = ctxt->v4.pkeyalgo;
   
   if (t != 0)
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
@@ -491,183 +489,232 @@
   if (pk >= ARRAY_SIZE (pkalgos) || pkalgos[pk].name == NULL)
     return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
 
-  hash = grub_crypto_lookup_md_by_name (hashes[h]);
-  if (!hash)
+  ctxt->hash = grub_crypto_lookup_md_by_name (hashes[h]);
+  if (!ctxt->hash)
     return grub_error (GRUB_ERR_BAD_SIGNATURE, "hash `%s' not loaded", hashes[h]);
 
   grub_dprintf ("crypt", "alive\n");
 
-  {
-    void *context = NULL;
-    unsigned char *hval;
-    grub_ssize_t rem = grub_be_to_cpu16 (v4.hashed_sub);
-    grub_uint32_t headlen = grub_cpu_to_be32 (rem + 6);
-    grub_uint8_t s;
-    grub_uint16_t unhashed_sub;
-    grub_ssize_t r;
-    grub_uint8_t hash_start[2];
-    gcry_mpi_t hmpi;
-    grub_uint64_t keyid = 0;
-    struct grub_public_subkey *sk;
-    grub_uint8_t *readbuf = NULL;
+  ctxt->sig = sig;
 
-    context = grub_zalloc (hash->contextsize);
-    readbuf = grub_zalloc (READBUF_SIZE);
-    if (!context || !readbuf)
-      goto fail;
+  ctxt->hash_context = grub_zalloc (ctxt->hash->contextsize);
+  if (!ctxt->hash_context)
+    return grub_errno;
 
-    hash->init (context);
-    if (buf)
-      hash->write (context, buf, size);
-    else 
-      while (1)
-	{
-	  r = grub_file_read (f, readbuf, READBUF_SIZE);
-	  if (r < 0)
-	    goto fail;
-	  if (r == 0)
-	    break;
-	  hash->write (context, readbuf, r);
-	}
+  ctxt->hash->init (ctxt->hash_context);
 
-    hash->write (context, &v, sizeof (v));
-    hash->write (context, &v4, sizeof (v4));
-    while (rem)
-      {
-	r = grub_file_read (sig, readbuf,
-			    rem < READBUF_SIZE ? rem : READBUF_SIZE);
-	if (r < 0)
-	  goto fail;
-	if (r == 0)
-	  break;
-	hash->write (context, readbuf, r);
-	rem -= r;
-      }
-    hash->write (context, &v, sizeof (v));
-    s = 0xff;
-    hash->write (context, &s, sizeof (s));
-    hash->write (context, &headlen, sizeof (headlen));
-    r = grub_file_read (sig, &unhashed_sub, sizeof (unhashed_sub));
-    if (r != sizeof (unhashed_sub))
-      goto fail;
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_pubkey_write (void *ctxt_, void *buf, grub_size_t size)
+{
+  struct grub_pubkey_context *ctxt = ctxt_;
+  ctxt->hash->write (ctxt->hash_context, buf, size);
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_verify_signature_real (struct grub_pubkey_context *ctxt,
+			    struct grub_public_key *pkey)
+{
+  gcry_mpi_t mpis[10];
+  grub_uint8_t pk = ctxt->v4.pkeyalgo;
+  grub_size_t i;
+  grub_uint8_t *readbuf = NULL;
+  unsigned char *hval;
+  grub_ssize_t rem = grub_be_to_cpu16 (ctxt->v4.hashed_sub);
+  grub_uint32_t headlen = grub_cpu_to_be32 (rem + 6);
+  grub_uint8_t s;
+  grub_uint16_t unhashed_sub;
+  grub_ssize_t r;
+  grub_uint8_t hash_start[2];
+  gcry_mpi_t hmpi;
+  grub_uint64_t keyid = 0;
+  struct grub_public_subkey *sk;
+
+  readbuf = grub_malloc (READBUF_SIZE);
+  if (!readbuf)
+    goto fail;
+
+  ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v));
+  ctxt->hash->write (ctxt->hash_context, &ctxt->v4, sizeof (ctxt->v4));
+  while (rem)
     {
-      grub_uint8_t *ptr;
-      grub_uint32_t l;
-      rem = grub_be_to_cpu16 (unhashed_sub);
-      if (rem > READBUF_SIZE)
+      r = grub_file_read (ctxt->sig, readbuf,
+			  rem < READBUF_SIZE ? rem : READBUF_SIZE);
+      if (r < 0)
 	goto fail;
-      r = grub_file_read (sig, readbuf, rem);
-      if (r != rem)
+      if (r == 0)
+	break;
+      ctxt->hash->write (ctxt->hash_context, readbuf, r);
+      rem -= r;
+    }
+  ctxt->hash->write (ctxt->hash_context, &ctxt->v, sizeof (ctxt->v));
+  s = 0xff;
+  ctxt->hash->write (ctxt->hash_context, &s, sizeof (s));
+  ctxt->hash->write (ctxt->hash_context, &headlen, sizeof (headlen));
+  r = grub_file_read (ctxt->sig, &unhashed_sub, sizeof (unhashed_sub));
+  if (r != sizeof (unhashed_sub))
+    goto fail;
+  {
+    grub_uint8_t *ptr;
+    grub_uint32_t l;
+    rem = grub_be_to_cpu16 (unhashed_sub);
+    if (rem > READBUF_SIZE)
+      goto fail;
+    r = grub_file_read (ctxt->sig, readbuf, rem);
+    if (r != rem)
+      goto fail;
+    for (ptr = readbuf; ptr < readbuf + rem; ptr += l)
+      {
+	if (*ptr < 192)
+	  l = *ptr++;
+	else if (*ptr < 255)
+	  {
+	    if (ptr + 1 >= readbuf + rem)
+	      break;
+	    l = (((ptr[0] & ~192) << GRUB_CHAR_BIT) | ptr[1]) + 192;
+	    ptr += 2;
+	  }
+	else
+	  {
+	    if (ptr + 5 >= readbuf + rem)
+	      break;
+	    l = grub_be_to_cpu32 (grub_get_unaligned32 (ptr + 1));
+	    ptr += 5;
+	  }
+	if (*ptr == 0x10 && l >= 8)
+	  keyid = grub_get_unaligned64 (ptr + 1);
+      }
+  }
+
+  ctxt->hash->final (ctxt->hash_context);
+
+  grub_dprintf ("crypt", "alive\n");
+
+  hval = ctxt->hash->read (ctxt->hash_context);
+
+  if (grub_file_read (ctxt->sig, hash_start, sizeof (hash_start)) != sizeof (hash_start))
+    goto fail;
+  if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0)
+    goto fail;
+
+  grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (ctxt->sig));
+
+  for (i = 0; i < pkalgos[pk].nmpisig; i++)
+    {
+      grub_uint16_t l;
+      grub_size_t lb;
+      grub_dprintf ("crypt", "alive\n");
+      if (grub_file_read (ctxt->sig, &l, sizeof (l)) != sizeof (l))
 	goto fail;
-      for (ptr = readbuf; ptr < readbuf + rem; ptr += l)
-	{
-	  if (*ptr < 192)
-	    l = *ptr++;
-	  else if (*ptr < 255)
-	    {
-	      if (ptr + 1 >= readbuf + rem)
-		break;
-	      l = (((ptr[0] & ~192) << GRUB_CHAR_BIT) | ptr[1]) + 192;
-	      ptr += 2;
-	    }
-	  else
-	    {
-	      if (ptr + 5 >= readbuf + rem)
-		break;
-	      l = grub_be_to_cpu32 (grub_get_unaligned32 (ptr + 1));
-	      ptr += 5;
-	    }
-	  if (*ptr == 0x10 && l >= 8)
-	    keyid = grub_get_unaligned64 (ptr + 1);
-	}
+      grub_dprintf ("crypt", "alive\n");
+      lb = (grub_be_to_cpu16 (l) + 7) / 8;
+      grub_dprintf ("crypt", "l = 0x%04x\n", grub_be_to_cpu16 (l));
+      if (lb > READBUF_SIZE - sizeof (grub_uint16_t))
+	goto fail;
+      grub_dprintf ("crypt", "alive\n");
+      if (grub_file_read (ctxt->sig, readbuf + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb)
+	goto fail;
+      grub_dprintf ("crypt", "alive\n");
+      grub_memcpy (readbuf, &l, sizeof (l));
+      grub_dprintf ("crypt", "alive\n");
+
+      if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP,
+			 readbuf, lb + sizeof (grub_uint16_t), 0))
+	goto fail;
+      grub_dprintf ("crypt", "alive\n");
     }
 
-    hash->final (context);
-
-    grub_dprintf ("crypt", "alive\n");
-
-    hval = hash->read (context);
-
-    if (grub_file_read (sig, hash_start, sizeof (hash_start)) != sizeof (hash_start))
+  if (pkey)
+    sk = grub_crypto_pk_locate_subkey (keyid, pkey);
+  else
+    sk = grub_crypto_pk_locate_subkey_in_trustdb (keyid);
+  if (!sk)
+    {
+      /* TRANSLATORS: %08x is 32-bit key id.  */
+      grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"),
+		  keyid);
       goto fail;
-    if (grub_memcmp (hval, hash_start, sizeof (hash_start)) != 0)
+    }
+
+  if (pkalgos[pk].pad (&hmpi, hval, ctxt->hash, sk))
+    goto fail;
+  if (!*pkalgos[pk].algo)
+    {
+      grub_dl_load (pkalgos[pk].module);
+      grub_errno = GRUB_ERR_NONE;
+    }
+
+  if (!*pkalgos[pk].algo)
+    {
+      grub_error (GRUB_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"),
+		  pkalgos[pk].module);
       goto fail;
+    }
+  if ((*pkalgos[pk].algo)->verify (0, hmpi, mpis, sk->mpis, 0, 0))
+    goto fail;
 
-    grub_dprintf ("crypt", "@ %x\n", (int)grub_file_tell (sig));
+  grub_free (readbuf);
 
-    for (i = 0; i < pkalgos[pk].nmpisig; i++)
-      {
-	grub_uint16_t l;
-	grub_size_t lb;
-	grub_dprintf ("crypt", "alive\n");
-	if (grub_file_read (sig, &l, sizeof (l)) != sizeof (l))
-	  goto fail;
-	grub_dprintf ("crypt", "alive\n");
-	lb = (grub_be_to_cpu16 (l) + 7) / 8;
-	grub_dprintf ("crypt", "l = 0x%04x\n", grub_be_to_cpu16 (l));
-	if (lb > READBUF_SIZE - sizeof (grub_uint16_t))
-	  goto fail;
-	grub_dprintf ("crypt", "alive\n");
-	if (grub_file_read (sig, readbuf + sizeof (grub_uint16_t), lb) != (grub_ssize_t) lb)
-	  goto fail;
-	grub_dprintf ("crypt", "alive\n");
-	grub_memcpy (readbuf, &l, sizeof (l));
-	grub_dprintf ("crypt", "alive\n");
+  return GRUB_ERR_NONE;
 
-	if (gcry_mpi_scan (&mpis[i], GCRYMPI_FMT_PGP,
-			   readbuf, lb + sizeof (grub_uint16_t), 0))
-	  goto fail;
-	grub_dprintf ("crypt", "alive\n");
-      }
+ fail:
+  grub_free (readbuf);
+  if (!grub_errno)
+    return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
+  return grub_errno;
+}
 
-    if (pkey)
-      sk = grub_crypto_pk_locate_subkey (keyid, pkey);
-    else
-      sk = grub_crypto_pk_locate_subkey_in_trustdb (keyid);
-    if (!sk)
-      {
-	/* TRANSLATORS: %08x is 32-bit key id.  */
-	grub_error (GRUB_ERR_BAD_SIGNATURE, N_("public key %08x not found"),
-		    keyid);
-	goto fail;
-      }
+static void
+grub_pubkey_close_real (struct grub_pubkey_context *ctxt)
+{
+  if (ctxt->sig)
+    grub_file_close (ctxt->sig);
+  if (ctxt->hash_context)
+    grub_free (ctxt->hash_context);
+}
 
-    if (pkalgos[pk].pad (&hmpi, hval, hash, sk))
-      goto fail;
-    if (!*pkalgos[pk].algo)
-      {
-	grub_dl_load (pkalgos[pk].module);
-	grub_errno = GRUB_ERR_NONE;
-      }
-
-    if (!*pkalgos[pk].algo)
-      {
-	grub_error (GRUB_ERR_BAD_SIGNATURE, N_("module `%s' isn't loaded"),
-		    pkalgos[pk].module);
-	goto fail;
-      }
-    if ((*pkalgos[pk].algo)->verify (0, hmpi, mpis, sk->mpis, 0, 0))
-      goto fail;
-
-    grub_free (context);
-    grub_free (readbuf);
-
-    return GRUB_ERR_NONE;
-
-  fail:
-    grub_free (context);
-    grub_free (readbuf);
-    if (!grub_errno)
-      return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad signature"));
-    return grub_errno;
-  }
+static void
+grub_pubkey_close (void *ctxt)
+{
+  grub_pubkey_close_real (ctxt);
+  grub_free (ctxt);
 }
 
 grub_err_t
 grub_verify_signature (grub_file_t f, grub_file_t sig,
 		       struct grub_public_key *pkey)
 {
-  return grub_verify_signature_real (0, 0, f, sig, pkey);
+  grub_err_t err;
+  struct grub_pubkey_context ctxt;
+  grub_uint8_t *readbuf = NULL;
+  err = grub_verify_signature_init (&ctxt, sig);
+  if (err)
+    return err;
+
+  readbuf = grub_zalloc (READBUF_SIZE);
+  if (!readbuf)
+    goto fail;
+
+  while (1)
+    {
+      grub_ssize_t r;
+      r = grub_file_read (f, readbuf, READBUF_SIZE);
+      if (r < 0)
+	goto fail;
+      if (r == 0)
+	break;
+      err = grub_pubkey_write (&ctxt, readbuf, r);
+      if (err)
+	return err;
+    }
+
+  grub_verify_signature_real (&ctxt, pkey);
+ fail:
+  grub_pubkey_close_real (&ctxt);
+  return grub_errno;
 }
 
 static grub_err_t
@@ -819,134 +866,52 @@
 
 static int sec = 0;
 
-static void
-verified_free (grub_verified_t verified)
-{
-  if (verified)
-    {
-      grub_free (verified->buf);
-      grub_free (verified);
-    }
-}
-
-static grub_ssize_t
-verified_read (struct grub_file *file, char *buf, grub_size_t len)
-{
-  grub_verified_t verified = file->data;
-
-  grub_memcpy (buf, (char *) verified->buf + file->offset, len);
-  return len;
-}
-
 static grub_err_t
-verified_close (struct grub_file *file)
-{
-  grub_verified_t verified = file->data;
-
-  grub_file_close (verified->file);
-  verified_free (verified);
-  file->data = 0;
-
-  /* device and name are freed by parent */
-  file->device = 0;
-  file->name = 0;
-
-  return grub_errno;
-}
-
-struct grub_fs verified_fs =
-{
-  .name = "verified_read",
-  .read = verified_read,
-  .close = verified_close
-};
-
-static grub_file_t
-grub_pubkey_open (grub_file_t io, enum grub_file_type type)
+grub_pubkey_init (grub_file_t io, enum grub_file_type type __attribute__ ((unused)),
+		  void **context, enum grub_verify_flags *flags)
 {
   grub_file_t sig;
   char *fsuf, *ptr;
   grub_err_t err;
-  grub_file_t ret;
-  grub_verified_t verified;
-
-  if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_SIGNATURE
-      || (type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_VERIFY_SIGNATURE
-      || (type & GRUB_FILE_TYPE_SKIP_SIGNATURE))
-    return io;
 
   if (!sec)
-    return io;
-  if (io->device->disk && 
-      (io->device->disk->dev->id == GRUB_DISK_DEVICE_MEMDISK_ID
-       || io->device->disk->dev->id == GRUB_DISK_DEVICE_PROCFS_ID))
-    return io;
+    {
+      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+      return GRUB_ERR_NONE;
+    }
+
   fsuf = grub_malloc (grub_strlen (io->name) + sizeof (".sig"));
   if (!fsuf)
-    return NULL;
+    return grub_errno;
   ptr = grub_stpcpy (fsuf, io->name);
   grub_memcpy (ptr, ".sig", sizeof (".sig"));
 
   sig = grub_file_open (fsuf, GRUB_FILE_TYPE_SIGNATURE);
   grub_free (fsuf);
   if (!sig)
-    return NULL;
+    return grub_errno;
 
-  ret = grub_malloc (sizeof (*ret));
-  if (!ret)
-    {
-      grub_free (fsuf);
-      return NULL;
-    }
-  *ret = *io;
 
-  ret->fs = &verified_fs;
-  ret->not_easily_seekable = 0;
-  if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1))
-    {
-      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-		  "big file signature isn't implemented yet");
-      grub_file_close (sig);
-      grub_free (ret);
-      return NULL;
-    }
-  verified = grub_malloc (sizeof (*verified));
-  if (!verified)
+  struct grub_pubkey_context *ctxt = grub_malloc (sizeof (*ctxt));
+  if (!ctxt)
     {
       grub_file_close (sig);
-      grub_free (ret);
-      return NULL;
+      return grub_errno;
     }
-  verified->buf = grub_malloc (ret->size);
-  if (!verified->buf)
-    {
-      grub_file_close (sig);
-      verified_free (verified);
-      grub_free (ret);
-      return NULL;
-    }
-  if (grub_file_read (io, verified->buf, ret->size) != (grub_ssize_t) ret->size)
-    {
-      if (!grub_errno)
-	grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
-		    io->name);
-      grub_file_close (sig);
-      verified_free (verified);
-      grub_free (ret);
-      return NULL;
-    }
-
-  err = grub_verify_signature_real (verified->buf, ret->size, 0, sig, NULL);
-  grub_file_close (sig);
+  err = grub_verify_signature_init (ctxt, sig);
   if (err)
     {
-      verified_free (verified);
-      grub_free (ret);
-      return NULL;
+      grub_pubkey_close (ctxt);
+      return err;
     }
-  verified->file = io;
-  ret->data = verified;
-  return ret;
+  *context = ctxt;
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_pubkey_fini (void *ctxt)
+{
+  return grub_verify_signature_real (ctxt, NULL);
 }
 
 static char *
@@ -970,8 +935,16 @@
   {
     .name = "pseudo",
     .read = pseudo_read
-};
+  };
 
+struct grub_file_verifier grub_pubkey_verifier =
+  {
+    .name = "pgp",
+    .init = grub_pubkey_init,
+    .fini = grub_pubkey_fini,
+    .write = grub_pubkey_write,
+    .close = grub_pubkey_close,
+  };
 
 static grub_extcmd_t cmd, cmd_trust;
 static grub_command_t cmd_distrust, cmd_list;
@@ -986,8 +959,6 @@
     sec = 1;
   else
     sec = 0;
-    
-  grub_file_filter_register (GRUB_FILE_FILTER_PUBKEY, grub_pubkey_open);
 
   grub_register_variable_hook ("check_signatures", 0, grub_env_write_sec);
   grub_env_export ("check_signatures");
@@ -1033,11 +1004,13 @@
   cmd_distrust = grub_register_command ("distrust", grub_cmd_distrust,
 					N_("PUBKEY_ID"),
 					N_("Remove PUBKEY_ID from trusted keys."));
+
+  grub_verifier_register (&grub_pubkey_verifier);
 }
 
 GRUB_MOD_FINI(verify)
 {
-  grub_file_filter_unregister (GRUB_FILE_FILTER_PUBKEY);
+  grub_verifier_unregister (&grub_pubkey_verifier);
   grub_unregister_extcmd (cmd);
   grub_unregister_extcmd (cmd_trust);
   grub_unregister_command (cmd_list);
diff --git a/grub-lakitu/grub-core/commands/verify_helper.c b/grub-lakitu/grub-core/commands/verify_helper.c
new file mode 100644
index 0000000..13f1bbb
--- /dev/null
+++ b/grub-lakitu/grub-core/commands/verify_helper.c
@@ -0,0 +1,172 @@
+#include <grub/file.h>
+#include <grub/verify.h>
+#include <grub/dl.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_file_verifier *grub_file_verifiers;
+
+struct grub_verified
+{
+  grub_file_t file;
+  void *buf;
+};
+typedef struct grub_verified *grub_verified_t;
+
+static void
+verified_free (grub_verified_t verified)
+{
+  if (verified)
+    {
+      grub_free (verified->buf);
+      grub_free (verified);
+    }
+}
+
+static grub_ssize_t
+verified_read (struct grub_file *file, char *buf, grub_size_t len)
+{
+  grub_verified_t verified = file->data;
+
+  grub_memcpy (buf, (char *) verified->buf + file->offset, len);
+  return len;
+}
+
+static grub_err_t
+verified_close (struct grub_file *file)
+{
+  grub_verified_t verified = file->data;
+
+  grub_file_close (verified->file);
+  verified_free (verified);
+  file->data = 0;
+
+  /* device and name are freed by parent */
+  file->device = 0;
+  file->name = 0;
+
+  return grub_errno;
+}
+
+struct grub_fs verified_fs =
+{
+  .name = "verified_read",
+  .read = verified_read,
+  .close = verified_close
+};
+
+static grub_file_t
+grub_verify_helper_open (grub_file_t io, enum grub_file_type type)
+{
+  grub_verified_t verified = 0;
+  struct grub_file_verifier *ver;
+  void *context;
+  grub_file_t ret = 0;
+  grub_err_t err;
+
+  if ((type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_SIGNATURE
+      || (type & GRUB_FILE_TYPE_MASK) == GRUB_FILE_TYPE_VERIFY_SIGNATURE
+      || (type & GRUB_FILE_TYPE_SKIP_SIGNATURE))
+    return io;
+
+  if (io->device->disk && 
+      (io->device->disk->dev->id == GRUB_DISK_DEVICE_MEMDISK_ID
+       || io->device->disk->dev->id == GRUB_DISK_DEVICE_PROCFS_ID))
+    return io;
+
+  FOR_LIST_ELEMENTS(ver, grub_file_verifiers)
+    {
+      enum grub_verify_flags flags = 0;
+      err = ver->init (io, type, &context, &flags);
+      if (err)
+	goto fail_noclose;
+      if (!(flags & GRUB_VERIFY_FLAGS_SKIP_VERIFICATION))
+	break;
+    }
+  if (!ver)
+    /* No verifiers wanted to verify. Just return underlying file.  */
+    return io;
+
+  ret = grub_malloc (sizeof (*ret));
+  if (!ret)
+    {
+      goto fail;
+    }
+  *ret = *io;
+
+  ret->fs = &verified_fs;
+  ret->not_easily_seekable = 0;
+  if (ret->size >> (sizeof (grub_size_t) * GRUB_CHAR_BIT - 1))
+    {
+      grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+		  "big file signature isn't implemented yet");
+      goto fail;
+    }
+  verified = grub_malloc (sizeof (*verified));
+  if (!verified)
+    {
+      goto fail;
+    }
+  verified->buf = grub_malloc (ret->size);
+  if (!verified->buf)
+    {
+      goto fail;
+    }
+  if (grub_file_read (io, verified->buf, ret->size) != (grub_ssize_t) ret->size)
+    {
+      if (!grub_errno)
+	grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
+		    io->name);
+      goto fail;
+    }
+  
+  err = ver->write (context, verified->buf, ret->size);
+  if (err)
+    goto fail;
+
+  err = ver->fini (context);
+  if (err)
+    goto fail;
+
+  ver->close (context);
+
+  FOR_LIST_ELEMENTS_NEXT(ver, grub_file_verifiers)
+    {
+      enum grub_verify_flags flags = 0;
+      err = ver->init (io, type, &context, &flags);
+      if (err)
+	goto fail_noclose;
+      if (flags & GRUB_VERIFY_FLAGS_SKIP_VERIFICATION)
+	continue;
+      err = ver->write (context, verified->buf, ret->size);
+      if (err)
+	goto fail;
+
+      err = ver->fini (context);
+      if (err)
+	goto fail;
+
+      ver->close (context);
+    }
+
+  verified->file = io;
+  ret->data = verified;
+  return ret;
+
+ fail:
+  ver->close (context);
+ fail_noclose:
+  verified_free (verified);
+  grub_free (ret);
+  return NULL;
+}
+
+GRUB_MOD_INIT(verify_helper)
+{
+  grub_file_filter_register (GRUB_FILE_FILTER_VERIFY, grub_verify_helper_open);
+}
+
+GRUB_MOD_FINI(verify_helper)
+{
+  grub_file_filter_unregister (GRUB_FILE_FILTER_VERIFY);
+}
diff --git a/grub-lakitu/include/grub/file.h b/grub-lakitu/include/grub/file.h
index e2795d1..c55901c 100644
--- a/grub-lakitu/include/grub/file.h
+++ b/grub-lakitu/include/grub/file.h
@@ -170,7 +170,7 @@
 /* Filters with lower ID are executed first.  */
 typedef enum grub_file_filter_id
   {
-    GRUB_FILE_FILTER_PUBKEY,
+    GRUB_FILE_FILTER_VERIFY,
     GRUB_FILE_FILTER_GZIO,
     GRUB_FILE_FILTER_XZIO,
     GRUB_FILE_FILTER_LZOPIO,
diff --git a/grub-lakitu/include/grub/list.h b/grub-lakitu/include/grub/list.h
index d170ff6..b13acb9 100644
--- a/grub-lakitu/include/grub/list.h
+++ b/grub-lakitu/include/grub/list.h
@@ -35,6 +35,7 @@
 void EXPORT_FUNC(grub_list_remove) (grub_list_t item);
 
 #define FOR_LIST_ELEMENTS(var, list) for ((var) = (list); (var); (var) = (var)->next)
+#define FOR_LIST_ELEMENTS_NEXT(var, list) for ((var) = (var)->next; (var); (var) = (var)->next)
 #define FOR_LIST_ELEMENTS_SAFE(var, nxt, list) for ((var) = (list), (nxt) = ((var) ? (var)->next : 0); (var); (var) = (nxt), ((nxt) = (var) ? (var)->next : 0))
 
 static inline void *
diff --git a/grub-lakitu/include/grub/verify.h b/grub-lakitu/include/grub/verify.h
new file mode 100644
index 0000000..4598a7e
--- /dev/null
+++ b/grub-lakitu/include/grub/verify.h
@@ -0,0 +1,42 @@
+#include <grub/file.h>
+#include <grub/list.h>
+
+enum grub_verify_flags
+  {
+    GRUB_VERIFY_FLAGS_SKIP_VERIFICATION = 1,
+    GRUB_VERIFY_FLAGS_SINGLE_CHUNK = 2,
+  };
+
+struct grub_file_verifier
+{
+  struct grub_file_verifier *next;
+  struct grub_file_verifier **prev;
+
+  const char *name;
+
+  /* Check if file needs to be verified and set up context.  */
+  /* init/read/fini is structured in the same way as hash interface.  */
+  grub_err_t (*init) (grub_file_t io, enum grub_file_type type,
+		      void **context, enum grub_verify_flags *verify_flags);
+  /* Right now we pass the whole file in one call but it
+     will change in the future. If you insist on single buffer we can
+     you need to set GRUB_VERIFY_FLAGS_SINGLE_CHUNK in verify_flags.
+  */
+  grub_err_t (*write) (void *context, void *buf, grub_size_t sz);
+  grub_err_t (*fini) (void *context);
+  void (*close) (void *context);
+};
+
+extern struct grub_file_verifier *grub_file_verifiers;
+
+static inline void
+grub_verifier_register (struct grub_file_verifier *ver)
+{
+  grub_list_push (GRUB_AS_LIST_P (&grub_file_verifiers), GRUB_AS_LIST (ver));
+}
+
+static inline void
+grub_verifier_unregister (struct grub_file_verifier *ver)
+{
+  grub_list_remove (GRUB_AS_LIST (ver));
+}