Blame SOURCES/0366-appended-signatures-parse-PKCS-7-signedData-and-X.50.patch

3efed6
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
3efed6
From: Daniel Axtens <dja@axtens.net>
3efed6
Date: Thu, 30 Jul 2020 01:33:46 +1000
3efed6
Subject: [PATCH] appended signatures: parse PKCS#7 signedData and X.509
3efed6
 certificates
3efed6
3efed6
This code allows us to parse:
3efed6
3efed6
 - PKCS#7 signedData messages. Only a single signerInfo is supported,
3efed6
   which is all that the Linux sign-file utility supports creating
3efed6
   out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported.
3efed6
   Any certificate embedded in the PKCS#7 message will be ignored.
3efed6
3efed6
 - X.509 certificates: at least enough to verify the signatures on the
3efed6
   PKCS#7 messages. We expect that the certificates embedded in grub will
3efed6
   be leaf certificates, not CA certificates. The parser enforces this.
3efed6
3efed6
Signed-off-by: Daniel Axtens <dja@axtens.net>
3efed6
---
3efed6
 grub-core/commands/appendedsig/asn1util.c    | 102 +++
3efed6
 grub-core/commands/appendedsig/pkcs7.c       | 305 +++++++++
3efed6
 grub-core/commands/appendedsig/x509.c        | 972 +++++++++++++++++++++++++++
3efed6
 grub-core/commands/appendedsig/appendedsig.h | 110 +++
3efed6
 4 files changed, 1489 insertions(+)
3efed6
 create mode 100644 grub-core/commands/appendedsig/asn1util.c
3efed6
 create mode 100644 grub-core/commands/appendedsig/pkcs7.c
3efed6
 create mode 100644 grub-core/commands/appendedsig/x509.c
3efed6
 create mode 100644 grub-core/commands/appendedsig/appendedsig.h
3efed6
3efed6
diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c
3efed6
new file mode 100644
b71686
index 000000000..eff095a9d
3efed6
--- /dev/null
3efed6
+++ b/grub-core/commands/appendedsig/asn1util.c
3efed6
@@ -0,0 +1,102 @@
3efed6
+/*
3efed6
+ *  GRUB  --  GRand Unified Bootloader
3efed6
+ *  Copyright (C) 2020  IBM Corporation.
3efed6
+ *
3efed6
+ *  GRUB is free software: you can redistribute it and/or modify
3efed6
+ *  it under the terms of the GNU General Public License as published by
3efed6
+ *  the Free Software Foundation, either version 3 of the License, or
3efed6
+ *  (at your option) any later version.
3efed6
+ *
3efed6
+ *  GRUB is distributed in the hope that it will be useful,
3efed6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
3efed6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3efed6
+ *  GNU General Public License for more details.
3efed6
+ *
3efed6
+ *  You should have received a copy of the GNU General Public License
3efed6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
3efed6
+ */
3efed6
+
3efed6
+#include <grub/libtasn1.h>
3efed6
+#include <grub/types.h>
3efed6
+#include <grub/err.h>
3efed6
+#include <grub/mm.h>
3efed6
+#include <grub/crypto.h>
3efed6
+#include <grub/gcrypt/gcrypt.h>
3efed6
+
3efed6
+#include "appendedsig.h"
3efed6
+
3efed6
+asn1_node _gnutls_gnutls_asn = ASN1_TYPE_EMPTY;
3efed6
+asn1_node _gnutls_pkix_asn = ASN1_TYPE_EMPTY;
3efed6
+
3efed6
+extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[];
3efed6
+extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
3efed6
+
3efed6
+/*
3efed6
+ * Read a value from an ASN1 node, allocating memory to store it.
3efed6
+ *
3efed6
+ * It will work for anything where the size libtasn1 returns is right:
3efed6
+ *  - Integers
3efed6
+ *  - Octet strings
3efed6
+ *  - DER encoding of other structures
3efed6
+ * It will _not_ work for things where libtasn1 size requires adjustment:
3efed6
+ *  - Strings that require an extra NULL byte at the end
3efed6
+ *  - Bit strings because libtasn1 returns the length in bits, not bytes.
3efed6
+ *
3efed6
+ * If the function returns a non-NULL value, the caller must free it.
3efed6
+ */
3efed6
+void *
3efed6
+grub_asn1_allocate_and_read (asn1_node node, const char *name,
3efed6
+			     const char *friendly_name, int *content_size)
3efed6
+{
3efed6
+  int result;
3efed6
+  grub_uint8_t *tmpstr = NULL;
3efed6
+  int tmpstr_size = 0;
3efed6
+
3efed6
+  result = asn1_read_value (node, name, NULL, &tmpstr_size);
3efed6
+  if (result != ASN1_MEM_ERROR)
3efed6
+    {
3efed6
+      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
3efed6
+		     _
3efed6
+		     ("Reading size of %s did not return expected status: %s"),
3efed6
+		     friendly_name, asn1_strerror (result));
3efed6
+      grub_errno = GRUB_ERR_BAD_FILE_TYPE;
3efed6
+      return NULL;
3efed6
+    }
3efed6
+
3efed6
+  tmpstr = grub_malloc (tmpstr_size);
3efed6
+  if (tmpstr == NULL)
3efed6
+    {
3efed6
+      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
3efed6
+		     "Could not allocate memory to store %s", friendly_name);
3efed6
+      grub_errno = GRUB_ERR_OUT_OF_MEMORY;
3efed6
+      return NULL;
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_read_value (node, name, tmpstr, &tmpstr_size);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      grub_free (tmpstr);
3efed6
+      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
3efed6
+		     "Error reading %s: %s",
3efed6
+		     friendly_name, asn1_strerror (result));
3efed6
+      grub_errno = GRUB_ERR_BAD_FILE_TYPE;
3efed6
+      return NULL;
3efed6
+    }
3efed6
+
3efed6
+  *content_size = tmpstr_size;
3efed6
+
3efed6
+  return tmpstr;
3efed6
+}
3efed6
+
3efed6
+int
3efed6
+asn1_init (void)
3efed6
+{
3efed6
+  int res;
3efed6
+  res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return res;
3efed6
+    }
3efed6
+  res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL);
3efed6
+  return res;
3efed6
+}
3efed6
diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c
3efed6
new file mode 100644
b71686
index 000000000..dc6afe203
3efed6
--- /dev/null
3efed6
+++ b/grub-core/commands/appendedsig/pkcs7.c
3efed6
@@ -0,0 +1,305 @@
3efed6
+/*
3efed6
+ *  GRUB  --  GRand Unified Bootloader
3efed6
+ *  Copyright (C) 2020  IBM Corporation.
3efed6
+ *
3efed6
+ *  GRUB is free software: you can redistribute it and/or modify
3efed6
+ *  it under the terms of the GNU General Public License as published by
3efed6
+ *  the Free Software Foundation, either version 3 of the License, or
3efed6
+ *  (at your option) any later version.
3efed6
+ *
3efed6
+ *  GRUB is distributed in the hope that it will be useful,
3efed6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
3efed6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3efed6
+ *  GNU General Public License for more details.
3efed6
+ *
3efed6
+ *  You should have received a copy of the GNU General Public License
3efed6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
3efed6
+ */
3efed6
+
3efed6
+#include "appendedsig.h"
3efed6
+#include <grub/misc.h>
3efed6
+#include <grub/crypto.h>
3efed6
+#include <grub/gcrypt/gcrypt.h>
3efed6
+
3efed6
+
3efed6
+static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
3efed6
+
3efed6
+/*
3efed6
+ * RFC 5652 s 5.1
3efed6
+ */
3efed6
+const char *signedData_oid = "1.2.840.113549.1.7.2";
3efed6
+
3efed6
+/*
3efed6
+ * RFC 4055 s 2.1
3efed6
+ */
3efed6
+const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
3efed6
+const char *sha512_oid = "2.16.840.1.101.3.4.2.3";
3efed6
+
3efed6
+static grub_err_t
3efed6
+process_content (grub_uint8_t * content, int size,
3efed6
+		 struct pkcs7_signedData *msg)
3efed6
+{
3efed6
+  int res;
3efed6
+  asn1_node signed_part;
3efed6
+  grub_err_t err = GRUB_ERR_NONE;
3efed6
+  char algo_oid[MAX_OID_LEN];
3efed6
+  int algo_oid_size = sizeof (algo_oid);
3efed6
+  int algo_count;
3efed6
+  char version;
3efed6
+  int version_size = sizeof (version);
3efed6
+  grub_uint8_t *result_buf;
3efed6
+  int result_size = 0;
3efed6
+  int crls_size = 0;
3efed6
+  gcry_error_t gcry_err;
3efed6
+
3efed6
+  res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData",
3efed6
+			     &signed_part);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			 "Could not create ASN.1 structure for PKCS#7 signed part.");
3efed6
+    }
3efed6
+
3efed6
+  res = asn1_der_decoding2 (&signed_part, content, &size,
3efed6
+			    ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Error reading PKCS#7 signed data: %s", asn1_error);
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  /* SignedData ::= SEQUENCE {
3efed6
+   *     version CMSVersion,
3efed6
+   *     digestAlgorithms DigestAlgorithmIdentifiers,
3efed6
+   *     encapContentInfo EncapsulatedContentInfo,
3efed6
+   *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
3efed6
+   *     crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
3efed6
+   *     signerInfos SignerInfos }
3efed6
+   */
3efed6
+
3efed6
+  /* version per the algo in 5.1, must be 1 */
3efed6
+  res = asn1_read_value (signed_part, "version", &version, &version_size);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Error reading signedData version: %s",
3efed6
+		    asn1_strerror (res));
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  if (version != 1)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Unexpected signature version v%d, only v1 supported",
3efed6
+		    version);
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  /*
3efed6
+   * digestAlgorithms DigestAlgorithmIdentifiers
3efed6
+   *
3efed6
+   * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
3efed6
+   * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1)
3efed6
+   * 
3efed6
+   * RFC 4055 s 2.1:
3efed6
+   * sha256Identifier  AlgorithmIdentifier  ::=  { id-sha256, NULL }
3efed6
+   * sha512Identifier  AlgorithmIdentifier  ::=  { id-sha512, NULL }
3efed6
+   *
3efed6
+   * We only support 1 element in the set, and we do not check parameters atm.
3efed6
+   */
3efed6
+  res =
3efed6
+    asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Error counting number of digest algorithms: %s",
3efed6
+		    asn1_strerror (res));
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  if (algo_count != 1)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3efed6
+		    "Only 1 digest algorithm is supported");
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  res =
3efed6
+    asn1_read_value (signed_part, "digestAlgorithms.?1.algorithm", algo_oid,
3efed6
+		     &algo_oid_size);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Error reading digest algorithm: %s",
3efed6
+		    asn1_strerror (res));
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
3efed6
+    {
3efed6
+      msg->hash = grub_crypto_lookup_md_by_name ("sha512");
3efed6
+    }
3efed6
+  else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
3efed6
+    {
3efed6
+      msg->hash = grub_crypto_lookup_md_by_name ("sha256");
3efed6
+    }
3efed6
+  else
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3efed6
+		    "Only SHA-256 and SHA-512 hashes are supported, found OID %s",
3efed6
+		    algo_oid);
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  if (!msg->hash)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Hash algorithm for OID %s not loaded", algo_oid);
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  /*
3efed6
+   * We ignore the certificates, but we don't permit CRLs.
3efed6
+   * A CRL entry might be revoking the certificate we're using, and we have
3efed6
+   * no way of dealing with that at the moment.
3efed6
+   */
3efed6
+  res = asn1_read_value (signed_part, "crls", NULL, &crls_size);
3efed6
+  if (res != ASN1_ELEMENT_NOT_FOUND)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3efed6
+		    "PKCS#7 messages with embedded CRLs are not supported");
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  /* read the signature */
3efed6
+  result_buf =
3efed6
+    grub_asn1_allocate_and_read (signed_part, "signerInfos.?1.signature",
3efed6
+				 "signature data", &result_size);
3efed6
+  if (!result_buf)
3efed6
+    {
3efed6
+      err = grub_errno;
3efed6
+      goto cleanup_signed_part;
3efed6
+    }
3efed6
+
3efed6
+  gcry_err =
3efed6
+    gcry_mpi_scan (&(msg->sig_mpi), GCRYMPI_FMT_USG, result_buf, result_size,
3efed6
+		   NULL);
3efed6
+  if (gcry_err != GPG_ERR_NO_ERROR)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Error loading signature into MPI structure: %d",
3efed6
+		    gcry_err);
3efed6
+      goto cleanup_result;
3efed6
+    }
3efed6
+
3efed6
+cleanup_result:
3efed6
+  grub_free (result_buf);
3efed6
+cleanup_signed_part:
3efed6
+  asn1_delete_structure (&signed_part);
3efed6
+
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+grub_err_t
3efed6
+parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
3efed6
+			struct pkcs7_signedData *msg)
3efed6
+{
3efed6
+  int res;
3efed6
+  asn1_node content_info;
3efed6
+  grub_err_t err = GRUB_ERR_NONE;
3efed6
+  char content_oid[MAX_OID_LEN];
3efed6
+  grub_uint8_t *content;
3efed6
+  int content_size;
3efed6
+  int content_oid_size = sizeof (content_oid);
3efed6
+  int size;
3efed6
+
3efed6
+  if (data_size > GRUB_INT_MAX)
3efed6
+    return grub_error (GRUB_ERR_OUT_OF_RANGE,
3efed6
+		       "Cannot parse a PKCS#7 message where data size > INT_MAX");
3efed6
+  size = (int) data_size;
3efed6
+
3efed6
+  res = asn1_create_element (_gnutls_pkix_asn,
3efed6
+			     "PKIX1.pkcs-7-ContentInfo", &content_info);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			 "Could not create ASN.1 structure for PKCS#7 data: %s",
3efed6
+			 asn1_strerror (res));
3efed6
+    }
3efed6
+
3efed6
+  res = asn1_der_decoding2 (&content_info, sigbuf, &size,
3efed6
+			    ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Error decoding PKCS#7 message DER: %s", asn1_error);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  /*
3efed6
+   * ContentInfo ::= SEQUENCE {
3efed6
+   *     contentType ContentType,
3efed6
+   *     content [0] EXPLICIT ANY DEFINED BY contentType }
3efed6
+   *
3efed6
+   * ContentType ::= OBJECT IDENTIFIER
3efed6
+   */
3efed6
+  res =
3efed6
+    asn1_read_value (content_info, "contentType", content_oid,
3efed6
+		     &content_oid_size);
3efed6
+  if (res != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Error reading PKCS#7 content type: %s",
3efed6
+		    asn1_strerror (res));
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  /* OID for SignedData defined in 5.1 */
3efed6
+  if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_SIGNATURE,
3efed6
+		    "Unexpected content type in PKCS#7 message: OID %s",
3efed6
+		    content_oid);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  content =
3efed6
+    grub_asn1_allocate_and_read (content_info, "content",
3efed6
+				 "PKCS#7 message content", &content_size);
3efed6
+  if (!content)
3efed6
+    {
3efed6
+      err = grub_errno;
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  err = process_content (content, content_size, msg);
3efed6
+  grub_free (content);
3efed6
+
3efed6
+cleanup:
3efed6
+  asn1_delete_structure (&content_info);
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+/*
3efed6
+ * Release all the storage associated with the PKCS#7 message.
3efed6
+ * If the caller dynamically allocated the message, it must free it.
3efed6
+ */
3efed6
+void
3efed6
+pkcs7_signedData_release (struct pkcs7_signedData *msg)
3efed6
+{
3efed6
+  gcry_mpi_release (msg->sig_mpi);
3efed6
+}
3efed6
diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
3efed6
new file mode 100644
b71686
index 000000000..652e4f168
3efed6
--- /dev/null
3efed6
+++ b/grub-core/commands/appendedsig/x509.c
3efed6
@@ -0,0 +1,972 @@
3efed6
+/*
3efed6
+ *  GRUB  --  GRand Unified Bootloader
3efed6
+ *  Copyright (C) 2020  IBM Corporation.
3efed6
+ *
3efed6
+ *  GRUB is free software: you can redistribute it and/or modify
3efed6
+ *  it under the terms of the GNU General Public License as published by
3efed6
+ *  the Free Software Foundation, either version 3 of the License, or
3efed6
+ *  (at your option) any later version.
3efed6
+ *
3efed6
+ *  GRUB is distributed in the hope that it will be useful,
3efed6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
3efed6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3efed6
+ *  GNU General Public License for more details.
3efed6
+ *
3efed6
+ *  You should have received a copy of the GNU General Public License
3efed6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
3efed6
+ */
3efed6
+
3efed6
+#include <grub/libtasn1.h>
3efed6
+#include <grub/types.h>
3efed6
+#include <grub/err.h>
3efed6
+#include <grub/mm.h>
3efed6
+#include <grub/crypto.h>
3efed6
+#include <grub/gcrypt/gcrypt.h>
3efed6
+
3efed6
+#include "appendedsig.h"
3efed6
+
3efed6
+static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
3efed6
+
3efed6
+/*
3efed6
+ * RFC 3279 2.3.1  RSA Keys
3efed6
+ */
3efed6
+const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
3efed6
+
3efed6
+/*
3efed6
+ * RFC 5280 Appendix A
3efed6
+ */
3efed6
+const char *commonName_oid = "2.5.4.3";
3efed6
+
3efed6
+/*
3efed6
+ * RFC 5280 4.2.1.3 Key Usage
3efed6
+ */
3efed6
+const char *keyUsage_oid = "2.5.29.15";
3efed6
+
3efed6
+/*
3efed6
+ * RFC 5280 4.2.1.9 Basic Constraints
3efed6
+ */
3efed6
+const char *basicConstraints_oid = "2.5.29.19";
3efed6
+
3efed6
+/*
3efed6
+ * RFC 3279 2.3.1
3efed6
+ *
3efed6
+ *  The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey:
3efed6
+ *
3efed6
+ *     RSAPublicKey ::= SEQUENCE {
3efed6
+ *        modulus            INTEGER,    -- n
3efed6
+ *        publicExponent     INTEGER  }  -- e
3efed6
+ *
3efed6
+ *  where modulus is the modulus n, and publicExponent is the public
3efed6
+ *  exponent e.
3efed6
+ */
3efed6
+static grub_err_t
3efed6
+grub_parse_rsa_pubkey (grub_uint8_t * der, int dersize,
3efed6
+		       struct x509_certificate *certificate)
3efed6
+{
3efed6
+  int result;
3efed6
+  asn1_node spk = ASN1_TYPE_EMPTY;
3efed6
+  grub_uint8_t *m_data, *e_data;
3efed6
+  int m_size, e_size;
3efed6
+  grub_err_t err = GRUB_ERR_NONE;
3efed6
+  gcry_error_t gcry_err;
3efed6
+
3efed6
+  result =
3efed6
+    asn1_create_element (_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk;;
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			 "Cannot create storage for public key ASN.1 data");
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_der_decoding2 (&spk, der, &dersize,
3efed6
+			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Cannot decode certificate public key DER: %s",
3efed6
+		    asn1_error);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  m_data =
3efed6
+    grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size);
3efed6
+  if (!m_data)
3efed6
+    {
3efed6
+      err = grub_errno;
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  e_data =
3efed6
+    grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent",
3efed6
+				 &e_size);
3efed6
+  if (!e_data)
3efed6
+    {
3efed6
+      err = grub_errno;
3efed6
+      goto cleanup_m_data;
3efed6
+    }
3efed6
+
3efed6
+  /*
3efed6
+   * convert m, e to mpi
3efed6
+   *
3efed6
+   * nscanned is not set for FMT_USG, it's only set for FMT_PGP, 
3efed6
+   * so we can't verify it
3efed6
+   */
3efed6
+  gcry_err =
3efed6
+    gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size,
3efed6
+		   NULL);
3efed6
+  if (gcry_err != GPG_ERR_NO_ERROR)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Error loading RSA modulus into MPI structure: %d",
3efed6
+		    gcry_err);
3efed6
+      goto cleanup_e_data;
3efed6
+    }
3efed6
+
3efed6
+  gcry_err =
3efed6
+    gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size,
3efed6
+		   NULL);
3efed6
+  if (gcry_err != GPG_ERR_NO_ERROR)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Error loading RSA exponent into MPI structure: %d",
3efed6
+		    gcry_err);
3efed6
+      goto cleanup_m_mpi;
3efed6
+    }
3efed6
+
3efed6
+  grub_free (e_data);
3efed6
+  grub_free (m_data);
3efed6
+  asn1_delete_structure (&spk;;
3efed6
+  return GRUB_ERR_NONE;
3efed6
+
3efed6
+cleanup_m_mpi:
3efed6
+  gcry_mpi_release (certificate->mpis[0]);
3efed6
+cleanup_e_data:
3efed6
+  grub_free (e_data);
3efed6
+cleanup_m_data:
3efed6
+  grub_free (m_data);
3efed6
+cleanup:
3efed6
+  asn1_delete_structure (&spk;;
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+
3efed6
+/*
3efed6
+ * RFC 5280:
3efed6
+ *   SubjectPublicKeyInfo  ::=  SEQUENCE  {
3efed6
+ *       algorithm            AlgorithmIdentifier,
3efed6
+ *       subjectPublicKey     BIT STRING  }
3efed6
+ *
3efed6
+ * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we
3efed6
+ * only support RSA Encryption.
3efed6
+ */
3efed6
+
3efed6
+static grub_err_t
3efed6
+grub_x509_read_subject_public_key (asn1_node asn,
3efed6
+				   struct x509_certificate *results)
3efed6
+{
3efed6
+  int result;
3efed6
+  grub_err_t err;
3efed6
+  const char *algo_name =
3efed6
+    "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
3efed6
+  const char *params_name =
3efed6
+    "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
3efed6
+  const char *pk_name =
3efed6
+    "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
3efed6
+  char algo_oid[MAX_OID_LEN];
3efed6
+  int algo_size = sizeof (algo_oid);
3efed6
+  char params_value[2];
3efed6
+  int params_size = sizeof (params_value);
3efed6
+  grub_uint8_t *key_data = NULL;
3efed6
+  int key_size = 0;
3efed6
+  unsigned int key_type;
3efed6
+
3efed6
+  /* algorithm: see notes for rsaEncryption_oid */
3efed6
+  result = asn1_read_value (asn, algo_name, algo_oid, &algo_size);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Error reading x509 public key algorithm: %s",
3efed6
+			 asn1_strerror (result));
3efed6
+    }
3efed6
+
3efed6
+  if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid))
3efed6
+      != 0)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3efed6
+			 "Unsupported x509 public key algorithm: %s",
3efed6
+			 algo_oid);
3efed6
+    }
3efed6
+
3efed6
+  /* 
3efed6
+   * RFC 3279 2.3.1
3efed6
+   * The rsaEncryption OID is intended to be used in the algorithm field
3efed6
+   * of a value of type AlgorithmIdentifier.  The parameters field MUST
3efed6
+   * have ASN.1 type NULL for this algorithm identifier.
3efed6
+   */
3efed6
+  result = asn1_read_value (asn, params_name, params_value, &params_size);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Error reading x509 public key parameters: %s",
3efed6
+			 asn1_strerror (result));
3efed6
+    }
3efed6
+
3efed6
+  if (params_value[0] != ASN1_TAG_NULL)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Invalid x509 public key parameters: expected NULL");
3efed6
+    }
3efed6
+
3efed6
+  /*
3efed6
+   * RFC 3279 2.3.1:  The DER encoded RSAPublicKey is the value of the BIT
3efed6
+   * STRING subjectPublicKey.
3efed6
+   */
3efed6
+  result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type);
3efed6
+  if (result != ASN1_MEM_ERROR)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Error reading size of x509 public key: %s",
3efed6
+			 asn1_strerror (result));
3efed6
+    }
3efed6
+  if (key_type != ASN1_ETYPE_BIT_STRING)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Unexpected ASN.1 type when reading x509 public key: %x",
3efed6
+			 key_type);
3efed6
+    }
3efed6
+
3efed6
+  /* length is in bits */
3efed6
+  key_size = (key_size + 7) / 8;
3efed6
+
3efed6
+  key_data = grub_malloc (key_size);
3efed6
+  if (!key_data)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			 "Out of memory for x509 public key");
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_read_value (asn, pk_name, key_data, &key_size);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      grub_free (key_data);
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Error reading public key data");
3efed6
+    }
3efed6
+  key_size = (key_size + 7) / 8;
3efed6
+
3efed6
+  err = grub_parse_rsa_pubkey (key_data, key_size, results);
3efed6
+  grub_free (key_data);
3efed6
+
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+/* Decode a string as defined in Appendix A */
3efed6
+static grub_err_t
3efed6
+decode_string (char *der, int der_size, char **string,
3efed6
+	       grub_size_t * string_size)
3efed6
+{
3efed6
+  asn1_node strasn;
3efed6
+  int result;
3efed6
+  char *choice;
3efed6
+  int choice_size = 0;
3efed6
+  int tmp_size = 0;
3efed6
+  grub_err_t err = GRUB_ERR_NONE;
3efed6
+
3efed6
+  result =
3efed6
+    asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			 "Could not create ASN.1 structure for certificate: %s",
3efed6
+			 asn1_strerror (result));
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_der_decoding2 (&strasn, der, &der_size,
3efed6
+			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Could not parse DER for DirectoryString: %s",
3efed6
+		    asn1_error);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  choice =
3efed6
+    grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice",
3efed6
+				 &choice_size);
3efed6
+  if (!choice)
3efed6
+    {
3efed6
+      err = grub_errno;
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  if (grub_strncmp ("utf8String", choice, choice_size) == 0)
3efed6
+    {
3efed6
+      result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size);
3efed6
+      if (result != ASN1_MEM_ERROR)
3efed6
+	{
3efed6
+	  err =
3efed6
+	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			"Error reading size of UTF-8 string: %s",
3efed6
+			asn1_strerror (result));
3efed6
+	  goto cleanup_choice;
3efed6
+	}
3efed6
+    }
3efed6
+  else if (grub_strncmp("printableString", choice, choice_size) == 0)
3efed6
+    {
3efed6
+      result = asn1_read_value (strasn, "printableString", NULL, &tmp_size);
3efed6
+      if (result != ASN1_MEM_ERROR)
3efed6
+	{
3efed6
+	  err =
3efed6
+	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			"Error reading size of UTF-8 string: %s",
3efed6
+			asn1_strerror (result));
3efed6
+	  goto cleanup_choice;
3efed6
+	}
3efed6
+    }
3efed6
+  else
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
3efed6
+		    "Only UTF-8 and printable DirectoryStrings are supported, got %s",
3efed6
+		    choice);
3efed6
+      goto cleanup_choice;
3efed6
+    }
3efed6
+
3efed6
+  /* read size does not include trailing null */
3efed6
+  tmp_size++;
3efed6
+
3efed6
+  *string = grub_malloc (tmp_size);
3efed6
+  if (!*string)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+		    "Cannot allocate memory for DirectoryString contents");
3efed6
+      goto cleanup_choice;
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_read_value (strasn, choice, *string, &tmp_size);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Error reading out %s in DirectoryString: %s",
3efed6
+		    choice, asn1_strerror (result));
3efed6
+      grub_free (*string);
3efed6
+      goto cleanup_choice;
3efed6
+    }
3efed6
+  *string_size = tmp_size + 1;
3efed6
+  (*string)[tmp_size] = '\0';
3efed6
+
3efed6
+cleanup_choice:
3efed6
+  grub_free (choice);
3efed6
+cleanup:
3efed6
+  asn1_delete_structure (&strasn);
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+/*
3efed6
+ * TBSCertificate  ::=  SEQUENCE  {
3efed6
+ *       version         [0]  EXPLICIT Version DEFAULT v1,
3efed6
+ * ...
3efed6
+ * 
3efed6
+ * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
3efed6
+ */
3efed6
+static grub_err_t
3efed6
+check_version (asn1_node certificate)
3efed6
+{
3efed6
+  int rc;
3efed6
+  const char *name = "tbsCertificate.version";
3efed6
+  grub_uint8_t version;
3efed6
+  int len = 1;
3efed6
+
3efed6
+  rc = asn1_read_value (certificate, name, &version, &len;;
3efed6
+
3efed6
+  /* require version 3 */
3efed6
+  if (rc != ASN1_SUCCESS || len != 1)
3efed6
+    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		       "Error reading certificate version");
3efed6
+
3efed6
+  if (version != 0x02)
3efed6
+    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		       "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x",
3efed6
+		       version);
3efed6
+
3efed6
+  return GRUB_ERR_NONE;
3efed6
+}
3efed6
+
3efed6
+/*
3efed6
+ * This is an X.501 Name, which is complex.
3efed6
+ *
3efed6
+ * For simplicity, we extract only the CN.
3efed6
+ */
3efed6
+static grub_err_t
3efed6
+read_name (asn1_node asn, const char *name_path, char **name,
3efed6
+	   grub_size_t * name_size)
3efed6
+{
3efed6
+  int seq_components, set_components;
3efed6
+  int result;
3efed6
+  int i, j;
3efed6
+  char *top_path, *set_path, *type_path, *val_path;
3efed6
+  char type[MAX_OID_LEN];
3efed6
+  int type_len = sizeof (type);
3efed6
+  int string_size = 0;
3efed6
+  char *string_der;
3efed6
+  grub_err_t err;
3efed6
+
3efed6
+  *name = NULL;
3efed6
+
3efed6
+  top_path = grub_xasprintf ("%s.rdnSequence", name_path);
3efed6
+  if (!top_path)
3efed6
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+		       "Could not allocate memory for %s name parsing path",
3efed6
+		       name_path);
3efed6
+
3efed6
+  result = asn1_number_of_elements (asn, top_path, &seq_components);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Error counting name components: %s",
3efed6
+		    asn1_strerror (result));
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  for (i = 1; i <= seq_components; i++)
3efed6
+    {
3efed6
+      set_path = grub_xasprintf ("%s.?%d", top_path, i);
3efed6
+      if (!set_path)
3efed6
+	{
3efed6
+	  err =
3efed6
+	    grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			"Could not allocate memory for %s name set parsing path",
3efed6
+			name_path);
3efed6
+	  goto cleanup_set;
3efed6
+	}
3efed6
+      /* this brings us, hopefully, to a set */
3efed6
+      result = asn1_number_of_elements (asn, set_path, &set_components);
3efed6
+      if (result != ASN1_SUCCESS)
3efed6
+	{
3efed6
+	  err =
3efed6
+	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			"Error counting name sub-components components (element %d): %s",
3efed6
+			i, asn1_strerror (result));
3efed6
+	  goto cleanup_set;
3efed6
+	}
3efed6
+      for (j = 1; j <= set_components; j++)
3efed6
+	{
3efed6
+	  type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j);
3efed6
+	  if (!type_path)
3efed6
+	    {
3efed6
+	      err =
3efed6
+		grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			    "Could not allocate memory for %s name component type path",
3efed6
+			    name_path);
3efed6
+	      goto cleanup_set;
3efed6
+	    }
3efed6
+	  type_len = sizeof (type);
3efed6
+	  result = asn1_read_value (asn, type_path, type, &type_len);
3efed6
+	  if (result != ASN1_SUCCESS)
3efed6
+	    {
3efed6
+	      err =
3efed6
+		grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			    "Error reading %s name component type: %s",
3efed6
+			    name_path, asn1_strerror (result));
3efed6
+	      goto cleanup_type;
3efed6
+	    }
3efed6
+
3efed6
+	  if (grub_strncmp (type, commonName_oid, type_len) != 0)
3efed6
+	    {
3efed6
+	      grub_free (type_path);
3efed6
+	      continue;
3efed6
+	    }
3efed6
+
3efed6
+	  val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j);
3efed6
+	  if (!val_path)
3efed6
+	    {
3efed6
+	      err =
3efed6
+		grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			    "Could not allocate memory for %s name component value path",
3efed6
+			    name_path);
3efed6
+	      goto cleanup_set;
3efed6
+	    }
3efed6
+
3efed6
+	  string_der =
3efed6
+	    grub_asn1_allocate_and_read (asn, val_path, name_path,
3efed6
+					 &string_size);
3efed6
+	  if (!string_der)
3efed6
+	    {
3efed6
+	      err = grub_errno;
3efed6
+	      goto cleanup_val_path;
3efed6
+	    }
3efed6
+
3efed6
+	  err = decode_string (string_der, string_size, name, name_size);
3efed6
+	  if (err)
3efed6
+	    goto cleanup_string;
3efed6
+
3efed6
+	  grub_free (string_der);
3efed6
+	  grub_free (type_path);
3efed6
+	  grub_free (val_path);
3efed6
+	  break;
3efed6
+	}
3efed6
+      grub_free (set_path);
3efed6
+
3efed6
+      if (*name)
3efed6
+	break;
3efed6
+    }
3efed6
+
3efed6
+  return GRUB_ERR_NONE;
3efed6
+
3efed6
+cleanup_string:
3efed6
+  grub_free (string_der);
3efed6
+cleanup_val_path:
3efed6
+  grub_free (val_path);
3efed6
+cleanup_type:
3efed6
+  grub_free (type_path);
3efed6
+cleanup_set:
3efed6
+  grub_free (set_path);
3efed6
+cleanup:
3efed6
+  grub_free (top_path);
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+/*
3efed6
+ * details here
3efed6
+ */
3efed6
+static grub_err_t
3efed6
+verify_key_usage (grub_uint8_t * value, int value_size)
3efed6
+{
3efed6
+  asn1_node usageasn;
3efed6
+  int result;
3efed6
+  grub_err_t err = GRUB_ERR_NONE;
3efed6
+  grub_uint8_t usage = 0xff;
3efed6
+  int usage_size = 1;
3efed6
+
3efed6
+  result =
3efed6
+    asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			 "Could not create ASN.1 structure for key usage");
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_der_decoding2 (&usageasn, value, &value_size,
3efed6
+			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Error parsing DER for Key Usage: %s", asn1_error);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_read_value (usageasn, "", &usage, &usage_size);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Error reading Key Usage value: %s",
3efed6
+		    asn1_strerror (result));
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  /* Only the first bit is permitted to be set */
3efed6
+  if (usage != 0x80)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x",
3efed6
+		    usage);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+cleanup:
3efed6
+  asn1_delete_structure (&usageasn);
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+/*
3efed6
+ * BasicConstraints ::= SEQUENCE {
3efed6
+ *       cA                      BOOLEAN DEFAULT FALSE,
3efed6
+ *       pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
3efed6
+ */
3efed6
+static grub_err_t
3efed6
+verify_basic_constraints (grub_uint8_t * value, int value_size)
3efed6
+{
3efed6
+  asn1_node basicasn;
3efed6
+  int result;
3efed6
+  grub_err_t err = GRUB_ERR_NONE;
3efed6
+  char cA[6];			/* FALSE or TRUE */
3efed6
+  int cA_size = sizeof (cA);
3efed6
+
3efed6
+  result =
3efed6
+    asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints",
3efed6
+			 &basicasn);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			 "Could not create ASN.1 structure for Basic Constraints");
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_der_decoding2 (&basicasn, value, &value_size,
3efed6
+			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Error parsing DER for Basic Constraints: %s",
3efed6
+		    asn1_error);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_read_value (basicasn, "cA", cA, &cA_size);
3efed6
+  if (result == ASN1_ELEMENT_NOT_FOUND)
3efed6
+    {
3efed6
+      /* Not present, default is False, so this is OK */
3efed6
+      err = GRUB_ERR_NONE;
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+  else if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Error reading Basic Constraints cA value: %s",
3efed6
+		    asn1_strerror (result));
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  /* The certificate must not be a CA certificate */
3efed6
+  if (grub_strncmp ("FALSE", cA, cA_size) != 0)
3efed6
+    {
3efed6
+      err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected CA value: %s",
3efed6
+			cA);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+cleanup:
3efed6
+  asn1_delete_structure (&basicasn);
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+
3efed6
+/*
3efed6
+ * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
3efed6
+ *
3efed6
+ * Extension  ::=  SEQUENCE  {
3efed6
+ *      extnID      OBJECT IDENTIFIER,
3efed6
+ *      critical    BOOLEAN DEFAULT FALSE,
3efed6
+ *      extnValue   OCTET STRING
3efed6
+ *                  -- contains the DER encoding of an ASN.1 value
3efed6
+ *                  -- corresponding to the extension type identified
3efed6
+ *                  -- by extnID
3efed6
+ * }
3efed6
+ *
3efed6
+ * We require that a certificate:
3efed6
+ *  - contain the Digital Signature usage only
3efed6
+ *  - not be a CA
3efed6
+ *  - MUST not contain any other critical extensions (RFC 5280 s 4.2)
3efed6
+ */
3efed6
+static grub_err_t
3efed6
+verify_extensions (asn1_node cert)
3efed6
+{
3efed6
+  int result;
3efed6
+  int ext, num_extensions = 0;
3efed6
+  int usage_present = 0, constraints_present = 0;
3efed6
+  char *oid_path, *critical_path, *value_path;
3efed6
+  char extnID[MAX_OID_LEN];
3efed6
+  int extnID_size;
3efed6
+  grub_err_t err;
3efed6
+  char critical[6];		/* we get either "TRUE" or "FALSE" */
3efed6
+  int critical_size;
3efed6
+  grub_uint8_t *value;
3efed6
+  int value_size;
3efed6
+
3efed6
+  result =
3efed6
+    asn1_number_of_elements (cert, "tbsCertificate.extensions",
3efed6
+			     &num_extensions);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Error counting number of extensions: %s",
3efed6
+			 asn1_strerror (result));
3efed6
+    }
3efed6
+
3efed6
+  if (num_extensions < 2)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Insufficient number of extensions for certificate, need at least 2, got %d",
3efed6
+			 num_extensions);
3efed6
+    }
3efed6
+
3efed6
+  for (ext = 1; ext <= num_extensions; ext++)
3efed6
+    {
3efed6
+      oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext);
3efed6
+
3efed6
+      extnID_size = sizeof (extnID);
3efed6
+      result = asn1_read_value (cert, oid_path, extnID, &extnID_size);
3efed6
+      if (result != GRUB_ERR_NONE)
3efed6
+	{
3efed6
+	  err =
3efed6
+	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			"Error reading extension OID: %s",
3efed6
+			asn1_strerror (result));
3efed6
+	  goto cleanup_oid_path;
3efed6
+	}
3efed6
+
3efed6
+      critical_path =
3efed6
+	grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext);
3efed6
+      critical_size = sizeof (critical);
3efed6
+      result =
3efed6
+	asn1_read_value (cert, critical_path, critical, &critical_size);
3efed6
+      if (result == ASN1_ELEMENT_NOT_FOUND)
3efed6
+	{
3efed6
+	  critical[0] = '\0';
3efed6
+	}
3efed6
+      else if (result != ASN1_SUCCESS)
3efed6
+	{
3efed6
+	  err =
3efed6
+	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			"Error reading extension criticality: %s",
3efed6
+			asn1_strerror (result));
3efed6
+	  goto cleanup_critical_path;
3efed6
+	}
3efed6
+
3efed6
+      value_path =
3efed6
+	grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext);
3efed6
+      value =
3efed6
+	grub_asn1_allocate_and_read (cert, value_path,
3efed6
+				     "certificate extension value",
3efed6
+				     &value_size);
3efed6
+      if (!value)
3efed6
+	{
3efed6
+	  err = grub_errno;
3efed6
+	  goto cleanup_value_path;
3efed6
+	}
3efed6
+
3efed6
+      /*
3efed6
+       * Now we must see if we recognise the OID.
3efed6
+       * If we have an unrecognised critical extension we MUST bail.
3efed6
+       */
3efed6
+      if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0)
3efed6
+	{
3efed6
+	  err = verify_key_usage (value, value_size);
3efed6
+	  if (err != GRUB_ERR_NONE)
3efed6
+	    {
3efed6
+	      goto cleanup_value;
3efed6
+	    }
3efed6
+	  usage_present++;
3efed6
+	}
3efed6
+      else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0)
3efed6
+	{
3efed6
+	  err = verify_basic_constraints (value, value_size);
3efed6
+	  if (err != GRUB_ERR_NONE)
3efed6
+	    {
3efed6
+	      goto cleanup_value;
3efed6
+	    }
3efed6
+	  constraints_present++;
3efed6
+	}
3efed6
+      else if (grub_strncmp ("TRUE", critical, critical_size) == 0)
3efed6
+	{
3efed6
+	  /*
3efed6
+	   * per the RFC, we must not process a certificate with
3efed6
+	   * a critical extension we do not understand.
3efed6
+	   */
3efed6
+	  err =
3efed6
+	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			"Unhandled critical x509 extension with OID %s",
3efed6
+			extnID);
3efed6
+	  goto cleanup_value;
3efed6
+	}
3efed6
+
3efed6
+      grub_free (value);
3efed6
+      grub_free (value_path);
3efed6
+      grub_free (critical_path);
3efed6
+      grub_free (oid_path);
3efed6
+    }
3efed6
+
3efed6
+  if (usage_present != 1)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Unexpected number of Key Usage extensions - expected 1, got %d",
3efed6
+			 usage_present);
3efed6
+    }
3efed6
+  if (constraints_present != 1)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+			 "Unexpected number of basic constraints extensions - expected 1, got %d",
3efed6
+			 constraints_present);
3efed6
+    }
3efed6
+  return GRUB_ERR_NONE;
3efed6
+
3efed6
+cleanup_value:
3efed6
+  grub_free (value);
3efed6
+cleanup_value_path:
3efed6
+  grub_free (value_path);
3efed6
+cleanup_critical_path:
3efed6
+  grub_free (critical_path);
3efed6
+cleanup_oid_path:
3efed6
+  grub_free (oid_path);
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+/*
3efed6
+ * Parse a certificate whose DER-encoded form is in @data, of size @data_size.
3efed6
+ * Return the results in @results, which must point to an allocated x509 certificate.
3efed6
+ */
3efed6
+grub_err_t
3efed6
+certificate_import (void *data, grub_size_t data_size,
3efed6
+		    struct x509_certificate *results)
3efed6
+{
3efed6
+  int result = 0;
3efed6
+  asn1_node cert;
3efed6
+  grub_err_t err;
3efed6
+  int size;
3efed6
+  int tmp_size;
3efed6
+
3efed6
+  if (data_size > GRUB_INT_MAX)
3efed6
+    return grub_error (GRUB_ERR_OUT_OF_RANGE,
3efed6
+		       "Cannot parse a certificate where data size > INT_MAX");
3efed6
+  size = (int) data_size;
3efed6
+
3efed6
+  result = asn1_create_element (_gnutls_pkix_asn, "PKIX1.Certificate", &cert);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
3efed6
+			 "Could not create ASN.1 structure for certificate: %s",
3efed6
+			 asn1_strerror (result));
3efed6
+    }
3efed6
+
3efed6
+  result = asn1_der_decoding2 (&cert, data, &size,
3efed6
+			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
3efed6
+  if (result != ASN1_SUCCESS)
3efed6
+    {
3efed6
+      err =
3efed6
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
3efed6
+		    "Could not parse DER for certificate: %s", asn1_error);
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  /* 
3efed6
+   * TBSCertificate  ::=  SEQUENCE {
3efed6
+   *     version         [0]  EXPLICIT Version DEFAULT v1
3efed6
+   */
3efed6
+  err = check_version (cert);
3efed6
+  if (err != GRUB_ERR_NONE)
3efed6
+    {
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+
3efed6
+  /*
3efed6
+   * serialNumber         CertificateSerialNumber,
3efed6
+   *
3efed6
+   * CertificateSerialNumber  ::=  INTEGER
3efed6
+   */
3efed6
+  results->serial =
3efed6
+    grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber",
3efed6
+				 "certificate serial number", &tmp_size);
3efed6
+  if (!results->serial)
3efed6
+    {
3efed6
+      err = grub_errno;
3efed6
+      goto cleanup;
3efed6
+    }
3efed6
+  /*
3efed6
+   * It's safe to cast the signed int to an unsigned here, we know
3efed6
+   * length is non-negative
3efed6
+   */
3efed6
+  results->serial_len = tmp_size;
3efed6
+
3efed6
+  /* 
3efed6
+   * signature            AlgorithmIdentifier,
3efed6
+   *
3efed6
+   * We don't load the signature or issuer at the moment,
3efed6
+   * as we don't attempt x509 verification.
3efed6
+   */
3efed6
+
3efed6
+  /*
3efed6
+   * issuer               Name,
3efed6
+   *
3efed6
+   * The RFC only requires the serial number to be unique within
3efed6
+   * issuers, so to avoid ambiguity we _technically_ ought to make
3efed6
+   * this available.
3efed6
+   */
3efed6
+
3efed6
+  /*
3efed6
+   * validity             Validity,
3efed6
+   *
3efed6
+   * Validity ::= SEQUENCE {
3efed6
+   *     notBefore      Time,
3efed6
+   *     notAfter       Time }
3efed6
+   *
3efed6
+   * We can't validate this reasonably, we have no true time source on several
3efed6
+   * platforms. For now we do not parse them.
3efed6
+   */
3efed6
+
3efed6
+  /*
3efed6
+   * subject              Name,
3efed6
+   * 
3efed6
+   * This is an X501 name, we parse out just the CN.
3efed6
+   */
3efed6
+  err =
3efed6
+    read_name (cert, "tbsCertificate.subject", &results->subject,
3efed6
+	       &results->subject_len);
3efed6
+  if (err != GRUB_ERR_NONE)
3efed6
+    goto cleanup_serial;
3efed6
+
3efed6
+  /*
3efed6
+   * TBSCertificate  ::=  SEQUENCE  {
3efed6
+   *    ...
3efed6
+   *    subjectPublicKeyInfo SubjectPublicKeyInfo,
3efed6
+   *    ...
3efed6
+   */
3efed6
+  err = grub_x509_read_subject_public_key (cert, results);
3efed6
+  if (err != GRUB_ERR_NONE)
3efed6
+    goto cleanup_name;
3efed6
+
3efed6
+  /*
3efed6
+   * TBSCertificate  ::=  SEQUENCE  {
3efed6
+   *    ...
3efed6
+   *    extensions      [3]  EXPLICIT Extensions OPTIONAL
3efed6
+   *                         -- If present, version MUST be v3
3efed6
+   * }
3efed6
+   */
3efed6
+
3efed6
+  err = verify_extensions (cert);
3efed6
+  if (err != GRUB_ERR_NONE)
3efed6
+    goto cleanup_name;
3efed6
+
3efed6
+
3efed6
+  /*
3efed6
+   * We do not read or check the signature on the certificate:
3efed6
+   * as discussed we do not try to validate the certificate but trust
3efed6
+   * it implictly.
3efed6
+   */
3efed6
+
3efed6
+  asn1_delete_structure (&cert);
3efed6
+  return GRUB_ERR_NONE;
3efed6
+
3efed6
+
3efed6
+cleanup_name:
3efed6
+  grub_free (results->subject);
3efed6
+cleanup_serial:
3efed6
+  grub_free (results->serial);
3efed6
+cleanup:
3efed6
+  asn1_delete_structure (&cert);
3efed6
+  return err;
3efed6
+}
3efed6
+
3efed6
+/*
3efed6
+ * Release all the storage associated with the x509 certificate.
3efed6
+ * If the caller dynamically allocated the certificate, it must free it.
3efed6
+ * The caller is also responsible for maintenance of the linked list.
3efed6
+ */
3efed6
+void
3efed6
+certificate_release (struct x509_certificate *cert)
3efed6
+{
3efed6
+  grub_free (cert->subject);
3efed6
+  grub_free (cert->serial);
3efed6
+  gcry_mpi_release (cert->mpis[0]);
3efed6
+  gcry_mpi_release (cert->mpis[1]);
3efed6
+}
3efed6
diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
3efed6
new file mode 100644
b71686
index 000000000..9792ef390
3efed6
--- /dev/null
3efed6
+++ b/grub-core/commands/appendedsig/appendedsig.h
3efed6
@@ -0,0 +1,110 @@
3efed6
+/*
3efed6
+ *  GRUB  --  GRand Unified Bootloader
3efed6
+ *  Copyright (C) 2020  IBM Corporation.
3efed6
+ *
3efed6
+ *  GRUB is free software: you can redistribute it and/or modify
3efed6
+ *  it under the terms of the GNU General Public License as published by
3efed6
+ *  the Free Software Foundation, either version 3 of the License, or
3efed6
+ *  (at your option) any later version.
3efed6
+ *
3efed6
+ *  GRUB is distributed in the hope that it will be useful,
3efed6
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
3efed6
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3efed6
+ *  GNU General Public License for more details.
3efed6
+ *
3efed6
+ *  You should have received a copy of the GNU General Public License
3efed6
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
3efed6
+ */
3efed6
+
3efed6
+#include <grub/crypto.h>
3efed6
+#include <grub/libtasn1.h>
3efed6
+
3efed6
+extern asn1_node _gnutls_gnutls_asn;
3efed6
+extern asn1_node _gnutls_pkix_asn;
3efed6
+
3efed6
+#define MAX_OID_LEN 32
3efed6
+
3efed6
+/*
3efed6
+ * One or more x509 certificates.
3efed6
+ *
3efed6
+ * We do limited parsing: extracting only the serial, CN and RSA public key.
3efed6
+ */
3efed6
+struct x509_certificate
3efed6
+{
3efed6
+  struct x509_certificate *next;
3efed6
+
3efed6
+  grub_uint8_t *serial;
3efed6
+  grub_size_t serial_len;
3efed6
+
3efed6
+  char *subject;
3efed6
+  grub_size_t subject_len;
3efed6
+
3efed6
+  /* We only support RSA public keys. This encodes [modulus, publicExponent] */
3efed6
+  gcry_mpi_t mpis[2];
3efed6
+};
3efed6
+
3efed6
+/*
3efed6
+ * A PKCS#7 signedData message.
3efed6
+ *
3efed6
+ * We make no attempt to match intelligently, so we don't save any info about
3efed6
+ * the signer. We also support only 1 signerInfo, so we only store a single
3efed6
+ * MPI for the signature.
3efed6
+ */
3efed6
+struct pkcs7_signedData
3efed6
+{
3efed6
+  const gcry_md_spec_t *hash;
3efed6
+  gcry_mpi_t sig_mpi;
3efed6
+};
3efed6
+
3efed6
+
3efed6
+/* Do libtasn1 init */
3efed6
+int asn1_init (void);
3efed6
+
3efed6
+/*
3efed6
+ * Import a DER-encoded certificate at 'data', of size 'size'.
3efed6
+ *
3efed6
+ * Place the results into 'results', which must be already allocated.
3efed6
+ */
3efed6
+grub_err_t
3efed6
+certificate_import (void *data, grub_size_t size,
3efed6
+		    struct x509_certificate *results);
3efed6
+
3efed6
+/*
3efed6
+ * Release all the storage associated with the x509 certificate.
3efed6
+ * If the caller dynamically allocated the certificate, it must free it.
3efed6
+ * The caller is also responsible for maintenance of the linked list.
3efed6
+ */
3efed6
+void certificate_release (struct x509_certificate *cert);
3efed6
+
3efed6
+/*
3efed6
+ * Parse a PKCS#7 message, which must be a signedData message.
3efed6
+ *
3efed6
+ * The message must be in 'sigbuf' and of size 'data_size'. The result is
3efed6
+ * placed in 'msg', which must already be allocated.
3efed6
+ */
3efed6
+grub_err_t
3efed6
+parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size,
3efed6
+			struct pkcs7_signedData *msg);
3efed6
+
3efed6
+/*
3efed6
+ * Release all the storage associated with the PKCS#7 message.
3efed6
+ * If the caller dynamically allocated the message, it must free it.
3efed6
+ */
3efed6
+void pkcs7_signedData_release (struct pkcs7_signedData *msg);
3efed6
+
3efed6
+/*
3efed6
+ * Read a value from an ASN1 node, allocating memory to store it.
3efed6
+ *
3efed6
+ * It will work for anything where the size libtasn1 returns is right:
3efed6
+ *  - Integers
3efed6
+ *  - Octet strings
3efed6
+ *  - DER encoding of other structures
3efed6
+ * It will _not_ work for things where libtasn1 size requires adjustment:
3efed6
+ *  - Strings that require an extra NULL byte at the end
3efed6
+ *  - Bit strings because libtasn1 returns the length in bits, not bytes.
3efed6
+ *
3efed6
+ * If the function returns a non-NULL value, the caller must free it.
3efed6
+ */
3efed6
+void *grub_asn1_allocate_and_read (asn1_node node, const char *name,
3efed6
+				   const char *friendly_name,
3efed6
+				   int *content_size);