All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/22] appended signature secure boot support
@ 2021-06-30  8:40 Daniel Axtens
  2021-06-30  8:40 ` [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE Daniel Axtens
                   ` (21 more replies)
  0 siblings, 22 replies; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

This patch set contains v2 of the consolidated version of the patch
sets sent for secure boot using appended signatures on powerpc,
rebased on top of git HEAD.

The series consists of 4 main parts:

 0) Patches 1-3: powerpc-ieee1275 memory enablement.

These patches have already been posted to the mailing list and are
unchanged. I have included them here as well to make this one
monolithic series of everything needed for full support for appended
signatures on powerpc-ieee1275.


 1) Patches 4-6: signing grub.elf with an appended signature

Part of a secure boot chain is allowing boot firmware to verify the
grub core image. For UEFI platforms, this is done by signing the PE
binary with a tool like pesign or sb-sign. However, for platforms that
don't implement UEFI, an alternative scheme is required.

These patches provide some infrastructure and documentation for
signing grub's core.elf with a Linux-kernel-module style appended
signature.

An appended signature is a 'dumb' signature over the contents of a
file. (It is distinct from schemes like Authenticode that are aware of
the structure of the file and only sign certain parts.) The signature
is wrapped in a PKCS#7 message, and is appended to the signed file
along with some metadata and a magic string. The signatures are
validated against a public key which is usually provided as an x509
certificate.

Because some platforms, such as powerpc-ieee1275, may load grub from a
raw disk partition rather than a filesystem, we extend grub-install to
add an ELF note that allows us to specify the size and location of the
signature.

This has attracted some controversy in the past, with suggestions that
we could avoid the ELF note by placing the signature at the end of
core.elf if the image was loaded from a filesystem or network, and by
placing it at the end of the PReP partition if it is loaded from
there. This is not currently supported by either proprietary or open
source firmware, but the current solution does not preclude this
solution being added in the future.

There was also a suggestion of allowing grub-{install,mkimage} to call
out to openssl directly to sign itself. I'm not opposed to doing this,
but as I expect signing to mostly be something done by distros rather
than the average grub-install user, I'm interested to hear any
thoughts on whether that's actually going to be useful.


 2) Patches 7 - 21: Teach grub to verify appended signatures

Part of a secure boot chain is allowing grub to verify the boot
kernel. For UEFI platforms, this is usually delegated to the
shim. However, for platforms that do not implement UEFI, an
alternative scheme is required.

This part teaches grub how to verify Linux kernel-style appended
signatures. Kernels on powerpc are already signed with this scheme and
can be verified by IMA for kexec.

As PKCS#7 messages and x509 certificates are both based on ASN.1, we
import libtasn1 to parse them. Because ASN.1 isn't self-documenting,
we import from GNUTLS the information we need to navigate their
structure.

This section is composed of the following patches:
 
 - patch 7 is a small fix to allow persistent modules to work on the
   emu target.

 - patches 8 and 9 are small refactorings.

 - patch 10 prepares posix_wrap for importing libtasn1

 - patches 11 through 15 import libtasn1 and add tests. I've taken a
   different approach from gcrypt. We import gcrypt via a script that
   transforms the code into something that works for grub. Rather than
   taking that approach, we import libtasn1 through first just copying
   a subset of the code in (patch 11), then disabling parts we don't
   need for grub (patch 12), making changes for grub compatibility
   (patch 13) and then compiling it into a module (patch 14) and
   testing it (patch 15).

   This means that should we want to upgrade our version of libtasn1,
   we should be able to copy the new files in (repeat the process in
   patch 11) and then just cherry-pick/reapply patches 12 and 13 to
   repeat the process of disabling unused code and making grub
   compatiblity fixes. Hopefully that makes sense!

 - patch 16 allows x509 certificates to be built in to the grub core
   in much the same way as PGP keys.

 - patch 17 brings in the code from GNUTLS that allows us to parse
   PKCS#7 and x509 with libtasn1.

 - patch 18 is our PKCS#7 and x509 parser. They're minimal and fairly
   strict parsers that extract only the bits we need to verify the
   signatures.

 - patch 19 is the guts of the appended signature verifier. It uses
   the verifier infrastructure like pgp, and adds a number of
   user-friendly commands that mirror the pgp module.

 - patch 20 adds tests, and patch 21 adds documentation.

This chunk is where all the v2 changes are. They're documented in the
patches themselves, but the big feature changes are: support for
Extended Key Usage, thanks to Javier Martinez; and support for
verifying a file with multiple signatures in the pkcs7 message. If any
trusted key can verify any signature, the file will be considered to
have passed verification.


 3) Patch 22: Enter lockdown if in powerpc secure boot

This is now a much neater and nicer solution than before 2.06 - it
detects if the DT property advertising SB is set, and enters lockdown
if it is. The main appended signature series now tests for lockdown to
enter 'forced' mode.


Thanks to Nayna Jain and Stefan Berger for providing review comments
on v1.

I've pushed this all to
https://github.com/daxtens/grub/tree/appendedsig-2.11

This patch series is easy to experiment with. In particular, the
appended signature verifier doesn't require any particular
platform. It works under emu and passes tests under x86_64-efi.

I have some information about testing all the parts together at
https://gist.github.com/daxtens/cfc0a7e15614b0383e0c57f308cacdd1
It's largely unchanged from
https://lists.gnu.org/archive/html/grub-devel/2020-10/msg00048.html

Kind regards,
Daniel

Alastair D'Silva (1):
  grub-install: support embedding x509 certificates

Daniel Axtens (20):
  ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE
  ieee1275: claim more memory
  ieee1275: request memory with ibm,client-architecture-support
  docs/grub: Document signing grub under UEFI
  docs/grub: Document signing grub with an appended signature
  dl: provide a fake grub_dl_set_persistent for the emu target
  pgp: factor out rsa_pad
  crypto: move storage for grub_crypto_pk_* to crypto.c
  posix_wrap: tweaks in preparation for libtasn1
  libtasn1: import libtasn1-4.16.0
  libtasn1: disable code not needed in grub
  libtasn1: changes for grub compatibility
  libtasn1: compile into asn1 module
  test_asn1: test module for libtasn1
  appended signatures: import GNUTLS's ASN.1 description files
  appended signatures: parse PKCS#7 signedData and X.509 certificates
  appended signatures: support verifying appended signatures
  appended signatures: verification tests
  appended signatures: documentation
  ieee1275: enter lockdown based on /ibm,secure-boot

Rashmica Gupta (1):
  Add suport for signing grub with an appended signature

 .gitignore                                    |    1 +
 Makefile.util.def                             |    6 +
 docs/grub-dev.texi                            |    6 +-
 docs/grub.texi                                |  259 +-
 grub-core/Makefile.core.def                   |   57 +
 grub-core/commands/appendedsig/appendedsig.c  |  669 +++++
 grub-core/commands/appendedsig/appendedsig.h  |  118 +
 grub-core/commands/appendedsig/asn1util.c     |  103 +
 .../commands/appendedsig/gnutls_asn1_tab.c    |  121 +
 grub-core/commands/appendedsig/pkcs7.c        |  509 ++++
 .../commands/appendedsig/pkix_asn1_tab.c      |  484 ++++
 grub-core/commands/appendedsig/x509.c         | 1079 +++++++
 grub-core/commands/pgp.c                      |   34 +-
 grub-core/kern/ieee1275/cmain.c               |    3 +
 grub-core/kern/ieee1275/init.c                |  265 +-
 grub-core/lib/crypto.c                        |    4 +
 grub-core/lib/libtasn1/LICENSE                |   16 +
 grub-core/lib/libtasn1/README.md              |   91 +
 grub-core/lib/libtasn1/lib/coding.c           | 1423 ++++++++++
 grub-core/lib/libtasn1/lib/decoding.c         | 2481 +++++++++++++++++
 grub-core/lib/libtasn1/lib/element.c          | 1112 ++++++++
 grub-core/lib/libtasn1/lib/element.h          |   40 +
 grub-core/lib/libtasn1/lib/errors.c           |  103 +
 grub-core/lib/libtasn1/lib/gstr.c             |   74 +
 grub-core/lib/libtasn1/lib/gstr.h             |   47 +
 grub-core/lib/libtasn1/lib/int.h              |  221 ++
 grub-core/lib/libtasn1/lib/parser_aux.c       | 1174 ++++++++
 grub-core/lib/libtasn1/lib/parser_aux.h       |  172 ++
 grub-core/lib/libtasn1/lib/structure.c        | 1222 ++++++++
 grub-core/lib/libtasn1/lib/structure.h        |   45 +
 .../tests/CVE-2018-1000654-1_asn1_tab.h       |   32 +
 .../tests/CVE-2018-1000654-2_asn1_tab.h       |   36 +
 .../libtasn1_wrap/tests/CVE-2018-1000654.c    |   61 +
 .../lib/libtasn1_wrap/tests/Test_overflow.c   |  138 +
 .../lib/libtasn1_wrap/tests/Test_simple.c     |  207 ++
 .../lib/libtasn1_wrap/tests/Test_strings.c    |  150 +
 .../libtasn1_wrap/tests/object-id-decoding.c  |  116 +
 .../libtasn1_wrap/tests/object-id-encoding.c  |  120 +
 .../lib/libtasn1_wrap/tests/octet-string.c    |  211 ++
 .../lib/libtasn1_wrap/tests/reproducers.c     |   81 +
 grub-core/lib/libtasn1_wrap/wrap.c            |   26 +
 grub-core/lib/libtasn1_wrap/wrap_tests.c      |   75 +
 grub-core/lib/libtasn1_wrap/wrap_tests.h      |   38 +
 grub-core/lib/pkcs1_v15.c                     |   59 +
 grub-core/lib/posix_wrap/limits.h             |    1 +
 grub-core/lib/posix_wrap/stdlib.h             |    8 +
 grub-core/lib/posix_wrap/sys/types.h          |    1 +
 grub-core/tests/appended_signature_test.c     |  273 ++
 grub-core/tests/appended_signatures.h         |  975 +++++++
 grub-core/tests/lib/functional_test.c         |    1 +
 include/grub/dl.h                             |   11 +
 include/grub/file.h                           |    2 +
 include/grub/ieee1275/ieee1275.h              |    6 +
 include/grub/kernel.h                         |    3 +-
 include/grub/libtasn1.h                       |  589 ++++
 include/grub/lockdown.h                       |    3 +-
 include/grub/pkcs1_v15.h                      |   27 +
 include/grub/util/install.h                   |   15 +-
 include/grub/util/mkimage.h                   |    4 +-
 tests/test_asn1.in                            |   12 +
 util/grub-install-common.c                    |   37 +-
 util/grub-mkimage.c                           |   26 +-
 util/grub-mkimagexx.c                         |   39 +-
 util/mkimage.c                                |   54 +-
 64 files changed, 15267 insertions(+), 109 deletions(-)
 create mode 100644 grub-core/commands/appendedsig/appendedsig.c
 create mode 100644 grub-core/commands/appendedsig/appendedsig.h
 create mode 100644 grub-core/commands/appendedsig/asn1util.c
 create mode 100644 grub-core/commands/appendedsig/gnutls_asn1_tab.c
 create mode 100644 grub-core/commands/appendedsig/pkcs7.c
 create mode 100644 grub-core/commands/appendedsig/pkix_asn1_tab.c
 create mode 100644 grub-core/commands/appendedsig/x509.c
 create mode 100644 grub-core/lib/libtasn1/LICENSE
 create mode 100644 grub-core/lib/libtasn1/README.md
 create mode 100644 grub-core/lib/libtasn1/lib/coding.c
 create mode 100644 grub-core/lib/libtasn1/lib/decoding.c
 create mode 100644 grub-core/lib/libtasn1/lib/element.c
 create mode 100644 grub-core/lib/libtasn1/lib/element.h
 create mode 100644 grub-core/lib/libtasn1/lib/errors.c
 create mode 100644 grub-core/lib/libtasn1/lib/gstr.c
 create mode 100644 grub-core/lib/libtasn1/lib/gstr.h
 create mode 100644 grub-core/lib/libtasn1/lib/int.h
 create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.c
 create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.h
 create mode 100644 grub-core/lib/libtasn1/lib/structure.c
 create mode 100644 grub-core/lib/libtasn1/lib/structure.h
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_simple.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_strings.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/octet-string.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/reproducers.c
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.c
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.h
 create mode 100644 grub-core/lib/pkcs1_v15.c
 create mode 100644 grub-core/tests/appended_signature_test.c
 create mode 100644 grub-core/tests/appended_signatures.h
 create mode 100644 include/grub/libtasn1.h
 create mode 100644 include/grub/pkcs1_v15.h
 create mode 100644 tests/test_asn1.in

-- 
2.30.2



^ permalink raw reply	[flat|nested] 56+ messages in thread

* [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:33   ` Stefan Berger
  2021-07-14 16:21   ` Daniel Kiper
  2021-06-30  8:40 ` [PATCH v2 02/22] ieee1275: claim more memory Daniel Axtens
                   ` (20 subsequent siblings)
  21 siblings, 2 replies; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

HEAP_MAX_ADDR is confusing. Currently it is set to 32MB, except
on ieee1275 on x86, where it is 64MB.

There is a comment which purports to explain it:

/* If possible, we will avoid claiming heap above this address, because it
   seems to cause relocation problems with OSes that link at 4 MiB */

This doesn't make a lot of sense when the constants are well above 4MB
already. It was not always this way. Prior to
commit 7b5d0fe4440c ("Increase heap limit") in 2010, HEAP_MAX_SIZE and
HEAP_MAX_ADDR were indeed 4MB. However, when the constants were increased
the comment was left unchanged.

It's been over a decade. It doesn't seem like we have problems with
claims over 4MB on powerpc or x86 ieee1275. (sparc does things completely
differently and never used the constant.)

Drop the constant and the check.

The only use of HEAP_MIN_SIZE was to potentially override the
HEAP_MAX_ADDR check. It is now unused. Remove it.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/kern/ieee1275/init.c | 17 -----------------
 1 file changed, 17 deletions(-)

diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
index d483e35eed2b..c5d091689f29 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -45,9 +45,6 @@
 #include <grub/machine/kernel.h>
 #endif
 
-/* The minimal heap size we can live with. */
-#define HEAP_MIN_SIZE		(unsigned long) (2 * 1024 * 1024)
-
 /* The maximum heap size we're going to claim */
 #ifdef __i386__
 #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
@@ -55,14 +52,6 @@
 #define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
 #endif
 
-/* If possible, we will avoid claiming heap above this address, because it
-   seems to cause relocation problems with OSes that link at 4 MiB */
-#ifdef __i386__
-#define HEAP_MAX_ADDR		(unsigned long) (64 * 1024 * 1024)
-#else
-#define HEAP_MAX_ADDR		(unsigned long) (32 * 1024 * 1024)
-#endif
-
 extern char _start[];
 extern char _end[];
 
@@ -183,12 +172,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
   if (*total + len > HEAP_MAX_SIZE)
     len = HEAP_MAX_SIZE - *total;
 
-  /* Avoid claiming anything above HEAP_MAX_ADDR, if possible. */
-  if ((addr < HEAP_MAX_ADDR) &&				/* if it's too late, don't bother */
-      (addr + len > HEAP_MAX_ADDR) &&				/* if it wasn't available anyway, don't bother */
-      (*total + (HEAP_MAX_ADDR - addr) > HEAP_MIN_SIZE))	/* only limit ourselves when we can afford to */
-     len = HEAP_MAX_ADDR - addr;
-
   /* In theory, firmware should already prevent this from happening by not
      listing our own image in /memory/available.  The check below is intended
      as a safeguard in case that doesn't happen.  However, it doesn't protect
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 02/22] ieee1275: claim more memory
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
  2021-06-30  8:40 ` [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:35   ` Stefan Berger
  2021-07-15 21:51   ` Daniel Kiper
  2021-06-30  8:40 ` [PATCH v2 03/22] ieee1275: request memory with ibm, client-architecture-support Daniel Axtens
                   ` (19 subsequent siblings)
  21 siblings, 2 replies; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

On powerpc-ieee1275, we are running out of memory trying to verify
anything. This is because:

 - we have to load an entire file into memory to verify it. This is
   extremely difficult to change with appended signatures.
 - We only have 32MB of heap.
 - Distro kernels are now often around 30MB.

So we want to claim more memory from OpenFirmware for our heap.

There are some complications:

 - The grub mm code isn't the only thing that will make claims on
   memory from OpenFirmware:

    * PFW/SLOF will have claimed some for their own use.

    * The ieee1275 loader will try to find other bits of memory that we
      haven't claimed to place the kernel and initrd when we go to boot.

    * Once we load Linux, it will also try to claim memory. It claims
      memory without any reference to /memory/available, it just starts
      at min(top of RMO, 768MB) and works down. So we need to avoid this
      area. See arch/powerpc/kernel/prom_init.c as of v5.11.

 - The smallest amount of memory a ppc64 KVM guest can have is 256MB.
   It doesn't work with distro kernels but can work with custom kernels.
   We should maintain support for that. (ppc32 can boot with even less,
   and we shouldn't break that either.)

 - Even if a VM has more memory, the memory OpenFirmware makes available
   as Real Memory Area can be restricted. A freshly created LPAR on a
   PowerVM machine is likely to have only 256MB available to OpenFirmware
   even if it has many gigabytes of memory allocated.

EFI systems will attempt to allocate 1/4th of the available memory,
clamped to between 1M and 1600M. That seems like a good sort of
approach, we just need to figure out if 1/4 is the right fraction
for us.

We don't know in advance how big the kernel and initrd are going to be,
which makes figuring out how much memory we can take a bit tricky.

To figure out how much memory we should leave unused, I looked at:

 - an Ubuntu 20.04.1 ppc64le pseries KVM guest:
    vmlinux: ~30MB
    initrd:  ~50MB

 - a RHEL8.2 ppc64le pseries KVM guest:
    vmlinux: ~30MB
    initrd:  ~30MB

Ubuntu VMs struggle to boot with just 256MB under SLOF.
RHEL likewise has a higher minimum supported memory figure.
So lets first consider a distro kernel and 512MB of addressible memory.
(This is the default case for anything booting under PFW.) Say we lose
131MB to PFW (based on some tests). This leaves us 381MB. 1/4 of 381MB
is ~95MB. That should be enough to verify a 30MB vmlinux and should
leave plenty of space to load Linux and the initrd.

If we consider 256MB of RMA under PFW, we have just 125MB remaining. 1/4
of that is a smidge under 32MB, which gives us very poor odds of verifying
a distro-sized kernel. However, if we need 80MB just to put the kernel
and initrd in memory, we can't claim any more than 45MB anyway. So 1/4
will do. We'll come back to this later.

grub is always built as a 32-bit binary, even if it's loading a ppc64
kernel. So we can't address memory beyond 4GB. This gives a natural cap
of 1GB for powerpc-ieee1275.

Also apply this 1/4 approach to i386-ieee1275, but keep the 32MB cap.

make check still works for both i386 and powerpc and I've booted
powerpc grub with this change under SLOF and PFW.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 docs/grub-dev.texi             |  6 ++-
 grub-core/kern/ieee1275/init.c | 81 +++++++++++++++++++++++++++-------
 2 files changed, 69 insertions(+), 18 deletions(-)

diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
index 6c629a23e2dc..c11f1ac46de2 100644
--- a/docs/grub-dev.texi
+++ b/docs/grub-dev.texi
@@ -1047,7 +1047,9 @@ space is limited to 4GiB. GRUB allocates pages from EFI for its heap, at most
 1.6 GiB.
 
 On i386-ieee1275 and powerpc-ieee1275 GRUB uses same stack as IEEE1275.
-It allocates at most 32MiB for its heap.
+
+On i386-ieee1275, GRUB allocates at most 32MiB for its heap. On
+powerpc-ieee1275, GRUB allocates up to 1GiB.
 
 On sparc64-ieee1275 stack is 256KiB and heap is 2MiB.
 
@@ -1075,7 +1077,7 @@ In short:
 @item i386-qemu               @tab 60 KiB  @tab < 4 GiB
 @item *-efi                   @tab ?       @tab < 1.6 GiB
 @item i386-ieee1275           @tab ?       @tab < 32 MiB
-@item powerpc-ieee1275        @tab ?       @tab < 32 MiB
+@item powerpc-ieee1275        @tab ?       @tab < 1 GiB
 @item sparc64-ieee1275        @tab 256KiB  @tab 2 MiB
 @item arm-uboot               @tab 256KiB  @tab 2 MiB
 @item mips(el)-qemu_mips      @tab 2MiB    @tab 253 MiB
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
index c5d091689f29..4162b5949df4 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -45,11 +45,12 @@
 #include <grub/machine/kernel.h>
 #endif
 
-/* The maximum heap size we're going to claim */
+/* The maximum heap size we're going to claim. Not used by sparc.
+   We allocate 1/4 of the available memory under 4G, up to this limit. */
 #ifdef __i386__
 #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
-#else
-#define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
+#else // __powerpc__
+#define HEAP_MAX_SIZE		(unsigned long) (1 * 1024 * 1024 * 1024)
 #endif
 
 extern char _start[];
@@ -145,16 +146,45 @@ grub_claim_heap (void)
 				 + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000);
 }
 #else
-/* Helper for grub_claim_heap.  */
+/* Helper for grub_claim_heap on powerpc. */
+static int
+heap_size (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
+	   void *data)
+{
+  grub_uint32_t total = *(grub_uint32_t *)data;
+
+  if (type != GRUB_MEMORY_AVAILABLE)
+    return 0;
+
+  /* Do not consider memory beyond 4GB */
+  if (addr > 0xffffffffUL)
+    return 0;
+
+  if (addr + len > 0xffffffffUL)
+    len = 0xffffffffUL - addr;
+
+  total += len;
+  *(grub_uint32_t *)data = total;
+
+  return 0;
+}
+
 static int
 heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
 	   void *data)
 {
-  unsigned long *total = data;
+  grub_uint32_t total = *(grub_uint32_t *)data;
 
   if (type != GRUB_MEMORY_AVAILABLE)
     return 0;
 
+  /* Do not consider memory beyond 4GB */
+  if (addr > 0xffffffffUL)
+    return 0;
+
+  if (addr + len > 0xffffffffUL)
+    len = 0xffffffffUL - addr;
+
   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM))
     {
       if (addr + len <= 0x180000)
@@ -168,10 +198,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
     }
   len -= 1; /* Required for some firmware.  */
 
-  /* Never exceed HEAP_MAX_SIZE  */
-  if (*total + len > HEAP_MAX_SIZE)
-    len = HEAP_MAX_SIZE - *total;
-
   /* In theory, firmware should already prevent this from happening by not
      listing our own image in /memory/available.  The check below is intended
      as a safeguard in case that doesn't happen.  However, it doesn't protect
@@ -183,6 +209,18 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
       len = 0;
     }
 
+  /* If this block contains 0x30000000 (768MB), do not claim below that.
+     Linux likes to claim memory at min(RMO top, 768MB) and works down
+     without reference to /memory/available. */
+  if ((addr < 0x30000000) && ((addr + len) > 0x30000000))
+    {
+      len = len - (0x30000000 - addr);
+      addr = 0x30000000;
+    }
+
+  if (len > total)
+    len = total;
+
   if (len)
     {
       grub_err_t err;
@@ -191,10 +229,12 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
       if (err)
 	return err;
       grub_mm_init_region ((void *) (grub_addr_t) addr, len);
+      total -= len;
     }
 
-  *total += len;
-  if (*total >= HEAP_MAX_SIZE)
+  *(grub_uint32_t *)data = total;
+
+  if (total == 0)
     return 1;
 
   return 0;
@@ -203,13 +243,22 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
 static void 
 grub_claim_heap (void)
 {
-  unsigned long total = 0;
+  grub_uint32_t total = 0;
 
   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
-    heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
-	       1, &total);
-  else
-    grub_machine_mmap_iterate (heap_init, &total);
+    {
+      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
+		 1, &total);
+      return;
+    }
+
+  grub_machine_mmap_iterate (heap_size, &total);
+
+  total = total / 4;
+  if (total > HEAP_MAX_SIZE)
+    total = HEAP_MAX_SIZE;
+
+  grub_machine_mmap_iterate (heap_init, &total);
 }
 #endif
 
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 03/22] ieee1275: request memory with ibm, client-architecture-support
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
  2021-06-30  8:40 ` [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE Daniel Axtens
  2021-06-30  8:40 ` [PATCH v2 02/22] ieee1275: claim more memory Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:40   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 04/22] Add suport for signing grub with an appended signature Daniel Axtens
                   ` (18 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

On PowerVM, the first time we boot a Linux partition, we may only get
256MB of real memory area, even if the partition has more memory.

This isn't really enough. Fortunately, the Power Architecture Platform
Reference (PAPR) defines a method we can call to ask for more memory.
This is part of the broad and powerful ibm,client-architecture-support
(CAS) method.

CAS can do an enormous amount of things on a PAPR platform: as well as
asking for memory, you can set the supported processor level, the interrupt
controller, hash vs radix mmu, and so on. We want to touch as little of
this as possible because we don't want to step on the toes of the future OS.

If:

 - we are running under what we think is PowerVM (compatible property of /
   begins with "IBM"), and

 - the full amount of RMA is less than 512MB (as determined by the reg
   property of /memory)

then call CAS as follows: (refer to the Linux on Power Architecture
Reference, LoPAR, which is public, at B.5.2.3):

 - Use the "any" PVR value and supply 2 option vectors.

 - Set option vector 1 (PowerPC Server Processor Architecture Level)
   to "ignore".

 - Set option vector 2 with default or Linux-like options, including a
   min-rma-size of 512MB.

This will cause a CAS reboot and the partition will restart with 512MB
of RMA. Grub will notice the 512MB and not call CAS again.

(A partition can be configured with only 256MB of memory, which would
mean this request couldn't be satisfied, but PFW refuses to load with
only 256MB of memory, so it's a bit moot. SLOF will run fine with 256MB,
but we will never call CAS under qemu/SLOF because /compatible won't
begin with "IBM".)

One of the first things Linux does while still running under OpenFirmware
is to call CAS with a much fuller set of options (including asking for
512MB of memory). This includes a much more restrictive set of PVR values
and processor support levels, and this will induce another reboot. On this
reboot grub will again notice the higher RMA, and not call CAS. We will get
to Linux, Linux will call CAS but because the values are now set for Linux
this will not induce another CAS reboot and we will finally boot.

On all subsequent boots, everything will be configured with 512MB of RMA
and all the settings Linux likes, so there will be no further CAS reboots.

(phyp is super sticky with the RMA size - it persists even on cold boots.
So if you've ever booted Linux in a partition, you'll probably never have
grub call CAS. It'll only ever fire the first time a partition loads grub,
or if you deliberately lower the amount of memory your partition has below
512MB.)

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/kern/ieee1275/cmain.c  |   3 +
 grub-core/kern/ieee1275/init.c   | 144 ++++++++++++++++++++++++++++++-
 include/grub/ieee1275/ieee1275.h |   6 ++
 3 files changed, 151 insertions(+), 2 deletions(-)

diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c
index 20cbbd761ec3..cc98811f4f99 100644
--- a/grub-core/kern/ieee1275/cmain.c
+++ b/grub-core/kern/ieee1275/cmain.c
@@ -124,6 +124,9 @@ grub_ieee1275_find_options (void)
 	      break;
 	    }
 	}
+
+      if (grub_strncmp (tmp, "IBM,", 4) == 0)
+	grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY);
     }
 
   if (is_smartfirmware)
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
index 4162b5949df4..4586bec939b2 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -240,6 +240,135 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
   return 0;
 }
 
+/* How much memory does OF believe it has? (regardless of whether
+   it's accessible or not) */
+static grub_err_t
+grub_ieee1275_total_mem (grub_uint64_t *total)
+{
+  grub_ieee1275_phandle_t root;
+  grub_ieee1275_phandle_t memory;
+  grub_uint32_t reg[4];
+  grub_ssize_t reg_size;
+  grub_uint32_t address_cells = 1;
+  grub_uint32_t size_cells = 1;
+  grub_uint64_t size;
+
+  /* If we fail to get to the end, report 0. */
+  *total = 0;
+
+  /* Determine the format of each entry in `reg'.  */
+  grub_ieee1275_finddevice ("/", &root);
+  grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells,
+				      sizeof address_cells, 0);
+  grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells,
+				      sizeof size_cells, 0);
+
+  if (size_cells > address_cells)
+    address_cells = size_cells;
+
+  /* Load `/memory/reg'.  */
+  if (grub_ieee1275_finddevice ("/memory", &memory))
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+		       "couldn't find /memory node");
+  if (grub_ieee1275_get_integer_property (memory, "reg", reg,
+					  sizeof reg, &reg_size))
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+		       "couldn't examine /memory/reg property");
+  if (reg_size < 0 || (grub_size_t) reg_size > sizeof (reg))
+    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
+                       "/memory response buffer exceeded");
+
+  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS))
+    {
+      address_cells = 1;
+      size_cells = 1;
+    }
+
+  /* Decode only the size */
+  size = reg[address_cells];
+  if (size_cells == 2)
+    size = (size << 32) | reg[address_cells + 1];
+
+  *total = size;
+
+  return grub_errno;
+}
+
+/* Based on linux - arch/powerpc/kernel/prom_init.c */
+struct option_vector2 {
+	grub_uint8_t byte1;
+	grub_uint16_t reserved;
+	grub_uint32_t real_base;
+	grub_uint32_t real_size;
+	grub_uint32_t virt_base;
+	grub_uint32_t virt_size;
+	grub_uint32_t load_base;
+	grub_uint32_t min_rma;
+	grub_uint32_t min_load;
+	grub_uint8_t min_rma_percent;
+	grub_uint8_t max_pft_size;
+} __attribute__((packed));
+
+struct pvr_entry {
+	  grub_uint32_t mask;
+	  grub_uint32_t entry;
+};
+
+struct cas_vector {
+    struct {
+      struct pvr_entry terminal;
+    } pvr_list;
+    grub_uint8_t num_vecs;
+    grub_uint8_t vec1_size;
+    grub_uint8_t vec1;
+    grub_uint8_t vec2_size;
+    struct option_vector2 vec2;
+} __attribute__((packed));
+
+/* Call ibm,client-architecture-support to try to get more RMA.
+   We ask for 512MB which should be enough to verify a distro kernel.
+   We ignore most errors: if we don't succeed we'll proceed with whatever
+   memory we have. */
+static void
+grub_ieee1275_ibm_cas (void)
+{
+  int rc;
+  grub_ieee1275_ihandle_t root;
+  struct cas_args {
+    struct grub_ieee1275_common_hdr common;
+    grub_ieee1275_cell_t method;
+    grub_ieee1275_ihandle_t ihandle;
+    grub_ieee1275_cell_t cas_addr;
+    grub_ieee1275_cell_t result;
+  } args;
+  struct cas_vector vector = {
+    .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */
+    .num_vecs = 2 - 1,
+    .vec1_size = 0,
+    .vec1 = 0x80, /* ignore */
+    .vec2_size = 1 + sizeof(struct option_vector2) - 2,
+    .vec2 = {
+      0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48
+    },
+  };
+
+  INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2);
+  args.method = (grub_ieee1275_cell_t)"ibm,client-architecture-support";
+  rc = grub_ieee1275_open("/", &root);
+  if (rc) {
+	  grub_error (GRUB_ERR_IO, "could not open root when trying to call CAS");
+	  return;
+  }
+  args.ihandle = root;
+  args.cas_addr = (grub_ieee1275_cell_t)&vector;
+
+  grub_printf("Calling ibm,client-architecture-support...");
+  IEEE1275_CALL_ENTRY_FN (&args);
+  grub_printf("done\n");
+
+  grub_ieee1275_close(root);
+}
+
 static void 
 grub_claim_heap (void)
 {
@@ -247,11 +376,22 @@ grub_claim_heap (void)
 
   if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
     {
-      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
-		 1, &total);
+      heap_init (GRUB_IEEE1275_STATIC_HEAP_START,
+		 GRUB_IEEE1275_STATIC_HEAP_LEN, 1, &total);
       return;
     }
 
+  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY))
+    {
+      grub_uint64_t rma_size;
+      grub_err_t err;
+
+      err = grub_ieee1275_total_mem (&rma_size);
+      /* if we have an error, don't call CAS, just hope for the best */
+      if (!err && rma_size < (512 * 1024 * 1024))
+	grub_ieee1275_ibm_cas();
+    }
+
   grub_machine_mmap_iterate (heap_size, &total);
 
   total = total / 4;
diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
index 73e2f4644751..18c479b668ce 100644
--- a/include/grub/ieee1275/ieee1275.h
+++ b/include/grub/ieee1275/ieee1275.h
@@ -148,6 +148,12 @@ enum grub_ieee1275_flag
   GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN,
 
   GRUB_IEEE1275_FLAG_RAW_DEVNAMES,
+
+  /* On PFW, the first time we boot a Linux partition, we may only get 256MB
+     of real memory area, even if the partition has more memory. Set this flag
+     if we think we're running under PFW. Then, if this flag is set, and the
+     RMA is only 256MB in size, try asking for more with CAS. */
+  GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY,
 };
 
 extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag);
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 04/22] Add suport for signing grub with an appended signature
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (2 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 03/22] ieee1275: request memory with ibm, client-architecture-support Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:43   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 05/22] docs/grub: Document signing grub under UEFI Daniel Axtens
                   ` (17 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

From: Rashmica Gupta <rashmica.g@gmail.com>

Add infrastructure to allow firmware to verify the integrity of grub
by use of a Linux-kernel-module-style appended signature. We initially
target powerpc-ieee1275, but the code should be extensible to other
platforms.

Usually these signatures are appended to a file without modifying the
ELF file itself. (This is what the 'sign-file' tool does, for example.)
The verifier loads the signed file from the file system and looks at the
end of the file for the appended signature. However, on powerpc-ieee1275
platforms, the bootloader is often stored directly in the PReP partition
as raw bytes without a file-system. This makes determining the location
of an appended signature more difficult.

To address this, we add a new ELF note.

The name field of shall be the string "Appended-Signature", zero-padded
to 4 byte alignment. The type field shall be 0x41536967 (the ASCII values
for the string "ASig"). It must be the final section in the ELF binary.

The description shall contain the appended signature structure as defined
by the Linux kernel. The description will also be padded to be a multiple
of 4 bytes. The padding shall be added before the appended signature
structure (not at the end) so that the final bytes of a signed ELF file
are the appended signature magic.

A subsequent patch documents how to create a grub core.img validly signed
under this scheme.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>

---

You can experiment with this code with a patched version of SLOF
that verifies these signatures. You can find one at:
   https://github.com/daxtens/SLOF

I will be proposing this for inclusion in a future Power Architecture
Platform Reference (PAPR).
---
 include/grub/util/install.h |  8 ++++++--
 include/grub/util/mkimage.h |  4 ++--
 util/grub-install-common.c  | 15 +++++++++++---
 util/grub-mkimage.c         | 11 +++++++++++
 util/grub-mkimagexx.c       | 39 ++++++++++++++++++++++++++++++++++++-
 util/mkimage.c              | 13 +++++++------
 6 files changed, 76 insertions(+), 14 deletions(-)

diff --git a/include/grub/util/install.h b/include/grub/util/install.h
index 7df3191f47ef..cf4531e02b66 100644
--- a/include/grub/util/install.h
+++ b/include/grub/util/install.h
@@ -67,6 +67,9 @@
       N_("SBAT metadata"), 0 },						\
   { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
       N_("disable shim_lock verifier"), 0 },				\
+  { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\
+    "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \
+    1},                                                                 \
   { "verbose", 'v', 0, 0,						\
     N_("print verbose messages."), 1 }
 
@@ -128,7 +131,8 @@ enum grub_install_options {
   GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS,
   GRUB_INSTALL_OPTIONS_DTB,
   GRUB_INSTALL_OPTIONS_SBAT,
-  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK
+  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK,
+  GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE
 };
 
 extern char *grub_install_source_directory;
@@ -188,7 +192,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
 			     size_t npubkeys,
 			     char *config_path,
 			     const struct grub_install_image_target_desc *image_target,
-			     int note,
+			     int note, size_t appsig_size,
 			     grub_compression_t comp, const char *dtb_file,
 			     const char *sbat_path, const int disable_shim_lock);
 
diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h
index 3819a67441c8..6f1da89b9b65 100644
--- a/include/grub/util/mkimage.h
+++ b/include/grub/util/mkimage.h
@@ -51,12 +51,12 @@ grub_mkimage_load_image64 (const char *kernel_path,
 			   const struct grub_install_image_target_desc *image_target);
 void
 grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target,
-			     int note, char **core_img, size_t *core_size,
+			     int note, size_t appsig_size, char **core_img, size_t *core_size,
 			     Elf32_Addr target_addr,
 			     struct grub_mkimage_layout *layout);
 void
 grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target,
-			     int note, char **core_img, size_t *core_size,
+			     int note, size_t appsig_size, char **core_img, size_t *core_size,
 			     Elf64_Addr target_addr,
 			     struct grub_mkimage_layout *layout);
 
diff --git a/util/grub-install-common.c b/util/grub-install-common.c
index 4e212e690c52..1216a203c292 100644
--- a/util/grub-install-common.c
+++ b/util/grub-install-common.c
@@ -461,10 +461,12 @@ static size_t npubkeys;
 static char *sbat;
 static int disable_shim_lock;
 static grub_compression_t compression;
+static size_t appsig_size;
 
 int
 grub_install_parse (int key, char *arg)
 {
+  const char *end;
   switch (key)
     {
     case 'C':
@@ -562,6 +564,12 @@ grub_install_parse (int key, char *arg)
       grub_util_error (_("Unrecognized compression `%s'"), arg);
     case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE:
       return 1;
+    case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE:
+      grub_errno = 0;
+      appsig_size = grub_strtol(arg, &end, 10);
+      if (grub_errno)
+        return 0;
+      return 1;
     default:
       return 0;
     }
@@ -661,10 +669,11 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
 		  " --output '%s' "
 		  " --dtb '%s' "
 		  "--sbat '%s' "
-		  "--format '%s' --compression '%s' %s %s %s\n",
+		  "--format '%s' --compression '%s' "
+		  "--appended-signature-size %zu %s %s %s\n",
 		  dir, prefix,
 		  outname, dtb ? : "", sbat ? : "", mkimage_target,
-		  compnames[compression], note ? "--note" : "",
+		  compnames[compression], appsig_size, note ? "--note" : "",
 		  disable_shim_lock ? "--disable-shim-lock" : "", s);
   free (s);
 
@@ -675,7 +684,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
   grub_install_generate_image (dir, prefix, fp, outname,
 			       modules.entries, memdisk_path,
 			       pubkeys, npubkeys, config_path, tgt,
-			       note, compression, dtb, sbat,
+			       note, appsig_size, compression, dtb, sbat,
 			       disable_shim_lock);
   while (dc--)
     grub_install_pop_module ();
diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
index c0d559937020..d01eaeb8443a 100644
--- a/util/grub-mkimage.c
+++ b/util/grub-mkimage.c
@@ -84,6 +84,7 @@ static struct argp_option options[] = {
   {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0},
   {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0},
   {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
+  {"appended-signature-size", 'S', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0},
   { 0, 0, 0, 0, 0, 0 }
 };
 
@@ -128,6 +129,7 @@ struct arguments
   char *sbat;
   int note;
   int disable_shim_lock;
+  size_t appsig_size;
   const struct grub_install_image_target_desc *image_target;
   grub_compression_t comp;
 };
@@ -138,6 +140,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
   /* Get the input argument from argp_parse, which we
      know is a pointer to our arguments structure. */
   struct arguments *arguments = state->input;
+  const char* end;
 
   switch (key)
     {
@@ -170,6 +173,13 @@ argp_parser (int key, char *arg, struct argp_state *state)
       arguments->note = 1;
       break;
 
+    case 'S':
+      grub_errno = 0;
+      arguments->appsig_size = grub_strtol(arg, &end, 10);
+      if (grub_errno)
+        return 0;
+      break;
+
     case 'm':
       if (arguments->memdisk)
 	free (arguments->memdisk);
@@ -324,6 +334,7 @@ main (int argc, char *argv[])
 			       arguments.memdisk, arguments.pubkeys,
 			       arguments.npubkeys, arguments.config,
 			       arguments.image_target, arguments.note,
+			       arguments.appsig_size,
 			       arguments.comp, arguments.dtb,
 			       arguments.sbat, arguments.disable_shim_lock);
 
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index d78fa3e53308..393119486d3f 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -84,6 +84,15 @@ struct grub_ieee1275_note
   struct grub_ieee1275_note_desc descriptor;
 };
 
+#define GRUB_APPENDED_SIGNATURE_NOTE_NAME "Appended-Signature"
+#define GRUB_APPENDED_SIGNATURE_NOTE_TYPE 0x41536967 /* "ASig" */
+
+struct grub_appended_signature_note
+{
+  Elf32_Nhdr header;
+  char name[ALIGN_UP(sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME), 4)];
+};
+
 #define GRUB_XEN_NOTE_NAME "Xen"
 
 struct fixup_block_list
@@ -207,7 +216,7 @@ grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
 
 void
 SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target,
-				    int note, char **core_img, size_t *core_size,
+				    int note, size_t appsig_size, char **core_img, size_t *core_size,
 				    Elf_Addr target_addr,
 				    struct grub_mkimage_layout *layout)
 {
@@ -221,6 +230,12 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
   int shnum = 4;
   int string_size = sizeof (".text") + sizeof ("mods") + 1;
 
+  if (appsig_size)
+    {
+      phnum++;
+      footer_size += ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4);
+    }
+
   if (image_target->id != IMAGE_LOONGSON_ELF)
     phnum += 2;
 
@@ -484,6 +499,28 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
       phdr->p_offset = grub_host_to_target32 (header_size + program_size);
     }
 
+  if (appsig_size) {
+    int note_size = ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4);
+    struct grub_appended_signature_note *note_ptr = (struct grub_appended_signature_note *)
+      (elf_img + program_size + header_size + (note ? sizeof (struct grub_ieee1275_note) : 0));
+
+    note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME));
+    /* needs to sit at the end, so we round this up and sign some zero padding */
+    note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(appsig_size, 4));
+    note_ptr->header.n_type = grub_host_to_target32 (GRUB_APPENDED_SIGNATURE_NOTE_TYPE);
+    strcpy (note_ptr->name, GRUB_APPENDED_SIGNATURE_NOTE_NAME);
+
+    phdr++;
+    phdr->p_type = grub_host_to_target32 (PT_NOTE);
+    phdr->p_flags = grub_host_to_target32 (PF_R);
+    phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof);
+    phdr->p_vaddr = 0;
+    phdr->p_paddr = 0;
+    phdr->p_filesz = grub_host_to_target32 (note_size);
+    phdr->p_memsz = 0;
+    phdr->p_offset = grub_host_to_target32 (header_size + program_size + (note ? sizeof (struct grub_ieee1275_note) : 0));
+  }
+
   {
     char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)
 		       + shnum * sizeof (*shdr));
diff --git a/util/mkimage.c b/util/mkimage.c
index a26cf76f72f2..d2cb33883557 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -869,8 +869,9 @@ grub_install_generate_image (const char *dir, const char *prefix,
 			     char *memdisk_path, char **pubkey_paths,
 			     size_t npubkeys, char *config_path,
 			     const struct grub_install_image_target_desc *image_target,
-			     int note, grub_compression_t comp, const char *dtb_path,
-			     const char *sbat_path, int disable_shim_lock)
+			     int note, size_t appsig_size, grub_compression_t comp,
+			     const char *dtb_path, const char *sbat_path, 
+			     int disable_shim_lock)
 {
   char *kernel_img, *core_img;
   size_t total_module_size, core_size;
@@ -1773,11 +1774,11 @@ grub_install_generate_image (const char *dir, const char *prefix,
 	else
 	  target_addr = image_target->link_addr;
 	if (image_target->voidp_sizeof == 4)
-	  grub_mkimage_generate_elf32 (image_target, note, &core_img, &core_size,
-				       target_addr, &layout);
+	  grub_mkimage_generate_elf32 (image_target, note, appsig_size, &core_img,
+				       &core_size, target_addr, &layout);
 	else
-	  grub_mkimage_generate_elf64 (image_target, note, &core_img, &core_size,
-				       target_addr, &layout);
+	  grub_mkimage_generate_elf64 (image_target, note, appsig_size, &core_img,
+				       &core_size, target_addr, &layout);
       }
       break;
     }
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 05/22] docs/grub: Document signing grub under UEFI
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (3 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 04/22] Add suport for signing grub with an appended signature Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:44   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 06/22] docs/grub: Document signing grub with an appended signature Daniel Axtens
                   ` (16 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

Before adding information about how grub is signed with an appended
signature scheme, it's worth adding some information about how it
can currently be signed for UEFI.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 docs/grub.texi | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/docs/grub.texi b/docs/grub.texi
index f8b4b3b21a7f..2ffc3b417312 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -5795,6 +5795,7 @@ environment variables and commands are listed in the same order.
 * Secure Boot Advanced Targeting::   Embedded information for generation number based revocation
 * Measured Boot::                    Measuring boot components
 * Lockdown::                         Lockdown when booting on a secure setup
+* Signing GRUB itself::              Ensuring the integrity of the GRUB core image
 @end menu
 
 @node Authentication and authorisation
@@ -5873,7 +5874,7 @@ commands.
 
 GRUB's @file{core.img} can optionally provide enforcement that all files
 subsequently read from disk are covered by a valid digital signature.
-This document does @strong{not} cover how to ensure that your
+This section does @strong{not} cover how to ensure that your
 platform's firmware (e.g., Coreboot) validates @file{core.img}.
 
 If environment variable @code{check_signatures}
@@ -6035,6 +6036,25 @@ be restricted and some operations/commands cannot be executed.
 The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down.
 Otherwise it does not exit.
 
+@node Signing GRUB itself
+@section Signing GRUB itself
+
+To ensure a complete secure-boot chain, there must be a way for the code that
+loads GRUB to verify the integrity of the core image.
+
+This is ultimately platform-specific and individual platforms can define their
+own mechanisms. However, there are general-purpose mechanisms that can be used
+with GRUB.
+
+@section Signing GRUB for UEFI secure boot
+
+On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed
+with a tool such as @command{pesign} or @command{sbsign}. Refer to the
+suggestions in @pxref{UEFI secure boot and shim} to ensure that the final
+image works under UEFI secure boot and can maintain the secure-boot chain. It
+will also be necessary to enrol the public key used into a relevant firmware
+key database.
+
 @node Platform limitations
 @chapter Platform limitations
 
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 06/22] docs/grub: Document signing grub with an appended signature
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (4 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 05/22] docs/grub: Document signing grub under UEFI Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:46   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 07/22] dl: provide a fake grub_dl_set_persistent for the emu target Daniel Axtens
                   ` (15 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

Signing grub for firmware that verifies an appended signature is a
bit fiddly. I don't want people to have to figure it out from scratch
so document it here.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 docs/grub.texi | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/docs/grub.texi b/docs/grub.texi
index 2ffc3b417312..bed565371460 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6055,6 +6055,48 @@ image works under UEFI secure boot and can maintain the secure-boot chain. It
 will also be necessary to enrol the public key used into a relevant firmware
 key database.
 
+@section Signing GRUB with an appended signature
+
+The @file{core.img} itself can be signed with a Linux kernel module-style
+appended signature.
+
+To support IEEE1275 platforms where the boot image is often loaded directly
+from a disk partition rather than from a file system, the @file{core.img}
+can specify the size and location of the appended signature with an ELF
+note added by @command{grub-install}.
+
+An image can be signed this way using the @command{sign-file} command from
+the Linux kernel:
+
+@example
+@group
+# grub.key is your private key and certificate.der is your public key
+
+# Determine the size of the appended signature. It depends on the signing
+# certificate and the hash algorithm
+touch empty
+sign-file SHA256 grub.key certificate.der empty empty.sig
+SIG_SIZE=`stat -c '%s' empty.sig`
+rm empty empty.sig
+
+# Build a grub image with $SIG_SIZE reserved for the signature
+grub-install --appended-signature-size $SIG_SIZE --modules="..." ...
+
+# Replace the reserved size with a signature:
+# cut off the last $SIG_SIZE bytes with truncate's minus modifier
+truncate -s -$SIG_SIZE /boot/grub/powerpc-ieee1275/core.elf core.elf.unsigned
+# sign the trimmed file with an appended signature, restoring the correct size
+sign-file SHA256 grub.key certificate.der core.elf.unsigned core.elf.signed
+
+# Don't forget to install the signed image as required
+# (e.g. on powerpc-ieee1275, to the PReP partition)
+@end group
+@end example
+
+As with UEFI secure boot, it is necessary to build in the required modules,
+or sign them separately.
+
+
 @node Platform limitations
 @chapter Platform limitations
 
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 07/22] dl: provide a fake grub_dl_set_persistent for the emu target
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (5 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 06/22] docs/grub: Document signing grub with an appended signature Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:48   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 08/22] pgp: factor out rsa_pad Daniel Axtens
                   ` (14 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

Trying to start grub-emu with a module that calls grub_dl_set_persistent
will crash because grub-emu fakes modules and passes NULL to the module
init function.

Provide an empty function for the emu case.

Fixes: ee7808e2197c (dl: Add support for persistent modules)
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 include/grub/dl.h | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/include/grub/dl.h b/include/grub/dl.h
index b3753c9ca262..5decbe2f2fb9 100644
--- a/include/grub/dl.h
+++ b/include/grub/dl.h
@@ -243,11 +243,22 @@ grub_dl_get (const char *name)
   return 0;
 }
 
+#ifdef GRUB_MACHINE_EMU
+/*
+ * Under grub-emu, modules are faked and NULL is passed to GRUB_MOD_INIT.
+ * So we fake this out to avoid a NULL deref.
+ */
+static inline void
+grub_dl_set_persistent (grub_dl_t mod __attribute__((unused)))
+{
+}
+#else
 static inline void
 grub_dl_set_persistent (grub_dl_t mod)
 {
   mod->persistent = 1;
 }
+#endif
 
 static inline int
 grub_dl_is_persistent (grub_dl_t mod)
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 08/22] pgp: factor out rsa_pad
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (6 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 07/22] dl: provide a fake grub_dl_set_persistent for the emu target Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:52   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 09/22] crypto: move storage for grub_crypto_pk_* to crypto.c Daniel Axtens
                   ` (13 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

rsa_pad does the PKCS#1 v1.5 padding for the RSA signature scheme.
We want to use it in other RSA signature verification applications.

I considered and rejected putting it in lib/crypto.c. That file doesn't
currently require any MPI functions, but rsa_pad does. That's not so
much of a problem for the grub kernel and modules, but crypto.c also
gets built into all the grub utilities. So - despite the utils not
using any asymmetric ciphers -  we would need to built the entire MPI
infrastructure in to them.

A better and simpler solution is just to spin rsa_pad out into its own
PKCS#1 v1.5 module.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/Makefile.core.def |  8 +++++
 grub-core/commands/pgp.c    | 28 ++----------------
 grub-core/lib/pkcs1_v15.c   | 59 +++++++++++++++++++++++++++++++++++++
 include/grub/pkcs1_v15.h    | 27 +++++++++++++++++
 4 files changed, 96 insertions(+), 26 deletions(-)
 create mode 100644 grub-core/lib/pkcs1_v15.c
 create mode 100644 include/grub/pkcs1_v15.h

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 8022e1c0a794..915287d44c13 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2469,6 +2469,14 @@ module = {
   cppflags = '$(CPPFLAGS_GCRY)';
 };
 
+module = {
+  name = pkcs1_v15;
+  common = lib/pkcs1_v15.c;
+
+  cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
+  cppflags = '$(CPPFLAGS_GCRY)';
+};
+
 module = {
   name = all_video;
   common = lib/fake_module.c;
diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
index 5daa1e9d00c7..2408db4994f6 100644
--- a/grub-core/commands/pgp.c
+++ b/grub-core/commands/pgp.c
@@ -24,6 +24,7 @@
 #include <grub/file.h>
 #include <grub/command.h>
 #include <grub/crypto.h>
+#include <grub/pkcs1_v15.h>
 #include <grub/i18n.h>
 #include <grub/gcrypt/gcrypt.h>
 #include <grub/pubkey.h>
@@ -411,32 +412,7 @@ static int
 rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
 	 const gcry_md_spec_t *hash, struct grub_public_subkey *sk)
 {
-  grub_size_t tlen, emlen, fflen;
-  grub_uint8_t *em, *emptr;
-  unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]);
-  int ret;
-  tlen = hash->mdlen + hash->asnlen;
-  emlen = (nbits + 7) / 8;
-  if (emlen < tlen + 11)
-    return 1;
-
-  em = grub_malloc (emlen);
-  if (!em)
-    return 1;
-
-  em[0] = 0x00;
-  em[1] = 0x01;
-  fflen = emlen - tlen - 3;
-  for (emptr = em + 2; emptr < em + 2 + fflen; emptr++)
-    *emptr = 0xff;
-  *emptr++ = 0x00;
-  grub_memcpy (emptr, hash->asnoid, hash->asnlen);
-  emptr += hash->asnlen;
-  grub_memcpy (emptr, hval, hash->mdlen);
-
-  ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0);
-  grub_free (em);
-  return ret;
+  return grub_crypto_rsa_pad(hmpi, hval, hash, sk->mpis[0]);
 }
 
 struct grub_pubkey_context
diff --git a/grub-core/lib/pkcs1_v15.c b/grub-core/lib/pkcs1_v15.c
new file mode 100644
index 000000000000..dbacd563d014
--- /dev/null
+++ b/grub-core/lib/pkcs1_v15.c
@@ -0,0 +1,59 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2013  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/gcrypt/gcrypt.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/*
+ * Given a hash value 'hval', of hash specification 'hash', perform
+ * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod'
+ * (see RFC 8017 s 9.2) and place the result in 'hmpi'.
+ */
+gcry_err_code_t
+grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval,
+		     const gcry_md_spec_t * hash, gcry_mpi_t mod)
+{
+  grub_size_t tlen, emlen, fflen;
+  grub_uint8_t *em, *emptr;
+  unsigned nbits = gcry_mpi_get_nbits (mod);
+  int ret;
+  tlen = hash->mdlen + hash->asnlen;
+  emlen = (nbits + 7) / 8;
+  if (emlen < tlen + 11)
+    return GPG_ERR_TOO_SHORT;
+
+  em = grub_malloc (emlen);
+  if (!em)
+    return 1;
+
+  em[0] = 0x00;
+  em[1] = 0x01;
+  fflen = emlen - tlen - 3;
+  for (emptr = em + 2; emptr < em + 2 + fflen; emptr++)
+    *emptr = 0xff;
+  *emptr++ = 0x00;
+  grub_memcpy (emptr, hash->asnoid, hash->asnlen);
+  emptr += hash->asnlen;
+  grub_memcpy (emptr, hval, hash->mdlen);
+
+  ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0);
+  grub_free (em);
+  return ret;
+}
diff --git a/include/grub/pkcs1_v15.h b/include/grub/pkcs1_v15.h
new file mode 100644
index 000000000000..5c338c84a158
--- /dev/null
+++ b/include/grub/pkcs1_v15.h
@@ -0,0 +1,27 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2013  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Given a hash value 'hval', of hash specification 'hash', perform
+ * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod'
+ * (See RFC 8017 s 9.2)
+ */
+gcry_err_code_t
+grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval,
+		     const gcry_md_spec_t * hash, gcry_mpi_t mod);
+
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 09/22] crypto: move storage for grub_crypto_pk_* to crypto.c
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (7 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 08/22] pgp: factor out rsa_pad Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:54   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 10/22] posix_wrap: tweaks in preparation for libtasn1 Daniel Axtens
                   ` (12 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

The way gcry_rsa and friends (the asymmetric ciphers) are loaded for the
pgp module is a bit quirky.

include/grub/crypto.h contains:
  extern struct gcry_pk_spec *grub_crypto_pk_rsa;

commands/pgp.c contains the actual storage:
  struct gcry_pk_spec *grub_crypto_pk_rsa;

And the module itself saves to the storage in pgp.c:
  GRUB_MOD_INIT(gcry_rsa)
  {
    grub_crypto_pk_rsa = &_gcry_pubkey_spec_rsa;
  }

This is annoying: gcry_rsa now has a dependency on pgp!

We want to be able to bring in gcry_rsa without bringing in PGP,
so move the storage to crypto.c.

Previously, gcry_rsa depended on pgp and mpi. Now it depends on
crypto and mpi. As pgp depends on crypto, this doesn't add any new
module dependencies using the PGP verfier.

[FWIW, the story is different for the symmetric ciphers. cryptodisk
and friends (zfs encryption etc) use grub_crypto_lookup_cipher_by_name()
to get a cipher handle. That depends on grub_ciphers being populated
by people calling grub_cipher_register. import_gcry.py ensures that the
symmetric ciphers call it.]

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/commands/pgp.c | 4 ----
 grub-core/lib/crypto.c   | 4 ++++
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
index 2408db4994f6..355a43844acc 100644
--- a/grub-core/commands/pgp.c
+++ b/grub-core/commands/pgp.c
@@ -147,10 +147,6 @@ const char *hashes[] = {
   [0x0b] = "sha224"
 };
 
-struct gcry_pk_spec *grub_crypto_pk_dsa;
-struct gcry_pk_spec *grub_crypto_pk_ecdsa;
-struct gcry_pk_spec *grub_crypto_pk_rsa;
-
 static int
 dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
 	 const gcry_md_spec_t *hash, struct grub_public_subkey *sk);
diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c
index ca334d5a40e0..c578128a59db 100644
--- a/grub-core/lib/crypto.c
+++ b/grub-core/lib/crypto.c
@@ -121,6 +121,10 @@ grub_md_unregister (gcry_md_spec_t *cipher)
       }
 }
 
+struct gcry_pk_spec *grub_crypto_pk_dsa;
+struct gcry_pk_spec *grub_crypto_pk_ecdsa;
+struct gcry_pk_spec *grub_crypto_pk_rsa;
+
 void
 grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in,
 		  grub_size_t inlen)
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 10/22] posix_wrap: tweaks in preparation for libtasn1
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (8 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 09/22] crypto: move storage for grub_crypto_pk_* to crypto.c Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 12:56   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 11/22] libtasn1: import libtasn1-4.16.0 Daniel Axtens
                   ` (11 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

 - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
   SIZEOF_UNSIGNED_LONG.

 - Define WORD_BIT, the size in bits of an int. This is a defined
   in the Single Unix Specification and in gnulib's limits.h. gnulib
   assumes it's 32 bits on all our platforms, including 64 bit
   platforms, so we also use that value.

 - Provide strto[u]l[l] preprocessor macros that resolve to
   grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
   also define HAVE_STRTOUL here.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/lib/posix_wrap/limits.h    | 1 +
 grub-core/lib/posix_wrap/stdlib.h    | 8 ++++++++
 grub-core/lib/posix_wrap/sys/types.h | 1 +
 3 files changed, 10 insertions(+)

diff --git a/grub-core/lib/posix_wrap/limits.h b/grub-core/lib/posix_wrap/limits.h
index 7217138ffd6b..591dbf3289d8 100644
--- a/grub-core/lib/posix_wrap/limits.h
+++ b/grub-core/lib/posix_wrap/limits.h
@@ -37,5 +37,6 @@
 #define LONG_MAX GRUB_LONG_MAX
 
 #define CHAR_BIT 8
+#define WORD_BIT 32
 
 #endif
diff --git a/grub-core/lib/posix_wrap/stdlib.h b/grub-core/lib/posix_wrap/stdlib.h
index 7a8d385e973a..4634db09f294 100644
--- a/grub-core/lib/posix_wrap/stdlib.h
+++ b/grub-core/lib/posix_wrap/stdlib.h
@@ -58,4 +58,12 @@ abs (int c)
   return (c >= 0) ? c : -c;
 }
 
+#define strtol grub_strtol
+
+/* for libgcrypt */
+#define HAVE_STRTOUL
+#define strtoul grub_strtoul
+
+#define strtoull grub_strtoull
+
 #endif
diff --git a/grub-core/lib/posix_wrap/sys/types.h b/grub-core/lib/posix_wrap/sys/types.h
index 854eb0122efa..f63412c8da06 100644
--- a/grub-core/lib/posix_wrap/sys/types.h
+++ b/grub-core/lib/posix_wrap/sys/types.h
@@ -51,6 +51,7 @@ typedef grub_uint8_t byte;
 typedef grub_addr_t uintptr_t;
 
 #define SIZEOF_UNSIGNED_LONG GRUB_CPU_SIZEOF_LONG
+#define SIZEOF_UNSIGNED_LONG_INT GRUB_CPU_SIZEOF_LONG
 #define SIZEOF_UNSIGNED_INT 4
 #define SIZEOF_UNSIGNED_LONG_LONG 8
 #define SIZEOF_UNSIGNED_SHORT 2
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 11/22] libtasn1: import libtasn1-4.16.0
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (9 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 10/22] posix_wrap: tweaks in preparation for libtasn1 Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-20 21:46   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 12/22] libtasn1: disable code not needed in grub Daniel Axtens
                   ` (10 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

Import a very trimmed-down set of libtasn1 files:

pushd /tmp
wget https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.16.0.tar.gz
popd
pushd grub-core/lib
mkdir libtasn1
cp /tmp/libtasn1-4.16.0/{README.md,LICENSE} libtasn1/
mkdir libtasn1/lib
cp /tmp/libtasn1-4.16.0/lib/{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h}  libtasn1/lib
cp /tmp/libtasn1-4.16.0/lib/includes/libtasn1.h ../../include/grub/
git add libtasn1/ ../../include/grub/libtasn1.h
popd

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/lib/libtasn1/LICENSE          |   16 +
 grub-core/lib/libtasn1/README.md        |   91 +
 grub-core/lib/libtasn1/lib/coding.c     | 1415 +++++++++++++
 grub-core/lib/libtasn1/lib/decoding.c   | 2478 +++++++++++++++++++++++
 grub-core/lib/libtasn1/lib/element.c    | 1111 ++++++++++
 grub-core/lib/libtasn1/lib/element.h    |   40 +
 grub-core/lib/libtasn1/lib/errors.c     |  100 +
 grub-core/lib/libtasn1/lib/gstr.c       |   74 +
 grub-core/lib/libtasn1/lib/gstr.h       |   47 +
 grub-core/lib/libtasn1/lib/int.h        |  221 ++
 grub-core/lib/libtasn1/lib/parser_aux.c | 1173 +++++++++++
 grub-core/lib/libtasn1/lib/parser_aux.h |  172 ++
 grub-core/lib/libtasn1/lib/structure.c  | 1220 +++++++++++
 grub-core/lib/libtasn1/lib/structure.h  |   45 +
 include/grub/libtasn1.h                 |  588 ++++++
 15 files changed, 8791 insertions(+)
 create mode 100644 grub-core/lib/libtasn1/LICENSE
 create mode 100644 grub-core/lib/libtasn1/README.md
 create mode 100644 grub-core/lib/libtasn1/lib/coding.c
 create mode 100644 grub-core/lib/libtasn1/lib/decoding.c
 create mode 100644 grub-core/lib/libtasn1/lib/element.c
 create mode 100644 grub-core/lib/libtasn1/lib/element.h
 create mode 100644 grub-core/lib/libtasn1/lib/errors.c
 create mode 100644 grub-core/lib/libtasn1/lib/gstr.c
 create mode 100644 grub-core/lib/libtasn1/lib/gstr.h
 create mode 100644 grub-core/lib/libtasn1/lib/int.h
 create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.c
 create mode 100644 grub-core/lib/libtasn1/lib/parser_aux.h
 create mode 100644 grub-core/lib/libtasn1/lib/structure.c
 create mode 100644 grub-core/lib/libtasn1/lib/structure.h
 create mode 100644 include/grub/libtasn1.h

diff --git a/grub-core/lib/libtasn1/LICENSE b/grub-core/lib/libtasn1/LICENSE
new file mode 100644
index 000000000000..e8b3628db9b8
--- /dev/null
+++ b/grub-core/lib/libtasn1/LICENSE
@@ -0,0 +1,16 @@
+LICENSING
+=========
+
+The libtasn1 library is released under the GNU Lesser General Public
+License (LGPL) version 2.1 or later; see [COPYING.LESSER](doc/COPYING.LESSER)
+for the license terms.
+
+The GNU LGPL applies to the main libtasn1 library, while the
+included applications library are under the GNU GPL version 3.
+The libtasn1 library is located in the lib directory, while the applications
+in src/.
+
+The documentation in doc/ is under the GNU FDL license 1.3.
+
+For any copyright year range specified as YYYY-ZZZZ in this package
+note that the range specifies every single year in that closed interval.
diff --git a/grub-core/lib/libtasn1/README.md b/grub-core/lib/libtasn1/README.md
new file mode 100644
index 000000000000..50a8642296c3
--- /dev/null
+++ b/grub-core/lib/libtasn1/README.md
@@ -0,0 +1,91 @@
+|Branch|CI system|Status|
+|:----:|:-------:|-----:|
+|Master|Gitlab|[![build status](https://gitlab.com/gnutls/libtasn1/badges/master/pipeline.svg)](https://gitlab.com/gnutls/libtasn1/commits/master)[![coverage report](https://gitlab.com/gnutls/libtasn1/badges/master/coverage.svg)](https://gnutls.gitlab.io/libtasn1/coverage)|
+
+# libtasn1
+
+This is GNU Libtasn1, a small ASN.1 library.
+
+The C library (libtasn1.*) is licensed under the GNU Lesser General
+Public License version 2.1 or later.  See the file COPYING.LIB.
+
+The command line tool, self tests, examples, and other auxilliary
+files, are licensed under the GNU General Public License version 3.0
+or later.  See the file COPYING.
+
+## Building the library
+
+We require several tools to build the software, including:
+
+* [Make](https://www.gnu.org/software/make/)
+* [Automake](https://www.gnu.org/software/automake/) (use 1.11.3 or later)
+* [Autoconf](https://www.gnu.org/software/autoconf/)
+* [Libtool](https://www.gnu.org/software/libtool/)
+* [Texinfo](https://www.gnu.org/software/texinfo/)
+* [help2man](http://www.gnu.org/software/help2man/)
+* [Tar](https://www.gnu.org/software/tar/)
+* [Gzip](https://www.gnu.org/software/gzip/)
+* [bison](https://www.gnu.org/software/bison/)
+* [Texlive & epsf](https://www.tug.org/texlive/) (for PDF manual)
+* [GTK-DOC](https://www.gtk.org/gtk-doc/) (for API manual)
+* [Git](https://git-scm.com/)
+* [libabigail](https://pagure.io/libabigail/) (for abi comparison in make dist)
+* [Valgrind](https://valgrind.org/) (optional)
+
+The required software is typically distributed with your operating
+system, and the instructions for installing them differ.  Here are
+some hints:
+
+gNewSense/Debian/Ubuntu:
+```
+sudo apt-get install make git-core autoconf automake libtool
+sudo apt-get install texinfo texlive texlive-generic-recommended texlive-extra-utils
+sudo apt-get install help2man gtk-doc-tools valgrind abigail-tools
+```
+
+The next step is to run autoreconf, ./configure, etc:
+
+```
+$ ./bootstrap
+```
+
+Then build the project normally:
+
+```
+$ make
+$ make check
+```
+
+Happy hacking!
+
+
+## Manual
+
+The manual is in the `doc/` directory of the release.  You can also browse
+the manual online at:
+
+ - https://gnutls.gitlab.io/libtasn1/
+
+
+## Code coverage report
+
+The coverage report is at:
+
+ - https://gnutls.gitlab.io/libtasn1/coverage
+
+
+## Issue trackers
+
+ - [Main issue tracker](https://gitlab.com/gnutls/libtasn1/issues)
+ - [oss-fuzz found issues](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=libtasn1&can=2)
+
+
+## Homepage
+
+The project homepage at the gnu site is at:
+
+http://www.gnu.org/software/libtasn1/
+
+
+For any copyright year range specified as YYYY-ZZZZ in this package
+note that the range specifies every single year in that closed interval.
diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c
new file mode 100644
index 000000000000..245ea64cf0aa
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/coding.c
@@ -0,0 +1,1415 @@
+/*
+ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+/*****************************************************/
+/* File: coding.c                                    */
+/* Description: Functions to create a DER coding of  */
+/*   an ASN1 type.                                   */
+/*****************************************************/
+
+#include <int.h>
+#include "parser_aux.h"
+#include <gstr.h>
+#include "element.h"
+#include "minmax.h"
+#include <structure.h>
+
+#define MAX_TAG_LEN 16
+
+/******************************************************/
+/* Function : _asn1_error_description_value_not_found */
+/* Description: creates the ErrorDescription string   */
+/* for the ASN1_VALUE_NOT_FOUND error.                */
+/* Parameters:                                        */
+/*   node: node of the tree where the value is NULL.  */
+/*   ErrorDescription: string returned.               */
+/* Return:                                            */
+/******************************************************/
+static void
+_asn1_error_description_value_not_found (asn1_node node,
+					 char *ErrorDescription)
+{
+
+  if (ErrorDescription == NULL)
+    return;
+
+  Estrcpy (ErrorDescription, ":: value of element '");
+  _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription),
+			   ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40);
+  Estrcat (ErrorDescription, "' not found");
+
+}
+
+/**
+ * asn1_length_der:
+ * @len: value to convert.
+ * @der: buffer to hold the returned encoding (may be %NULL).
+ * @der_len: number of meaningful bytes of ANS (der[0]..der[der_len-1]).
+ *
+ * Creates the DER encoding of the provided length value.
+ * The @der buffer must have enough room for the output. The maximum
+ * length this function will encode is %ASN1_MAX_LENGTH_SIZE.
+ *
+ * To know the size of the DER encoding use a %NULL value for @der.
+ **/
+void
+asn1_length_der (unsigned long int len, unsigned char *der, int *der_len)
+{
+  int k;
+  unsigned char temp[ASN1_MAX_LENGTH_SIZE];
+#if SIZEOF_UNSIGNED_LONG_INT > 8
+  len &= 0xFFFFFFFFFFFFFFFF;
+#endif
+
+  if (len < 128)
+    {
+      /* short form */
+      if (der != NULL)
+	der[0] = (unsigned char) len;
+      *der_len = 1;
+    }
+  else
+    {
+      /* Long form */
+      k = 0;
+      while (len)
+	{
+	  temp[k++] = len & 0xFF;
+	  len = len >> 8;
+	}
+      *der_len = k + 1;
+      if (der != NULL)
+	{
+	  der[0] = ((unsigned char) k & 0x7F) + 128;
+	  while (k--)
+	    der[*der_len - 1 - k] = temp[k];
+	}
+    }
+}
+
+/******************************************************/
+/* Function : _asn1_tag_der                           */
+/* Description: creates the DER coding for the CLASS  */
+/* and TAG parameters.                                */
+/* It is limited by the ASN1_MAX_TAG_SIZE variable    */
+/* Parameters:                                        */
+/*   class: value to convert.                         */
+/*   tag_value: value to convert.                     */
+/*   ans: string returned.                            */
+/*   ans_len: number of meaningful bytes of ANS       */
+/*            (ans[0]..ans[ans_len-1]).               */
+/* Return:                                            */
+/******************************************************/
+static void
+_asn1_tag_der (unsigned char class, unsigned int tag_value,
+	       unsigned char ans[ASN1_MAX_TAG_SIZE], int *ans_len)
+{
+  int k;
+  unsigned char temp[ASN1_MAX_TAG_SIZE];
+
+  if (tag_value < 31)
+    {
+      /* short form */
+      ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F));
+      *ans_len = 1;
+    }
+  else
+    {
+      /* Long form */
+      ans[0] = (class & 0xE0) + 31;
+      k = 0;
+      while (tag_value != 0)
+	{
+	  temp[k++] = tag_value & 0x7F;
+	  tag_value >>= 7;
+
+	  if (k > ASN1_MAX_TAG_SIZE - 1)
+	    break;		/* will not encode larger tags */
+	}
+      *ans_len = k + 1;
+      while (k--)
+	ans[*ans_len - 1 - k] = temp[k] + 128;
+      ans[*ans_len - 1] -= 128;
+    }
+}
+
+/**
+ * asn1_octet_der:
+ * @str: the input data.
+ * @str_len: STR length (str[0]..str[*str_len-1]).
+ * @der: encoded string returned.
+ * @der_len: number of meaningful bytes of DER (der[0]..der[der_len-1]).
+ *
+ * Creates a length-value DER encoding for the input data.
+ * The DER encoding of the input data will be placed in the @der variable.
+ *
+ * Note that the OCTET STRING tag is not included in the output.
+ *
+ * This function does not return any value because it is expected
+ * that @der_len will contain enough bytes to store the string
+ * plus the DER encoding. The DER encoding size can be obtained using
+ * asn1_length_der().
+ **/
+void
+asn1_octet_der (const unsigned char *str, int str_len,
+		unsigned char *der, int *der_len)
+{
+  int len_len;
+
+  if (der == NULL || str_len < 0)
+    return;
+
+  asn1_length_der (str_len, der, &len_len);
+  memcpy (der + len_len, str, str_len);
+  *der_len = str_len + len_len;
+}
+
+
+/**
+ * asn1_encode_simple_der:
+ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
+ * @str: the string data.
+ * @str_len: the string length
+ * @tl: the encoded tag and length
+ * @tl_len: the bytes of the @tl field
+ *
+ * Creates the DER encoding for various simple ASN.1 types like strings etc.
+ * It stores the tag and length in @tl, which should have space for at least
+ * %ASN1_MAX_TL_SIZE bytes. Initially @tl_len should contain the size of @tl.
+ *
+ * The complete DER encoding should consist of the value in @tl appended
+ * with the provided @str.
+ *
+ * Returns: %ASN1_SUCCESS if successful or an error value.
+ **/
+int
+asn1_encode_simple_der (unsigned int etype, const unsigned char *str,
+			unsigned int str_len, unsigned char *tl,
+			unsigned int *tl_len)
+{
+  int tag_len, len_len;
+  unsigned tlen;
+  unsigned char der_tag[ASN1_MAX_TAG_SIZE];
+  unsigned char der_length[ASN1_MAX_LENGTH_SIZE];
+  unsigned char *p;
+
+  if (str == NULL)
+    return ASN1_VALUE_NOT_VALID;
+
+  if (ETYPE_OK (etype) == 0)
+    return ASN1_VALUE_NOT_VALID;
+
+  /* doesn't handle constructed classes */
+  if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL)
+    return ASN1_VALUE_NOT_VALID;
+
+  _asn1_tag_der (ETYPE_CLASS (etype), ETYPE_TAG (etype), der_tag, &tag_len);
+
+  asn1_length_der (str_len, der_length, &len_len);
+
+  if (tag_len <= 0 || len_len <= 0)
+    return ASN1_VALUE_NOT_VALID;
+
+  tlen = tag_len + len_len;
+
+  if (*tl_len < tlen)
+    return ASN1_MEM_ERROR;
+
+  p = tl;
+  memcpy (p, der_tag, tag_len);
+  p += tag_len;
+  memcpy (p, der_length, len_len);
+
+  *tl_len = tlen;
+
+  return ASN1_SUCCESS;
+}
+
+/******************************************************/
+/* Function : _asn1_time_der                          */
+/* Description: creates the DER coding for a TIME     */
+/* type (length included).                            */
+/* Parameters:                                        */
+/*   str: TIME null-terminated string.                */
+/*   der: string returned.                            */
+/*   der_len: number of meaningful bytes of DER       */
+/*            (der[0]..der[ans_len-1]). Initially it  */
+/*            if must store the lenght of DER.        */
+/* Return:                                            */
+/*   ASN1_MEM_ERROR when DER isn't big enough         */
+/*   ASN1_SUCCESS otherwise                           */
+/******************************************************/
+static int
+_asn1_time_der (unsigned char *str, int str_len, unsigned char *der,
+		int *der_len)
+{
+  int len_len;
+  int max_len;
+
+  if (der == NULL)
+    return ASN1_VALUE_NOT_VALID;
+
+  max_len = *der_len;
+
+  asn1_length_der (str_len, (max_len > 0) ? der : NULL, &len_len);
+
+  if ((len_len + str_len) <= max_len)
+    memcpy (der + len_len, str, str_len);
+  *der_len = len_len + str_len;
+
+  if ((*der_len) > max_len)
+    return ASN1_MEM_ERROR;
+
+  return ASN1_SUCCESS;
+}
+
+
+/*
+void
+_asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str)
+{
+  int len_len,str_len;
+  char temp[20];
+
+  if(str==NULL) return;
+  str_len=asn1_get_length_der(der,*der_len,&len_len);
+  if (str_len<0) return;
+  memcpy(temp,der+len_len,str_len);
+  *der_len=str_len+len_len;
+  switch(str_len)
+  {
+  case 11:
+    temp[10]=0;
+    strcat(temp,"00+0000");
+    break;
+  case 13:
+    temp[12]=0;
+    strcat(temp,"+0000");
+    break;
+  case 15:
+    temp[15]=0;
+    memmove(temp+12,temp+10,6);
+    temp[10]=temp[11]='0';
+    break;
+  case 17:
+    temp[17]=0;
+    break;
+  default:
+    return;
+  }
+  strcpy(str,temp);
+}
+*/
+
+static
+void encode_val(uint64_t val, unsigned char *der, int max_len, int *der_len)
+{
+  int first, k;
+  unsigned char bit7;
+
+  first = 0;
+  for (k = sizeof(val); k >= 0; k--)
+    {
+      bit7 = (val >> (k * 7)) & 0x7F;
+      if (bit7 || first || !k)
+	{
+	  if (k)
+	    bit7 |= 0x80;
+	  if (max_len > (*der_len))
+	    der[*der_len] = bit7;
+	  (*der_len)++;
+	  first = 1;
+	}
+    }
+}
+
+/******************************************************/
+/* Function : _asn1_object_id_der                     */
+/* Description: creates the DER coding for an         */
+/* OBJECT IDENTIFIER  type (length included).         */
+/* Parameters:                                        */
+/*   str: OBJECT IDENTIFIER null-terminated string.   */
+/*   der: string returned.                            */
+/*   der_len: number of meaningful bytes of DER       */
+/*            (der[0]..der[ans_len-1]). Initially it  */
+/*            must store the length of DER.           */
+/* Return:                                            */
+/*   ASN1_MEM_ERROR when DER isn't big enough         */
+/*   ASN1_SUCCESS if succesful                        */
+/*   or an error value.                               */
+/******************************************************/
+static int
+_asn1_object_id_der (const char *str, unsigned char *der, int *der_len)
+{
+  int len_len, counter, max_len;
+  char *temp, *n_end, *n_start;
+  uint64_t val, val1 = 0;
+  int str_len = _asn1_strlen (str);
+
+  max_len = *der_len;
+  *der_len = 0;
+
+  if (der == NULL && max_len > 0)
+    return ASN1_VALUE_NOT_VALID;
+
+  temp = malloc (str_len + 2);
+  if (temp == NULL)
+    return ASN1_MEM_ALLOC_ERROR;
+
+  memcpy (temp, str, str_len);
+  temp[str_len] = '.';
+  temp[str_len + 1] = 0;
+
+  counter = 0;
+  n_start = temp;
+  while ((n_end = strchr (n_start, '.')))
+    {
+      *n_end = 0;
+      val = _asn1_strtou64 (n_start, NULL, 10);
+      counter++;
+
+      if (counter == 1)
+        {
+	  val1 = val;
+	}
+      else if (counter == 2)
+	{
+	  uint64_t val0;
+
+          if (val1 > 2)
+            {
+              free(temp);
+              return ASN1_VALUE_NOT_VALID;
+            }
+          else if ((val1 == 0 || val1 == 1) && val > 39)
+            {
+              free(temp);
+              return ASN1_VALUE_NOT_VALID;
+            }
+
+	  val0 = 40 * val1 + val;
+	  encode_val(val0, der, max_len, der_len);
+	}
+      else
+	{
+	  encode_val(val, der, max_len, der_len);
+	}
+      n_start = n_end + 1;
+    }
+
+  asn1_length_der (*der_len, NULL, &len_len);
+  if (max_len >= (*der_len + len_len))
+    {
+      memmove (der + len_len, der, *der_len);
+      asn1_length_der (*der_len, der, &len_len);
+    }
+  *der_len += len_len;
+
+  free (temp);
+
+  if (max_len < (*der_len))
+    return ASN1_MEM_ERROR;
+
+  return ASN1_SUCCESS;
+}
+
+/**
+ * asn1_object_id_der:
+ * @str: An object identifier in numeric, dot format.
+ * @der: buffer to hold the returned encoding (may be %NULL).
+ * @der_len: initially the size of @der; will hold the final size.
+ * @flags: must be zero
+ *
+ * Creates the DER encoding of the provided object identifier.
+ *
+ * Returns: %ASN1_SUCCESS if DER encoding was OK, %ASN1_VALUE_NOT_VALID
+ *   if @str is not a valid OID, %ASN1_MEM_ERROR if the @der
+ *   vector isn't big enough and in this case @der_len will contain the
+ *   length needed.
+ **/
+int asn1_object_id_der(const char *str, unsigned char *der, int *der_len, unsigned flags)
+{
+  unsigned char tag_der[MAX_TAG_LEN];
+  int tag_len = 0, r;
+  int max_len = *der_len;
+
+  *der_len = 0;
+
+  _asn1_tag_der (ETYPE_CLASS (ASN1_ETYPE_OBJECT_ID), ETYPE_TAG (ASN1_ETYPE_OBJECT_ID),
+                 tag_der, &tag_len);
+
+  if (max_len > tag_len)
+    {
+      memcpy(der, tag_der, tag_len);
+    }
+  max_len -= tag_len;
+  der += tag_len;
+
+  r = _asn1_object_id_der (str, der, &max_len);
+  if (r == ASN1_MEM_ERROR || r == ASN1_SUCCESS)
+    {
+      *der_len = max_len + tag_len;
+    }
+
+  return r;
+}
+
+static const unsigned char bit_mask[] =
+  { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 };
+
+/**
+ * asn1_bit_der:
+ * @str: BIT string.
+ * @bit_len: number of meaningful bits in STR.
+ * @der: string returned.
+ * @der_len: number of meaningful bytes of DER
+ *   (der[0]..der[ans_len-1]).
+ *
+ * Creates a length-value DER encoding for the input data
+ * as it would have been for a BIT STRING.
+ * The DER encoded data will be copied in @der.
+ *
+ * Note that the BIT STRING tag is not included in the output.
+ *
+ * This function does not return any value because it is expected
+ * that @der_len will contain enough bytes to store the string
+ * plus the DER encoding. The DER encoding size can be obtained using
+ * asn1_length_der().
+ **/
+void
+asn1_bit_der (const unsigned char *str, int bit_len,
+	      unsigned char *der, int *der_len)
+{
+  int len_len, len_byte, len_pad;
+
+  if (der == NULL)
+    return;
+
+  len_byte = bit_len >> 3;
+  len_pad = 8 - (bit_len & 7);
+  if (len_pad == 8)
+    len_pad = 0;
+  else
+    len_byte++;
+  asn1_length_der (len_byte + 1, der, &len_len);
+  der[len_len] = len_pad;
+
+  if (str)
+    memcpy (der + len_len + 1, str, len_byte);
+  der[len_len + len_byte] &= bit_mask[len_pad];
+  *der_len = len_byte + len_len + 1;
+}
+
+
+/******************************************************/
+/* Function : _asn1_complete_explicit_tag             */
+/* Description: add the length coding to the EXPLICIT */
+/* tags.                                              */
+/* Parameters:                                        */
+/*   node: pointer to the tree element.               */
+/*   der: string with the DER coding of the whole tree*/
+/*   counter: number of meaningful bytes of DER       */
+/*            (der[0]..der[*counter-1]).              */
+/*   max_len: size of der vector                      */
+/* Return:                                            */
+/*   ASN1_MEM_ERROR if der vector isn't big enough,   */
+/*   otherwise ASN1_SUCCESS.                          */
+/******************************************************/
+static int
+_asn1_complete_explicit_tag (asn1_node node, unsigned char *der,
+			     int *counter, int *max_len)
+{
+  asn1_node p;
+  int is_tag_implicit, len2, len3;
+  unsigned char temp[SIZEOF_UNSIGNED_INT];
+
+  if (der == NULL && *max_len > 0)
+    return ASN1_VALUE_NOT_VALID;
+
+  is_tag_implicit = 0;
+
+  if (node->type & CONST_TAG)
+    {
+      p = node->down;
+      if (p == NULL)
+        return ASN1_DER_ERROR;
+      /* When there are nested tags we must complete them reverse to
+         the order they were created. This is because completing a tag
+         modifies all data within it, including the incomplete tags
+         which store buffer positions -- simon@josefsson.org 2002-09-06
+       */
+      while (p->right)
+	p = p->right;
+      while (p && p != node->down->left)
+	{
+	  if (type_field (p->type) == ASN1_ETYPE_TAG)
+	    {
+	      if (p->type & CONST_EXPLICIT)
+		{
+		  len2 = strtol (p->name, NULL, 10);
+		  _asn1_set_name (p, NULL);
+
+		  asn1_length_der (*counter - len2, temp, &len3);
+		  if (len3 <= (*max_len))
+		    {
+		      memmove (der + len2 + len3, der + len2,
+			       *counter - len2);
+		      memcpy (der + len2, temp, len3);
+		    }
+		  *max_len -= len3;
+		  *counter += len3;
+		  is_tag_implicit = 0;
+		}
+	      else
+		{		/* CONST_IMPLICIT */
+		  if (!is_tag_implicit)
+		    {
+		      is_tag_implicit = 1;
+		    }
+		}
+	    }
+	  p = p->left;
+	}
+    }
+
+  if (*max_len < 0)
+    return ASN1_MEM_ERROR;
+
+  return ASN1_SUCCESS;
+}
+
+const tag_and_class_st _asn1_tags[] = {
+  [ASN1_ETYPE_GENERALSTRING] =
+    {ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, "type:GENERALSTRING"},
+  [ASN1_ETYPE_NUMERIC_STRING] =
+    {ASN1_TAG_NUMERIC_STRING, ASN1_CLASS_UNIVERSAL, "type:NUMERIC_STR"},
+  [ASN1_ETYPE_IA5_STRING] =
+    {ASN1_TAG_IA5_STRING, ASN1_CLASS_UNIVERSAL, "type:IA5_STR"},
+  [ASN1_ETYPE_TELETEX_STRING] =
+    {ASN1_TAG_TELETEX_STRING, ASN1_CLASS_UNIVERSAL, "type:TELETEX_STR"},
+  [ASN1_ETYPE_PRINTABLE_STRING] =
+    {ASN1_TAG_PRINTABLE_STRING, ASN1_CLASS_UNIVERSAL, "type:PRINTABLE_STR"},
+  [ASN1_ETYPE_UNIVERSAL_STRING] =
+    {ASN1_TAG_UNIVERSAL_STRING, ASN1_CLASS_UNIVERSAL, "type:UNIVERSAL_STR"},
+  [ASN1_ETYPE_BMP_STRING] =
+    {ASN1_TAG_BMP_STRING, ASN1_CLASS_UNIVERSAL, "type:BMP_STR"},
+  [ASN1_ETYPE_UTF8_STRING] =
+    {ASN1_TAG_UTF8_STRING, ASN1_CLASS_UNIVERSAL, "type:UTF8_STR"},
+  [ASN1_ETYPE_VISIBLE_STRING] =
+    {ASN1_TAG_VISIBLE_STRING, ASN1_CLASS_UNIVERSAL, "type:VISIBLE_STR"},
+  [ASN1_ETYPE_OCTET_STRING] =
+    {ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, "type:OCT_STR"},
+  [ASN1_ETYPE_BIT_STRING] =
+    {ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, "type:BIT_STR"},
+  [ASN1_ETYPE_OBJECT_ID] =
+    {ASN1_TAG_OBJECT_ID, ASN1_CLASS_UNIVERSAL, "type:OBJ_ID"},
+  [ASN1_ETYPE_NULL] = {ASN1_TAG_NULL, ASN1_CLASS_UNIVERSAL, "type:NULL"},
+  [ASN1_ETYPE_BOOLEAN] =
+    {ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, "type:BOOLEAN"},
+  [ASN1_ETYPE_INTEGER] =
+    {ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, "type:INTEGER"},
+  [ASN1_ETYPE_ENUMERATED] =
+    {ASN1_TAG_ENUMERATED, ASN1_CLASS_UNIVERSAL, "type:ENUMERATED"},
+  [ASN1_ETYPE_SEQUENCE] =
+    {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
+     "type:SEQUENCE"},
+  [ASN1_ETYPE_SEQUENCE_OF] =
+    {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
+     "type:SEQ_OF"},
+  [ASN1_ETYPE_SET] =
+    {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET"},
+  [ASN1_ETYPE_SET_OF] =
+    {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED,
+     "type:SET_OF"},
+  [ASN1_ETYPE_GENERALIZED_TIME] =
+    {ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, "type:GENERALIZED_TIME"},
+  [ASN1_ETYPE_UTC_TIME] =
+    {ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, "type:UTC_TIME"},
+};
+
+unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
+
+/******************************************************/
+/* Function : _asn1_insert_tag_der                    */
+/* Description: creates the DER coding of tags of one */
+/* NODE.                                              */
+/* Parameters:                                        */
+/*   node: pointer to the tree element.               */
+/*   der: string returned                             */
+/*   counter: number of meaningful bytes of DER       */
+/*            (counter[0]..der[*counter-1]).          */
+/*   max_len: size of der vector                      */
+/* Return:                                            */
+/*   ASN1_GENERIC_ERROR if the type is unknown,       */
+/*   ASN1_MEM_ERROR if der vector isn't big enough,   */
+/*   otherwise ASN1_SUCCESS.                          */
+/******************************************************/
+static int
+_asn1_insert_tag_der (asn1_node node, unsigned char *der, int *counter,
+		      int *max_len)
+{
+  asn1_node p;
+  int tag_len, is_tag_implicit;
+  unsigned char class, class_implicit = 0, temp[MAX(SIZEOF_UNSIGNED_INT * 3 + 1, LTOSTR_MAX_SIZE)];
+  unsigned long tag_implicit = 0;
+  unsigned char tag_der[MAX_TAG_LEN];
+
+  is_tag_implicit = 0;
+
+  if (node->type & CONST_TAG)
+    {
+      p = node->down;
+      while (p)
+	{
+	  if (type_field (p->type) == ASN1_ETYPE_TAG)
+	    {
+	      if (p->type & CONST_APPLICATION)
+		class = ASN1_CLASS_APPLICATION;
+	      else if (p->type & CONST_UNIVERSAL)
+		class = ASN1_CLASS_UNIVERSAL;
+	      else if (p->type & CONST_PRIVATE)
+		class = ASN1_CLASS_PRIVATE;
+	      else
+		class = ASN1_CLASS_CONTEXT_SPECIFIC;
+
+	      if (p->type & CONST_EXPLICIT)
+		{
+		  if (is_tag_implicit)
+		    _asn1_tag_der (class_implicit, tag_implicit, tag_der,
+				   &tag_len);
+		  else
+		    _asn1_tag_der (class | ASN1_CLASS_STRUCTURED,
+				   _asn1_strtoul (p->value, NULL, 10),
+				   tag_der, &tag_len);
+
+		  *max_len -= tag_len;
+		  if (der && *max_len >= 0)
+		    memcpy (der + *counter, tag_der, tag_len);
+		  *counter += tag_len;
+
+		  _asn1_ltostr (*counter, (char *) temp);
+		  _asn1_set_name (p, (const char *) temp);
+
+		  is_tag_implicit = 0;
+		}
+	      else
+		{		/* CONST_IMPLICIT */
+		  if (!is_tag_implicit)
+		    {
+		      if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) ||
+			  (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF)
+			  || (type_field (node->type) == ASN1_ETYPE_SET)
+			  || (type_field (node->type) == ASN1_ETYPE_SET_OF))
+			class |= ASN1_CLASS_STRUCTURED;
+		      class_implicit = class;
+		      tag_implicit = _asn1_strtoul (p->value, NULL, 10);
+		      is_tag_implicit = 1;
+		    }
+		}
+	    }
+	  p = p->right;
+	}
+    }
+
+  if (is_tag_implicit)
+    {
+      _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len);
+    }
+  else
+    {
+      unsigned type = type_field (node->type);
+      switch (type)
+	{
+	CASE_HANDLED_ETYPES:
+	  _asn1_tag_der (_asn1_tags[type].class, _asn1_tags[type].tag,
+			 tag_der, &tag_len);
+	  break;
+	case ASN1_ETYPE_TAG:
+	case ASN1_ETYPE_CHOICE:
+	case ASN1_ETYPE_ANY:
+	  tag_len = 0;
+	  break;
+	default:
+	  return ASN1_GENERIC_ERROR;
+	}
+    }
+
+  *max_len -= tag_len;
+  if (der && *max_len >= 0)
+    memcpy (der + *counter, tag_der, tag_len);
+  *counter += tag_len;
+
+  if (*max_len < 0)
+    return ASN1_MEM_ERROR;
+
+  return ASN1_SUCCESS;
+}
+
+/******************************************************/
+/* Function : _asn1_ordering_set                      */
+/* Description: puts the elements of a SET type in    */
+/* the correct order according to DER rules.          */
+/* Parameters:                                        */
+/*   der: string with the DER coding.                 */
+/*   node: pointer to the SET element.                */
+/* Return:                                            */
+/*    ASN1_SUCCESS if successful                      */
+/*    or an error value.                              */
+/******************************************************/
+static int
+_asn1_ordering_set (unsigned char *der, int der_len, asn1_node node)
+{
+  struct vet
+  {
+    int end;
+    unsigned long value;
+    struct vet *next, *prev;
+  };
+
+  int counter, len, len2;
+  struct vet *first, *last, *p_vet, *p2_vet;
+  asn1_node p;
+  unsigned char class, *temp;
+  unsigned long tag, t;
+  int err;
+
+  counter = 0;
+
+  if (type_field (node->type) != ASN1_ETYPE_SET)
+    return ASN1_VALUE_NOT_VALID;
+
+  p = node->down;
+  while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) ||
+	 (type_field (p->type) == ASN1_ETYPE_SIZE)))
+    p = p->right;
+
+  if ((p == NULL) || (p->right == NULL))
+    return ASN1_SUCCESS;
+
+  first = last = NULL;
+  while (p)
+    {
+      p_vet = malloc (sizeof (struct vet));
+      if (p_vet == NULL)
+        {
+	  err = ASN1_MEM_ALLOC_ERROR;
+	  goto error;
+	}
+
+      p_vet->next = NULL;
+      p_vet->prev = last;
+      if (first == NULL)
+	first = p_vet;
+      else
+	last->next = p_vet;
+      last = p_vet;
+
+      /* tag value calculation */
+      err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len2,
+			      &tag);
+      if (err != ASN1_SUCCESS)
+	goto error;
+
+      t = ((unsigned int)class) << 24;
+      p_vet->value = t | tag;
+      counter += len2;
+
+      /* extraction and length */
+      len2 = asn1_get_length_der (der + counter, der_len - counter, &len);
+      if (len2 < 0)
+	{
+	  err = ASN1_DER_ERROR;
+	  goto error;
+	}
+      counter += len + len2;
+
+      p_vet->end = counter;
+      p = p->right;
+    }
+
+  p_vet = first;
+
+  while (p_vet)
+    {
+      p2_vet = p_vet->next;
+      counter = 0;
+      while (p2_vet)
+	{
+	  if (p_vet->value > p2_vet->value)
+	    {
+	      /* change position */
+	      temp = malloc (p_vet->end - counter);
+	      if (temp == NULL)
+		{
+		  err = ASN1_MEM_ALLOC_ERROR;
+		  goto error;
+		}
+
+	      memcpy (temp, der + counter, p_vet->end - counter);
+	      memcpy (der + counter, der + p_vet->end,
+		      p2_vet->end - p_vet->end);
+	      memcpy (der + counter + p2_vet->end - p_vet->end, temp,
+		      p_vet->end - counter);
+	      free (temp);
+
+	      tag = p_vet->value;
+	      p_vet->value = p2_vet->value;
+	      p2_vet->value = tag;
+
+	      p_vet->end = counter + (p2_vet->end - p_vet->end);
+	    }
+	  counter = p_vet->end;
+
+	  p2_vet = p2_vet->next;
+	  p_vet = p_vet->next;
+	}
+
+      if (p_vet != first)
+	p_vet->prev->next = NULL;
+      else
+	first = NULL;
+      free (p_vet);
+      p_vet = first;
+    }
+  return ASN1_SUCCESS;
+
+error:
+  while (first != NULL)
+    {
+      p_vet = first;
+      first = first->next;
+      free(p_vet);
+    }
+  return err;
+}
+
+struct vet
+{
+  unsigned char *ptr;
+  int size;
+};
+
+static int setof_compar(const void *_e1, const void *_e2)
+{
+  unsigned length;
+  const struct vet *e1 = _e1, *e2 = _e2;
+  int rval;
+
+  /* The encodings of the component values of a set-of value shall
+   * appear in ascending order, the encodings being compared
+   * as octet strings with the shorter components being
+   * padded at their trailing end with 0-octets.
+   * The padding octets are for comparison purposes and
+   * do not appear in the encodings.
+   */
+  length = MIN(e1->size, e2->size);
+
+  rval = memcmp(e1->ptr, e2->ptr, length);
+  if (rval == 0 && e1->size != e2->size)
+    {
+      if (e1->size > e2->size)
+        rval = 1;
+      else if (e2->size > e1->size)
+        rval = -1;
+    }
+
+  return rval;
+}
+
+/******************************************************/
+/* Function : _asn1_ordering_set_of                   */
+/* Description: puts the elements of a SET OF type in */
+/* the correct order according to DER rules.          */
+/* Parameters:                                        */
+/*   der: string with the DER coding.                 */
+/*   node: pointer to the SET OF element.             */
+/* Return:                                            */
+/*    ASN1_SUCCESS if successful                      */
+/*    or an error value.                              */
+/******************************************************/
+static int
+_asn1_ordering_set_of (unsigned char *der, int der_len, asn1_node node)
+{
+  int counter, len, len2;
+  struct vet *list = NULL, *tlist;
+  unsigned list_size = 0;
+  struct vet *p_vet;
+  asn1_node p;
+  unsigned char class;
+  unsigned i;
+  unsigned char *out = NULL;
+  int err;
+
+  if (der == NULL)
+    return ASN1_VALUE_NOT_VALID;
+
+  counter = 0;
+
+  if (type_field (node->type) != ASN1_ETYPE_SET_OF)
+    return ASN1_VALUE_NOT_VALID;
+
+  p = node->down;
+  while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) ||
+	 (type_field (p->type) == ASN1_ETYPE_SIZE)))
+    p = p->right;
+  if (p == NULL)
+    return ASN1_VALUE_NOT_VALID;
+  p = p->right;
+
+  if ((p == NULL) || (p->right == NULL))
+    return ASN1_SUCCESS;
+
+  while (p)
+    {
+      list_size++;
+      tlist = realloc (list, list_size*sizeof(struct vet));
+      if (tlist == NULL)
+	{
+	  err = ASN1_MEM_ALLOC_ERROR;
+	  goto error;
+	}
+      list = tlist;
+      p_vet = &list[list_size-1];
+
+      p_vet->ptr = der+counter;
+      p_vet->size = 0;
+
+      /* extraction of tag and length */
+      if (der_len - counter > 0)
+	{
+	  err = asn1_get_tag_der (der + counter, der_len - counter, &class,
+	                          &len, NULL);
+	  if (err != ASN1_SUCCESS)
+	    goto error;
+	  counter += len;
+          p_vet->size += len;
+
+	  len2 = asn1_get_length_der (der + counter, der_len - counter, &len);
+	  if (len2 < 0)
+	    {
+	      err = ASN1_DER_ERROR;
+	      goto error;
+	    }
+	  counter += len + len2;
+          p_vet->size += len + len2;
+
+	}
+      else
+	{
+	  err = ASN1_DER_ERROR;
+	  goto error;
+	}
+      p = p->right;
+    }
+
+  if (counter > der_len)
+    {
+      err = ASN1_DER_ERROR;
+      goto error;
+    }
+
+  qsort(list, list_size, sizeof(struct vet), setof_compar);
+
+  out = malloc(der_len);
+  if (out == NULL)
+    {
+      err = ASN1_MEM_ERROR;
+      goto error;
+    }
+
+  /* the sum of p_vet->size == der_len */
+  counter = 0;
+  for (i = 0; i < list_size; i++)
+    {
+      p_vet = &list[i];
+      memcpy(out+counter, p_vet->ptr, p_vet->size);
+      counter += p_vet->size;
+    }
+  memcpy(der, out, der_len);
+  free(out);
+
+  err = ASN1_SUCCESS;
+
+error:
+  free(list);
+  return err;
+}
+
+/**
+ * asn1_der_coding:
+ * @element: pointer to an ASN1 element
+ * @name: the name of the structure you want to encode (it must be
+ *   inside *POINTER).
+ * @ider: vector that will contain the DER encoding. DER must be a
+ *   pointer to memory cells already allocated.
+ * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy
+ *   holds the sizeof of der vector.
+ * @ErrorDescription: return the error description or an empty
+ *   string if success.
+ *
+ * Creates the DER encoding for the NAME structure (inside *POINTER
+ * structure).
+ *
+ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
+ *   if @name is not a valid element, %ASN1_VALUE_NOT_FOUND if there
+ *   is an element without a value, %ASN1_MEM_ERROR if the @ider
+ *   vector isn't big enough and in this case @len will contain the
+ *   length needed.
+ **/
+int
+asn1_der_coding (asn1_node_const element, const char *name, void *ider, int *len,
+		 char *ErrorDescription)
+{
+  asn1_node node, p, p2;
+  unsigned char temp[MAX(LTOSTR_MAX_SIZE, SIZEOF_UNSIGNED_LONG_INT * 3 + 1)];
+  int counter, counter_old, len2, len3, move, max_len, max_len_old;
+  int err;
+  unsigned char *der = ider;
+
+  if (ErrorDescription)
+    ErrorDescription[0] = 0;
+
+  node = asn1_find_node (element, name);
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  /* Node is now a locally allocated variable.
+   * That is because in some point we modify the
+   * structure, and I don't know why! --nmav
+   */
+  node = _asn1_copy_structure3 (node);
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  max_len = *len;
+
+  if (der == NULL && max_len > 0)
+    return ASN1_VALUE_NOT_VALID;
+
+  counter = 0;
+  move = DOWN;
+  p = node;
+
+  while (1)
+    {
+
+      counter_old = counter;
+      max_len_old = max_len;
+      if (move != UP)
+	{
+          p->start = counter;
+	  err = _asn1_insert_tag_der (p, der, &counter, &max_len);
+	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
+	    goto error;
+	}
+      switch (type_field (p->type))
+	{
+	case ASN1_ETYPE_NULL:
+	  max_len--;
+	  if (der != NULL && max_len >= 0)
+	    der[counter] = 0;
+	  counter++;
+	  move = RIGHT;
+	  break;
+	case ASN1_ETYPE_BOOLEAN:
+	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
+	    {
+	      counter = counter_old;
+	      max_len = max_len_old;
+	    }
+	  else
+	    {
+	      if (p->value == NULL)
+		{
+		  _asn1_error_description_value_not_found (p,
+							   ErrorDescription);
+		  err = ASN1_VALUE_NOT_FOUND;
+		  goto error;
+		}
+	      max_len -= 2;
+	      if (der != NULL && max_len >= 0)
+		{
+		  der[counter++] = 1;
+		  if (p->value[0] == 'F')
+		    der[counter++] = 0;
+		  else
+		    der[counter++] = 0xFF;
+		}
+	      else
+		counter += 2;
+	    }
+	  move = RIGHT;
+	  break;
+	case ASN1_ETYPE_INTEGER:
+	case ASN1_ETYPE_ENUMERATED:
+	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
+	    {
+	      counter = counter_old;
+	      max_len = max_len_old;
+	    }
+	  else
+	    {
+	      if (p->value == NULL)
+		{
+		  _asn1_error_description_value_not_found (p,
+							   ErrorDescription);
+		  err = ASN1_VALUE_NOT_FOUND;
+		  goto error;
+		}
+	      len2 = asn1_get_length_der (p->value, p->value_len, &len3);
+	      if (len2 < 0)
+		{
+		  err = ASN1_DER_ERROR;
+		  goto error;
+		}
+	      max_len -= len2 + len3;
+	      if (der != NULL && max_len >= 0)
+		memcpy (der + counter, p->value, len3 + len2);
+	      counter += len3 + len2;
+	    }
+	  move = RIGHT;
+	  break;
+	case ASN1_ETYPE_OBJECT_ID:
+	  if ((p->type & CONST_DEFAULT) && (p->value == NULL))
+	    {
+	      counter = counter_old;
+	      max_len = max_len_old;
+	    }
+	  else
+	    {
+	      if (p->value == NULL)
+		{
+		  _asn1_error_description_value_not_found (p,
+							   ErrorDescription);
+		  err = ASN1_VALUE_NOT_FOUND;
+		  goto error;
+		}
+	      len2 = max_len;
+	      err = _asn1_object_id_der ((char*)p->value, der + counter, &len2);
+	      if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
+		goto error;
+
+	      max_len -= len2;
+	      counter += len2;
+	    }
+	  move = RIGHT;
+	  break;
+	case ASN1_ETYPE_GENERALIZED_TIME:
+	case ASN1_ETYPE_UTC_TIME:
+	  if (p->value == NULL)
+	    {
+	      _asn1_error_description_value_not_found (p, ErrorDescription);
+	      err = ASN1_VALUE_NOT_FOUND;
+	      goto error;
+	    }
+	  len2 = max_len;
+	  err = _asn1_time_der (p->value, p->value_len, der + counter, &len2);
+	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
+	    goto error;
+
+	  max_len -= len2;
+	  counter += len2;
+	  move = RIGHT;
+	  break;
+	case ASN1_ETYPE_OCTET_STRING:
+	case ASN1_ETYPE_GENERALSTRING:
+	case ASN1_ETYPE_NUMERIC_STRING:
+	case ASN1_ETYPE_IA5_STRING:
+	case ASN1_ETYPE_TELETEX_STRING:
+	case ASN1_ETYPE_PRINTABLE_STRING:
+	case ASN1_ETYPE_UNIVERSAL_STRING:
+	case ASN1_ETYPE_BMP_STRING:
+	case ASN1_ETYPE_UTF8_STRING:
+	case ASN1_ETYPE_VISIBLE_STRING:
+	case ASN1_ETYPE_BIT_STRING:
+	  if (p->value == NULL)
+	    {
+	      _asn1_error_description_value_not_found (p, ErrorDescription);
+	      err = ASN1_VALUE_NOT_FOUND;
+	      goto error;
+	    }
+	  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
+	  if (len2 < 0)
+	    {
+	      err = ASN1_DER_ERROR;
+	      goto error;
+	    }
+	  max_len -= len2 + len3;
+	  if (der != NULL && max_len >= 0)
+	    memcpy (der + counter, p->value, len3 + len2);
+	  counter += len3 + len2;
+	  move = RIGHT;
+	  break;
+	case ASN1_ETYPE_SEQUENCE:
+	case ASN1_ETYPE_SET:
+	  if (move != UP)
+	    {
+	      p->tmp_ival = counter;
+	      if (p->down == NULL)
+		{
+		  move = UP;
+		  continue;
+		}
+	      else
+		{
+		  p2 = p->down;
+		  while (p2 && (type_field (p2->type) == ASN1_ETYPE_TAG))
+		    p2 = p2->right;
+		  if (p2)
+		    {
+		      p = p2;
+		      move = RIGHT;
+		      continue;
+		    }
+		  move = UP;
+		  continue;
+		}
+	    }
+	  else
+	    {			/* move==UP */
+	      len2 = p->tmp_ival;
+	      p->tmp_ival = 0;
+	      if ((type_field (p->type) == ASN1_ETYPE_SET) && (max_len >= 0))
+		{
+		  err = _asn1_ordering_set (der + len2, counter - len2, p);
+		  if (err != ASN1_SUCCESS)
+		    goto error;
+		}
+	      asn1_length_der (counter - len2, temp, &len3);
+	      max_len -= len3;
+	      if (der != NULL && max_len >= 0)
+		{
+		  memmove (der + len2 + len3, der + len2, counter - len2);
+		  memcpy (der + len2, temp, len3);
+		}
+	      counter += len3;
+	      move = RIGHT;
+	    }
+	  break;
+	case ASN1_ETYPE_SEQUENCE_OF:
+	case ASN1_ETYPE_SET_OF:
+	  if (move != UP)
+	    {
+	      p->tmp_ival = counter;
+	      p = p->down;
+	      while ((type_field (p->type) == ASN1_ETYPE_TAG)
+		     || (type_field (p->type) == ASN1_ETYPE_SIZE))
+		p = p->right;
+	      if (p->right)
+		{
+		  p = p->right;
+		  move = RIGHT;
+		  continue;
+		}
+	      else
+		p = _asn1_find_up (p);
+	      move = UP;
+	    }
+	  if (move == UP)
+	    {
+	      len2 = p->tmp_ival;
+	      p->tmp_ival = 0;
+	      if ((type_field (p->type) == ASN1_ETYPE_SET_OF)
+		  && (counter - len2 > 0) && (max_len >= 0))
+		{
+		  err = _asn1_ordering_set_of (der + len2, counter - len2, p);
+		  if (err != ASN1_SUCCESS)
+		    goto error;
+		}
+	      asn1_length_der (counter - len2, temp, &len3);
+	      max_len -= len3;
+	      if (der != NULL && max_len >= 0)
+		{
+		  memmove (der + len2 + len3, der + len2, counter - len2);
+		  memcpy (der + len2, temp, len3);
+		}
+	      counter += len3;
+	      move = RIGHT;
+	    }
+	  break;
+	case ASN1_ETYPE_ANY:
+	  if (p->value == NULL)
+	    {
+	      _asn1_error_description_value_not_found (p, ErrorDescription);
+	      err = ASN1_VALUE_NOT_FOUND;
+	      goto error;
+	    }
+	  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
+	  if (len2 < 0)
+	    {
+	      err = ASN1_DER_ERROR;
+	      goto error;
+	    }
+	  max_len -= len2;
+	  if (der != NULL && max_len >= 0)
+	    memcpy (der + counter, p->value + len3, len2);
+	  counter += len2;
+	  move = RIGHT;
+	  break;
+	default:
+	  move = (move == UP) ? RIGHT : DOWN;
+	  break;
+	}
+
+      if ((move != DOWN) && (counter != counter_old))
+	{
+          p->end = counter - 1;
+	  err = _asn1_complete_explicit_tag (p, der, &counter, &max_len);
+	  if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR)
+	    goto error;
+	}
+
+      if (p == node && move != DOWN)
+	break;
+
+      if (move == DOWN)
+	{
+	  if (p->down)
+	    p = p->down;
+	  else
+	    move = RIGHT;
+	}
+      if (move == RIGHT)
+	{
+	  if (p->right)
+	    p = p->right;
+	  else
+	    move = UP;
+	}
+      if (move == UP)
+	p = _asn1_find_up (p);
+    }
+
+  *len = counter;
+
+  if (max_len < 0)
+    {
+      err = ASN1_MEM_ERROR;
+      goto error;
+    }
+
+  err = ASN1_SUCCESS;
+
+error:
+  asn1_delete_structure (&node);
+  return err;
+}
diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c
new file mode 100644
index 000000000000..ff04eb778cba
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -0,0 +1,2478 @@
+/*
+ * Copyright (C) 2002-2016 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+/*****************************************************/
+/* File: decoding.c                                  */
+/* Description: Functions to manage DER decoding     */
+/*****************************************************/
+
+#include <int.h>
+#include <parser_aux.h>
+#include <gstr.h>
+#include <structure.h>
+#include <element.h>
+#include <limits.h>
+#include <intprops.h>
+#include <c-ctype.h>
+
+#ifdef DEBUG
+# define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__)
+#else
+# define warn()
+#endif
+
+#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0))
+
+#define HAVE_TWO(x) (x>=2?1:0)
+
+/* Decoding flags (dflags) used in several decoding functions.
+ *  DECODE_FLAG_HAVE_TAG: The provided buffer includes a tag
+ *  DECODE_FLAG_CONSTRUCTED: The provided buffer is of indefinite encoding (useful
+ *                           when no tags are present).
+ *  DECODE_FLAG_LEVEL1: Internal flag to indicate a level of recursion for BER strings.
+ *  DECODE_FLAG_LEVEL2: Internal flag to indicate two levels of recursion for BER strings.
+ *  DECODE_FLAG_LEVEL3: Internal flag to indicate three levels of recursion for BER strings.
+ *                      This is the maximum levels of recursion possible to prevent stack
+ *                      exhaustion.
+ */
+
+#define DECODE_FLAG_HAVE_TAG 1
+#define DECODE_FLAG_CONSTRUCTED (1<<1)
+#define DECODE_FLAG_LEVEL1 (1<<2)
+#define DECODE_FLAG_LEVEL2 (1<<3)
+#define DECODE_FLAG_LEVEL3 (1<<4)
+
+#define DECR_LEN(l, s) do { \
+	  l -= s; \
+	  if (l < 0) { \
+	    warn(); \
+	    result = ASN1_DER_ERROR; \
+	    goto cleanup; \
+	  } \
+	} while (0)
+
+static int
+_asn1_get_indefinite_length_string (const unsigned char *der, int der_len, int *len);
+
+static int
+_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
+			unsigned int _der_len, unsigned char **str,
+			unsigned int *str_len, unsigned int *ber_len,
+			unsigned dflags);
+
+static int
+_asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
+			unsigned int _der_len, const unsigned char **str,
+			unsigned int *str_len, unsigned dflags);
+
+static void
+_asn1_error_description_tag_error (asn1_node node, char *ErrorDescription)
+{
+
+  Estrcpy (ErrorDescription, ":: tag error near element '");
+  _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription),
+			   ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40);
+  Estrcat (ErrorDescription, "'");
+
+}
+
+/**
+ * asn1_get_length_der:
+ * @der: DER data to decode.
+ * @der_len: Length of DER data to decode.
+ * @len: Output variable containing the length of the DER length field.
+ *
+ * Extract a length field from DER data.
+ *
+ * Returns: Return the decoded length value, or -1 on indefinite
+ *   length, or -2 when the value was too big to fit in a int, or -4
+ *   when the decoded length value plus @len would exceed @der_len.
+ **/
+long
+asn1_get_length_der (const unsigned char *der, int der_len, int *len)
+{
+  unsigned int ans;
+  int k, punt, sum;
+
+  *len = 0;
+  if (der_len <= 0)
+    return 0;
+
+  if (!(der[0] & 128))
+    {
+      /* short form */
+      *len = 1;
+      ans = der[0];
+    }
+  else
+    {
+      /* Long form */
+      k = der[0] & 0x7F;
+      punt = 1;
+      if (k)
+	{ /* definite length method */
+	  ans = 0;
+	  while (punt <= k && punt < der_len)
+	    {
+	      if (INT_MULTIPLY_OVERFLOW (ans, 256))
+		return -2;
+	      ans *= 256;
+
+	      if (INT_ADD_OVERFLOW (ans, ((unsigned) der[punt])))
+		return -2;
+	      ans += der[punt];
+	      punt++;
+	    }
+	}
+      else
+	{			/* indefinite length method */
+	  *len = punt;
+	  return -1;
+	}
+
+      *len = punt;
+    }
+
+  sum = ans;
+  if (ans >= INT_MAX || INT_ADD_OVERFLOW (sum, (*len)))
+    return -2;
+  sum += *len;
+
+  if (sum > der_len)
+    return -4;
+
+  return ans;
+}
+
+/**
+ * asn1_get_tag_der:
+ * @der: DER data to decode.
+ * @der_len: Length of DER data to decode.
+ * @cls: Output variable containing decoded class.
+ * @len: Output variable containing the length of the DER TAG data.
+ * @tag: Output variable containing the decoded tag (may be %NULL).
+ *
+ * Decode the class and TAG from DER code.
+ *
+ * Returns: Returns %ASN1_SUCCESS on success, or an error.
+ **/
+int
+asn1_get_tag_der (const unsigned char *der, int der_len,
+		  unsigned char *cls, int *len, unsigned long *tag)
+{
+  unsigned int ris;
+  int punt;
+
+  if (der == NULL || der_len < 2 || len == NULL)
+    return ASN1_DER_ERROR;
+
+  *cls = der[0] & 0xE0;
+  if ((der[0] & 0x1F) != 0x1F)
+    {
+      /* short form */
+      *len = 1;
+      ris = der[0] & 0x1F;
+    }
+  else
+    {
+      /* Long form */
+      punt = 1;
+      ris = 0;
+      while (punt < der_len && der[punt] & 128)
+	{
+
+	  if (INT_MULTIPLY_OVERFLOW (ris, 128))
+	    return ASN1_DER_ERROR;
+	  ris *= 128;
+
+	  if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F))))
+	    return ASN1_DER_ERROR;
+	  ris += (der[punt] & 0x7F);
+	  punt++;
+	}
+
+      if (punt >= der_len)
+	return ASN1_DER_ERROR;
+
+      if (INT_MULTIPLY_OVERFLOW (ris, 128))
+	return ASN1_DER_ERROR;
+      ris *= 128;
+
+      if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F))))
+	return ASN1_DER_ERROR;
+      ris += (der[punt] & 0x7F);
+      punt++;
+
+      *len = punt;
+    }
+
+  if (tag)
+    *tag = ris;
+  return ASN1_SUCCESS;
+}
+
+/**
+ * asn1_get_length_ber:
+ * @ber: BER data to decode.
+ * @ber_len: Length of BER data to decode.
+ * @len: Output variable containing the length of the BER length field.
+ *
+ * Extract a length field from BER data.  The difference to
+ * asn1_get_length_der() is that this function will return a length
+ * even if the value has indefinite encoding.
+ *
+ * Returns: Return the decoded length value, or negative value when
+ *   the value was too big.
+ *
+ * Since: 2.0
+ **/
+long
+asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len)
+{
+  int ret;
+  long err;
+
+  ret = asn1_get_length_der (ber, ber_len, len);
+
+  if (ret == -1 && ber_len > 1)
+    {				/* indefinite length method */
+      err = _asn1_get_indefinite_length_string (ber + 1, ber_len-1, &ret);
+      if (err != ASN1_SUCCESS)
+	return -3;
+    }
+
+  return ret;
+}
+
+/**
+ * asn1_get_octet_der:
+ * @der: DER data to decode containing the OCTET SEQUENCE.
+ * @der_len: The length of the @der data to decode.
+ * @ret_len: Output variable containing the encoded length of the DER data.
+ * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in.
+ * @str_size: Length of pre-allocated output buffer.
+ * @str_len: Output variable containing the length of the contents of the OCTET SEQUENCE.
+ *
+ * Extract an OCTET SEQUENCE from DER data. Note that this function
+ * expects the DER data past the tag field, i.e., the length and
+ * content octets.
+ *
+ * Returns: Returns %ASN1_SUCCESS on success, or an error.
+ **/
+int
+asn1_get_octet_der (const unsigned char *der, int der_len,
+		    int *ret_len, unsigned char *str, int str_size,
+		    int *str_len)
+{
+  int len_len = 0;
+
+  if (der_len <= 0)
+    return ASN1_GENERIC_ERROR;
+
+  *str_len = asn1_get_length_der (der, der_len, &len_len);
+
+  if (*str_len < 0)
+    return ASN1_DER_ERROR;
+
+  *ret_len = *str_len + len_len;
+  if (str_size >= *str_len)
+    {
+      if (*str_len > 0 && str != NULL)
+        memcpy (str, der + len_len, *str_len);
+    }
+  else
+    {
+      return ASN1_MEM_ERROR;
+    }
+
+  return ASN1_SUCCESS;
+}
+
+
+/*-
+ * _asn1_get_time_der:
+ * @type: %ASN1_ETYPE_GENERALIZED_TIME or %ASN1_ETYPE_UTC_TIME
+ * @der: DER data to decode containing the time
+ * @der_len: Length of DER data to decode.
+ * @ret_len: Output variable containing the length of the DER data.
+ * @str: Pre-allocated output buffer to put the textual time in.
+ * @str_size: Length of pre-allocated output buffer.
+ * @flags: Zero or %ASN1_DECODE_FLAG_STRICT_DER
+ *
+ * Performs basic checks in the DER encoded time object and returns its textual form.
+ * The textual form will be in the YYYYMMDD000000Z format for GeneralizedTime
+ * and YYMMDD000000Z for UTCTime.
+ *
+ * Returns: %ASN1_SUCCESS on success, or an error.
+ -*/
+static int
+_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, int *ret_len,
+		    char *str, int str_size, unsigned flags)
+{
+  int len_len, str_len;
+  unsigned i;
+  unsigned sign_count = 0;
+  unsigned dot_count = 0;
+  const unsigned char *p;
+
+  if (der_len <= 0 || str == NULL)
+    return ASN1_DER_ERROR;
+
+  str_len = asn1_get_length_der (der, der_len, &len_len);
+  if (str_len <= 0 || str_size < str_len)
+    return ASN1_DER_ERROR;
+
+  /* perform some sanity checks on the data */
+  if (str_len < 8)
+    {
+      warn();
+      return ASN1_TIME_ENCODING_ERROR;
+    }
+
+  if ((flags & ASN1_DECODE_FLAG_STRICT_DER) && !(flags & ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME))
+    {
+      p = &der[len_len];
+      for (i=0;i<(unsigned)(str_len-1);i++)
+         {
+           if (c_isdigit(p[i]) == 0)
+             {
+               if (type == ASN1_ETYPE_GENERALIZED_TIME)
+                 {
+                   /* tolerate lax encodings */
+                   if (p[i] == '.' && dot_count == 0)
+                     {
+                       dot_count++;
+                       continue;
+                     }
+
+               /* This is not really valid DER, but there are
+                * structures using that */
+                   if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) &&
+                       (p[i] == '+' || p[i] == '-') && sign_count == 0)
+                     {
+                       sign_count++;
+                       continue;
+                     }
+                 }
+
+               warn();
+               return ASN1_TIME_ENCODING_ERROR;
+             }
+         }
+
+      if (sign_count == 0 && p[str_len-1] != 'Z')
+        {
+          warn();
+          return ASN1_TIME_ENCODING_ERROR;
+        }
+    }
+  memcpy (str, der + len_len, str_len);
+  str[str_len] = 0;
+  *ret_len = str_len + len_len;
+
+  return ASN1_SUCCESS;
+}
+
+/**
+ * asn1_get_object_id_der:
+ * @der: DER data to decode containing the OBJECT IDENTIFIER
+ * @der_len: Length of DER data to decode.
+ * @ret_len: Output variable containing the length of the DER data.
+ * @str: Pre-allocated output buffer to put the textual object id in.
+ * @str_size: Length of pre-allocated output buffer.
+ *
+ * Converts a DER encoded object identifier to its textual form. This
+ * function expects the DER object identifier without the tag.
+ *
+ * Returns: %ASN1_SUCCESS on success, or an error.
+ **/
+int
+asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len,
+			char *str, int str_size)
+{
+  int len_len, len, k;
+  int leading, parsed;
+  char temp[LTOSTR_MAX_SIZE];
+  uint64_t val, val1, val0;
+
+  *ret_len = 0;
+  if (str && str_size > 0)
+    str[0] = 0;			/* no oid */
+
+  if (str == NULL || der_len <= 0)
+    return ASN1_GENERIC_ERROR;
+
+  len = asn1_get_length_der (der, der_len, &len_len);
+
+  if (len <= 0 || len + len_len > der_len)
+    return ASN1_DER_ERROR;
+
+  /* leading octet can never be 0x80 */
+  if (der[len_len] == 0x80)
+    return ASN1_DER_ERROR;
+
+  val0 = 0;
+
+  for (k = 0; k < len; k++)
+    {
+      if (INT_LEFT_SHIFT_OVERFLOW (val0, 7))
+	return ASN1_DER_ERROR;
+
+      val0 <<= 7;
+      val0 |= der[len_len + k] & 0x7F;
+      if (!(der[len_len + k] & 0x80))
+	break;
+    }
+  parsed = ++k;
+
+  /* val0 = (X*40) + Y, X={0,1,2}, Y<=39 when X={0,1} */
+  /* X = val, Y = val1 */
+
+  /* check if X == 0  */
+  val = 0;
+  val1 = val0;
+  if (val1 > 39)
+    {
+      val = 1;
+      val1 = val0 - 40;
+      if (val1  > 39)
+        {
+          val = 2;
+          val1 = val0 - 80;
+        }
+    }
+
+  _asn1_str_cpy (str, str_size, _asn1_ltostr (val, temp));
+  _asn1_str_cat (str, str_size, ".");
+  _asn1_str_cat (str, str_size, _asn1_ltostr (val1, temp));
+
+  val = 0;
+  leading = 1;
+  for (k = parsed; k < len; k++)
+    {
+      /* X.690 mandates that the leading byte must never be 0x80
+       */
+      if (leading != 0 && der[len_len + k] == 0x80)
+	return ASN1_DER_ERROR;
+      leading = 0;
+
+      /* check for wrap around */
+      if (INT_LEFT_SHIFT_OVERFLOW (val, 7))
+	return ASN1_DER_ERROR;
+
+      val = val << 7;
+      val |= der[len_len + k] & 0x7F;
+
+      if (!(der[len_len + k] & 0x80))
+	{
+	  _asn1_str_cat (str, str_size, ".");
+	  _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp));
+	  val = 0;
+	  leading = 1;
+	}
+    }
+
+  if (INT_ADD_OVERFLOW (len, len_len))
+    return ASN1_DER_ERROR;
+
+  *ret_len = len + len_len;
+
+  return ASN1_SUCCESS;
+}
+
+/**
+ * asn1_get_bit_der:
+ * @der: DER data to decode containing the BIT SEQUENCE.
+ * @der_len: Length of DER data to decode.
+ * @ret_len: Output variable containing the length of the DER data.
+ * @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in.
+ * @str_size: Length of pre-allocated output buffer.
+ * @bit_len: Output variable containing the size of the BIT SEQUENCE.
+ *
+ * Extract a BIT SEQUENCE from DER data.
+ *
+ * Returns: %ASN1_SUCCESS on success, or an error.
+ **/
+int
+asn1_get_bit_der (const unsigned char *der, int der_len,
+		  int *ret_len, unsigned char *str, int str_size,
+		  int *bit_len)
+{
+  int len_len = 0, len_byte;
+
+  if (der_len <= 0)
+    return ASN1_GENERIC_ERROR;
+
+  len_byte = asn1_get_length_der (der, der_len, &len_len) - 1;
+  if (len_byte < 0)
+    return ASN1_DER_ERROR;
+
+  *ret_len = len_byte + len_len + 1;
+  *bit_len = len_byte * 8 - der[len_len];
+
+  if (*bit_len < 0)
+    return ASN1_DER_ERROR;
+
+  if (str_size >= len_byte)
+    {
+      if (len_byte > 0 && str)
+        memcpy (str, der + len_len + 1, len_byte);
+    }
+  else
+    {
+      return ASN1_MEM_ERROR;
+    }
+
+  return ASN1_SUCCESS;
+}
+
+/* tag_len: the total tag length (explicit+inner)
+ * inner_tag_len: the inner_tag length
+ */
+static int
+_asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len,
+		       int *tag_len, int *inner_tag_len, unsigned flags)
+{
+  asn1_node p;
+  int counter, len2, len3, is_tag_implicit;
+  int result;
+  unsigned long tag, tag_implicit = 0;
+  unsigned char class, class2, class_implicit = 0;
+
+  if (der_len <= 0)
+    return ASN1_GENERIC_ERROR;
+
+  counter = is_tag_implicit = 0;
+
+  if (node->type & CONST_TAG)
+    {
+      p = node->down;
+      while (p)
+	{
+	  if (type_field (p->type) == ASN1_ETYPE_TAG)
+	    {
+	      if (p->type & CONST_APPLICATION)
+		class2 = ASN1_CLASS_APPLICATION;
+	      else if (p->type & CONST_UNIVERSAL)
+		class2 = ASN1_CLASS_UNIVERSAL;
+	      else if (p->type & CONST_PRIVATE)
+		class2 = ASN1_CLASS_PRIVATE;
+	      else
+		class2 = ASN1_CLASS_CONTEXT_SPECIFIC;
+
+	      if (p->type & CONST_EXPLICIT)
+		{
+		  if (asn1_get_tag_der
+		      (der + counter, der_len, &class, &len2,
+		       &tag) != ASN1_SUCCESS)
+		    return ASN1_DER_ERROR;
+
+                  DECR_LEN(der_len, len2);
+		  counter += len2;
+
+		  if (flags & ASN1_DECODE_FLAG_STRICT_DER)
+		    len3 =
+		      asn1_get_length_der (der + counter, der_len,
+					 &len2);
+		  else
+		    len3 =
+		      asn1_get_length_ber (der + counter, der_len,
+					 &len2);
+		  if (len3 < 0)
+		    return ASN1_DER_ERROR;
+
+                  DECR_LEN(der_len, len2);
+		  counter += len2;
+
+		  if (!is_tag_implicit)
+		    {
+		      if ((class != (class2 | ASN1_CLASS_STRUCTURED)) ||
+			  (tag != strtoul ((char *) p->value, NULL, 10)))
+			return ASN1_TAG_ERROR;
+		    }
+		  else
+		    {		/* ASN1_TAG_IMPLICIT */
+		      if ((class != class_implicit) || (tag != tag_implicit))
+			return ASN1_TAG_ERROR;
+		    }
+		  is_tag_implicit = 0;
+		}
+	      else
+		{		/* ASN1_TAG_IMPLICIT */
+		  if (!is_tag_implicit)
+		    {
+		      if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) ||
+			  (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF)
+			  || (type_field (node->type) == ASN1_ETYPE_SET)
+			  || (type_field (node->type) == ASN1_ETYPE_SET_OF))
+			class2 |= ASN1_CLASS_STRUCTURED;
+		      class_implicit = class2;
+		      tag_implicit = strtoul ((char *) p->value, NULL, 10);
+		      is_tag_implicit = 1;
+		    }
+		}
+	    }
+	  p = p->right;
+	}
+    }
+
+  if (is_tag_implicit)
+    {
+      if (asn1_get_tag_der
+	  (der + counter, der_len, &class, &len2,
+	   &tag) != ASN1_SUCCESS)
+	return ASN1_DER_ERROR;
+
+      DECR_LEN(der_len, len2);
+
+      if ((class != class_implicit) || (tag != tag_implicit))
+	{
+	  if (type_field (node->type) == ASN1_ETYPE_OCTET_STRING)
+	    {
+	      class_implicit |= ASN1_CLASS_STRUCTURED;
+	      if ((class != class_implicit) || (tag != tag_implicit))
+		return ASN1_TAG_ERROR;
+	    }
+	  else
+	    return ASN1_TAG_ERROR;
+	}
+    }
+  else
+    {
+      unsigned type = type_field (node->type);
+      if (type == ASN1_ETYPE_TAG)
+	{
+	  *tag_len = 0;
+	  if (inner_tag_len)
+	    *inner_tag_len = 0;
+	  return ASN1_SUCCESS;
+	}
+
+      if (asn1_get_tag_der
+	  (der + counter, der_len, &class, &len2,
+	   &tag) != ASN1_SUCCESS)
+	return ASN1_DER_ERROR;
+
+      DECR_LEN(der_len, len2);
+
+      switch (type)
+	{
+	case ASN1_ETYPE_NULL:
+	case ASN1_ETYPE_BOOLEAN:
+	case ASN1_ETYPE_INTEGER:
+	case ASN1_ETYPE_ENUMERATED:
+	case ASN1_ETYPE_OBJECT_ID:
+	case ASN1_ETYPE_GENERALSTRING:
+	case ASN1_ETYPE_NUMERIC_STRING:
+	case ASN1_ETYPE_IA5_STRING:
+	case ASN1_ETYPE_TELETEX_STRING:
+	case ASN1_ETYPE_PRINTABLE_STRING:
+	case ASN1_ETYPE_UNIVERSAL_STRING:
+	case ASN1_ETYPE_BMP_STRING:
+	case ASN1_ETYPE_UTF8_STRING:
+	case ASN1_ETYPE_VISIBLE_STRING:
+	case ASN1_ETYPE_BIT_STRING:
+	case ASN1_ETYPE_SEQUENCE:
+	case ASN1_ETYPE_SEQUENCE_OF:
+	case ASN1_ETYPE_SET:
+	case ASN1_ETYPE_SET_OF:
+	case ASN1_ETYPE_GENERALIZED_TIME:
+	case ASN1_ETYPE_UTC_TIME:
+	  if ((class != _asn1_tags[type].class)
+	      || (tag != _asn1_tags[type].tag))
+	    return ASN1_DER_ERROR;
+	  break;
+
+	case ASN1_ETYPE_OCTET_STRING:
+	  /* OCTET STRING is handled differently to allow
+	   * BER encodings (structured class). */
+	  if (((class != ASN1_CLASS_UNIVERSAL)
+	       && (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED)))
+	      || (tag != ASN1_TAG_OCTET_STRING))
+	    return ASN1_DER_ERROR;
+	  break;
+	case ASN1_ETYPE_ANY:
+	  counter -= len2;
+	  break;
+	case ASN1_ETYPE_CHOICE:
+	  counter -= len2;
+	  break;
+	default:
+	  return ASN1_DER_ERROR;
+	  break;
+	}
+    }
+
+  counter += len2;
+  *tag_len = counter;
+  if (inner_tag_len)
+    *inner_tag_len = len2;
+  return ASN1_SUCCESS;
+
+cleanup:
+  return result;
+}
+
+static int
+extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len,
+		       int *ret_len, int *inner_len, unsigned flags)
+{
+asn1_node p;
+int ris = ASN1_DER_ERROR;
+
+  if (type_field (node->type) == ASN1_ETYPE_CHOICE)
+    {
+      p = node->down;
+      while (p)
+        {
+          ris = _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, flags);
+          if (ris == ASN1_SUCCESS)
+            break;
+          p = p->right;
+	}
+
+      *ret_len = 0;
+      return ris;
+    }
+  else
+    return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, flags);
+}
+
+static int
+_asn1_delete_not_used (asn1_node node)
+{
+  asn1_node p, p2;
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = node;
+  while (p)
+    {
+      if (p->type & CONST_NOT_USED)
+	{
+	  p2 = NULL;
+	  if (p != node)
+	    {
+	      p2 = _asn1_find_left (p);
+	      if (!p2)
+		p2 = _asn1_find_up (p);
+	    }
+	  asn1_delete_structure (&p);
+	  p = p2;
+	}
+
+      if (!p)
+	break;			/* reach node */
+
+      if (p->down)
+	{
+	  p = p->down;
+	}
+      else
+	{
+	  if (p == node)
+	    p = NULL;
+	  else if (p->right)
+	    p = p->right;
+	  else
+	    {
+	      while (1)
+		{
+		  p = _asn1_find_up (p);
+		  if (p == node)
+		    {
+		      p = NULL;
+		      break;
+		    }
+		  if (p->right)
+		    {
+		      p = p->right;
+		      break;
+		    }
+		}
+	    }
+	}
+    }
+  return ASN1_SUCCESS;
+}
+
+static int
+_asn1_get_indefinite_length_string (const unsigned char *der,
+				    int der_len, int *len)
+{
+  int len2, len3, counter, indefinite;
+  int result;
+  unsigned long tag;
+  unsigned char class;
+
+  counter = indefinite = 0;
+
+  while (1)
+    {
+      if (HAVE_TWO(der_len) && (der[counter] == 0) && (der[counter + 1] == 0))
+	{
+	  counter += 2;
+	  DECR_LEN(der_len, 2);
+
+	  indefinite--;
+	  if (indefinite <= 0)
+	    break;
+	  else
+	    continue;
+	}
+
+      if (asn1_get_tag_der
+	  (der + counter, der_len, &class, &len2,
+	   &tag) != ASN1_SUCCESS)
+	return ASN1_DER_ERROR;
+
+      DECR_LEN(der_len, len2);
+      counter += len2;
+
+      len2 = asn1_get_length_der (der + counter, der_len, &len3);
+      if (len2 < -1)
+	return ASN1_DER_ERROR;
+
+      if (len2 == -1)
+	{
+	  indefinite++;
+	  counter += 1;
+          DECR_LEN(der_len, 1);
+	}
+      else
+	{
+	  counter += len2 + len3;
+          DECR_LEN(der_len, len2+len3);
+	}
+    }
+
+  *len = counter;
+  return ASN1_SUCCESS;
+
+cleanup:
+  return result;
+}
+
+static void delete_unneeded_choice_fields(asn1_node p)
+{
+  asn1_node p2;
+
+  while (p->right)
+    {
+      p2 = p->right;
+      asn1_delete_structure (&p2);
+    }
+}
+
+
+/**
+ * asn1_der_decoding2
+ * @element: pointer to an ASN1 structure.
+ * @ider: vector that contains the DER encoding.
+ * @max_ider_len: pointer to an integer giving the information about the
+ *   maximal number of bytes occupied by *@ider. The real size of the DER
+ *   encoding is returned through this pointer.
+ * @flags: flags controlling the behaviour of the function.
+ * @errorDescription: null-terminated string contains details when an
+ *   error occurred.
+ *
+ * Fill the structure *@element with values of a DER encoding string. The
+ * structure must just be created with function asn1_create_element().
+ *
+ * If %ASN1_DECODE_FLAG_ALLOW_PADDING flag is set then the function will ignore
+ * padding after the decoded DER data. Upon a successful return the value of
+ * *@max_ider_len will be set to the number of bytes decoded.
+ *
+ * If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will
+ * not decode any BER-encoded elements.
+ *
+ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
+ *   if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or
+ *   %ASN1_DER_ERROR if the der encoding doesn't match the structure
+ *   name (*@ELEMENT deleted).
+ **/
+int
+asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len,
+		    unsigned int flags, char *errorDescription)
+{
+  asn1_node node, p, p2, p3;
+  char temp[128];
+  int counter, len2, len3, len4, move, ris, tlen;
+  struct node_tail_cache_st tcache = {NULL, NULL};
+  unsigned char class;
+  unsigned long tag;
+  int tag_len;
+  int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len;
+  int inner_tag_len;
+  unsigned char *ptmp;
+  const unsigned char *ptag;
+  const unsigned char *der = ider;
+
+  node = *element;
+
+  if (errorDescription != NULL)
+    errorDescription[0] = 0;
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  if (node->type & CONST_OPTION)
+    {
+      result = ASN1_GENERIC_ERROR;
+      warn();
+      goto cleanup;
+    }
+
+  counter = 0;
+  move = DOWN;
+  p = node;
+  while (1)
+    {
+      tag_len = 0;
+      inner_tag_len = 0;
+      ris = ASN1_SUCCESS;
+      if (move != UP)
+	{
+	  if (p->type & CONST_SET)
+	    {
+	      p2 = _asn1_find_up (p);
+	      len2 = p2->tmp_ival;
+	      if (len2 == -1)
+		{
+		  if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1])
+		    {
+		      p = p2;
+		      move = UP;
+		      counter += 2;
+		      DECR_LEN(ider_len, 2);
+		      continue;
+		    }
+		}
+	      else if (counter == len2)
+		{
+		  p = p2;
+		  move = UP;
+		  continue;
+		}
+	      else if (counter > len2)
+		{
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+	      p2 = p2->down;
+	      while (p2)
+		{
+		  if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED))
+		    {
+		      ris =
+			  extract_tag_der_recursive (p2, der + counter,
+						     ider_len, &len2, NULL, flags);
+		      if (ris == ASN1_SUCCESS)
+			{
+			  p2->type &= ~CONST_NOT_USED;
+			  p = p2;
+			  break;
+			}
+		    }
+		  p2 = p2->right;
+		}
+	      if (p2 == NULL)
+		{
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+	    }
+
+	  /* the position in the DER structure this starts */
+	  p->start = counter;
+	  p->end = total_len - 1;
+
+	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
+	    {
+	      p2 = _asn1_find_up (p);
+	      len2 = p2->tmp_ival;
+	      if (counter == len2)
+		{
+		  if (p->right)
+		    {
+		      p2 = p->right;
+		      move = RIGHT;
+		    }
+		  else
+		    move = UP;
+
+		  if (p->type & CONST_OPTION)
+		    asn1_delete_structure (&p);
+
+		  p = p2;
+		  continue;
+		}
+	    }
+
+	  if (type_field (p->type) == ASN1_ETYPE_CHOICE)
+	    {
+	      while (p->down)
+		{
+		  ris =
+		      extract_tag_der_recursive (p->down, der + counter,
+					         ider_len, &len2, NULL, flags);
+
+		  if (ris == ASN1_SUCCESS)
+		    {
+		      delete_unneeded_choice_fields(p->down);
+		      break;
+		    }
+		  else if (ris == ASN1_ERROR_TYPE_ANY)
+		    {
+		      result = ASN1_ERROR_TYPE_ANY;
+                      warn();
+		      goto cleanup;
+		    }
+		  else
+		    {
+		      p2 = p->down;
+		      asn1_delete_structure (&p2);
+		    }
+		}
+
+	      if (p->down == NULL)
+		{
+		  if (!(p->type & CONST_OPTION))
+		    {
+		      result = ASN1_DER_ERROR;
+                      warn();
+		      goto cleanup;
+		    }
+		}
+	      else if (type_field (p->type) != ASN1_ETYPE_CHOICE)
+		p = p->down;
+
+	      p->start = counter;
+	    }
+
+	  if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT))
+	    {
+	      p2 = _asn1_find_up (p);
+	      len2 = p2->tmp_ival;
+
+	      if ((len2 != -1) && (counter > len2))
+		ris = ASN1_TAG_ERROR;
+	    }
+
+	  if (ris == ASN1_SUCCESS)
+	    ris =
+	      extract_tag_der_recursive (p, der + counter, ider_len,
+	                                 &tag_len, &inner_tag_len, flags);
+
+	  if (ris != ASN1_SUCCESS)
+	    {
+	      if (p->type & CONST_OPTION)
+		{
+		  p->type |= CONST_NOT_USED;
+		  move = RIGHT;
+		}
+	      else if (p->type & CONST_DEFAULT)
+		{
+		  _asn1_set_value (p, NULL, 0);
+		  move = RIGHT;
+		}
+	      else
+		{
+		  if (errorDescription != NULL)
+		    _asn1_error_description_tag_error (p, errorDescription);
+
+		  result = ASN1_TAG_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+	    }
+	  else
+	    {
+	      DECR_LEN(ider_len, tag_len);
+	      counter += tag_len;
+	    }
+	}
+
+      if (ris == ASN1_SUCCESS)
+	{
+	  switch (type_field (p->type))
+	    {
+	    case ASN1_ETYPE_NULL:
+	      DECR_LEN(ider_len, 1);
+	      if (der[counter])
+		{
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+	      counter++;
+	      move = RIGHT;
+	      break;
+	    case ASN1_ETYPE_BOOLEAN:
+	      DECR_LEN(ider_len, 2);
+
+	      if (der[counter++] != 1)
+		{
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+	      if (der[counter++] == 0)
+		_asn1_set_value (p, "F", 1);
+	      else
+		_asn1_set_value (p, "T", 1);
+	      move = RIGHT;
+	      break;
+	    case ASN1_ETYPE_INTEGER:
+	    case ASN1_ETYPE_ENUMERATED:
+	      len2 =
+		asn1_get_length_der (der + counter, ider_len, &len3);
+	      if (len2 < 0)
+		{
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+
+	      DECR_LEN(ider_len, len3+len2);
+
+	      _asn1_set_value (p, der + counter, len3 + len2);
+	      counter += len3 + len2;
+	      move = RIGHT;
+	      break;
+	    case ASN1_ETYPE_OBJECT_ID:
+	      result =
+		asn1_get_object_id_der (der + counter, ider_len, &len2,
+					temp, sizeof (temp));
+	      if (result != ASN1_SUCCESS)
+	        {
+                  warn();
+		  goto cleanup;
+		}
+
+	      DECR_LEN(ider_len, len2);
+
+	      tlen = strlen (temp);
+	      if (tlen > 0)
+		_asn1_set_value (p, temp, tlen + 1);
+
+	      counter += len2;
+	      move = RIGHT;
+	      break;
+	    case ASN1_ETYPE_GENERALIZED_TIME:
+	    case ASN1_ETYPE_UTC_TIME:
+	      result =
+		_asn1_get_time_der (type_field (p->type), der + counter, ider_len, &len2, temp,
+				    sizeof (temp) - 1, flags);
+	      if (result != ASN1_SUCCESS)
+	        {
+                  warn();
+                  goto cleanup;
+                }
+
+	      DECR_LEN(ider_len, len2);
+
+	      tlen = strlen (temp);
+	      if (tlen > 0)
+		_asn1_set_value (p, temp, tlen);
+
+	      counter += len2;
+	      move = RIGHT;
+	      break;
+	    case ASN1_ETYPE_OCTET_STRING:
+	      if (counter < inner_tag_len)
+	        {
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+	        }
+
+              ptag = der + counter - inner_tag_len;
+              if ((flags & ASN1_DECODE_FLAG_STRICT_DER) || !(ptag[0] & ASN1_CLASS_STRUCTURED))
+                {
+	          if (ptag[0] & ASN1_CLASS_STRUCTURED)
+		    {
+		      result = ASN1_DER_ERROR;
+                      warn();
+		      goto cleanup;
+		    }
+
+	          len2 =
+		    asn1_get_length_der (der + counter, ider_len, &len3);
+	          if (len2 < 0)
+		    {
+		      result = ASN1_DER_ERROR;
+                      warn();
+		      goto cleanup;
+		    }
+
+	          DECR_LEN(ider_len, len3+len2);
+
+	          _asn1_set_value (p, der + counter, len3 + len2);
+	          counter += len3 + len2;
+                }
+              else
+                {
+                  unsigned dflags = 0, vlen, ber_len;
+
+                  if (ptag[0] & ASN1_CLASS_STRUCTURED)
+                    dflags |= DECODE_FLAG_CONSTRUCTED;
+
+                  result = _asn1_decode_simple_ber(type_field (p->type), der+counter, ider_len, &ptmp, &vlen, &ber_len, dflags);
+                  if (result != ASN1_SUCCESS)
+	            {
+                      warn();
+		      goto cleanup;
+		    }
+
+		  DECR_LEN(ider_len, ber_len);
+
+		  _asn1_set_value_lv (p, ptmp, vlen);
+
+	          counter += ber_len;
+	          free(ptmp);
+                }
+	      move = RIGHT;
+	      break;
+	    case ASN1_ETYPE_GENERALSTRING:
+	    case ASN1_ETYPE_NUMERIC_STRING:
+	    case ASN1_ETYPE_IA5_STRING:
+	    case ASN1_ETYPE_TELETEX_STRING:
+	    case ASN1_ETYPE_PRINTABLE_STRING:
+	    case ASN1_ETYPE_UNIVERSAL_STRING:
+	    case ASN1_ETYPE_BMP_STRING:
+	    case ASN1_ETYPE_UTF8_STRING:
+	    case ASN1_ETYPE_VISIBLE_STRING:
+	    case ASN1_ETYPE_BIT_STRING:
+	      len2 =
+		asn1_get_length_der (der + counter, ider_len, &len3);
+	      if (len2 < 0)
+		{
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+
+	      DECR_LEN(ider_len, len3+len2);
+
+	      _asn1_set_value (p, der + counter, len3 + len2);
+	      counter += len3 + len2;
+	      move = RIGHT;
+	      break;
+	    case ASN1_ETYPE_SEQUENCE:
+	    case ASN1_ETYPE_SET:
+	      if (move == UP)
+		{
+		  len2 = p->tmp_ival;
+		  p->tmp_ival = 0;
+		  if (len2 == -1)
+		    {		/* indefinite length method */
+		      DECR_LEN(ider_len, 2);
+		      if ((der[counter]) || der[counter + 1])
+		        {
+		          result = ASN1_DER_ERROR;
+                          warn();
+		          goto cleanup;
+			}
+		      counter += 2;
+		    }
+		  else
+		    {		/* definite length method */
+		      if (len2 != counter)
+			{
+			  result = ASN1_DER_ERROR;
+                          warn();
+			  goto cleanup;
+			}
+		    }
+		  move = RIGHT;
+		}
+	      else
+		{		/* move==DOWN || move==RIGHT */
+		  len3 =
+		    asn1_get_length_der (der + counter, ider_len, &len2);
+                  if (IS_ERR(len3, flags))
+		    {
+		      result = ASN1_DER_ERROR;
+                      warn();
+		      goto cleanup;
+		    }
+
+	          DECR_LEN(ider_len, len2);
+		  counter += len2;
+
+		  if (len3 > 0)
+		    {
+		      p->tmp_ival = counter + len3;
+		      move = DOWN;
+		    }
+		  else if (len3 == 0)
+		    {
+		      p2 = p->down;
+		      while (p2)
+			{
+			  if (type_field (p2->type) != ASN1_ETYPE_TAG)
+			    {
+			      p3 = p2->right;
+			      asn1_delete_structure (&p2);
+			      p2 = p3;
+			    }
+			  else
+			    p2 = p2->right;
+			}
+		      move = RIGHT;
+		    }
+		  else
+		    {		/* indefinite length method */
+		      p->tmp_ival = -1;
+		      move = DOWN;
+		    }
+		}
+	      break;
+	    case ASN1_ETYPE_SEQUENCE_OF:
+	    case ASN1_ETYPE_SET_OF:
+	      if (move == UP)
+		{
+		  len2 = p->tmp_ival;
+		  if (len2 == -1)
+		    {		/* indefinite length method */
+		      if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1]))
+			{
+			  result = _asn1_append_sequence_set (p, &tcache);
+			  if (result != 0)
+			    {
+                              warn();
+		              goto cleanup;
+		            }
+			  p = tcache.tail;
+			  move = RIGHT;
+			  continue;
+			}
+
+		      p->tmp_ival = 0;
+		      tcache.tail = NULL; /* finished decoding this structure */
+		      tcache.head = NULL;
+		      DECR_LEN(ider_len, 2);
+		      counter += 2;
+		    }
+		  else
+		    {		/* definite length method */
+		      if (len2 > counter)
+			{
+			  result = _asn1_append_sequence_set (p, &tcache);
+			  if (result != 0)
+			    {
+                              warn();
+		              goto cleanup;
+		            }
+			  p = tcache.tail;
+			  move = RIGHT;
+			  continue;
+			}
+
+		      p->tmp_ival = 0;
+		      tcache.tail = NULL; /* finished decoding this structure */
+		      tcache.head = NULL;
+
+		      if (len2 != counter)
+			{
+			  result = ASN1_DER_ERROR;
+                          warn();
+			  goto cleanup;
+			}
+		    }
+		}
+	      else
+		{		/* move==DOWN || move==RIGHT */
+		  len3 =
+		    asn1_get_length_der (der + counter, ider_len, &len2);
+                  if (IS_ERR(len3, flags))
+		    {
+		      result = ASN1_DER_ERROR;
+                      warn();
+		      goto cleanup;
+		    }
+
+		  DECR_LEN(ider_len, len2);
+		  counter += len2;
+		  if (len3)
+		    {
+		      if (len3 > 0)
+			{	/* definite length method */
+		          p->tmp_ival = counter + len3;
+			}
+		      else
+			{	/* indefinite length method */
+		          p->tmp_ival = -1;
+			}
+
+		      p2 = p->down;
+                      if (p2 == NULL)
+		        {
+		          result = ASN1_DER_ERROR;
+                          warn();
+		          goto cleanup;
+		        }
+
+		      while ((type_field (p2->type) == ASN1_ETYPE_TAG)
+			     || (type_field (p2->type) == ASN1_ETYPE_SIZE))
+			p2 = p2->right;
+		      if (p2->right == NULL)
+		        {
+			  result = _asn1_append_sequence_set (p, &tcache);
+			  if (result != 0)
+			    {
+                              warn();
+		              goto cleanup;
+		            }
+			}
+		      p = p2;
+		    }
+		}
+	      move = RIGHT;
+	      break;
+	    case ASN1_ETYPE_ANY:
+	      /* Check indefinite lenth method in an EXPLICIT TAG */
+
+	      if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && (p->type & CONST_TAG) &&
+	          tag_len == 2 && (der[counter - 1] == 0x80))
+		indefinite = 1;
+	      else
+	        indefinite = 0;
+
+	      if (asn1_get_tag_der
+		  (der + counter, ider_len, &class, &len2,
+		   &tag) != ASN1_SUCCESS)
+		{
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+
+	      DECR_LEN(ider_len, len2);
+
+	      len4 =
+		asn1_get_length_der (der + counter + len2,
+				     ider_len, &len3);
+              if (IS_ERR(len4, flags))
+		{
+		  result = ASN1_DER_ERROR;
+                  warn();
+		  goto cleanup;
+		}
+	      if (len4 != -1) /* definite */
+		{
+		  len2 += len4;
+
+	          DECR_LEN(ider_len, len4+len3);
+		  _asn1_set_value_lv (p, der + counter, len2 + len3);
+		  counter += len2 + len3;
+		}
+	      else /* == -1 */
+		{		/* indefinite length */
+		  ider_len += len2; /* undo DECR_LEN */
+
+		  if (counter == 0)
+		    {
+		      result = ASN1_DER_ERROR;
+                      warn();
+		      goto cleanup;
+		    }
+
+		  result =
+		    _asn1_get_indefinite_length_string (der + counter, ider_len, &len2);
+		  if (result != ASN1_SUCCESS)
+		    {
+                      warn();
+                      goto cleanup;
+                    }
+
+	          DECR_LEN(ider_len, len2);
+		  _asn1_set_value_lv (p, der + counter, len2);
+		  counter += len2;
+
+		}
+
+	        /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with
+	           an indefinite length method. */
+	        if (indefinite)
+		  {
+	            DECR_LEN(ider_len, 2);
+		    if (!der[counter] && !der[counter + 1])
+		      {
+		        counter += 2;
+		      }
+		    else
+		      {
+		        result = ASN1_DER_ERROR;
+                        warn();
+		        goto cleanup;
+		      }
+		  }
+
+	      move = RIGHT;
+	      break;
+	    default:
+	      move = (move == UP) ? RIGHT : DOWN;
+	      break;
+	    }
+	}
+
+      if (p)
+        {
+          p->end = counter - 1;
+        }
+
+      if (p == node && move != DOWN)
+	break;
+
+      if (move == DOWN)
+	{
+	  if (p->down)
+	    p = p->down;
+	  else
+	    move = RIGHT;
+	}
+      if ((move == RIGHT) && !(p->type & CONST_SET))
+	{
+	  if (p->right)
+	    p = p->right;
+	  else
+	    move = UP;
+	}
+      if (move == UP)
+	p = _asn1_find_up (p);
+    }
+
+  _asn1_delete_not_used (*element);
+
+  if ((ider_len < 0) ||
+      (!(flags & ASN1_DECODE_FLAG_ALLOW_PADDING) && (ider_len != 0)))
+    {
+      warn();
+      result = ASN1_DER_ERROR;
+      goto cleanup;
+    }
+
+  *max_ider_len = total_len - ider_len;
+
+  return ASN1_SUCCESS;
+
+cleanup:
+  asn1_delete_structure (element);
+  return result;
+}
+
+
+/**
+ * asn1_der_decoding:
+ * @element: pointer to an ASN1 structure.
+ * @ider: vector that contains the DER encoding.
+ * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1].
+ * @errorDescription: null-terminated string contains details when an
+ *   error occurred.
+ *
+ * Fill the structure *@element with values of a DER encoding
+ * string. The structure must just be created with function
+ * asn1_create_element().
+ *
+ * Note that the *@element variable is provided as a pointer for
+ * historical reasons.
+ *
+ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
+ *   if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or
+ *   %ASN1_DER_ERROR if the der encoding doesn't match the structure
+ *   name (*@ELEMENT deleted).
+ **/
+int
+asn1_der_decoding (asn1_node * element, const void *ider, int ider_len,
+		   char *errorDescription)
+{
+  return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription);
+}
+
+/**
+ * asn1_der_decoding_element:
+ * @structure: pointer to an ASN1 structure
+ * @elementName: name of the element to fill
+ * @ider: vector that contains the DER encoding of the whole structure.
+ * @len: number of bytes of *der: der[0]..der[len-1]
+ * @errorDescription: null-terminated string contains details when an
+ *   error occurred.
+ *
+ * Fill the element named @ELEMENTNAME with values of a DER encoding
+ * string.  The structure must just be created with function
+ * asn1_create_element().  The DER vector must contain the encoding
+ * string of the whole @STRUCTURE.  If an error occurs during the
+ * decoding procedure, the *@STRUCTURE is deleted and set equal to
+ * %NULL.
+ *
+ * This function is deprecated and may just be an alias to asn1_der_decoding
+ * in future versions. Use asn1_der_decoding() instead.
+ *
+ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
+ *   if ELEMENT is %NULL or @elementName == NULL, and
+ *   %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding doesn't
+ *   match the structure @structure (*ELEMENT deleted).
+ **/
+int
+asn1_der_decoding_element (asn1_node * structure, const char *elementName,
+			   const void *ider, int len, char *errorDescription)
+{
+  return asn1_der_decoding(structure, ider, len, errorDescription);
+}
+
+/**
+ * asn1_der_decoding_startEnd:
+ * @element: pointer to an ASN1 element
+ * @ider: vector that contains the DER encoding.
+ * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1]
+ * @name_element: an element of NAME structure.
+ * @start: the position of the first byte of NAME_ELEMENT decoding
+ *   (@ider[*start])
+ * @end: the position of the last byte of NAME_ELEMENT decoding
+ *  (@ider[*end])
+ *
+ * Find the start and end point of an element in a DER encoding
+ * string. I mean that if you have a der encoding and you have already
+ * used the function asn1_der_decoding() to fill a structure, it may
+ * happen that you want to find the piece of string concerning an
+ * element of the structure.
+ *
+ * One example is the sequence "tbsCertificate" inside an X509
+ * certificate.
+ *
+ * Note that since libtasn1 3.7 the @ider and @ider_len parameters
+ * can be omitted, if the element is already decoded using asn1_der_decoding().
+ *
+ * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND
+ *   if ELEMENT is %asn1_node EMPTY or @name_element is not a valid
+ *   element, %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding
+ *   doesn't match the structure ELEMENT.
+ **/
+int
+asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len,
+			    const char *name_element, int *start, int *end)
+{
+  asn1_node node, node_to_find;
+  int result = ASN1_DER_ERROR;
+
+  node = element;
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  node_to_find = asn1_find_node (node, name_element);
+
+  if (node_to_find == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  *start = node_to_find->start;
+  *end = node_to_find->end;
+
+  if (*start == 0 && *end == 0)
+    {
+      if (ider == NULL || ider_len == 0)
+        return ASN1_GENERIC_ERROR;
+
+      /* it seems asn1_der_decoding() wasn't called before. Do it now */
+      result = asn1_der_decoding (&node, ider, ider_len, NULL);
+      if (result != ASN1_SUCCESS)
+        {
+          warn();
+          return result;
+        }
+
+      node_to_find = asn1_find_node (node, name_element);
+      if (node_to_find == NULL)
+        return ASN1_ELEMENT_NOT_FOUND;
+
+      *start = node_to_find->start;
+      *end = node_to_find->end;
+    }
+
+  if (*end < *start)
+    return ASN1_GENERIC_ERROR;
+
+  return ASN1_SUCCESS;
+}
+
+/**
+ * asn1_expand_any_defined_by:
+ * @definitions: ASN1 definitions
+ * @element: pointer to an ASN1 structure
+ *
+ * Expands every "ANY DEFINED BY" element of a structure created from
+ * a DER decoding process (asn1_der_decoding function). The element
+ * ANY must be defined by an OBJECT IDENTIFIER. The type used to
+ * expand the element ANY is the first one following the definition of
+ * the actual value of the OBJECT IDENTIFIER.
+ *
+ * Returns: %ASN1_SUCCESS if Substitution OK, %ASN1_ERROR_TYPE_ANY if
+ *   some "ANY DEFINED BY" element couldn't be expanded due to a
+ *   problem in OBJECT_ID -> TYPE association, or other error codes
+ *   depending on DER decoding.
+ **/
+int
+asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element)
+{
+  char name[2 * ASN1_MAX_NAME_SIZE + 2],
+    value[ASN1_MAX_NAME_SIZE];
+  int retCode = ASN1_SUCCESS, result;
+  int len, len2, len3;
+  asn1_node_const p2;
+  asn1_node p, p3, aux = NULL;
+  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+  const char *definitionsName;
+
+  if ((definitions == NULL) || (*element == NULL))
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  definitionsName = definitions->name;
+
+  p = *element;
+  while (p)
+    {
+
+      switch (type_field (p->type))
+	{
+	case ASN1_ETYPE_ANY:
+	  if ((p->type & CONST_DEFINED_BY) && (p->value))
+	    {
+	      /* search the "DEF_BY" element */
+	      p2 = p->down;
+	      while ((p2) && (type_field (p2->type) != ASN1_ETYPE_CONSTANT))
+		p2 = p2->right;
+
+	      if (!p2)
+		{
+		  retCode = ASN1_ERROR_TYPE_ANY;
+		  break;
+		}
+
+	      p3 = _asn1_find_up (p);
+
+	      if (!p3)
+		{
+		  retCode = ASN1_ERROR_TYPE_ANY;
+		  break;
+		}
+
+	      p3 = p3->down;
+	      while (p3)
+		{
+		  if (!(strcmp (p3->name, p2->name)))
+		    break;
+		  p3 = p3->right;
+		}
+
+	      if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) ||
+		  (p3->value == NULL))
+		{
+
+		  p3 = _asn1_find_up (p);
+		  p3 = _asn1_find_up (p3);
+
+		  if (!p3)
+		    {
+		      retCode = ASN1_ERROR_TYPE_ANY;
+		      break;
+		    }
+
+		  p3 = p3->down;
+
+		  while (p3)
+		    {
+		      if (!(strcmp (p3->name, p2->name)))
+			break;
+		      p3 = p3->right;
+		    }
+
+		  if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID)
+		      || (p3->value == NULL))
+		    {
+		      retCode = ASN1_ERROR_TYPE_ANY;
+		      break;
+		    }
+		}
+
+	      /* search the OBJECT_ID into definitions */
+	      p2 = definitions->down;
+	      while (p2)
+		{
+		  if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) &&
+		      (p2->type & CONST_ASSIGN))
+		    {
+		      snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name);
+
+		      len = ASN1_MAX_NAME_SIZE;
+		      result =
+			asn1_read_value (definitions, name, value, &len);
+
+		      if ((result == ASN1_SUCCESS)
+			  && (!_asn1_strcmp (p3->value, value)))
+			{
+			  p2 = p2->right;	/* pointer to the structure to
+						   use for expansion */
+			  while ((p2) && (p2->type & CONST_ASSIGN))
+			    p2 = p2->right;
+
+			  if (p2)
+			    {
+			      snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name);
+
+			      result =
+				asn1_create_element (definitions, name, &aux);
+			      if (result == ASN1_SUCCESS)
+				{
+				  _asn1_cpy_name (aux, p);
+				  len2 =
+				    asn1_get_length_der (p->value,
+							 p->value_len, &len3);
+				  if (len2 < 0)
+				    return ASN1_DER_ERROR;
+
+				  result =
+				    asn1_der_decoding (&aux, p->value + len3,
+						       len2,
+						       errorDescription);
+				  if (result == ASN1_SUCCESS)
+				    {
+
+				      _asn1_set_right (aux, p->right);
+				      _asn1_set_right (p, aux);
+
+				      result = asn1_delete_structure (&p);
+				      if (result == ASN1_SUCCESS)
+					{
+					  p = aux;
+					  aux = NULL;
+					  break;
+					}
+				      else
+					{	/* error with asn1_delete_structure */
+					  asn1_delete_structure (&aux);
+					  retCode = result;
+					  break;
+					}
+				    }
+				  else
+				    {	/* error with asn1_der_decoding */
+				      retCode = result;
+				      break;
+				    }
+				}
+			      else
+				{	/* error with asn1_create_element */
+				  retCode = result;
+				  break;
+				}
+			    }
+			  else
+			    {	/* error with the pointer to the structure to exapand */
+			      retCode = ASN1_ERROR_TYPE_ANY;
+			      break;
+			    }
+			}
+		    }
+		  p2 = p2->right;
+		}		/* end while */
+
+	      if (!p2)
+		{
+		  retCode = ASN1_ERROR_TYPE_ANY;
+		  break;
+		}
+
+	    }
+	  break;
+	default:
+	  break;
+	}
+
+
+      if (p->down)
+	{
+	  p = p->down;
+	}
+      else if (p == *element)
+	{
+	  p = NULL;
+	  break;
+	}
+      else if (p->right)
+	p = p->right;
+      else
+	{
+	  while (1)
+	    {
+	      p = _asn1_find_up (p);
+	      if (p == *element)
+		{
+		  p = NULL;
+		  break;
+		}
+	      if (p->right)
+		{
+		  p = p->right;
+		  break;
+		}
+	    }
+	}
+    }
+
+  return retCode;
+}
+
+/**
+ * asn1_expand_octet_string:
+ * @definitions: ASN1 definitions
+ * @element: pointer to an ASN1 structure
+ * @octetName: name of the OCTECT STRING field to expand.
+ * @objectName: name of the OBJECT IDENTIFIER field to use to define
+ *    the type for expansion.
+ *
+ * Expands an "OCTET STRING" element of a structure created from a DER
+ * decoding process (the asn1_der_decoding() function).  The type used
+ * for expansion is the first one following the definition of the
+ * actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME.
+ *
+ * Returns: %ASN1_SUCCESS if substitution OK, %ASN1_ELEMENT_NOT_FOUND
+ *   if @objectName or @octetName are not correct,
+ *   %ASN1_VALUE_NOT_VALID if it wasn't possible to find the type to
+ *   use for expansion, or other errors depending on DER decoding.
+ **/
+int
+asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
+			  const char *octetName, const char *objectName)
+{
+  char name[2 * ASN1_MAX_NAME_SIZE + 1], value[ASN1_MAX_NAME_SIZE];
+  int retCode = ASN1_SUCCESS, result;
+  int len, len2, len3;
+  asn1_node_const p2;
+  asn1_node aux = NULL;
+  asn1_node octetNode = NULL, objectNode = NULL;
+  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+  if ((definitions == NULL) || (*element == NULL))
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  octetNode = asn1_find_node (*element, octetName);
+  if (octetNode == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+  if (type_field (octetNode->type) != ASN1_ETYPE_OCTET_STRING)
+    return ASN1_ELEMENT_NOT_FOUND;
+  if (octetNode->value == NULL)
+    return ASN1_VALUE_NOT_FOUND;
+
+  objectNode = asn1_find_node (*element, objectName);
+  if (objectNode == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  if (type_field (objectNode->type) != ASN1_ETYPE_OBJECT_ID)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  if (objectNode->value == NULL)
+    return ASN1_VALUE_NOT_FOUND;
+
+
+  /* search the OBJECT_ID into definitions */
+  p2 = definitions->down;
+  while (p2)
+    {
+      if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) &&
+	  (p2->type & CONST_ASSIGN))
+	{
+	  strcpy (name, definitions->name);
+	  strcat (name, ".");
+	  strcat (name, p2->name);
+
+	  len = sizeof (value);
+	  result = asn1_read_value (definitions, name, value, &len);
+
+	  if ((result == ASN1_SUCCESS)
+	      && (!_asn1_strcmp (objectNode->value, value)))
+	    {
+
+	      p2 = p2->right;	/* pointer to the structure to
+				   use for expansion */
+	      while ((p2) && (p2->type & CONST_ASSIGN))
+		p2 = p2->right;
+
+	      if (p2)
+		{
+		  strcpy (name, definitions->name);
+		  strcat (name, ".");
+		  strcat (name, p2->name);
+
+		  result = asn1_create_element (definitions, name, &aux);
+		  if (result == ASN1_SUCCESS)
+		    {
+		      _asn1_cpy_name (aux, octetNode);
+		      len2 =
+			asn1_get_length_der (octetNode->value,
+					     octetNode->value_len, &len3);
+		      if (len2 < 0)
+			return ASN1_DER_ERROR;
+
+		      result =
+			asn1_der_decoding (&aux, octetNode->value + len3,
+					   len2, errorDescription);
+		      if (result == ASN1_SUCCESS)
+			{
+
+			  _asn1_set_right (aux, octetNode->right);
+			  _asn1_set_right (octetNode, aux);
+
+			  result = asn1_delete_structure (&octetNode);
+			  if (result == ASN1_SUCCESS)
+			    {
+			      aux = NULL;
+			      break;
+			    }
+			  else
+			    {	/* error with asn1_delete_structure */
+			      asn1_delete_structure (&aux);
+			      retCode = result;
+			      break;
+			    }
+			}
+		      else
+			{	/* error with asn1_der_decoding */
+			  retCode = result;
+			  break;
+			}
+		    }
+		  else
+		    {		/* error with asn1_create_element */
+		      retCode = result;
+		      break;
+		    }
+		}
+	      else
+		{		/* error with the pointer to the structure to exapand */
+		  retCode = ASN1_VALUE_NOT_VALID;
+		  break;
+		}
+	    }
+	}
+
+      p2 = p2->right;
+
+    }
+
+  if (!p2)
+    retCode = ASN1_VALUE_NOT_VALID;
+
+  return retCode;
+}
+
+/*-
+ * _asn1_decode_simple_der:
+ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
+ * @der: the encoded string
+ * @_der_len: the bytes of the encoded string
+ * @str: a pointer to the data
+ * @str_len: the length of the data
+ * @dflags: DECODE_FLAG_*
+ *
+ * Decodes a simple DER encoded type (e.g. a string, which is not constructed).
+ * The output is a pointer inside the @der.
+ *
+ * Returns: %ASN1_SUCCESS if successful or an error value.
+ -*/
+static int
+_asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
+			unsigned int _der_len, const unsigned char **str,
+			unsigned int *str_len, unsigned dflags)
+{
+  int tag_len, len_len;
+  const unsigned char *p;
+  int der_len = _der_len;
+  unsigned char class;
+  unsigned long tag;
+  long ret;
+
+  if (der == NULL || der_len == 0)
+    return ASN1_VALUE_NOT_VALID;
+
+  if (ETYPE_OK (etype) == 0 || ETYPE_IS_STRING(etype) == 0)
+    return ASN1_VALUE_NOT_VALID;
+
+  /* doesn't handle constructed classes */
+  class = ETYPE_CLASS(etype);
+  if (class != ASN1_CLASS_UNIVERSAL)
+    return ASN1_VALUE_NOT_VALID;
+
+  p = der;
+
+  if (dflags & DECODE_FLAG_HAVE_TAG)
+    {
+      ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag);
+      if (ret != ASN1_SUCCESS)
+        return ret;
+
+      if (class != ETYPE_CLASS (etype) || tag != ETYPE_TAG (etype))
+        {
+          warn();
+          return ASN1_DER_ERROR;
+        }
+
+      p += tag_len;
+      der_len -= tag_len;
+      if (der_len <= 0)
+        return ASN1_DER_ERROR;
+    }
+
+  ret = asn1_get_length_der (p, der_len, &len_len);
+  if (ret < 0)
+    return ASN1_DER_ERROR;
+
+  p += len_len;
+  der_len -= len_len;
+  if (der_len <= 0)
+    return ASN1_DER_ERROR;
+
+  *str_len = ret;
+  *str = p;
+
+  return ASN1_SUCCESS;
+}
+
+/**
+ * asn1_decode_simple_der:
+ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
+ * @der: the encoded string
+ * @_der_len: the bytes of the encoded string
+ * @str: a pointer to the data
+ * @str_len: the length of the data
+ *
+ * Decodes a simple DER encoded type (e.g. a string, which is not constructed).
+ * The output is a pointer inside the @der.
+ *
+ * Returns: %ASN1_SUCCESS if successful or an error value.
+ **/
+int
+asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
+			unsigned int _der_len, const unsigned char **str,
+			unsigned int *str_len)
+{
+  return _asn1_decode_simple_der(etype, der, _der_len, str, str_len, DECODE_FLAG_HAVE_TAG);
+}
+
+static int append(uint8_t **dst, unsigned *dst_size, const unsigned char *src, unsigned src_size)
+{
+  if (src_size == 0)
+    return ASN1_SUCCESS;
+
+  *dst = _asn1_realloc(*dst, *dst_size+src_size);
+  if (*dst == NULL)
+    return ASN1_MEM_ALLOC_ERROR;
+  memcpy(*dst + *dst_size, src, src_size);
+  *dst_size += src_size;
+  return ASN1_SUCCESS;
+}
+
+/*-
+ * _asn1_decode_simple_ber:
+ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
+ * @der: the encoded string
+ * @_der_len: the bytes of the encoded string
+ * @str: a pointer to the data
+ * @str_len: the length of the data
+ * @ber_len: the total length occupied by BER (may be %NULL)
+ * @have_tag: whether a DER tag is included
+ *
+ * Decodes a BER encoded type. The output is an allocated value
+ * of the data. This decodes BER STRINGS only. Other types are
+ * decoded as DER.
+ *
+ * Returns: %ASN1_SUCCESS if successful or an error value.
+ -*/
+static int
+_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
+			unsigned int _der_len, unsigned char **str,
+			unsigned int *str_len, unsigned int *ber_len,
+			unsigned dflags)
+{
+  int tag_len, len_len;
+  const unsigned char *p;
+  int der_len = _der_len;
+  uint8_t *total = NULL;
+  unsigned total_size = 0;
+  unsigned char class;
+  unsigned long tag;
+  unsigned char *out = NULL;
+  const unsigned char *cout = NULL;
+  unsigned out_len;
+  long result;
+
+  if (ber_len) *ber_len = 0;
+
+  if (der == NULL || der_len == 0)
+    {
+      warn();
+      return ASN1_VALUE_NOT_VALID;
+    }
+
+  if (ETYPE_OK (etype) == 0)
+    {
+      warn();
+      return ASN1_VALUE_NOT_VALID;
+    }
+
+  /* doesn't handle constructed + definite classes */
+  class = ETYPE_CLASS (etype);
+  if (class != ASN1_CLASS_UNIVERSAL)
+    {
+      warn();
+      return ASN1_VALUE_NOT_VALID;
+    }
+
+  p = der;
+
+  if (dflags & DECODE_FLAG_HAVE_TAG)
+    {
+      result = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag);
+        if (result != ASN1_SUCCESS)
+          {
+            warn();
+            return result;
+          }
+
+        if (tag != ETYPE_TAG (etype))
+          {
+            warn();
+            return ASN1_DER_ERROR;
+          }
+
+        p += tag_len;
+
+        DECR_LEN(der_len, tag_len);
+
+        if (ber_len) *ber_len += tag_len;
+    }
+
+  /* indefinite constructed */
+  if ((((dflags & DECODE_FLAG_CONSTRUCTED) || class == ASN1_CLASS_STRUCTURED) && ETYPE_IS_STRING(etype)) &&
+      !(dflags & DECODE_FLAG_LEVEL3))
+    {
+      if (der_len == 0)
+        {
+          warn();
+          result = ASN1_DER_ERROR;
+          goto cleanup;
+        }
+
+      if (der_len > 0 && p[0] == 0x80) /* indefinite */
+        {
+          len_len = 1;
+          DECR_LEN(der_len, len_len);
+          p += len_len;
+
+          if (ber_len) *ber_len += len_len;
+
+          /* decode the available octet strings */
+          do
+            {
+              unsigned tmp_len;
+              unsigned flags = DECODE_FLAG_HAVE_TAG;
+
+              if (dflags & DECODE_FLAG_LEVEL1)
+                flags |= DECODE_FLAG_LEVEL2;
+              else if (dflags & DECODE_FLAG_LEVEL2)
+                flags |= DECODE_FLAG_LEVEL3;
+              else
+		flags |= DECODE_FLAG_LEVEL1;
+
+              result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len,
+                                               flags);
+              if (result != ASN1_SUCCESS)
+                {
+                  warn();
+                  goto cleanup;
+                }
+
+              p += tmp_len;
+              DECR_LEN(der_len, tmp_len);
+
+              if (ber_len) *ber_len += tmp_len;
+
+              DECR_LEN(der_len, 2); /* we need the EOC */
+
+              result = append(&total, &total_size, out, out_len);
+              if (result != ASN1_SUCCESS)
+                {
+                  warn();
+                  goto cleanup;
+	        }
+
+              free(out);
+              out = NULL;
+
+	      if (p[0] == 0 && p[1] == 0) /* EOC */
+	        {
+                  if (ber_len) *ber_len += 2;
+                  break;
+                }
+
+              /* no EOC */
+              der_len += 2;
+
+              if (der_len == 2)
+                {
+                  warn();
+                  result = ASN1_DER_ERROR;
+                  goto cleanup;
+                }
+            }
+          while(1);
+        }
+      else /* constructed */
+        {
+          long const_len;
+
+          result = asn1_get_length_ber(p, der_len, &len_len);
+          if (result < 0)
+            {
+              warn();
+              result = ASN1_DER_ERROR;
+              goto cleanup;
+            }
+
+          DECR_LEN(der_len, len_len);
+          p += len_len;
+
+          const_len = result;
+
+          if (ber_len) *ber_len += len_len;
+
+          /* decode the available octet strings */
+          while(const_len > 0)
+            {
+              unsigned tmp_len;
+              unsigned flags = DECODE_FLAG_HAVE_TAG;
+
+              if (dflags & DECODE_FLAG_LEVEL1)
+                flags |= DECODE_FLAG_LEVEL2;
+              else if (dflags & DECODE_FLAG_LEVEL2)
+                flags |= DECODE_FLAG_LEVEL3;
+              else
+		flags |= DECODE_FLAG_LEVEL1;
+
+              result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len,
+                                               flags);
+              if (result != ASN1_SUCCESS)
+                {
+                  warn();
+                  goto cleanup;
+                }
+
+              p += tmp_len;
+              DECR_LEN(der_len, tmp_len);
+              DECR_LEN(const_len, tmp_len);
+
+              if (ber_len) *ber_len += tmp_len;
+
+              result = append(&total, &total_size, out, out_len);
+              if (result != ASN1_SUCCESS)
+                {
+                  warn();
+                  goto cleanup;
+	        }
+
+              free(out);
+              out = NULL;
+            }
+        }
+    }
+  else if (class == ETYPE_CLASS(etype))
+    {
+      if (ber_len)
+        {
+          result = asn1_get_length_der (p, der_len, &len_len);
+          if (result < 0)
+            {
+              warn();
+              result = ASN1_DER_ERROR;
+              goto cleanup;
+            }
+          *ber_len += result + len_len;
+        }
+
+      /* non-string values are decoded as DER */
+      result = _asn1_decode_simple_der(etype, der, _der_len, &cout, &out_len, dflags);
+      if (result != ASN1_SUCCESS)
+        {
+          warn();
+          goto cleanup;
+        }
+
+      result = append(&total, &total_size, cout, out_len);
+      if (result != ASN1_SUCCESS)
+        {
+          warn();
+          goto cleanup;
+        }
+    }
+  else
+    {
+      warn();
+      result = ASN1_DER_ERROR;
+      goto cleanup;
+    }
+
+  *str = total;
+  *str_len = total_size;
+
+  return ASN1_SUCCESS;
+cleanup:
+  free(out);
+  free(total);
+  return result;
+}
+
+/**
+ * asn1_decode_simple_ber:
+ * @etype: The type of the string to be encoded (ASN1_ETYPE_)
+ * @der: the encoded string
+ * @_der_len: the bytes of the encoded string
+ * @str: a pointer to the data
+ * @str_len: the length of the data
+ * @ber_len: the total length occupied by BER (may be %NULL)
+ *
+ * Decodes a BER encoded type. The output is an allocated value
+ * of the data. This decodes BER STRINGS only. Other types are
+ * decoded as DER.
+ *
+ * Returns: %ASN1_SUCCESS if successful or an error value.
+ **/
+int
+asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
+			unsigned int _der_len, unsigned char **str,
+			unsigned int *str_len, unsigned int *ber_len)
+{
+  return _asn1_decode_simple_ber(etype, der, _der_len, str, str_len, ber_len, DECODE_FLAG_HAVE_TAG);
+}
diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c
new file mode 100644
index 000000000000..997eb2725dca
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -0,0 +1,1111 @@
+/*
+ * Copyright (C) 2000-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+/*****************************************************/
+/* File: element.c                                   */
+/* Description: Functions with the read and write    */
+/*   functions.                                      */
+/*****************************************************/
+
+
+#include <int.h>
+#include "parser_aux.h"
+#include <gstr.h>
+#include "structure.h"
+#include "c-ctype.h"
+#include "element.h"
+
+void
+_asn1_hierarchical_name (asn1_node_const node, char *name, int name_size)
+{
+  asn1_node_const p;
+  char tmp_name[64];
+
+  p = node;
+
+  name[0] = 0;
+
+  while (p != NULL)
+    {
+      if (p->name[0] != 0)
+	{
+	  _asn1_str_cpy (tmp_name, sizeof (tmp_name), name),
+	    _asn1_str_cpy (name, name_size, p->name);
+	  _asn1_str_cat (name, name_size, ".");
+	  _asn1_str_cat (name, name_size, tmp_name);
+	}
+      p = _asn1_find_up (p);
+    }
+
+  if (name[0] == 0)
+    _asn1_str_cpy (name, name_size, "ROOT");
+}
+
+
+/******************************************************************/
+/* Function : _asn1_convert_integer                               */
+/* Description: converts an integer from a null terminated string */
+/*              to der decoding. The convertion from a null       */
+/*              terminated string to an integer is made with      */
+/*              the 'strtol' function.                            */
+/* Parameters:                                                    */
+/*   value: null terminated string to convert.                    */
+/*   value_out: convertion result (memory must be already         */
+/*              allocated).                                       */
+/*   value_out_size: number of bytes of value_out.                */
+/*   len: number of significant byte of value_out.                */
+/* Return: ASN1_MEM_ERROR or ASN1_SUCCESS                         */
+/******************************************************************/
+int
+_asn1_convert_integer (const unsigned char *value, unsigned char *value_out,
+		       int value_out_size, int *len)
+{
+  char negative;
+  unsigned char val[SIZEOF_UNSIGNED_LONG_INT];
+  long valtmp;
+  int k, k2;
+
+  valtmp = _asn1_strtol (value, NULL, 10);
+
+  for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++)
+    {
+      val[SIZEOF_UNSIGNED_LONG_INT - k - 1] = (valtmp >> (8 * k)) & 0xFF;
+    }
+
+  if (val[0] & 0x80)
+    negative = 1;
+  else
+    negative = 0;
+
+  for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT - 1; k++)
+    {
+      if (negative && (val[k] != 0xFF))
+	break;
+      else if (!negative && val[k])
+	break;
+    }
+
+  if ((negative && !(val[k] & 0x80)) || (!negative && (val[k] & 0x80)))
+    k--;
+
+  *len = SIZEOF_UNSIGNED_LONG_INT - k;
+
+  if (SIZEOF_UNSIGNED_LONG_INT - k > value_out_size)
+    /* VALUE_OUT is too short to contain the value conversion */
+    return ASN1_MEM_ERROR;
+
+  if (value_out != NULL)
+    {
+      for (k2 = k; k2 < SIZEOF_UNSIGNED_LONG_INT; k2++)
+        value_out[k2 - k] = val[k2];
+    }
+
+#if 0
+  printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len);
+  for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++)
+    printf (", vOut[%d]=%d", k, value_out[k]);
+  printf ("\n");
+#endif
+
+  return ASN1_SUCCESS;
+}
+
+/* Appends a new element into the sequence (or set) defined by this
+ * node. The new element will have a name of '?number', where number
+ * is a monotonically increased serial number.
+ *
+ * The last element in the list may be provided in @pcache, to avoid
+ * traversing the list, an expensive operation in long lists.
+ *
+ * On success it returns in @pcache the added element (which is the
+ * tail in the list of added elements).
+ */
+int
+_asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache)
+{
+  asn1_node p, p2;
+  char temp[LTOSTR_MAX_SIZE];
+  long n;
+
+  if (!node || !(node->down))
+    return ASN1_GENERIC_ERROR;
+
+  p = node->down;
+  while ((type_field (p->type) == ASN1_ETYPE_TAG)
+	 || (type_field (p->type) == ASN1_ETYPE_SIZE))
+    p = p->right;
+
+  p2 = _asn1_copy_structure3 (p);
+  if (p2 == NULL)
+    return ASN1_GENERIC_ERROR;
+
+  if (pcache == NULL || pcache->tail == NULL || pcache->head != node)
+    {
+      while (p->right)
+        {
+          p = p->right;
+        }
+    }
+  else
+    {
+      p = pcache->tail;
+    }
+
+  _asn1_set_right (p, p2);
+  if (pcache)
+    {
+      pcache->head = node;
+      pcache->tail = p2;
+    }
+
+  if (p->name[0] == 0)
+    _asn1_str_cpy (temp, sizeof (temp), "?1");
+  else
+    {
+      n = strtol (p->name + 1, NULL, 0);
+      n++;
+      temp[0] = '?';
+      _asn1_ltostr (n, temp + 1);
+    }
+  _asn1_set_name (p2, temp);
+  /*  p2->type |= CONST_OPTION; */
+
+  return ASN1_SUCCESS;
+}
+
+
+/**
+ * asn1_write_value:
+ * @node_root: pointer to a structure
+ * @name: the name of the element inside the structure that you want to set.
+ * @ivalue: vector used to specify the value to set. If len is >0,
+ *   VALUE must be a two's complement form integer.  if len=0 *VALUE
+ *   must be a null terminated string with an integer value.
+ * @len: number of bytes of *value to use to set the value:
+ *   value[0]..value[len-1] or 0 if value is a null terminated string
+ *
+ * Set the value of one element inside a structure.
+ *
+ * If an element is OPTIONAL and you want to delete it, you must use
+ * the value=NULL and len=0.  Using "pkix.asn":
+ *
+ * result=asn1_write_value(cert, "tbsCertificate.issuerUniqueID",
+ * NULL, 0);
+ *
+ * Description for each type:
+ *
+ * INTEGER: VALUE must contain a two's complement form integer.
+ *
+ *            value[0]=0xFF ,               len=1 -> integer=-1.
+ *            value[0]=0xFF value[1]=0xFF , len=2 -> integer=-1.
+ *            value[0]=0x01 ,               len=1 -> integer= 1.
+ *            value[0]=0x00 value[1]=0x01 , len=2 -> integer= 1.
+ *            value="123"                 , len=0 -> integer= 123.
+ *
+ * ENUMERATED: As INTEGER (but only with not negative numbers).
+ *
+ * BOOLEAN: VALUE must be the null terminated string "TRUE" or
+ *   "FALSE" and LEN != 0.
+ *
+ *            value="TRUE" , len=1 -> boolean=TRUE.
+ *            value="FALSE" , len=1 -> boolean=FALSE.
+ *
+ * OBJECT IDENTIFIER: VALUE must be a null terminated string with
+ *   each number separated by a dot (e.g. "1.2.3.543.1").  LEN != 0.
+ *
+ *            value="1 2 840 10040 4 3" , len=1 -> OID=dsa-with-sha.
+ *
+ * UTCTime: VALUE must be a null terminated string in one of these
+ *   formats: "YYMMDDhhmmssZ", "YYMMDDhhmmssZ",
+ *   "YYMMDDhhmmss+hh'mm'", "YYMMDDhhmmss-hh'mm'",
+ *   "YYMMDDhhmm+hh'mm'", or "YYMMDDhhmm-hh'mm'".  LEN != 0.
+ *
+ *            value="9801011200Z" , len=1 -> time=Jannuary 1st, 1998
+ *            at 12h 00m Greenwich Mean Time
+ *
+ * GeneralizedTime: VALUE must be in one of this format:
+ *   "YYYYMMDDhhmmss.sZ", "YYYYMMDDhhmmss.sZ",
+ *   "YYYYMMDDhhmmss.s+hh'mm'", "YYYYMMDDhhmmss.s-hh'mm'",
+ *   "YYYYMMDDhhmm+hh'mm'", or "YYYYMMDDhhmm-hh'mm'" where ss.s
+ *   indicates the seconds with any precision like "10.1" or "01.02".
+ *   LEN != 0
+ *
+ *            value="2001010112001.12-0700" , len=1 -> time=Jannuary
+ *            1st, 2001 at 12h 00m 01.12s Pacific Daylight Time
+ *
+ * OCTET STRING: VALUE contains the octet string and LEN is the
+ *   number of octets.
+ *
+ *            value="$\backslash$x01$\backslash$x02$\backslash$x03" ,
+ *            len=3 -> three bytes octet string
+ *
+ * GeneralString: VALUE contains the generalstring and LEN is the
+ *   number of octets.
+ *
+ *            value="$\backslash$x01$\backslash$x02$\backslash$x03" ,
+ *            len=3 -> three bytes generalstring
+ *
+ * BIT STRING: VALUE contains the bit string organized by bytes and
+ *   LEN is the number of bits.
+ *
+ *   value="$\backslash$xCF" , len=6 -> bit string="110011" (six
+ *   bits)
+ *
+ * CHOICE: if NAME indicates a choice type, VALUE must specify one of
+ *   the alternatives with a null terminated string. LEN != 0. Using
+ *   "pkix.asn"\:
+ *
+ *           result=asn1_write_value(cert,
+ *           "certificate1.tbsCertificate.subject", "rdnSequence",
+ *           1);
+ *
+ * ANY: VALUE indicates the der encoding of a structure.  LEN != 0.
+ *
+ * SEQUENCE OF: VALUE must be the null terminated string "NEW" and
+ *   LEN != 0. With this instruction another element is appended in
+ *   the sequence. The name of this element will be "?1" if it's the
+ *   first one, "?2" for the second and so on.
+ *
+ *   Using "pkix.asn"\:
+ *
+ *   result=asn1_write_value(cert,
+ *   "certificate1.tbsCertificate.subject.rdnSequence", "NEW", 1);
+ *
+ * SET OF: the same as SEQUENCE OF.  Using "pkix.asn":
+ *
+ *           result=asn1_write_value(cert,
+ *           "tbsCertificate.subject.rdnSequence.?LAST", "NEW", 1);
+ *
+ * Returns: %ASN1_SUCCESS if the value was set,
+ *   %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, and
+ *   %ASN1_VALUE_NOT_VALID if @ivalue has a wrong format.
+ **/
+int
+asn1_write_value (asn1_node node_root, const char *name,
+		  const void *ivalue, int len)
+{
+  asn1_node node, p, p2;
+  unsigned char *temp, *value_temp = NULL, *default_temp = NULL;
+  int len2, k, k2, negative;
+  size_t i;
+  const unsigned char *value = ivalue;
+  unsigned int type;
+
+  node = asn1_find_node (node_root, name);
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  if ((node->type & CONST_OPTION) && (value == NULL) && (len == 0))
+    {
+      asn1_delete_structure (&node);
+      return ASN1_SUCCESS;
+    }
+
+  type = type_field (node->type);
+
+  if ((type == ASN1_ETYPE_SEQUENCE_OF || type == ASN1_ETYPE_SET_OF) && (value == NULL) && (len == 0))
+    {
+      p = node->down;
+      while ((type_field (p->type) == ASN1_ETYPE_TAG)
+	     || (type_field (p->type) == ASN1_ETYPE_SIZE))
+	p = p->right;
+
+      while (p->right)
+	asn1_delete_structure (&p->right);
+
+      return ASN1_SUCCESS;
+    }
+
+  /* Don't allow element deletion for other types */
+  if (value == NULL)
+    {
+      return ASN1_VALUE_NOT_VALID;
+    }
+
+  switch (type)
+    {
+    case ASN1_ETYPE_BOOLEAN:
+      if (!_asn1_strcmp (value, "TRUE"))
+	{
+	  if (node->type & CONST_DEFAULT)
+	    {
+	      p = node->down;
+	      while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
+		p = p->right;
+	      if (p->type & CONST_TRUE)
+		_asn1_set_value (node, NULL, 0);
+	      else
+		_asn1_set_value (node, "T", 1);
+	    }
+	  else
+	    _asn1_set_value (node, "T", 1);
+	}
+      else if (!_asn1_strcmp (value, "FALSE"))
+	{
+	  if (node->type & CONST_DEFAULT)
+	    {
+	      p = node->down;
+	      while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
+		p = p->right;
+	      if (p->type & CONST_FALSE)
+		_asn1_set_value (node, NULL, 0);
+	      else
+		_asn1_set_value (node, "F", 1);
+	    }
+	  else
+	    _asn1_set_value (node, "F", 1);
+	}
+      else
+	return ASN1_VALUE_NOT_VALID;
+      break;
+    case ASN1_ETYPE_INTEGER:
+    case ASN1_ETYPE_ENUMERATED:
+      if (len == 0)
+	{
+	  if ((c_isdigit (value[0])) || (value[0] == '-'))
+	    {
+	      value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
+	      if (value_temp == NULL)
+		return ASN1_MEM_ALLOC_ERROR;
+
+	      _asn1_convert_integer (value, value_temp,
+				     SIZEOF_UNSIGNED_LONG_INT, &len);
+	    }
+	  else
+	    {			/* is an identifier like v1 */
+	      if (!(node->type & CONST_LIST))
+		return ASN1_VALUE_NOT_VALID;
+	      p = node->down;
+	      while (p)
+		{
+		  if (type_field (p->type) == ASN1_ETYPE_CONSTANT)
+		    {
+		      if (!_asn1_strcmp (p->name, value))
+			{
+			  value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
+			  if (value_temp == NULL)
+			    return ASN1_MEM_ALLOC_ERROR;
+
+			  _asn1_convert_integer (p->value,
+						 value_temp,
+						 SIZEOF_UNSIGNED_LONG_INT,
+						 &len);
+			  break;
+			}
+		    }
+		  p = p->right;
+		}
+	      if (p == NULL)
+		return ASN1_VALUE_NOT_VALID;
+	    }
+	}
+      else
+	{			/* len != 0 */
+	  value_temp = malloc (len);
+	  if (value_temp == NULL)
+	    return ASN1_MEM_ALLOC_ERROR;
+	  memcpy (value_temp, value, len);
+	}
+
+      if (value_temp[0] & 0x80)
+	negative = 1;
+      else
+	negative = 0;
+
+      if (negative && (type_field (node->type) == ASN1_ETYPE_ENUMERATED))
+	{
+	  free (value_temp);
+	  return ASN1_VALUE_NOT_VALID;
+	}
+
+      for (k = 0; k < len - 1; k++)
+	if (negative && (value_temp[k] != 0xFF))
+	  break;
+	else if (!negative && value_temp[k])
+	  break;
+
+      if ((negative && !(value_temp[k] & 0x80)) ||
+	  (!negative && (value_temp[k] & 0x80)))
+	k--;
+
+      _asn1_set_value_lv (node, value_temp + k, len - k);
+
+      if (node->type & CONST_DEFAULT)
+	{
+	  p = node->down;
+	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
+	    p = p->right;
+	  if ((c_isdigit (p->value[0])) || (p->value[0] == '-'))
+	    {
+	      default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
+	      if (default_temp == NULL)
+		{
+		  free (value_temp);
+		  return ASN1_MEM_ALLOC_ERROR;
+		}
+
+	      _asn1_convert_integer (p->value, default_temp,
+				     SIZEOF_UNSIGNED_LONG_INT, &len2);
+	    }
+	  else
+	    {			/* is an identifier like v1 */
+	      if (!(node->type & CONST_LIST))
+		{
+		  free (value_temp);
+		  return ASN1_VALUE_NOT_VALID;
+		}
+	      p2 = node->down;
+	      while (p2)
+		{
+		  if (type_field (p2->type) == ASN1_ETYPE_CONSTANT)
+		    {
+		      if (!_asn1_strcmp (p2->name, p->value))
+			{
+			  default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT);
+			  if (default_temp == NULL)
+			    {
+			      free (value_temp);
+			      return ASN1_MEM_ALLOC_ERROR;
+			    }
+
+			  _asn1_convert_integer (p2->value,
+						 default_temp,
+						 SIZEOF_UNSIGNED_LONG_INT,
+						 &len2);
+			  break;
+			}
+		    }
+		  p2 = p2->right;
+		}
+	      if (p2 == NULL)
+		{
+		  free (value_temp);
+		  return ASN1_VALUE_NOT_VALID;
+		}
+	    }
+
+
+	  if ((len - k) == len2)
+	    {
+	      for (k2 = 0; k2 < len2; k2++)
+		if (value_temp[k + k2] != default_temp[k2])
+		  {
+		    break;
+		  }
+	      if (k2 == len2)
+		_asn1_set_value (node, NULL, 0);
+	    }
+	  free (default_temp);
+	}
+      free (value_temp);
+      break;
+    case ASN1_ETYPE_OBJECT_ID:
+      for (i = 0; i < _asn1_strlen (value); i++)
+	if ((!c_isdigit (value[i])) && (value[i] != '.') && (value[i] != '+'))
+	  return ASN1_VALUE_NOT_VALID;
+      if (node->type & CONST_DEFAULT)
+	{
+	  p = node->down;
+	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
+	    p = p->right;
+	  if (!_asn1_strcmp (value, p->value))
+	    {
+	      _asn1_set_value (node, NULL, 0);
+	      break;
+	    }
+	}
+      _asn1_set_value (node, value, _asn1_strlen (value) + 1);
+      break;
+    case ASN1_ETYPE_UTC_TIME:
+      {
+	len = _asn1_strlen (value);
+	if (len < 11)
+	  return ASN1_VALUE_NOT_VALID;
+	for (k = 0; k < 10; k++)
+	  if (!c_isdigit (value[k]))
+	    return ASN1_VALUE_NOT_VALID;
+	switch (len)
+	  {
+	  case 11:
+	    if (value[10] != 'Z')
+	      return ASN1_VALUE_NOT_VALID;
+	    break;
+	  case 13:
+	    if ((!c_isdigit (value[10])) || (!c_isdigit (value[11])) ||
+		(value[12] != 'Z'))
+	      return ASN1_VALUE_NOT_VALID;
+	    break;
+	  case 15:
+	    if ((value[10] != '+') && (value[10] != '-'))
+	      return ASN1_VALUE_NOT_VALID;
+	    for (k = 11; k < 15; k++)
+	      if (!c_isdigit (value[k]))
+		return ASN1_VALUE_NOT_VALID;
+	    break;
+	  case 17:
+	    if ((!c_isdigit (value[10])) || (!c_isdigit (value[11])))
+	      return ASN1_VALUE_NOT_VALID;
+	    if ((value[12] != '+') && (value[12] != '-'))
+	      return ASN1_VALUE_NOT_VALID;
+	    for (k = 13; k < 17; k++)
+	      if (!c_isdigit (value[k]))
+		return ASN1_VALUE_NOT_VALID;
+	    break;
+	  default:
+	    return ASN1_VALUE_NOT_FOUND;
+	  }
+	_asn1_set_value (node, value, len);
+      }
+      break;
+    case ASN1_ETYPE_GENERALIZED_TIME:
+      len = _asn1_strlen (value);
+      _asn1_set_value (node, value, len);
+      break;
+    case ASN1_ETYPE_OCTET_STRING:
+    case ASN1_ETYPE_GENERALSTRING:
+    case ASN1_ETYPE_NUMERIC_STRING:
+    case ASN1_ETYPE_IA5_STRING:
+    case ASN1_ETYPE_TELETEX_STRING:
+    case ASN1_ETYPE_PRINTABLE_STRING:
+    case ASN1_ETYPE_UNIVERSAL_STRING:
+    case ASN1_ETYPE_BMP_STRING:
+    case ASN1_ETYPE_UTF8_STRING:
+    case ASN1_ETYPE_VISIBLE_STRING:
+      if (len == 0)
+	len = _asn1_strlen (value);
+      _asn1_set_value_lv (node, value, len);
+      break;
+    case ASN1_ETYPE_BIT_STRING:
+      if (len == 0)
+	len = _asn1_strlen (value);
+      asn1_length_der ((len >> 3) + 2, NULL, &len2);
+      temp = malloc ((len >> 3) + 2 + len2);
+      if (temp == NULL)
+	return ASN1_MEM_ALLOC_ERROR;
+
+      asn1_bit_der (value, len, temp, &len2);
+      _asn1_set_value_m (node, temp, len2);
+      temp = NULL;
+      break;
+    case ASN1_ETYPE_CHOICE:
+      p = node->down;
+      while (p)
+	{
+	  if (!_asn1_strcmp (p->name, value))
+	    {
+	      p2 = node->down;
+	      while (p2)
+		{
+		  if (p2 != p)
+		    {
+		      asn1_delete_structure (&p2);
+		      p2 = node->down;
+		    }
+		  else
+		    p2 = p2->right;
+		}
+	      break;
+	    }
+	  p = p->right;
+	}
+      if (!p)
+	return ASN1_ELEMENT_NOT_FOUND;
+      break;
+    case ASN1_ETYPE_ANY:
+      _asn1_set_value_lv (node, value, len);
+      break;
+    case ASN1_ETYPE_SEQUENCE_OF:
+    case ASN1_ETYPE_SET_OF:
+      if (_asn1_strcmp (value, "NEW"))
+	return ASN1_VALUE_NOT_VALID;
+      _asn1_append_sequence_set (node, NULL);
+      break;
+    default:
+      return ASN1_ELEMENT_NOT_FOUND;
+      break;
+    }
+
+  return ASN1_SUCCESS;
+}
+
+
+#define PUT_VALUE( ptr, ptr_size, data, data_size) \
+	*len = data_size; \
+	if (ptr_size < data_size) { \
+		return ASN1_MEM_ERROR; \
+	} else { \
+		if (ptr && data_size > 0) \
+		  memcpy (ptr, data, data_size); \
+	}
+
+#define PUT_STR_VALUE( ptr, ptr_size, data) \
+	*len = _asn1_strlen (data) + 1; \
+	if (ptr_size < *len) { \
+		return ASN1_MEM_ERROR; \
+	} else { \
+		/* this strcpy is checked */ \
+		if (ptr) { \
+		  _asn1_strcpy (ptr, data); \
+		} \
+	}
+
+#define PUT_AS_STR_VALUE( ptr, ptr_size, data, data_size) \
+	*len = data_size + 1; \
+	if (ptr_size < *len) { \
+		return ASN1_MEM_ERROR; \
+	} else { \
+		/* this strcpy is checked */ \
+		if (ptr) { \
+		  if (data_size > 0) \
+		    memcpy (ptr, data, data_size); \
+		  ptr[data_size] = 0; \
+		} \
+	}
+
+#define ADD_STR_VALUE( ptr, ptr_size, data) \
+        *len += _asn1_strlen(data); \
+        if (ptr_size < (int) *len) { \
+                (*len)++; \
+                return ASN1_MEM_ERROR; \
+        } else { \
+                /* this strcat is checked */ \
+                if (ptr) _asn1_strcat (ptr, data); \
+        }
+
+/**
+ * asn1_read_value:
+ * @root: pointer to a structure.
+ * @name: the name of the element inside a structure that you want to read.
+ * @ivalue: vector that will contain the element's content, must be a
+ *   pointer to memory cells already allocated (may be %NULL).
+ * @len: number of bytes of *value: value[0]..value[len-1]. Initialy
+ *   holds the sizeof value.
+ *
+ * Returns the value of one element inside a structure.
+ * If an element is OPTIONAL and this returns
+ * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present
+ * in the der encoding that created the structure.  The first element
+ * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and
+ * so on. If the @root provided is a node to specific sequence element,
+ * then the keyword "?CURRENT" is also acceptable and indicates the
+ * current sequence element of this node.
+ *
+ * Note that there can be valid values with length zero. In these case
+ * this function will succeed and @len will be zero.
+ *
+ * INTEGER: VALUE will contain a two's complement form integer.
+ *
+ *            integer=-1  -> value[0]=0xFF , len=1.
+ *            integer=1   -> value[0]=0x01 , len=1.
+ *
+ * ENUMERATED: As INTEGER (but only with not negative numbers).
+ *
+ * BOOLEAN: VALUE will be the null terminated string "TRUE" or
+ *   "FALSE" and LEN=5 or LEN=6.
+ *
+ * OBJECT IDENTIFIER: VALUE will be a null terminated string with
+ *   each number separated by a dot (i.e. "1.2.3.543.1").
+ *
+ *                      LEN = strlen(VALUE)+1
+ *
+ * UTCTime: VALUE will be a null terminated string in one of these
+ *   formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'".
+ *   LEN=strlen(VALUE)+1.
+ *
+ * GeneralizedTime: VALUE will be a null terminated string in the
+ *   same format used to set the value.
+ *
+ * OCTET STRING: VALUE will contain the octet string and LEN will be
+ *   the number of octets.
+ *
+ * GeneralString: VALUE will contain the generalstring and LEN will
+ *   be the number of octets.
+ *
+ * BIT STRING: VALUE will contain the bit string organized by bytes
+ *   and LEN will be the number of bits.
+ *
+ * CHOICE: If NAME indicates a choice type, VALUE will specify the
+ *   alternative selected.
+ *
+ * ANY: If NAME indicates an any type, VALUE will indicate the DER
+ *   encoding of the structure actually used.
+ *
+ * Returns: %ASN1_SUCCESS if value is returned,
+ *   %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element,
+ *   %ASN1_VALUE_NOT_FOUND if there isn't any value for the element
+ *   selected, and %ASN1_MEM_ERROR if The value vector isn't big enough
+ *   to store the result, and in this case @len will contain the number of
+ *   bytes needed. On the occasion that the stored data are of zero-length
+ *   this function may return %ASN1_SUCCESS even if the provided @len is zero.
+ **/
+int
+asn1_read_value (asn1_node_const root, const char *name, void *ivalue, int *len)
+{
+  return asn1_read_value_type (root, name, ivalue, len, NULL);
+}
+
+/**
+ * asn1_read_value_type:
+ * @root: pointer to a structure.
+ * @name: the name of the element inside a structure that you want to read.
+ * @ivalue: vector that will contain the element's content, must be a
+ *   pointer to memory cells already allocated (may be %NULL).
+ * @len: number of bytes of *value: value[0]..value[len-1]. Initialy
+ *   holds the sizeof value.
+ * @etype: The type of the value read (ASN1_ETYPE)
+ *
+ * Returns the type and value of one element inside a structure.
+ * If an element is OPTIONAL and this returns
+ * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present
+ * in the der encoding that created the structure.  The first element
+ * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and
+ * so on. If the @root provided is a node to specific sequence element,
+ * then the keyword "?CURRENT" is also acceptable and indicates the
+ * current sequence element of this node.
+ *
+ * Note that there can be valid values with length zero. In these case
+ * this function will succeed and @len will be zero.
+ *
+ *
+ * INTEGER: VALUE will contain a two's complement form integer.
+ *
+ *            integer=-1  -> value[0]=0xFF , len=1.
+ *            integer=1   -> value[0]=0x01 , len=1.
+ *
+ * ENUMERATED: As INTEGER (but only with not negative numbers).
+ *
+ * BOOLEAN: VALUE will be the null terminated string "TRUE" or
+ *   "FALSE" and LEN=5 or LEN=6.
+ *
+ * OBJECT IDENTIFIER: VALUE will be a null terminated string with
+ *   each number separated by a dot (i.e. "1.2.3.543.1").
+ *
+ *                      LEN = strlen(VALUE)+1
+ *
+ * UTCTime: VALUE will be a null terminated string in one of these
+ *   formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'".
+ *   LEN=strlen(VALUE)+1.
+ *
+ * GeneralizedTime: VALUE will be a null terminated string in the
+ *   same format used to set the value.
+ *
+ * OCTET STRING: VALUE will contain the octet string and LEN will be
+ *   the number of octets.
+ *
+ * GeneralString: VALUE will contain the generalstring and LEN will
+ *   be the number of octets.
+ *
+ * BIT STRING: VALUE will contain the bit string organized by bytes
+ *   and LEN will be the number of bits.
+ *
+ * CHOICE: If NAME indicates a choice type, VALUE will specify the
+ *   alternative selected.
+ *
+ * ANY: If NAME indicates an any type, VALUE will indicate the DER
+ *   encoding of the structure actually used.
+ *
+ * Returns: %ASN1_SUCCESS if value is returned,
+ *   %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element,
+ *   %ASN1_VALUE_NOT_FOUND if there isn't any value for the element
+ *   selected, and %ASN1_MEM_ERROR if The value vector isn't big enough
+ *   to store the result, and in this case @len will contain the number of
+ *   bytes needed. On the occasion that the stored data are of zero-length
+ *   this function may return %ASN1_SUCCESS even if the provided @len is zero.
+ **/
+int
+asn1_read_value_type (asn1_node_const root, const char *name, void *ivalue,
+		      int *len, unsigned int *etype)
+{
+  asn1_node_const node, p, p2;
+  int len2, len3, result;
+  int value_size = *len;
+  unsigned char *value = ivalue;
+  unsigned type;
+
+  node = asn1_find_node (root, name);
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  type = type_field (node->type);
+
+  if ((type != ASN1_ETYPE_NULL) &&
+      (type != ASN1_ETYPE_CHOICE) &&
+      !(node->type & CONST_DEFAULT) && !(node->type & CONST_ASSIGN) &&
+      (node->value == NULL))
+    return ASN1_VALUE_NOT_FOUND;
+
+  if (etype)
+    *etype = type;
+  switch (type)
+    {
+    case ASN1_ETYPE_NULL:
+      PUT_STR_VALUE (value, value_size, "NULL");
+      break;
+    case ASN1_ETYPE_BOOLEAN:
+      if ((node->type & CONST_DEFAULT) && (node->value == NULL))
+	{
+	  p = node->down;
+	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
+	    p = p->right;
+	  if (p->type & CONST_TRUE)
+	    {
+	      PUT_STR_VALUE (value, value_size, "TRUE");
+	    }
+	  else
+	    {
+	      PUT_STR_VALUE (value, value_size, "FALSE");
+	    }
+	}
+      else if (node->value[0] == 'T')
+	{
+	  PUT_STR_VALUE (value, value_size, "TRUE");
+	}
+      else
+	{
+	  PUT_STR_VALUE (value, value_size, "FALSE");
+	}
+      break;
+    case ASN1_ETYPE_INTEGER:
+    case ASN1_ETYPE_ENUMERATED:
+      if ((node->type & CONST_DEFAULT) && (node->value == NULL))
+	{
+	  p = node->down;
+	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
+	    p = p->right;
+	  if ((c_isdigit (p->value[0])) || (p->value[0] == '-')
+	      || (p->value[0] == '+'))
+	    {
+	      result = _asn1_convert_integer
+		  (p->value, value, value_size, len);
+              if (result != ASN1_SUCCESS)
+		return result;
+	    }
+	  else
+	    {			/* is an identifier like v1 */
+	      p2 = node->down;
+	      while (p2)
+		{
+		  if (type_field (p2->type) == ASN1_ETYPE_CONSTANT)
+		    {
+		      if (!_asn1_strcmp (p2->name, p->value))
+			{
+			  result = _asn1_convert_integer
+			      (p2->value, value, value_size,
+			       len);
+			  if (result != ASN1_SUCCESS)
+			    return result;
+			  break;
+			}
+		    }
+		  p2 = p2->right;
+		}
+	    }
+	}
+      else
+	{
+	  len2 = -1;
+	  result = asn1_get_octet_der
+	      (node->value, node->value_len, &len2, value, value_size,
+	       len);
+          if (result != ASN1_SUCCESS)
+	    return result;
+	}
+      break;
+    case ASN1_ETYPE_OBJECT_ID:
+      if (node->type & CONST_ASSIGN)
+	{
+	  *len = 0;
+	  if (value)
+	    value[0] = 0;
+	  p = node->down;
+	  while (p)
+	    {
+	      if (type_field (p->type) == ASN1_ETYPE_CONSTANT)
+		{
+		  ADD_STR_VALUE (value, value_size, p->value);
+		  if (p->right)
+		    {
+		      ADD_STR_VALUE (value, value_size, ".");
+		    }
+		}
+	      p = p->right;
+	    }
+	  (*len)++;
+	}
+      else if ((node->type & CONST_DEFAULT) && (node->value == NULL))
+	{
+	  p = node->down;
+	  while (type_field (p->type) != ASN1_ETYPE_DEFAULT)
+	    p = p->right;
+	  PUT_STR_VALUE (value, value_size, p->value);
+	}
+      else
+	{
+	  PUT_STR_VALUE (value, value_size, node->value);
+	}
+      break;
+    case ASN1_ETYPE_GENERALIZED_TIME:
+    case ASN1_ETYPE_UTC_TIME:
+      PUT_AS_STR_VALUE (value, value_size, node->value, node->value_len);
+      break;
+    case ASN1_ETYPE_OCTET_STRING:
+    case ASN1_ETYPE_GENERALSTRING:
+    case ASN1_ETYPE_NUMERIC_STRING:
+    case ASN1_ETYPE_IA5_STRING:
+    case ASN1_ETYPE_TELETEX_STRING:
+    case ASN1_ETYPE_PRINTABLE_STRING:
+    case ASN1_ETYPE_UNIVERSAL_STRING:
+    case ASN1_ETYPE_BMP_STRING:
+    case ASN1_ETYPE_UTF8_STRING:
+    case ASN1_ETYPE_VISIBLE_STRING:
+      len2 = -1;
+      result = asn1_get_octet_der
+	  (node->value, node->value_len, &len2, value, value_size,
+	   len);
+      if (result != ASN1_SUCCESS)
+	return result;
+      break;
+    case ASN1_ETYPE_BIT_STRING:
+      len2 = -1;
+      result = asn1_get_bit_der
+	  (node->value, node->value_len, &len2, value, value_size,
+	   len);
+      if (result != ASN1_SUCCESS)
+	return result;
+      break;
+    case ASN1_ETYPE_CHOICE:
+      PUT_STR_VALUE (value, value_size, node->down->name);
+      break;
+    case ASN1_ETYPE_ANY:
+      len3 = -1;
+      len2 = asn1_get_length_der (node->value, node->value_len, &len3);
+      if (len2 < 0)
+	return ASN1_DER_ERROR;
+      PUT_VALUE (value, value_size, node->value + len3, len2);
+      break;
+    default:
+      return ASN1_ELEMENT_NOT_FOUND;
+      break;
+    }
+  return ASN1_SUCCESS;
+}
+
+
+/**
+ * asn1_read_tag:
+ * @root: pointer to a structure
+ * @name: the name of the element inside a structure.
+ * @tagValue:  variable that will contain the TAG value.
+ * @classValue: variable that will specify the TAG type.
+ *
+ * Returns the TAG and the CLASS of one element inside a structure.
+ * CLASS can have one of these constants: %ASN1_CLASS_APPLICATION,
+ * %ASN1_CLASS_UNIVERSAL, %ASN1_CLASS_PRIVATE or
+ * %ASN1_CLASS_CONTEXT_SPECIFIC.
+ *
+ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
+ *   @name is not a valid element.
+ **/
+int
+asn1_read_tag (asn1_node_const root, const char *name, int *tagValue,
+	       int *classValue)
+{
+  asn1_node node, p, pTag;
+
+  node = asn1_find_node (root, name);
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = node->down;
+
+  /* pTag will points to the IMPLICIT TAG */
+  pTag = NULL;
+  if (node->type & CONST_TAG)
+    {
+      while (p)
+	{
+	  if (type_field (p->type) == ASN1_ETYPE_TAG)
+	    {
+	      if ((p->type & CONST_IMPLICIT) && (pTag == NULL))
+		pTag = p;
+	      else if (p->type & CONST_EXPLICIT)
+		pTag = NULL;
+	    }
+	  p = p->right;
+	}
+    }
+
+  if (pTag)
+    {
+      *tagValue = _asn1_strtoul (pTag->value, NULL, 10);
+
+      if (pTag->type & CONST_APPLICATION)
+	*classValue = ASN1_CLASS_APPLICATION;
+      else if (pTag->type & CONST_UNIVERSAL)
+	*classValue = ASN1_CLASS_UNIVERSAL;
+      else if (pTag->type & CONST_PRIVATE)
+	*classValue = ASN1_CLASS_PRIVATE;
+      else
+	*classValue = ASN1_CLASS_CONTEXT_SPECIFIC;
+    }
+  else
+    {
+      unsigned type = type_field (node->type);
+      *classValue = ASN1_CLASS_UNIVERSAL;
+
+      switch (type)
+	{
+	CASE_HANDLED_ETYPES:
+	  *tagValue = _asn1_tags[type].tag;
+	  break;
+	case ASN1_ETYPE_TAG:
+	case ASN1_ETYPE_CHOICE:
+	case ASN1_ETYPE_ANY:
+	  *tagValue = -1;
+	  break;
+	default:
+	  break;
+	}
+    }
+
+  return ASN1_SUCCESS;
+}
+
+/**
+ * asn1_read_node_value:
+ * @node: pointer to a node.
+ * @data: a point to a asn1_data_node_st
+ *
+ * Returns the value a data node inside a asn1_node structure.
+ * The data returned should be handled as constant values.
+ *
+ * Returns: %ASN1_SUCCESS if the node exists.
+ **/
+int
+asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data)
+{
+  data->name = node->name;
+  data->value = node->value;
+  data->value_len = node->value_len;
+  data->type = type_field (node->type);
+
+  return ASN1_SUCCESS;
+}
diff --git a/grub-core/lib/libtasn1/lib/element.h b/grub-core/lib/libtasn1/lib/element.h
new file mode 100644
index 000000000000..440a33f4bb1e
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/element.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2000-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _ELEMENT_H
+#define _ELEMENT_H
+
+
+struct node_tail_cache_st
+{
+	asn1_node head; /* the first element of the sequence */
+	asn1_node tail;
+};
+
+int _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcached);
+
+int _asn1_convert_integer (const unsigned char *value,
+			   unsigned char *value_out,
+			   int value_out_size, int *len);
+
+void _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size);
+
+#endif
diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c
new file mode 100644
index 000000000000..cee74daf7959
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/errors.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include <int.h>
+#ifdef STDC_HEADERS
+#include <stdarg.h>
+#endif
+
+#define LIBTASN1_ERROR_ENTRY(name) { #name, name }
+
+struct libtasn1_error_entry
+{
+  const char *name;
+  int number;
+};
+typedef struct libtasn1_error_entry libtasn1_error_entry;
+
+static const libtasn1_error_entry error_algorithms[] = {
+  LIBTASN1_ERROR_ENTRY (ASN1_SUCCESS),
+  LIBTASN1_ERROR_ENTRY (ASN1_FILE_NOT_FOUND),
+  LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_FOUND),
+  LIBTASN1_ERROR_ENTRY (ASN1_IDENTIFIER_NOT_FOUND),
+  LIBTASN1_ERROR_ENTRY (ASN1_DER_ERROR),
+  LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_FOUND),
+  LIBTASN1_ERROR_ENTRY (ASN1_GENERIC_ERROR),
+  LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_VALID),
+  LIBTASN1_ERROR_ENTRY (ASN1_TAG_ERROR),
+  LIBTASN1_ERROR_ENTRY (ASN1_TAG_IMPLICIT),
+  LIBTASN1_ERROR_ENTRY (ASN1_ERROR_TYPE_ANY),
+  LIBTASN1_ERROR_ENTRY (ASN1_SYNTAX_ERROR),
+  LIBTASN1_ERROR_ENTRY (ASN1_MEM_ERROR),
+  LIBTASN1_ERROR_ENTRY (ASN1_MEM_ALLOC_ERROR),
+  LIBTASN1_ERROR_ENTRY (ASN1_DER_OVERFLOW),
+  LIBTASN1_ERROR_ENTRY (ASN1_NAME_TOO_LONG),
+  LIBTASN1_ERROR_ENTRY (ASN1_ARRAY_ERROR),
+  LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_EMPTY),
+  LIBTASN1_ERROR_ENTRY (ASN1_TIME_ENCODING_ERROR),
+  LIBTASN1_ERROR_ENTRY (ASN1_RECURSION),
+  {0, 0}
+};
+
+/**
+ * asn1_perror:
+ * @error: is an error returned by a libtasn1 function.
+ *
+ * Prints a string to stderr with a description of an error.  This
+ * function is like perror().  The only difference is that it accepts
+ * an error returned by a libtasn1 function.
+ *
+ * Since: 1.6
+ **/
+void
+asn1_perror (int error)
+{
+  const char *str = asn1_strerror (error);
+  fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)");
+}
+
+/**
+ * asn1_strerror:
+ * @error: is an error returned by a libtasn1 function.
+ *
+ * Returns a string with a description of an error.  This function is
+ * similar to strerror.  The only difference is that it accepts an
+ * error (number) returned by a libtasn1 function.
+ *
+ * Returns: Pointer to static zero-terminated string describing error
+ *   code.
+ *
+ * Since: 1.6
+ **/
+const char *
+asn1_strerror (int error)
+{
+  const libtasn1_error_entry *p;
+
+  for (p = error_algorithms; p->name != NULL; p++)
+    if (p->number == error)
+      return p->name + sizeof ("ASN1_") - 1;
+
+  return NULL;
+}
diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c
new file mode 100644
index 000000000000..e91a3a151c0d
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/gstr.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include <int.h>
+#include "gstr.h"
+
+/* These function are like strcat, strcpy. They only
+ * do bounds checking (they shouldn't cause buffer overruns),
+ * and they always produce null terminated strings.
+ *
+ * They should be used only with null terminated strings.
+ */
+void
+_asn1_str_cat (char *dest, size_t dest_tot_size, const char *src)
+{
+  size_t str_size = strlen (src);
+  size_t dest_size = strlen (dest);
+
+  if (dest_tot_size - dest_size > str_size)
+    {
+      strcat (dest, src);
+    }
+  else
+    {
+      if (dest_tot_size - dest_size > 0)
+	{
+	  strncat (dest, src, (dest_tot_size - dest_size) - 1);
+	  dest[dest_tot_size - 1] = 0;
+	}
+    }
+}
+
+/* Returns the bytes copied (not including the null terminator) */
+unsigned int
+_asn1_str_cpy (char *dest, size_t dest_tot_size, const char *src)
+{
+  size_t str_size = strlen (src);
+
+  if (dest_tot_size > str_size)
+    {
+      strcpy (dest, src);
+      return str_size;
+    }
+  else
+    {
+      if (dest_tot_size > 0)
+	{
+	  str_size = dest_tot_size - 1;
+	  memcpy (dest, src, str_size);
+	  dest[str_size] = 0;
+	  return str_size;
+	}
+      else
+	return 0;
+    }
+}
diff --git a/grub-core/lib/libtasn1/lib/gstr.h b/grub-core/lib/libtasn1/lib/gstr.h
new file mode 100644
index 000000000000..48229844ff3e
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/gstr.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef GSTR_H
+# define GSTR_H
+
+unsigned int _asn1_str_cpy (char *dest, size_t dest_tot_size,
+			    const char *src);
+void _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src);
+
+#define Estrcpy(x,y) _asn1_str_cpy(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y)
+#define Estrcat(x,y) _asn1_str_cat(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y)
+
+inline static
+void safe_memset(void *data, int c, size_t size)
+{
+	volatile unsigned volatile_zero = 0;
+	volatile char *vdata = (volatile char*)data;
+
+	/* This is based on a nice trick for safe memset,
+	 * sent by David Jacobson in the openssl-dev mailing list.
+	 */
+
+	if (size > 0) do {
+		memset(data, c, size);
+	} while(vdata[volatile_zero] != c);
+}
+
+#endif /* GSTR_H */
diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
new file mode 100644
index 000000000000..ea1625786c1b
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/int.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef INT_H
+#define INT_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <libtasn1.h>
+
+#define ASN1_SMALL_VALUE_SIZE 16
+
+/* This structure is also in libtasn1.h, but then contains less
+   fields.  You cannot make any modifications to these first fields
+   without breaking ABI.  */
+struct asn1_node_st
+{
+  /* public fields: */
+  char name[ASN1_MAX_NAME_SIZE + 1];	/* Node name */
+  unsigned int name_hash;
+  unsigned int type;		/* Node type */
+  unsigned char *value;		/* Node value */
+  int value_len;
+  asn1_node down;		/* Pointer to the son node */
+  asn1_node right;		/* Pointer to the brother node */
+  asn1_node left;		/* Pointer to the next list element */
+  /* private fields: */
+  unsigned char small_value[ASN1_SMALL_VALUE_SIZE];	/* For small values */
+
+  /* values used during decoding/coding */
+  int tmp_ival;
+  unsigned start; /* the start of the DER sequence - if decoded */
+  unsigned end; /* the end of the DER sequence - if decoded */
+};
+
+typedef struct tag_and_class_st
+{
+  unsigned tag;
+  unsigned class;
+  const char *desc;
+} tag_and_class_st;
+
+/* the types that are handled in _asn1_tags */
+#define CASE_HANDLED_ETYPES \
+	case ASN1_ETYPE_NULL: \
+	case ASN1_ETYPE_BOOLEAN: \
+	case ASN1_ETYPE_INTEGER: \
+	case ASN1_ETYPE_ENUMERATED: \
+	case ASN1_ETYPE_OBJECT_ID: \
+	case ASN1_ETYPE_OCTET_STRING: \
+	case ASN1_ETYPE_GENERALSTRING: \
+        case ASN1_ETYPE_NUMERIC_STRING: \
+        case ASN1_ETYPE_IA5_STRING: \
+        case ASN1_ETYPE_TELETEX_STRING: \
+        case ASN1_ETYPE_PRINTABLE_STRING: \
+        case ASN1_ETYPE_UNIVERSAL_STRING: \
+        case ASN1_ETYPE_BMP_STRING: \
+        case ASN1_ETYPE_UTF8_STRING: \
+        case ASN1_ETYPE_VISIBLE_STRING: \
+	case ASN1_ETYPE_BIT_STRING: \
+	case ASN1_ETYPE_SEQUENCE: \
+	case ASN1_ETYPE_SEQUENCE_OF: \
+	case ASN1_ETYPE_SET: \
+	case ASN1_ETYPE_UTC_TIME: \
+	case ASN1_ETYPE_GENERALIZED_TIME: \
+	case ASN1_ETYPE_SET_OF
+
+#define ETYPE_TAG(etype) (_asn1_tags[etype].tag)
+#define ETYPE_CLASS(etype) (_asn1_tags[etype].class)
+#define ETYPE_OK(etype) (((etype) != ASN1_ETYPE_INVALID && \
+                          (etype) <= _asn1_tags_size && \
+                          _asn1_tags[(etype)].desc != NULL)?1:0)
+
+#define ETYPE_IS_STRING(etype) ((etype == ASN1_ETYPE_GENERALSTRING || \
+	etype == ASN1_ETYPE_NUMERIC_STRING || etype == ASN1_ETYPE_IA5_STRING || \
+	etype == ASN1_ETYPE_TELETEX_STRING || etype == ASN1_ETYPE_PRINTABLE_STRING || \
+	etype == ASN1_ETYPE_UNIVERSAL_STRING || etype == ASN1_ETYPE_BMP_STRING || \
+	etype == ASN1_ETYPE_UTF8_STRING || etype == ASN1_ETYPE_VISIBLE_STRING || \
+	etype == ASN1_ETYPE_OCTET_STRING)?1:0)
+
+extern unsigned int _asn1_tags_size;
+extern const tag_and_class_st _asn1_tags[];
+
+#define _asn1_strlen(s) strlen((const char *) s)
+#define _asn1_strtol(n,e,b) strtol((const char *) n, e, b)
+#define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
+#define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
+#define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
+#define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
+
+#if SIZEOF_UNSIGNED_LONG_INT == 8
+# define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
+#else
+# define _asn1_strtou64(n,e,b) strtoull((const char *) n, e, b)
+#endif
+
+#define MAX_LOG_SIZE 1024	/* maximum number of characters of a log message */
+
+/* Define used for visiting trees. */
+#define UP     1
+#define RIGHT  2
+#define DOWN   3
+
+/***********************************************************************/
+/* List of constants to better specify the type of typedef asn1_node_st.   */
+/***********************************************************************/
+/*  Used with TYPE_TAG  */
+#define CONST_UNIVERSAL   (1U<<8)
+#define CONST_PRIVATE     (1U<<9)
+#define CONST_APPLICATION (1U<<10)
+#define CONST_EXPLICIT    (1U<<11)
+#define CONST_IMPLICIT    (1U<<12)
+
+#define CONST_TAG         (1U<<13)	/*  Used in ASN.1 assignement  */
+#define CONST_OPTION      (1U<<14)
+#define CONST_DEFAULT     (1U<<15)
+#define CONST_TRUE        (1U<<16)
+#define CONST_FALSE       (1U<<17)
+
+#define CONST_LIST        (1U<<18)	/*  Used with TYPE_INTEGER and TYPE_BIT_STRING  */
+#define CONST_MIN_MAX     (1U<<19)
+
+#define CONST_1_PARAM     (1U<<20)
+
+#define CONST_SIZE        (1U<<21)
+
+#define CONST_DEFINED_BY  (1U<<22)
+
+/* Those two are deprecated and used for backwards compatibility */
+#define CONST_GENERALIZED (1U<<23)
+#define CONST_UTC         (1U<<24)
+
+/* #define CONST_IMPORTS     (1U<<25) */
+
+#define CONST_NOT_USED    (1U<<26)
+#define CONST_SET         (1U<<27)
+#define CONST_ASSIGN      (1U<<28)
+
+#define CONST_DOWN        (1U<<29)
+#define CONST_RIGHT       (1U<<30)
+
+
+#define ASN1_ETYPE_TIME 17
+/****************************************/
+/* Returns the first 8 bits.            */
+/* Used with the field type of asn1_node_st */
+/****************************************/
+inline static unsigned int
+type_field (unsigned int ntype)
+{
+  return (ntype & 0xff);
+}
+
+/* To convert old types from a static structure */
+inline static unsigned int
+convert_old_type (unsigned int ntype)
+{
+  unsigned int type = ntype & 0xff;
+  if (type == ASN1_ETYPE_TIME)
+    {
+      if (ntype & CONST_UTC)
+	type = ASN1_ETYPE_UTC_TIME;
+      else
+	type = ASN1_ETYPE_GENERALIZED_TIME;
+
+      ntype &= ~(CONST_UTC | CONST_GENERALIZED);
+      ntype &= 0xffffff00;
+      ntype |= type;
+
+      return ntype;
+    }
+  else
+    return ntype;
+}
+
+static inline
+void *_asn1_realloc(void *ptr, size_t size)
+{
+  void *ret;
+
+  if (size == 0)
+    return ptr;
+
+  ret = realloc(ptr, size);
+  if (ret == NULL)
+    {
+      free(ptr);
+    }
+  return ret;
+}
+
+#endif /* INT_H */
diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c
new file mode 100644
index 000000000000..d5dbbf8765da
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/parser_aux.c
@@ -0,0 +1,1173 @@
+/*
+ * Copyright (C) 2000-2016 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#include <limits.h> // WORD_BIT
+
+#include "int.h"
+#include "parser_aux.h"
+#include "gstr.h"
+#include "structure.h"
+#include "element.h"
+#include "c-ctype.h"
+
+char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not found */
+
+/* Return a hash of the N bytes of X using the method described by
+   Bruno Haible in https://www.haible.de/bruno/hashfunc.html.
+   Note that while many hash functions reduce their result via modulo
+   to a 0..table_size-1 range, this function does not do that.
+
+   This implementation has been changed from size_t -> unsigned int. */
+
+#ifdef __clang__
+__attribute__((no_sanitize("integer")))
+#endif
+_GL_ATTRIBUTE_PURE
+static unsigned int
+_asn1_hash_name (const char *x)
+{
+  const unsigned char *s = (unsigned char *) x;
+  unsigned h = 0;
+
+  while (*s)
+    h = (*s++) + ((h << 9) | (h >> (WORD_BIT - 9)));
+
+  return h;
+}
+
+/******************************************************/
+/* Function : _asn1_add_static_node                   */
+/* Description: creates a new NODE_ASN element and    */
+/* puts it in the list pointed by e_list.       */
+/* Parameters:                                        */
+/*   e_list: of type list_type; must be NULL initially */
+/*   type: type of the new element (see ASN1_ETYPE_   */
+/*         and CONST_ constants).                     */
+/* Return: pointer to the new element.                */
+/******************************************************/
+asn1_node
+_asn1_add_static_node (list_type **e_list, unsigned int type)
+{
+  list_type *p;
+  asn1_node punt;
+
+  punt = calloc (1, sizeof (struct asn1_node_st));
+  if (punt == NULL)
+    return NULL;
+
+  p = malloc (sizeof (list_type));
+  if (p == NULL)
+    {
+      free (punt);
+      return NULL;
+    }
+
+  p->node = punt;
+  p->next = *e_list;
+  *e_list = p;
+
+  punt->type = type;
+
+  return punt;
+}
+
+static
+int _asn1_add_static_node2 (list_type **e_list, asn1_node node)
+{
+  list_type *p;
+
+  p = malloc (sizeof (list_type));
+  if (p == NULL)
+    {
+      return -1;
+    }
+
+  p->node = node;
+  p->next = *e_list;
+  *e_list = p;
+
+  return 0;
+}
+
+/**
+ * asn1_find_node:
+ * @pointer: NODE_ASN element pointer.
+ * @name: null terminated string with the element's name to find.
+ *
+ * Searches for an element called @name starting from @pointer.  The
+ * name is composed by different identifiers separated by dots.  When
+ * *@pointer has a name, the first identifier must be the name of
+ * *@pointer, otherwise it must be the name of one child of *@pointer.
+ *
+ * Returns: the search result, or %NULL if not found.
+ **/
+asn1_node
+asn1_find_node (asn1_node_const pointer, const char *name)
+{
+  asn1_node_const p;
+  char *n_end, n[ASN1_MAX_NAME_SIZE + 1];
+  const char *n_start;
+  unsigned int nsize;
+  unsigned int nhash;
+
+  if (pointer == NULL)
+    return NULL;
+
+  if (name == NULL)
+    return NULL;
+
+  p = pointer;
+  n_start = name;
+
+  if (name[0] == '?' && name[1] == 'C' && p->name[0] == '?')
+    { /* ?CURRENT */
+      n_start = strchr(n_start, '.');
+      if (n_start)
+        n_start++;
+    }
+  else if (p->name[0] != 0)
+    {				/* has *pointer got a name ? */
+      n_end = strchr (n_start, '.');	/* search the first dot */
+      if (n_end)
+	{
+	  nsize = n_end - n_start;
+	  if (nsize >= sizeof(n))
+		return NULL;
+
+	  memcpy (n, n_start, nsize);
+	  n[nsize] = 0;
+	  n_start = n_end;
+	  n_start++;
+
+	  nhash = _asn1_hash_name (n);
+	}
+      else
+	{
+	  _asn1_str_cpy (n, sizeof (n), n_start);
+	  nhash = _asn1_hash_name (n);
+
+	  n_start = NULL;
+	}
+
+      while (p)
+	{
+	  if (nhash == p->name_hash && (!strcmp (p->name, n)))
+	    break;
+	  else
+	    p = p->right;
+	}			/* while */
+
+      if (p == NULL)
+	return NULL;
+    }
+  else
+    {				/* *pointer doesn't have a name */
+      if (n_start[0] == 0)
+	return (asn1_node) p;
+    }
+
+  while (n_start)
+    {				/* Has the end of NAME been reached? */
+      n_end = strchr (n_start, '.');	/* search the next dot */
+      if (n_end)
+	{
+	  nsize = n_end - n_start;
+	  if (nsize >= sizeof(n))
+		return NULL;
+
+	  memcpy (n, n_start, nsize);
+	  n[nsize] = 0;
+	  n_start = n_end;
+	  n_start++;
+
+	  nhash = _asn1_hash_name (n);
+	}
+      else
+	{
+	  _asn1_str_cpy (n, sizeof (n), n_start);
+	  nhash = _asn1_hash_name (n);
+	  n_start = NULL;
+	}
+
+      if (p->down == NULL)
+	return NULL;
+
+      p = p->down;
+      if (p == NULL)
+        return NULL;
+
+      /* The identifier "?LAST" indicates the last element
+         in the right chain. */
+      if (n[0] == '?' && n[1] == 'L') /* ?LAST */
+	{
+	  while (p->right)
+	    p = p->right;
+	}
+      else
+	{			/* no "?LAST" */
+	  while (p)
+	    {
+	      if (p->name_hash == nhash && !strcmp (p->name, n))
+		break;
+	      else
+		p = p->right;
+	    }
+	}
+      if (p == NULL)
+        return NULL;
+    }				/* while */
+
+  return (asn1_node) p;
+}
+
+
+/******************************************************************/
+/* Function : _asn1_set_value                                     */
+/* Description: sets the field VALUE in a NODE_ASN element. The   */
+/*              previous value (if exist) will be lost            */
+/* Parameters:                                                    */
+/*   node: element pointer.                                       */
+/*   value: pointer to the value that you want to set.            */
+/*   len: character number of value.                              */
+/* Return: pointer to the NODE_ASN element.                       */
+/******************************************************************/
+asn1_node
+_asn1_set_value (asn1_node node, const void *value, unsigned int len)
+{
+  if (node == NULL)
+    return node;
+  if (node->value)
+    {
+      if (node->value != node->small_value)
+	free (node->value);
+      node->value = NULL;
+      node->value_len = 0;
+    }
+
+  if (!len)
+    return node;
+
+  if (len < sizeof (node->small_value))
+    {
+      node->value = node->small_value;
+    }
+  else
+    {
+      node->value = malloc (len);
+      if (node->value == NULL)
+	return NULL;
+    }
+  node->value_len = len;
+
+  memcpy (node->value, value, len);
+  return node;
+}
+
+/******************************************************************/
+/* Function : _asn1_set_value_lv                                  */
+/* Description: sets the field VALUE in a NODE_ASN element. The   */
+/*              previous value (if exist) will be lost. The value */
+/*		given is stored as an length-value format (LV     */
+/* Parameters:                                                    */
+/*   node: element pointer.                                       */
+/*   value: pointer to the value that you want to set.            */
+/*   len: character number of value.                              */
+/* Return: pointer to the NODE_ASN element.                       */
+/******************************************************************/
+asn1_node
+_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len)
+{
+  int len2;
+  void *temp;
+
+  if (node == NULL)
+    return node;
+
+  asn1_length_der (len, NULL, &len2);
+  temp = malloc (len + len2);
+  if (temp == NULL)
+    return NULL;
+
+  asn1_octet_der (value, len, temp, &len2);
+  return _asn1_set_value_m (node, temp, len2);
+}
+
+/* the same as _asn1_set_value except that it sets an already malloc'ed
+ * value.
+ */
+asn1_node
+_asn1_set_value_m (asn1_node node, void *value, unsigned int len)
+{
+  if (node == NULL)
+    return node;
+
+  if (node->value)
+    {
+      if (node->value != node->small_value)
+	free (node->value);
+      node->value = NULL;
+      node->value_len = 0;
+    }
+
+  if (!len)
+    return node;
+
+  node->value = value;
+  node->value_len = len;
+
+  return node;
+}
+
+/******************************************************************/
+/* Function : _asn1_append_value                                  */
+/* Description: appends to the field VALUE in a NODE_ASN element. */
+/*							          */
+/* Parameters:                                                    */
+/*   node: element pointer.                                       */
+/*   value: pointer to the value that you want to be appended.    */
+/*   len: character number of value.                              */
+/* Return: pointer to the NODE_ASN element.                       */
+/******************************************************************/
+asn1_node
+_asn1_append_value (asn1_node node, const void *value, unsigned int len)
+{
+  if (node == NULL)
+    return node;
+
+  if (node->value == NULL)
+    return _asn1_set_value (node, value, len);
+
+  if (len == 0)
+    return node;
+
+  if (node->value == node->small_value)
+    {
+      /* value is in node */
+      int prev_len = node->value_len;
+      node->value_len += len;
+      node->value = malloc (node->value_len);
+      if (node->value == NULL)
+	{
+	  node->value_len = 0;
+	  return NULL;
+	}
+
+      if (prev_len > 0)
+        memcpy (node->value, node->small_value, prev_len);
+
+      memcpy (&node->value[prev_len], value, len);
+
+      return node;
+    }
+  else /* if (node->value != NULL && node->value != node->small_value) */
+    {
+      /* value is allocated */
+      int prev_len = node->value_len;
+      node->value_len += len;
+
+      node->value = _asn1_realloc (node->value, node->value_len);
+      if (node->value == NULL)
+	{
+	  node->value_len = 0;
+	  return NULL;
+	}
+
+      memcpy (&node->value[prev_len], value, len);
+
+      return node;
+    }
+}
+
+/******************************************************************/
+/* Function : _asn1_set_name                                      */
+/* Description: sets the field NAME in a NODE_ASN element. The    */
+/*              previous value (if exist) will be lost            */
+/* Parameters:                                                    */
+/*   node: element pointer.                                       */
+/*   name: a null terminated string with the name that you want   */
+/*         to set.                                                */
+/* Return: pointer to the NODE_ASN element.                       */
+/******************************************************************/
+asn1_node
+_asn1_set_name (asn1_node node, const char *name)
+{
+  if (node == NULL)
+    return node;
+
+  _asn1_str_cpy (node->name, sizeof (node->name), name ? name : "");
+  node->name_hash = _asn1_hash_name (node->name);
+
+  return node;
+}
+
+/******************************************************************/
+/* Function : _asn1_cpy_name                                      */
+/* Description: copies the field NAME in a NODE_ASN element.      */
+/* Parameters:                                                    */
+/*   dst: a dest element pointer.                                 */
+/*   src: a source element pointer.                               */
+/* Return: pointer to the NODE_ASN element.                       */
+/******************************************************************/
+asn1_node
+_asn1_cpy_name (asn1_node dst, asn1_node_const src)
+{
+  if (dst == NULL)
+    return dst;
+
+  if (src == NULL)
+    {
+      dst->name[0] = 0;
+      dst->name_hash = _asn1_hash_name (dst->name);
+      return dst;
+    }
+
+  _asn1_str_cpy (dst->name, sizeof (dst->name), src->name);
+  dst->name_hash = src->name_hash;
+
+  return dst;
+}
+
+/******************************************************************/
+/* Function : _asn1_set_right                                     */
+/* Description: sets the field RIGHT in a NODE_ASN element.       */
+/* Parameters:                                                    */
+/*   node: element pointer.                                       */
+/*   right: pointer to a NODE_ASN element that you want be pointed*/
+/*          by NODE.                                              */
+/* Return: pointer to *NODE.                                      */
+/******************************************************************/
+asn1_node
+_asn1_set_right (asn1_node node, asn1_node right)
+{
+  if (node == NULL)
+    return node;
+  node->right = right;
+  if (right)
+    right->left = node;
+  return node;
+}
+
+
+/******************************************************************/
+/* Function : _asn1_get_last_right                                */
+/* Description: return the last element along the right chain.    */
+/* Parameters:                                                    */
+/*   node: starting element pointer.                              */
+/* Return: pointer to the last element along the right chain.     */
+/******************************************************************/
+asn1_node
+_asn1_get_last_right (asn1_node_const node)
+{
+  asn1_node_const p;
+
+  if (node == NULL)
+    return NULL;
+  p = node;
+  while (p->right)
+    p = p->right;
+  return (asn1_node) p;
+}
+
+/******************************************************************/
+/* Function : _asn1_remove_node                                   */
+/* Description: gets free the memory allocated for an NODE_ASN    */
+/*              element (not the elements pointed by it).         */
+/* Parameters:                                                    */
+/*   node: NODE_ASN element pointer.                              */
+/*   flags: ASN1_DELETE_FLAG_*                                    */
+/******************************************************************/
+void
+_asn1_remove_node (asn1_node node, unsigned int flags)
+{
+  if (node == NULL)
+    return;
+
+  if (node->value != NULL)
+    {
+      if (flags & ASN1_DELETE_FLAG_ZEROIZE)
+        {
+          safe_memset(node->value, 0, node->value_len);
+        }
+
+      if (node->value != node->small_value)
+        free (node->value);
+    }
+  free (node);
+}
+
+/******************************************************************/
+/* Function : _asn1_find_up                                       */
+/* Description: return the father of the NODE_ASN element.        */
+/* Parameters:                                                    */
+/*   node: NODE_ASN element pointer.                              */
+/* Return: Null if not found.                                     */
+/******************************************************************/
+asn1_node
+_asn1_find_up (asn1_node_const node)
+{
+  asn1_node_const p;
+
+  if (node == NULL)
+    return NULL;
+
+  p = node;
+
+  while ((p->left != NULL) && (p->left->right == p))
+    p = p->left;
+
+  return p->left;
+}
+
+static
+unsigned _asn1_is_up (asn1_node_const up_cand, asn1_node_const down)
+{
+  asn1_node_const d, u;
+
+  if (up_cand == NULL || down == NULL)
+    return 0;
+
+  d = down;
+
+  while ((u = _asn1_find_up(d)) != NULL && u != d)
+    {
+      if (u == up_cand)
+        return 1;
+      d = u;
+    }
+
+  return 0;
+}
+
+/******************************************************************/
+/* Function : _asn1_delete_node_from_list                         */
+/* Description: deletes the list element given                    */
+/******************************************************************/
+void
+_asn1_delete_node_from_list (list_type *list, asn1_node node)
+{
+  list_type *p = list;
+
+  while (p)
+    {
+      if (p->node == node)
+        p->node = NULL;
+      p = p->next;
+    }
+}
+
+/******************************************************************/
+/* Function : _asn1_delete_list                                   */
+/* Description: deletes the list elements (not the elements       */
+/*  pointed by them).                                             */
+/******************************************************************/
+void
+_asn1_delete_list (list_type *e_list)
+{
+  list_type *p;
+
+  while (e_list)
+    {
+      p = e_list;
+      e_list = e_list->next;
+      free (p);
+    }
+}
+
+/******************************************************************/
+/* Function : _asn1_delete_list_and nodes                         */
+/* Description: deletes the list elements and the elements        */
+/*  pointed by them.                                              */
+/******************************************************************/
+void
+_asn1_delete_list_and_nodes (list_type *e_list)
+{
+  list_type *p;
+
+  while (e_list)
+    {
+      p = e_list;
+      e_list = e_list->next;
+      _asn1_remove_node (p->node, 0);
+      free (p);
+    }
+}
+
+
+char *
+_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
+{
+  uint64_t d, r;
+  char temp[LTOSTR_MAX_SIZE];
+  int count, k, start;
+  uint64_t val;
+
+  if (v < 0)
+    {
+      str[0] = '-';
+      start = 1;
+      val = -((uint64_t)v);
+    }
+  else
+    {
+      val = v;
+      start = 0;
+    }
+
+  count = 0;
+  do
+    {
+      d = val / 10;
+      r = val - d * 10;
+      temp[start + count] = '0' + (char) r;
+      count++;
+      val = d;
+    }
+  while (val && ((start+count) < LTOSTR_MAX_SIZE-1));
+
+  for (k = 0; k < count; k++)
+    str[k + start] = temp[start + count - k - 1];
+  str[count + start] = 0;
+  return str;
+}
+
+
+/******************************************************************/
+/* Function : _asn1_change_integer_value                          */
+/* Description: converts into DER coding the value assign to an   */
+/*   INTEGER constant.                                            */
+/* Parameters:                                                    */
+/*   node: root of an ASN1element.                                */
+/* Return:                                                        */
+/*   ASN1_ELEMENT_NOT_FOUND if NODE is NULL,                       */
+/*   otherwise ASN1_SUCCESS                                             */
+/******************************************************************/
+int
+_asn1_change_integer_value (asn1_node node)
+{
+  asn1_node p;
+  unsigned char val[SIZEOF_UNSIGNED_LONG_INT];
+  unsigned char val2[SIZEOF_UNSIGNED_LONG_INT + 1];
+  int len;
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = node;
+  while (p)
+    {
+      if ((type_field (p->type) == ASN1_ETYPE_INTEGER)
+	  && (p->type & CONST_ASSIGN))
+	{
+	  if (p->value)
+	    {
+	      _asn1_convert_integer (p->value, val, sizeof (val), &len);
+	      asn1_octet_der (val, len, val2, &len);
+	      _asn1_set_value (p, val2, len);
+	    }
+	}
+
+      if (p->down)
+	{
+	  p = p->down;
+	}
+      else
+	{
+	  if (p == node)
+	    p = NULL;
+	  else if (p->right)
+	    p = p->right;
+	  else
+	    {
+	      while (1)
+		{
+		  p = _asn1_find_up (p);
+		  if (p == node)
+		    {
+		      p = NULL;
+		      break;
+		    }
+		  if (p && p->right)
+		    {
+		      p = p->right;
+		      break;
+		    }
+		}
+	    }
+	}
+    }
+
+  return ASN1_SUCCESS;
+}
+
+#define MAX_CONSTANTS 1024
+/******************************************************************/
+/* Function : _asn1_expand_object_id                              */
+/* Description: expand the IDs of an OBJECT IDENTIFIER constant.  */
+/* Parameters:                                                    */
+/*   list: root of an object list                                 */
+/*   node: root of an ASN1 element.                               */
+/* Return:                                                        */
+/*   ASN1_ELEMENT_NOT_FOUND if NODE is NULL,                      */
+/*   otherwise ASN1_SUCCESS                                       */
+/******************************************************************/
+int
+_asn1_expand_object_id (list_type **list, asn1_node node)
+{
+  asn1_node p, p2, p3, p4, p5;
+  char name_root[ASN1_MAX_NAME_SIZE], name2[2 * ASN1_MAX_NAME_SIZE + 1];
+  int move, tlen, tries;
+  unsigned max_constants;
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  _asn1_str_cpy (name_root, sizeof (name_root), node->name);
+
+  p = node;
+  move = DOWN;
+  tries = 0;
+
+  while (!((p == node) && (move == UP)))
+    {
+      if (move != UP)
+	{
+	  if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID)
+	      && (p->type & CONST_ASSIGN))
+	    {
+	      p2 = p->down;
+	      if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT))
+		{
+		  if (p2->value && !c_isdigit (p2->value[0]))
+		    {
+		      _asn1_str_cpy (name2, sizeof (name2), name_root);
+		      _asn1_str_cat (name2, sizeof (name2), ".");
+		      _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
+		      p3 = asn1_find_node (node, name2);
+		      if (!p3 || _asn1_is_up(p2, p3) ||
+			  (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) ||
+			  !(p3->type & CONST_ASSIGN))
+			return ASN1_ELEMENT_NOT_FOUND;
+
+		      _asn1_set_down (p, p2->right);
+		      if (p2->down)
+			_asn1_delete_structure (*list, &p2->down, 0);
+		      _asn1_delete_node_from_list(*list, p2);
+		      _asn1_remove_node (p2, 0);
+		      p2 = p;
+		      p4 = p3->down;
+		      max_constants = 0;
+		      while (p4)
+			{
+			  if (type_field (p4->type) == ASN1_ETYPE_CONSTANT)
+			    {
+			      max_constants++;
+			      if (max_constants == MAX_CONSTANTS)
+                                return ASN1_RECURSION;
+
+			      p5 =
+				_asn1_add_single_node (ASN1_ETYPE_CONSTANT);
+			      _asn1_set_name (p5, p4->name);
+			      if (p4->value)
+			        {
+			          tlen = _asn1_strlen (p4->value);
+			          if (tlen > 0)
+			            _asn1_set_value (p5, p4->value, tlen + 1);
+			        }
+			      _asn1_add_static_node2(list, p5);
+
+			      if (p2 == p)
+				{
+				  _asn1_set_right (p5, p->down);
+				  _asn1_set_down (p, p5);
+				}
+			      else
+				{
+				  _asn1_set_right (p5, p2->right);
+				  _asn1_set_right (p2, p5);
+				}
+			      p2 = p5;
+			    }
+			  p4 = p4->right;
+			}
+		      move = DOWN;
+
+		      tries++;
+                      if (tries >= EXPAND_OBJECT_ID_MAX_RECURSION)
+                        return ASN1_RECURSION;
+
+		      continue;
+		    }
+		}
+	    }
+	  move = DOWN;
+	}
+      else
+	move = RIGHT;
+
+      tries = 0;
+      if (move == DOWN)
+	{
+	  if (p->down)
+	    p = p->down;
+	  else
+	    move = RIGHT;
+	}
+
+      if (p == node)
+	{
+	  move = UP;
+	  continue;
+	}
+
+      if (move == RIGHT)
+	{
+	  if (p && p->right)
+	    p = p->right;
+	  else
+	    move = UP;
+	}
+      if (move == UP)
+	p = _asn1_find_up (p);
+    }
+
+  /*******************************/
+  /*       expand DEFAULT        */
+  /*******************************/
+  p = node;
+  move = DOWN;
+
+  while (!((p == node) && (move == UP)))
+    {
+      if (move != UP)
+	{
+	  if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
+	      (p->type & CONST_DEFAULT))
+	    {
+	      p2 = p->down;
+	      if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT))
+		{
+		  _asn1_str_cpy (name2, sizeof (name2), name_root);
+		  _asn1_str_cat (name2, sizeof (name2), ".");
+		  if (p2->value)
+		    _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
+		  p3 = asn1_find_node (node, name2);
+		  if (!p3 || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID)
+		      || !(p3->type & CONST_ASSIGN))
+		    return ASN1_ELEMENT_NOT_FOUND;
+		  p4 = p3->down;
+		  name2[0] = 0;
+		  while (p4)
+		    {
+		      if (type_field (p4->type) == ASN1_ETYPE_CONSTANT)
+			{
+			  if (p4->value == NULL)
+			    return ASN1_VALUE_NOT_FOUND;
+
+			  if (name2[0])
+			    _asn1_str_cat (name2, sizeof (name2), ".");
+			  _asn1_str_cat (name2, sizeof (name2),
+					 (char *) p4->value);
+			}
+		      p4 = p4->right;
+		    }
+		  tlen = strlen (name2);
+		  if (tlen > 0)
+		    _asn1_set_value (p2, name2, tlen + 1);
+		}
+	    }
+	  move = DOWN;
+	}
+      else
+	move = RIGHT;
+
+      if (move == DOWN)
+	{
+	  if (p->down)
+	    p = p->down;
+	  else
+	    move = RIGHT;
+	}
+
+      if (p == node)
+	{
+	  move = UP;
+	  continue;
+	}
+
+      if (move == RIGHT)
+	{
+	  if (p && p->right)
+	    p = p->right;
+	  else
+	    move = UP;
+	}
+      if (move == UP)
+	p = _asn1_find_up (p);
+    }
+
+  return ASN1_SUCCESS;
+}
+
+
+/******************************************************************/
+/* Function : _asn1_type_set_config                               */
+/* Description: sets the CONST_SET and CONST_NOT_USED properties  */
+/*   in the fields of the SET elements.                           */
+/* Parameters:                                                    */
+/*   node: root of an ASN1 element.                               */
+/* Return:                                                        */
+/*   ASN1_ELEMENT_NOT_FOUND if NODE is NULL,                       */
+/*   otherwise ASN1_SUCCESS                                             */
+/******************************************************************/
+int
+_asn1_type_set_config (asn1_node node)
+{
+  asn1_node p, p2;
+  int move;
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = node;
+  move = DOWN;
+
+  while (!((p == node) && (move == UP)))
+    {
+      if (move != UP)
+	{
+	  if (type_field (p->type) == ASN1_ETYPE_SET)
+	    {
+	      p2 = p->down;
+	      while (p2)
+		{
+		  if (type_field (p2->type) != ASN1_ETYPE_TAG)
+		    p2->type |= CONST_SET | CONST_NOT_USED;
+		  p2 = p2->right;
+		}
+	    }
+	  move = DOWN;
+	}
+      else
+	move = RIGHT;
+
+      if (move == DOWN)
+	{
+	  if (p->down)
+	    p = p->down;
+	  else
+	    move = RIGHT;
+	}
+
+      if (p == node)
+	{
+	  move = UP;
+	  continue;
+	}
+
+      if (move == RIGHT)
+	{
+	  if (p && p->right)
+	    p = p->right;
+	  else
+	    move = UP;
+	}
+      if (move == UP)
+	p = _asn1_find_up (p);
+    }
+
+  return ASN1_SUCCESS;
+}
+
+
+/******************************************************************/
+/* Function : _asn1_check_identifier                              */
+/* Description: checks the definitions of all the identifiers     */
+/*   and the first element of an OBJECT_ID (e.g. {pkix 0 4}).     */
+/*   The _asn1_identifierMissing global variable is filled if     */
+/*   necessary.                                                   */
+/* Parameters:                                                    */
+/*   node: root of an ASN1 element.                               */
+/* Return:                                                        */
+/*   ASN1_ELEMENT_NOT_FOUND      if NODE is NULL,                 */
+/*   ASN1_IDENTIFIER_NOT_FOUND   if an identifier is not defined, */
+/*   otherwise ASN1_SUCCESS                                       */
+/******************************************************************/
+int
+_asn1_check_identifier (asn1_node_const node)
+{
+  asn1_node_const p, p2;
+  char name2[ASN1_MAX_NAME_SIZE * 2 + 2];
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = node;
+  while (p)
+    {
+      if (p->value && type_field (p->type) == ASN1_ETYPE_IDENTIFIER)
+	{
+	  _asn1_str_cpy (name2, sizeof (name2), node->name);
+	  _asn1_str_cat (name2, sizeof (name2), ".");
+	  _asn1_str_cat (name2, sizeof (name2), (char *) p->value);
+	  p2 = asn1_find_node (node, name2);
+	  if (p2 == NULL)
+	    {
+	      if (p->value)
+		_asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p->value);
+	      else
+		_asn1_strcpy (_asn1_identifierMissing, "(null)");
+	      return ASN1_IDENTIFIER_NOT_FOUND;
+	    }
+	}
+      else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
+	       (p->type & CONST_DEFAULT))
+	{
+	  p2 = p->down;
+	  if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT))
+	    {
+	      _asn1_str_cpy (name2, sizeof (name2), node->name);
+	      if (p2->value)
+	        {
+	          _asn1_str_cat (name2, sizeof (name2), ".");
+	          _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
+	          _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p2->value);
+	        }
+	      else
+		_asn1_strcpy (_asn1_identifierMissing, "(null)");
+
+	      p2 = asn1_find_node (node, name2);
+	      if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) ||
+		  !(p2->type & CONST_ASSIGN))
+		return ASN1_IDENTIFIER_NOT_FOUND;
+	      else
+		_asn1_identifierMissing[0] = 0;
+	    }
+	}
+      else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
+	       (p->type & CONST_ASSIGN))
+	{
+	  p2 = p->down;
+	  if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT))
+	    {
+	      if (p2->value && !c_isdigit (p2->value[0]))
+		{
+		  _asn1_str_cpy (name2, sizeof (name2), node->name);
+		  _asn1_str_cat (name2, sizeof (name2), ".");
+		  _asn1_str_cat (name2, sizeof (name2), (char *) p2->value);
+		  _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p2->value);
+
+		  p2 = asn1_find_node (node, name2);
+		  if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID)
+		      || !(p2->type & CONST_ASSIGN))
+		    return ASN1_IDENTIFIER_NOT_FOUND;
+		  else
+		    _asn1_identifierMissing[0] = 0;
+		}
+	    }
+	}
+
+      if (p->down)
+	{
+	  p = p->down;
+	}
+      else if (p->right)
+	p = p->right;
+      else
+	{
+	  while (p)
+	    {
+	      p = _asn1_find_up (p);
+	      if (p == node)
+		{
+		  p = NULL;
+		  break;
+		}
+	      if (p && p->right)
+		{
+		  p = p->right;
+		  break;
+		}
+	    }
+	}
+    }
+
+  return ASN1_SUCCESS;
+}
+
+
+/******************************************************************/
+/* Function : _asn1_set_default_tag                               */
+/* Description: sets the default IMPLICIT or EXPLICIT property in */
+/*   the tagged elements that don't have this declaration.        */
+/* Parameters:                                                    */
+/*   node: pointer to a DEFINITIONS element.                      */
+/* Return:                                                        */
+/*   ASN1_ELEMENT_NOT_FOUND if NODE is NULL or not a pointer to   */
+/*     a DEFINITIONS element,                                     */
+/*   otherwise ASN1_SUCCESS                                       */
+/******************************************************************/
+int
+_asn1_set_default_tag (asn1_node node)
+{
+  asn1_node p;
+
+  if ((node == NULL) || (type_field (node->type) != ASN1_ETYPE_DEFINITIONS))
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = node;
+  while (p)
+    {
+      if ((type_field (p->type) == ASN1_ETYPE_TAG) &&
+	  !(p->type & CONST_EXPLICIT) && !(p->type & CONST_IMPLICIT))
+	{
+	  if (node->type & CONST_EXPLICIT)
+	    p->type |= CONST_EXPLICIT;
+	  else
+	    p->type |= CONST_IMPLICIT;
+	}
+
+      if (p->down)
+	{
+	  p = p->down;
+	}
+      else if (p->right)
+	p = p->right;
+      else
+	{
+	  while (1)
+	    {
+	      p = _asn1_find_up (p);
+	      if (p == node)
+		{
+		  p = NULL;
+		  break;
+		}
+	      if (p && p->right)
+		{
+		  p = p->right;
+		  break;
+		}
+	    }
+	}
+    }
+
+  return ASN1_SUCCESS;
+}
diff --git a/grub-core/lib/libtasn1/lib/parser_aux.h b/grub-core/lib/libtasn1/lib/parser_aux.h
new file mode 100644
index 000000000000..598e684b3559
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/parser_aux.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2000-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+#ifndef _PARSER_AUX_H
+#define _PARSER_AUX_H
+
+/***********************************************/
+/* Type: list_type                             */
+/* Description: type used in the list during   */
+/* the structure creation.                     */
+/***********************************************/
+typedef struct list_struct
+{
+  asn1_node node;
+  struct list_struct *next;
+} list_type;
+
+/***************************************/
+/*  Functions used by ASN.1 parser     */
+/***************************************/
+asn1_node _asn1_add_static_node (list_type **e_list, unsigned int type);
+
+void _asn1_delete_list (list_type *e_list);
+
+void _asn1_delete_list_and_nodes (list_type *e_list);
+
+void _asn1_delete_node_from_list (list_type *list, asn1_node node);
+
+asn1_node
+_asn1_set_value (asn1_node node, const void *value, unsigned int len);
+
+asn1_node _asn1_set_value_m (asn1_node node, void *value, unsigned int len);
+
+asn1_node
+_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len);
+
+asn1_node
+_asn1_append_value (asn1_node node, const void *value, unsigned int len);
+
+asn1_node _asn1_set_name (asn1_node node, const char *name);
+
+asn1_node _asn1_cpy_name (asn1_node dst, asn1_node_const src);
+
+asn1_node _asn1_set_right (asn1_node node, asn1_node right);
+
+asn1_node _asn1_get_last_right (asn1_node_const node);
+
+void _asn1_remove_node (asn1_node node, unsigned int flags);
+
+/* Max 64-bit integer length is 20 chars + 1 for sign + 1 for null termination */
+#define LTOSTR_MAX_SIZE 22
+char *_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]);
+
+asn1_node _asn1_find_up (asn1_node_const node);
+
+int _asn1_change_integer_value (asn1_node node);
+
+#define EXPAND_OBJECT_ID_MAX_RECURSION 16
+int _asn1_expand_object_id (list_type **list, asn1_node node);
+
+int _asn1_type_set_config (asn1_node node);
+
+int _asn1_check_identifier (asn1_node_const node);
+
+int _asn1_set_default_tag (asn1_node node);
+
+/******************************************************************/
+/* Function : _asn1_get_right                                     */
+/* Description: returns the element pointed by the RIGHT field of */
+/*              a NODE_ASN element.                               */
+/* Parameters:                                                    */
+/*   node: NODE_ASN element pointer.                              */
+/* Return: field RIGHT of NODE.                                   */
+/******************************************************************/
+inline static asn1_node
+_asn1_get_right (asn1_node_const node)
+{
+  if (node == NULL)
+    return NULL;
+  return node->right;
+}
+
+/******************************************************************/
+/* Function : _asn1_set_down                                      */
+/* Description: sets the field DOWN in a NODE_ASN element.        */
+/* Parameters:                                                    */
+/*   node: element pointer.                                       */
+/*   down: pointer to a NODE_ASN element that you want be pointed */
+/*          by NODE.                                              */
+/* Return: pointer to *NODE.                                      */
+/******************************************************************/
+inline static asn1_node
+_asn1_set_down (asn1_node node, asn1_node down)
+{
+  if (node == NULL)
+    return node;
+  node->down = down;
+  if (down)
+    down->left = node;
+  return node;
+}
+
+/******************************************************************/
+/* Function : _asn1_get_down                                      */
+/* Description: returns the element pointed by the DOWN field of  */
+/*              a NODE_ASN element.                               */
+/* Parameters:                                                    */
+/*   node: NODE_ASN element pointer.                              */
+/* Return: field DOWN of NODE.                                    */
+/******************************************************************/
+inline static asn1_node
+_asn1_get_down (asn1_node_const node)
+{
+  if (node == NULL)
+    return NULL;
+  return node->down;
+}
+
+/******************************************************************/
+/* Function : _asn1_get_name                                      */
+/* Description: returns the name of a NODE_ASN element.           */
+/* Parameters:                                                    */
+/*   node: NODE_ASN element pointer.                              */
+/* Return: a null terminated string.                              */
+/******************************************************************/
+inline static char *
+_asn1_get_name (asn1_node_const node)
+{
+  if (node == NULL)
+    return NULL;
+  return (char *) node->name;
+}
+
+/******************************************************************/
+/* Function : _asn1_mod_type                                      */
+/* Description: change the field TYPE of an NODE_ASN element.     */
+/*              The new value is the old one | (bitwise or) the   */
+/*              paramener VALUE.                                  */
+/* Parameters:                                                    */
+/*   node: NODE_ASN element pointer.                              */
+/*   value: the integer value that must be or-ed with the current */
+/*          value of field TYPE.                                  */
+/* Return: NODE pointer.                                          */
+/******************************************************************/
+inline static asn1_node
+_asn1_mod_type (asn1_node node, unsigned int value)
+{
+  if (node == NULL)
+    return node;
+  node->type |= value;
+  return node;
+}
+
+#endif
diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c
new file mode 100644
index 000000000000..8189c56a4c93
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/structure.c
@@ -0,0 +1,1220 @@
+/*
+ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+
+/*****************************************************/
+/* File: structure.c                                 */
+/* Description: Functions to create and delete an    */
+/*  ASN1 tree.                                       */
+/*****************************************************/
+
+
+#include <int.h>
+#include <structure.h>
+#include "parser_aux.h"
+#include <gstr.h>
+
+
+extern char _asn1_identifierMissing[];
+
+
+/******************************************************/
+/* Function : _asn1_add_single_node                     */
+/* Description: creates a new NODE_ASN element.       */
+/* Parameters:                                        */
+/*   type: type of the new element (see ASN1_ETYPE_         */
+/*         and CONST_ constants).                     */
+/* Return: pointer to the new element.                */
+/******************************************************/
+asn1_node
+_asn1_add_single_node (unsigned int type)
+{
+  asn1_node punt;
+
+  punt = calloc (1, sizeof (struct asn1_node_st));
+  if (punt == NULL)
+    return NULL;
+
+  punt->type = type;
+
+  return punt;
+}
+
+
+/******************************************************************/
+/* Function : _asn1_find_left                                     */
+/* Description: returns the NODE_ASN element with RIGHT field that*/
+/*              points the element NODE.                          */
+/* Parameters:                                                    */
+/*   node: NODE_ASN element pointer.                              */
+/* Return: NULL if not found.                                     */
+/******************************************************************/
+asn1_node
+_asn1_find_left (asn1_node_const node)
+{
+  if ((node == NULL) || (node->left == NULL) || (node->left->down == node))
+    return NULL;
+
+  return node->left;
+}
+
+
+int
+_asn1_create_static_structure (asn1_node_const pointer, char *output_file_name,
+			       char *vector_name)
+{
+  FILE *file;
+  asn1_node_const p;
+  unsigned long t;
+
+  file = fopen (output_file_name, "w");
+
+  if (file == NULL)
+    return ASN1_FILE_NOT_FOUND;
+
+  fprintf (file, "#if HAVE_CONFIG_H\n");
+  fprintf (file, "# include \"config.h\"\n");
+  fprintf (file, "#endif\n\n");
+
+  fprintf (file, "#include <libtasn1.h>\n\n");
+
+  fprintf (file, "const asn1_static_node %s[] = {\n", vector_name);
+
+  p = pointer;
+
+  while (p)
+    {
+      fprintf (file, "  { ");
+
+      if (p->name[0] != 0)
+	fprintf (file, "\"%s\", ", p->name);
+      else
+	fprintf (file, "NULL, ");
+
+      t = p->type;
+      if (p->down)
+	t |= CONST_DOWN;
+      if (p->right)
+	t |= CONST_RIGHT;
+
+      fprintf (file, "%lu, ", t);
+
+      if (p->value)
+	fprintf (file, "\"%s\"},\n", p->value);
+      else
+	fprintf (file, "NULL },\n");
+
+      if (p->down)
+	{
+	  p = p->down;
+	}
+      else if (p->right)
+	{
+	  p = p->right;
+	}
+      else
+	{
+	  while (1)
+	    {
+	      p = _asn1_find_up (p);
+	      if (p == pointer)
+		{
+		  p = NULL;
+		  break;
+		}
+	      if (p->right)
+		{
+		  p = p->right;
+		  break;
+		}
+	    }
+	}
+    }
+
+  fprintf (file, "  { NULL, 0, NULL }\n};\n");
+
+  fclose (file);
+
+  return ASN1_SUCCESS;
+}
+
+
+/**
+ * asn1_array2tree:
+ * @array: specify the array that contains ASN.1 declarations
+ * @definitions: return the pointer to the structure created by
+ *   *ARRAY ASN.1 declarations
+ * @errorDescription: return the error description.
+ *
+ * Creates the structures needed to manage the ASN.1 definitions.
+ * @array is a vector created by asn1_parser2array().
+ *
+ * Returns: %ASN1_SUCCESS if structure was created correctly,
+ *   %ASN1_ELEMENT_NOT_EMPTY if *@definitions not NULL,
+ *   %ASN1_IDENTIFIER_NOT_FOUND if in the file there is an identifier
+ *   that is not defined (see @errorDescription for more information),
+ *   %ASN1_ARRAY_ERROR if the array pointed by @array is wrong.
+ **/
+int
+asn1_array2tree (const asn1_static_node * array, asn1_node * definitions,
+		 char *errorDescription)
+{
+  asn1_node p, p_last = NULL;
+  unsigned long k;
+  int move;
+  int result;
+  unsigned int type;
+  list_type *e_list = NULL;
+
+  if (errorDescription)
+    errorDescription[0] = 0;
+
+  if (*definitions != NULL)
+    return ASN1_ELEMENT_NOT_EMPTY;
+
+  move = UP;
+
+  for (k = 0; array[k].value || array[k].type || array[k].name; k++)
+    {
+      type = convert_old_type (array[k].type);
+
+      p = _asn1_add_static_node (&e_list, type & (~CONST_DOWN));
+      if (array[k].name)
+	_asn1_set_name (p, array[k].name);
+      if (array[k].value)
+	_asn1_set_value (p, array[k].value, strlen (array[k].value) + 1);
+
+      if (*definitions == NULL)
+	*definitions = p;
+
+      if (move == DOWN)
+	{
+	  if (p_last && p_last->down)
+	      _asn1_delete_structure (e_list, &p_last->down, 0);
+	  _asn1_set_down (p_last, p);
+	}
+      else if (move == RIGHT)
+        {
+	  if (p_last && p_last->right)
+	      _asn1_delete_structure (e_list, &p_last->right, 0);
+	  _asn1_set_right (p_last, p);
+        }
+
+      p_last = p;
+
+      if (type & CONST_DOWN)
+	move = DOWN;
+      else if (type & CONST_RIGHT)
+	move = RIGHT;
+      else
+	{
+	  while (p_last != *definitions)
+	    {
+	      p_last = _asn1_find_up (p_last);
+
+	      if (p_last == NULL)
+		break;
+
+	      if (p_last->type & CONST_RIGHT)
+		{
+		  p_last->type &= ~CONST_RIGHT;
+		  move = RIGHT;
+		  break;
+		}
+	    }			/* while */
+	}
+    }				/* while */
+
+  if (p_last == *definitions)
+    {
+      result = _asn1_check_identifier (*definitions);
+      if (result == ASN1_SUCCESS)
+	{
+	  _asn1_change_integer_value (*definitions);
+	  result = _asn1_expand_object_id (&e_list, *definitions);
+	}
+    }
+  else
+    {
+      result = ASN1_ARRAY_ERROR;
+    }
+
+  if (errorDescription != NULL)
+    {
+      if (result == ASN1_IDENTIFIER_NOT_FOUND)
+	{
+	  Estrcpy (errorDescription, ":: identifier '");
+	  Estrcat (errorDescription, _asn1_identifierMissing);
+	  Estrcat (errorDescription, "' not found");
+	}
+      else
+	errorDescription[0] = 0;
+    }
+
+  if (result != ASN1_SUCCESS)
+    {
+      _asn1_delete_list_and_nodes (e_list);
+      *definitions = NULL;
+    }
+  else
+    _asn1_delete_list (e_list);
+
+  return result;
+}
+
+/**
+ * asn1_delete_structure:
+ * @structure: pointer to the structure that you want to delete.
+ *
+ * Deletes the structure *@structure.  At the end, *@structure is set
+ * to NULL.
+ *
+ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
+ *   *@structure was NULL.
+ **/
+int
+asn1_delete_structure (asn1_node * structure)
+{
+  return _asn1_delete_structure (NULL, structure, 0);
+}
+
+/**
+ * asn1_delete_structure2:
+ * @structure: pointer to the structure that you want to delete.
+ * @flags: additional flags (see %ASN1_DELETE_FLAG)
+ *
+ * Deletes the structure *@structure.  At the end, *@structure is set
+ * to NULL.
+ *
+ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
+ *   *@structure was NULL.
+ **/
+int
+asn1_delete_structure2 (asn1_node * structure, unsigned int flags)
+{
+  return _asn1_delete_structure (NULL, structure, flags);
+}
+
+int
+_asn1_delete_structure (list_type *e_list, asn1_node * structure, unsigned int flags)
+{
+  asn1_node p, p2, p3;
+
+  if (*structure == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = *structure;
+  while (p)
+    {
+      if (p->down)
+	{
+	  p = p->down;
+	}
+      else
+	{			/* no down */
+	  p2 = p->right;
+	  if (p != *structure)
+	    {
+	      p3 = _asn1_find_up (p);
+	      _asn1_set_down (p3, p2);
+	      if (e_list)
+		_asn1_delete_node_from_list (e_list, p);
+	      _asn1_remove_node (p, flags);
+	      p = p3;
+	    }
+	  else
+	    {			/* p==root */
+	      p3 = _asn1_find_left (p);
+	      if (!p3)
+		{
+		  p3 = _asn1_find_up (p);
+		  if (p3)
+		    _asn1_set_down (p3, p2);
+		  else
+		    {
+		      if (p->right)
+			p->right->left = NULL;
+		    }
+		}
+	      else
+		_asn1_set_right (p3, p2);
+	      if (e_list)
+		_asn1_delete_node_from_list (e_list, p);
+	      _asn1_remove_node (p, flags);
+	      p = NULL;
+	    }
+	}
+    }
+
+  *structure = NULL;
+  return ASN1_SUCCESS;
+}
+
+
+/**
+ * asn1_delete_element:
+ * @structure: pointer to the structure that contains the element you
+ *   want to delete.
+ * @element_name: element's name you want to delete.
+ *
+ * Deletes the element named *@element_name inside *@structure.
+ *
+ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
+ *   the @element_name was not found.
+ **/
+int
+asn1_delete_element (asn1_node structure, const char *element_name)
+{
+  asn1_node p2, p3, source_node;
+
+  source_node = asn1_find_node (structure, element_name);
+
+  if (source_node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p2 = source_node->right;
+  p3 = _asn1_find_left (source_node);
+  if (!p3)
+    {
+      p3 = _asn1_find_up (source_node);
+      if (p3)
+	_asn1_set_down (p3, p2);
+      else if (source_node->right)
+	source_node->right->left = NULL;
+    }
+  else
+    _asn1_set_right (p3, p2);
+
+  return asn1_delete_structure (&source_node);
+}
+
+#ifndef __clang_analyzer__
+asn1_node
+_asn1_copy_structure3 (asn1_node_const source_node)
+{
+  asn1_node_const p_s;
+  asn1_node dest_node, p_d, p_d_prev;
+  int move;
+
+  if (source_node == NULL)
+    return NULL;
+
+  dest_node = _asn1_add_single_node (source_node->type);
+
+  p_s = source_node;
+  p_d = dest_node;
+
+  move = DOWN;
+
+  do
+    {
+      if (move != UP)
+	{
+	  if (p_s->name[0] != 0)
+	    _asn1_cpy_name (p_d, p_s);
+	  if (p_s->value)
+	    _asn1_set_value (p_d, p_s->value, p_s->value_len);
+	  if (p_s->down)
+	    {
+	      p_s = p_s->down;
+	      p_d_prev = p_d;
+	      p_d = _asn1_add_single_node (p_s->type);
+	      _asn1_set_down (p_d_prev, p_d);
+	      continue;
+	    }
+	  p_d->start = p_s->start;
+	  p_d->end = p_s->end;
+	}
+
+      if (p_s == source_node)
+	break;
+
+      if (p_s->right)
+	{
+	  move = RIGHT;
+	  p_s = p_s->right;
+	  p_d_prev = p_d;
+	  p_d = _asn1_add_single_node (p_s->type);
+	  _asn1_set_right (p_d_prev, p_d);
+	}
+      else
+	{
+	  move = UP;
+	  p_s = _asn1_find_up (p_s);
+	  p_d = _asn1_find_up (p_d);
+	}
+    }
+  while (p_s != source_node);
+  return dest_node;
+}
+#else
+
+/* Non-production code */
+asn1_node
+_asn1_copy_structure3 (asn1_node_const source_node)
+{
+  return NULL;
+}
+#endif /* __clang_analyzer__ */
+
+
+static asn1_node
+_asn1_copy_structure2 (asn1_node_const root, const char *source_name)
+{
+  asn1_node source_node;
+
+  source_node = asn1_find_node (root, source_name);
+
+  return _asn1_copy_structure3 (source_node);
+
+}
+
+
+static int
+_asn1_type_choice_config (asn1_node node)
+{
+  asn1_node p, p2, p3, p4;
+  int move, tlen;
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = node;
+  move = DOWN;
+
+  while (!((p == node) && (move == UP)))
+    {
+      if (move != UP)
+	{
+	  if ((type_field (p->type) == ASN1_ETYPE_CHOICE)
+	      && (p->type & CONST_TAG))
+	    {
+	      p2 = p->down;
+	      while (p2)
+		{
+		  if (type_field (p2->type) != ASN1_ETYPE_TAG)
+		    {
+		      p2->type |= CONST_TAG;
+		      p3 = _asn1_find_left (p2);
+		      while (p3)
+			{
+			  if (type_field (p3->type) == ASN1_ETYPE_TAG)
+			    {
+			      p4 = _asn1_add_single_node (p3->type);
+			      tlen = _asn1_strlen (p3->value);
+			      if (tlen > 0)
+				_asn1_set_value (p4, p3->value, tlen + 1);
+			      _asn1_set_right (p4, p2->down);
+			      _asn1_set_down (p2, p4);
+			    }
+			  p3 = _asn1_find_left (p3);
+			}
+		    }
+		  p2 = p2->right;
+		}
+	      p->type &= ~(CONST_TAG);
+	      p2 = p->down;
+	      while (p2)
+		{
+		  p3 = p2->right;
+		  if (type_field (p2->type) == ASN1_ETYPE_TAG)
+		    asn1_delete_structure (&p2);
+		  p2 = p3;
+		}
+	    }
+	  move = DOWN;
+	}
+      else
+	move = RIGHT;
+
+      if (move == DOWN)
+	{
+	  if (p->down)
+	    p = p->down;
+	  else
+	    move = RIGHT;
+	}
+
+      if (p == node)
+	{
+	  move = UP;
+	  continue;
+	}
+
+      if (move == RIGHT)
+	{
+	  if (p->right)
+	    p = p->right;
+	  else
+	    move = UP;
+	}
+      if (move == UP)
+	p = _asn1_find_up (p);
+    }
+
+  return ASN1_SUCCESS;
+}
+
+
+static int
+_asn1_expand_identifier (asn1_node * node, asn1_node_const root)
+{
+  asn1_node p, p2, p3;
+  char name2[ASN1_MAX_NAME_SIZE + 2];
+  int move;
+
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = *node;
+  move = DOWN;
+
+  while (!((p == *node) && (move == UP)))
+    {
+      if (move != UP)
+	{
+	  if (type_field (p->type) == ASN1_ETYPE_IDENTIFIER)
+	    {
+	      snprintf (name2, sizeof (name2), "%s.%s", root->name, p->value);
+	      p2 = _asn1_copy_structure2 (root, name2);
+	      if (p2 == NULL)
+		{
+		  return ASN1_IDENTIFIER_NOT_FOUND;
+		}
+	      _asn1_cpy_name (p2, p);
+	      p2->right = p->right;
+	      p2->left = p->left;
+	      if (p->right)
+		p->right->left = p2;
+	      p3 = p->down;
+	      if (p3)
+		{
+		  while (p3->right)
+		    p3 = p3->right;
+		  _asn1_set_right (p3, p2->down);
+		  _asn1_set_down (p2, p->down);
+		}
+
+	      p3 = _asn1_find_left (p);
+	      if (p3)
+		_asn1_set_right (p3, p2);
+	      else
+		{
+		  p3 = _asn1_find_up (p);
+		  if (p3)
+		    _asn1_set_down (p3, p2);
+		  else
+		    {
+		      p2->left = NULL;
+		    }
+		}
+
+	      if (p->type & CONST_SIZE)
+		p2->type |= CONST_SIZE;
+	      if (p->type & CONST_TAG)
+		p2->type |= CONST_TAG;
+	      if (p->type & CONST_OPTION)
+		p2->type |= CONST_OPTION;
+	      if (p->type & CONST_DEFAULT)
+		p2->type |= CONST_DEFAULT;
+	      if (p->type & CONST_SET)
+		p2->type |= CONST_SET;
+	      if (p->type & CONST_NOT_USED)
+		p2->type |= CONST_NOT_USED;
+
+	      if (p == *node)
+		*node = p2;
+	      _asn1_remove_node (p, 0);
+	      p = p2;
+	      move = DOWN;
+	      continue;
+	    }
+	  move = DOWN;
+	}
+      else
+	move = RIGHT;
+
+      if (move == DOWN)
+	{
+	  if (p->down)
+	    p = p->down;
+	  else
+	    move = RIGHT;
+	}
+
+      if (p == *node)
+	{
+	  move = UP;
+	  continue;
+	}
+
+      if (move == RIGHT)
+	{
+	  if (p->right)
+	    p = p->right;
+	  else
+	    move = UP;
+	}
+      if (move == UP)
+	p = _asn1_find_up (p);
+    }
+
+  return ASN1_SUCCESS;
+}
+
+
+/**
+ * asn1_create_element:
+ * @definitions: pointer to the structure returned by "parser_asn1" function
+ * @source_name: the name of the type of the new structure (must be
+ *   inside p_structure).
+ * @element: pointer to the structure created.
+ *
+ * Creates a structure of type @source_name.  Example using
+ *  "pkix.asn":
+ *
+ * rc = asn1_create_element(cert_def, "PKIX1.Certificate", certptr);
+ *
+ * Returns: %ASN1_SUCCESS if creation OK, %ASN1_ELEMENT_NOT_FOUND if
+ *   @source_name is not known.
+ **/
+int
+asn1_create_element (asn1_node_const definitions, const char *source_name,
+		     asn1_node * element)
+{
+  asn1_node dest_node;
+  int res;
+
+  dest_node = _asn1_copy_structure2 (definitions, source_name);
+
+  if (dest_node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  _asn1_set_name (dest_node, "");
+
+  res = _asn1_expand_identifier (&dest_node, definitions);
+  _asn1_type_choice_config (dest_node);
+
+  *element = dest_node;
+
+  return res;
+}
+
+
+/**
+ * asn1_print_structure:
+ * @out: pointer to the output file (e.g. stdout).
+ * @structure: pointer to the structure that you want to visit.
+ * @name: an element of the structure
+ * @mode: specify how much of the structure to print, can be
+ *   %ASN1_PRINT_NAME, %ASN1_PRINT_NAME_TYPE,
+ *   %ASN1_PRINT_NAME_TYPE_VALUE, or %ASN1_PRINT_ALL.
+ *
+ * Prints on the @out file descriptor the structure's tree starting
+ * from the @name element inside the structure @structure.
+ **/
+void
+asn1_print_structure (FILE * out, asn1_node_const structure, const char *name,
+		      int mode)
+{
+  asn1_node_const p, root;
+  int k, indent = 0, len, len2, len3;
+
+  if (out == NULL)
+    return;
+
+  root = asn1_find_node (structure, name);
+
+  if (root == NULL)
+    return;
+
+  p = root;
+  while (p)
+    {
+      if (mode == ASN1_PRINT_ALL)
+	{
+	  for (k = 0; k < indent; k++)
+	    fprintf (out, " ");
+	  fprintf (out, "name:");
+	  if (p->name[0] != 0)
+	    fprintf (out, "%s  ", p->name);
+	  else
+	    fprintf (out, "NULL  ");
+	}
+      else
+	{
+	  switch (type_field (p->type))
+	    {
+	    case ASN1_ETYPE_CONSTANT:
+	    case ASN1_ETYPE_TAG:
+	    case ASN1_ETYPE_SIZE:
+	      break;
+	    default:
+	      for (k = 0; k < indent; k++)
+		fprintf (out, " ");
+	      fprintf (out, "name:");
+	      if (p->name[0] != 0)
+		fprintf (out, "%s  ", p->name);
+	      else
+		fprintf (out, "NULL  ");
+	    }
+	}
+
+      if (mode != ASN1_PRINT_NAME)
+	{
+	  unsigned type = type_field (p->type);
+	  switch (type)
+	    {
+	    case ASN1_ETYPE_CONSTANT:
+	      if (mode == ASN1_PRINT_ALL)
+		fprintf (out, "type:CONST");
+	      break;
+	    case ASN1_ETYPE_TAG:
+	      if (mode == ASN1_PRINT_ALL)
+		fprintf (out, "type:TAG");
+	      break;
+	    case ASN1_ETYPE_SIZE:
+	      if (mode == ASN1_PRINT_ALL)
+		fprintf (out, "type:SIZE");
+	      break;
+	    case ASN1_ETYPE_DEFAULT:
+	      fprintf (out, "type:DEFAULT");
+	      break;
+	    case ASN1_ETYPE_IDENTIFIER:
+	      fprintf (out, "type:IDENTIFIER");
+	      break;
+	    case ASN1_ETYPE_ANY:
+	      fprintf (out, "type:ANY");
+	      break;
+	    case ASN1_ETYPE_CHOICE:
+	      fprintf (out, "type:CHOICE");
+	      break;
+	    case ASN1_ETYPE_DEFINITIONS:
+	      fprintf (out, "type:DEFINITIONS");
+	      break;
+	    CASE_HANDLED_ETYPES:
+	      fprintf (out, "%s", _asn1_tags[type].desc);
+	      break;
+	    default:
+	      break;
+	    }
+	}
+
+      if ((mode == ASN1_PRINT_NAME_TYPE_VALUE) || (mode == ASN1_PRINT_ALL))
+	{
+	  switch (type_field (p->type))
+	    {
+	    case ASN1_ETYPE_CONSTANT:
+	      if (mode == ASN1_PRINT_ALL)
+		if (p->value)
+		  fprintf (out, "  value:%s", p->value);
+	      break;
+	    case ASN1_ETYPE_TAG:
+	      if (mode == ASN1_PRINT_ALL)
+		if (p->value)
+		  fprintf (out, "  value:%s", p->value);
+	      break;
+	    case ASN1_ETYPE_SIZE:
+	      if (mode == ASN1_PRINT_ALL)
+		if (p->value)
+		  fprintf (out, "  value:%s", p->value);
+	      break;
+	    case ASN1_ETYPE_DEFAULT:
+	      if (p->value)
+		fprintf (out, "  value:%s", p->value);
+	      else if (p->type & CONST_TRUE)
+		fprintf (out, "  value:TRUE");
+	      else if (p->type & CONST_FALSE)
+		fprintf (out, "  value:FALSE");
+	      break;
+	    case ASN1_ETYPE_IDENTIFIER:
+	      if (p->value)
+		fprintf (out, "  value:%s", p->value);
+	      break;
+	    case ASN1_ETYPE_INTEGER:
+	      if (p->value)
+		{
+		  len2 = -1;
+		  len = asn1_get_length_der (p->value, p->value_len, &len2);
+		  fprintf (out, "  value:0x");
+		  if (len > 0)
+		    for (k = 0; k < len; k++)
+		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
+		}
+	      break;
+	    case ASN1_ETYPE_ENUMERATED:
+	      if (p->value)
+		{
+		  len2 = -1;
+		  len = asn1_get_length_der (p->value, p->value_len, &len2);
+		  fprintf (out, "  value:0x");
+		  if (len > 0)
+		    for (k = 0; k < len; k++)
+		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
+		}
+	      break;
+	    case ASN1_ETYPE_BOOLEAN:
+	      if (p->value)
+		{
+		  if (p->value[0] == 'T')
+		    fprintf (out, "  value:TRUE");
+		  else if (p->value[0] == 'F')
+		    fprintf (out, "  value:FALSE");
+		}
+	      break;
+	    case ASN1_ETYPE_BIT_STRING:
+	      if (p->value)
+		{
+		  len2 = -1;
+		  len = asn1_get_length_der (p->value, p->value_len, &len2);
+		  if (len > 0)
+		    {
+		      fprintf (out, "  value(%i):",
+			       (len - 1) * 8 - (p->value[len2]));
+		      for (k = 1; k < len; k++)
+			fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
+		    }
+		}
+	      break;
+	    case ASN1_ETYPE_GENERALIZED_TIME:
+	    case ASN1_ETYPE_UTC_TIME:
+	      if (p->value)
+		{
+		  fprintf (out, "  value:");
+		  for (k = 0; k < p->value_len; k++)
+		    fprintf (out, "%c", (p->value)[k]);
+		}
+	      break;
+	    case ASN1_ETYPE_GENERALSTRING:
+	    case ASN1_ETYPE_NUMERIC_STRING:
+	    case ASN1_ETYPE_IA5_STRING:
+	    case ASN1_ETYPE_TELETEX_STRING:
+	    case ASN1_ETYPE_PRINTABLE_STRING:
+	    case ASN1_ETYPE_UNIVERSAL_STRING:
+	    case ASN1_ETYPE_UTF8_STRING:
+	    case ASN1_ETYPE_VISIBLE_STRING:
+	      if (p->value)
+		{
+		  len2 = -1;
+		  len = asn1_get_length_der (p->value, p->value_len, &len2);
+		  fprintf (out, "  value:");
+		  if (len > 0)
+		    for (k = 0; k < len; k++)
+		      fprintf (out, "%c", (p->value)[k + len2]);
+		}
+	      break;
+	    case ASN1_ETYPE_BMP_STRING:
+	    case ASN1_ETYPE_OCTET_STRING:
+	      if (p->value)
+		{
+		  len2 = -1;
+		  len = asn1_get_length_der (p->value, p->value_len, &len2);
+		  fprintf (out, "  value:");
+		  if (len > 0)
+		    for (k = 0; k < len; k++)
+		      fprintf (out, "%02x", (unsigned) (p->value)[k + len2]);
+		}
+	      break;
+	    case ASN1_ETYPE_OBJECT_ID:
+	      if (p->value)
+		fprintf (out, "  value:%s", p->value);
+	      break;
+	    case ASN1_ETYPE_ANY:
+	      if (p->value)
+		{
+		  len3 = -1;
+		  len2 = asn1_get_length_der (p->value, p->value_len, &len3);
+		  fprintf (out, "  value:");
+		  if (len2 > 0)
+		    for (k = 0; k < len2; k++)
+		      fprintf (out, "%02x", (unsigned) (p->value)[k + len3]);
+		}
+	      break;
+	    case ASN1_ETYPE_SET:
+	    case ASN1_ETYPE_SET_OF:
+	    case ASN1_ETYPE_CHOICE:
+	    case ASN1_ETYPE_DEFINITIONS:
+	    case ASN1_ETYPE_SEQUENCE_OF:
+	    case ASN1_ETYPE_SEQUENCE:
+	    case ASN1_ETYPE_NULL:
+	      break;
+	    default:
+	      break;
+	    }
+	}
+
+      if (mode == ASN1_PRINT_ALL)
+	{
+	  if (p->type & 0x1FFFFF00)
+	    {
+	      fprintf (out, "  attr:");
+	      if (p->type & CONST_UNIVERSAL)
+		fprintf (out, "UNIVERSAL,");
+	      if (p->type & CONST_PRIVATE)
+		fprintf (out, "PRIVATE,");
+	      if (p->type & CONST_APPLICATION)
+		fprintf (out, "APPLICATION,");
+	      if (p->type & CONST_EXPLICIT)
+		fprintf (out, "EXPLICIT,");
+	      if (p->type & CONST_IMPLICIT)
+		fprintf (out, "IMPLICIT,");
+	      if (p->type & CONST_TAG)
+		fprintf (out, "TAG,");
+	      if (p->type & CONST_DEFAULT)
+		fprintf (out, "DEFAULT,");
+	      if (p->type & CONST_TRUE)
+		fprintf (out, "TRUE,");
+	      if (p->type & CONST_FALSE)
+		fprintf (out, "FALSE,");
+	      if (p->type & CONST_LIST)
+		fprintf (out, "LIST,");
+	      if (p->type & CONST_MIN_MAX)
+		fprintf (out, "MIN_MAX,");
+	      if (p->type & CONST_OPTION)
+		fprintf (out, "OPTION,");
+	      if (p->type & CONST_1_PARAM)
+		fprintf (out, "1_PARAM,");
+	      if (p->type & CONST_SIZE)
+		fprintf (out, "SIZE,");
+	      if (p->type & CONST_DEFINED_BY)
+		fprintf (out, "DEF_BY,");
+	      if (p->type & CONST_GENERALIZED)
+		fprintf (out, "GENERALIZED,");
+	      if (p->type & CONST_UTC)
+		fprintf (out, "UTC,");
+	      if (p->type & CONST_SET)
+		fprintf (out, "SET,");
+	      if (p->type & CONST_NOT_USED)
+		fprintf (out, "NOT_USED,");
+	      if (p->type & CONST_ASSIGN)
+		fprintf (out, "ASSIGNMENT,");
+	    }
+	}
+
+      if (mode == ASN1_PRINT_ALL)
+	{
+	  fprintf (out, "\n");
+	}
+      else
+	{
+	  switch (type_field (p->type))
+	    {
+	    case ASN1_ETYPE_CONSTANT:
+	    case ASN1_ETYPE_TAG:
+	    case ASN1_ETYPE_SIZE:
+	      break;
+	    default:
+	      fprintf (out, "\n");
+	    }
+	}
+
+      if (p->down)
+	{
+	  p = p->down;
+	  indent += 2;
+	}
+      else if (p == root)
+	{
+	  p = NULL;
+	  break;
+	}
+      else if (p->right)
+	p = p->right;
+      else
+	{
+	  while (1)
+	    {
+	      p = _asn1_find_up (p);
+	      if (p == root)
+		{
+		  p = NULL;
+		  break;
+		}
+	      indent -= 2;
+	      if (p->right)
+		{
+		  p = p->right;
+		  break;
+		}
+	    }
+	}
+    }
+}
+
+
+
+/**
+ * asn1_number_of_elements:
+ * @element: pointer to the root of an ASN1 structure.
+ * @name: the name of a sub-structure of ROOT.
+ * @num: pointer to an integer where the result will be stored
+ *
+ * Counts the number of elements of a sub-structure called NAME with
+ * names equal to "?1","?2", ...
+ *
+ * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if
+ *   @name is not known, %ASN1_GENERIC_ERROR if pointer @num is %NULL.
+ **/
+int
+asn1_number_of_elements (asn1_node_const element, const char *name, int *num)
+{
+  asn1_node_const node, p;
+
+  if (num == NULL)
+    return ASN1_GENERIC_ERROR;
+
+  *num = 0;
+
+  node = asn1_find_node (element, name);
+  if (node == NULL)
+    return ASN1_ELEMENT_NOT_FOUND;
+
+  p = node->down;
+
+  while (p)
+    {
+      if (p->name[0] == '?')
+	(*num)++;
+      p = p->right;
+    }
+
+  return ASN1_SUCCESS;
+}
+
+
+/**
+ * asn1_find_structure_from_oid:
+ * @definitions: ASN1 definitions
+ * @oidValue: value of the OID to search (e.g. "1.2.3.4").
+ *
+ * Search the structure that is defined just after an OID definition.
+ *
+ * Returns: %NULL when @oidValue not found, otherwise the pointer to a
+ *   constant string that contains the element name defined just after
+ *   the OID.
+ **/
+const char *
+asn1_find_structure_from_oid (asn1_node_const definitions, const char *oidValue)
+{
+  char name[2 * ASN1_MAX_NAME_SIZE + 2];
+  char value[ASN1_MAX_NAME_SIZE];
+  asn1_node p;
+  int len;
+  int result;
+  const char *definitionsName;
+
+  if ((definitions == NULL) || (oidValue == NULL))
+    return NULL;		/* ASN1_ELEMENT_NOT_FOUND; */
+
+  definitionsName = definitions->name;
+
+  /* search the OBJECT_ID into definitions */
+  p = definitions->down;
+  while (p)
+    {
+      if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) &&
+	  (p->type & CONST_ASSIGN))
+	{
+          snprintf(name, sizeof(name), "%s.%s", definitionsName, p->name);
+
+	  len = ASN1_MAX_NAME_SIZE;
+	  result = asn1_read_value (definitions, name, value, &len);
+
+	  if ((result == ASN1_SUCCESS) && (!strcmp (oidValue, value)))
+	    {
+	      p = p->right;
+	      if (p == NULL)	/* reach the end of ASN1 definitions */
+		return NULL;	/* ASN1_ELEMENT_NOT_FOUND; */
+
+	      return p->name;
+	    }
+	}
+      p = p->right;
+    }
+
+  return NULL;			/* ASN1_ELEMENT_NOT_FOUND; */
+}
+
+/**
+ * asn1_copy_node:
+ * @dst: Destination asn1 node.
+ * @dst_name: Field name in destination node.
+ * @src: Source asn1 node.
+ * @src_name: Field name in source node.
+ *
+ * Create a deep copy of a asn1_node variable. That
+ * function requires @dst to be expanded using asn1_create_element().
+ *
+ * Returns: Return %ASN1_SUCCESS on success.
+ **/
+int
+asn1_copy_node (asn1_node dst, const char *dst_name,
+		asn1_node_const src, const char *src_name)
+{
+  int result;
+  asn1_node dst_node;
+  void *data = NULL;
+  int size = 0;
+
+  result = asn1_der_coding (src, src_name, NULL, &size, NULL);
+  if (result != ASN1_MEM_ERROR)
+    return result;
+
+  data = malloc (size);
+  if (data == NULL)
+    return ASN1_MEM_ERROR;
+
+  result = asn1_der_coding (src, src_name, data, &size, NULL);
+  if (result != ASN1_SUCCESS)
+    {
+      free (data);
+      return result;
+    }
+
+  dst_node = asn1_find_node (dst, dst_name);
+  if (dst_node == NULL)
+    {
+      free (data);
+      return ASN1_ELEMENT_NOT_FOUND;
+    }
+
+  result = asn1_der_decoding (&dst_node, data, size, NULL);
+
+  free (data);
+
+  return result;
+}
+
+/**
+ * asn1_dup_node:
+ * @src: Source asn1 node.
+ * @src_name: Field name in source node.
+ *
+ * Create a deep copy of a asn1_node variable. This function
+ * will return an exact copy of the provided structure.
+ *
+ * Returns: Return %NULL on failure.
+ **/
+asn1_node
+asn1_dup_node (asn1_node_const src, const char *src_name)
+{
+  return _asn1_copy_structure2(src, src_name);
+}
diff --git a/grub-core/lib/libtasn1/lib/structure.h b/grub-core/lib/libtasn1/lib/structure.h
new file mode 100644
index 000000000000..99e685da07a9
--- /dev/null
+++ b/grub-core/lib/libtasn1/lib/structure.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * The LIBTASN1 library 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.
+ *
+ * This library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ */
+
+/*************************************************/
+/* File: structure.h                             */
+/* Description: list of exported object by       */
+/*   "structure.c"                               */
+/*************************************************/
+
+#ifndef _STRUCTURE_H
+#define _STRUCTURE_H
+
+#include "parser_aux.h" // list_type
+
+int _asn1_create_static_structure (asn1_node_const pointer,
+				   char *output_file_name, char *vector_name);
+
+asn1_node _asn1_copy_structure3 (asn1_node_const source_node);
+
+asn1_node _asn1_add_single_node (unsigned int type);
+
+asn1_node _asn1_find_left (asn1_node_const node);
+
+int
+_asn1_delete_structure (list_type *e_list, asn1_node *structure, unsigned int flags);
+
+#endif
diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
new file mode 100644
index 000000000000..6fd7a30dc35c
--- /dev/null
+++ b/include/grub/libtasn1.h
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2002-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * LIBTASN1 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.
+ *
+ * LIBTASN1 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 LIBTASN1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
+ *
+ */
+
+/**
+ * libtasn1:Short_Description:
+ *
+ * GNU ASN.1 library
+ */
+/**
+ * libtasn1:Long_Description:
+ *
+ * The Libtasn1 library provides Abstract Syntax Notation One (ASN.1, as
+ * specified by the X.680 ITU-T recommendation) parsing and structures
+ * management, and Distinguished Encoding Rules (DER, as per X.690)
+ * encoding and decoding functions.
+ */
+
+
+#ifndef LIBTASN1_H
+#define LIBTASN1_H
+
+#ifndef ASN1_API
+#if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY
+#define ASN1_API __attribute__((__visibility__("default")))
+#elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC
+#define ASN1_API __declspec(dllexport)
+#elif defined _MSC_VER && ! defined ASN1_STATIC
+#define ASN1_API __declspec(dllimport)
+#else
+#define ASN1_API
+#endif
+#endif
+
+#ifdef __GNUC__
+# define __LIBTASN1_CONST__  __attribute__((const))
+# define __LIBTASN1_PURE__  __attribute__((pure))
+#else
+# define __LIBTASN1_CONST__
+# define __LIBTASN1_PURE__
+#endif
+
+#include <sys/types.h>
+#include <time.h>
+#include <stdio.h>		/* for FILE* */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * ASN1_VERSION:
+ *
+ * Version of the library as a string.
+ */
+#define ASN1_VERSION "4.16.0"
+
+/**
+ * ASN1_VERSION_MAJOR:
+ *
+ * Major version number of the library.
+ */
+#define ASN1_VERSION_MAJOR 4
+
+/**
+ * ASN1_VERSION_MINOR:
+ *
+ * Minor version number of the library.
+ */
+#define ASN1_VERSION_MINOR 16
+
+/**
+ * ASN1_VERSION_PATCH:
+ *
+ * Patch version number of the library.
+ */
+#define ASN1_VERSION_PATCH 0
+
+/**
+ * ASN1_VERSION_NUMBER:
+ *
+ * Version number of the library as a number.
+ */
+#define ASN1_VERSION_NUMBER 0x041000
+
+
+#if defined __GNUC__ && !defined ASN1_INTERNAL_BUILD
+# define _ASN1_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+# if _ASN1_GCC_VERSION >= 30100
+#  define _ASN1_GCC_ATTR_DEPRECATED __attribute__ ((__deprecated__))
+# endif
+#endif
+
+#ifndef _ASN1_GCC_ATTR_DEPRECATED
+#define _ASN1_GCC_ATTR_DEPRECATED
+#endif
+
+/*****************************************/
+/* Errors returned by libtasn1 functions */
+/*****************************************/
+#define ASN1_SUCCESS			0
+#define ASN1_FILE_NOT_FOUND		1
+#define ASN1_ELEMENT_NOT_FOUND		2
+#define ASN1_IDENTIFIER_NOT_FOUND	3
+#define ASN1_DER_ERROR			4
+#define ASN1_VALUE_NOT_FOUND		5
+#define ASN1_GENERIC_ERROR		6
+#define ASN1_VALUE_NOT_VALID		7
+#define ASN1_TAG_ERROR			8
+#define ASN1_TAG_IMPLICIT		9
+#define ASN1_ERROR_TYPE_ANY		10
+#define ASN1_SYNTAX_ERROR		11
+#define ASN1_MEM_ERROR			12
+#define ASN1_MEM_ALLOC_ERROR		13
+#define ASN1_DER_OVERFLOW		14
+#define ASN1_NAME_TOO_LONG		15
+#define ASN1_ARRAY_ERROR		16
+#define ASN1_ELEMENT_NOT_EMPTY		17
+#define ASN1_TIME_ENCODING_ERROR	18
+#define ASN1_RECURSION			19
+
+/*************************************/
+/* Constants used in asn1_visit_tree */
+/*************************************/
+#define ASN1_PRINT_NAME			1
+#define ASN1_PRINT_NAME_TYPE		2
+#define ASN1_PRINT_NAME_TYPE_VALUE	3
+#define ASN1_PRINT_ALL			4
+
+/*****************************************/
+/* Constants returned by asn1_read_tag   */
+/*****************************************/
+#define ASN1_CLASS_UNIVERSAL		0x00	/* old: 1 */
+#define ASN1_CLASS_APPLICATION		0x40	/* old: 2 */
+#define ASN1_CLASS_CONTEXT_SPECIFIC	0x80	/* old: 3 */
+#define ASN1_CLASS_PRIVATE		0xC0	/* old: 4 */
+#define ASN1_CLASS_STRUCTURED		0x20
+
+/*****************************************/
+/* Constants returned by asn1_read_tag   */
+/*****************************************/
+#define ASN1_TAG_BOOLEAN		0x01
+#define ASN1_TAG_INTEGER		0x02
+#define ASN1_TAG_SEQUENCE		0x10
+#define ASN1_TAG_SET			0x11
+#define ASN1_TAG_OCTET_STRING		0x04
+#define ASN1_TAG_BIT_STRING		0x03
+#define ASN1_TAG_UTCTime		0x17
+#define ASN1_TAG_GENERALIZEDTime	0x18
+#define ASN1_TAG_OBJECT_ID		0x06
+#define ASN1_TAG_ENUMERATED		0x0A
+#define ASN1_TAG_NULL			0x05
+#define ASN1_TAG_GENERALSTRING		0x1B
+#define ASN1_TAG_NUMERIC_STRING		0x12
+#define ASN1_TAG_IA5_STRING		0x16
+#define ASN1_TAG_TELETEX_STRING		0x14
+#define ASN1_TAG_PRINTABLE_STRING	0x13
+#define ASN1_TAG_UNIVERSAL_STRING	0x1C
+#define ASN1_TAG_BMP_STRING		0x1E
+#define ASN1_TAG_UTF8_STRING		0x0C
+#define ASN1_TAG_VISIBLE_STRING		0x1A
+
+/**
+ * asn1_node:
+ *
+ * Structure definition used for the node of the tree
+ * that represents an ASN.1 DEFINITION.
+ */
+typedef struct asn1_node_st asn1_node_st;
+
+typedef asn1_node_st *asn1_node;
+typedef const asn1_node_st *asn1_node_const;
+
+/**
+ * ASN1_MAX_NAME_SIZE:
+ *
+ * Maximum number of characters of a name
+ * inside a file with ASN1 definitions.
+ */
+#define ASN1_MAX_NAME_SIZE 64
+
+
+/**
+ * asn1_static_node:
+ * @name: Node name
+ * @type: Node typ
+ * @value: Node value
+ *
+ * For the on-disk format of ASN.1 trees, created by asn1_parser2array().
+ */
+struct asn1_static_node_st
+{
+  const char *name;		/* Node name */
+  unsigned int type;		/* Node type */
+  const void *value;		/* Node value */
+};
+typedef struct asn1_static_node_st asn1_static_node;
+
+/* List of constants for field type of node_asn  */
+#define ASN1_ETYPE_INVALID        0
+#define ASN1_ETYPE_CONSTANT       1
+#define ASN1_ETYPE_IDENTIFIER     2
+#define ASN1_ETYPE_INTEGER        3
+#define ASN1_ETYPE_BOOLEAN        4
+#define ASN1_ETYPE_SEQUENCE       5
+#define ASN1_ETYPE_BIT_STRING     6
+#define ASN1_ETYPE_OCTET_STRING   7
+#define ASN1_ETYPE_TAG            8
+#define ASN1_ETYPE_DEFAULT        9
+#define ASN1_ETYPE_SIZE          10
+#define ASN1_ETYPE_SEQUENCE_OF   11
+#define ASN1_ETYPE_OBJECT_ID     12
+#define ASN1_ETYPE_ANY           13
+#define ASN1_ETYPE_SET           14
+#define ASN1_ETYPE_SET_OF        15
+#define ASN1_ETYPE_DEFINITIONS   16
+#define ASN1_ETYPE_CHOICE        18
+#define ASN1_ETYPE_IMPORTS       19
+#define ASN1_ETYPE_NULL          20
+#define ASN1_ETYPE_ENUMERATED    21
+#define ASN1_ETYPE_GENERALSTRING 27
+#define ASN1_ETYPE_NUMERIC_STRING 28
+#define ASN1_ETYPE_IA5_STRING     29
+#define ASN1_ETYPE_TELETEX_STRING 30
+#define ASN1_ETYPE_PRINTABLE_STRING 31
+#define ASN1_ETYPE_UNIVERSAL_STRING 32
+#define ASN1_ETYPE_BMP_STRING     33
+#define ASN1_ETYPE_UTF8_STRING    34
+#define ASN1_ETYPE_VISIBLE_STRING 35
+#define ASN1_ETYPE_UTC_TIME       36
+#define ASN1_ETYPE_GENERALIZED_TIME 37
+
+/**
+ * ASN1_DELETE_FLAG_ZEROIZE:
+ *
+ * Used by: asn1_delete_structure2()
+ *
+ * Zeroize values prior to deinitialization.
+ */
+#define ASN1_DELETE_FLAG_ZEROIZE 1
+
+/**
+ * ASN1_DECODE_FLAG_ALLOW_PADDING:
+ *
+ * Used by: asn1_der_decoding2()
+ *
+ * This flag would allow arbitrary data past the DER data.
+ */
+#define ASN1_DECODE_FLAG_ALLOW_PADDING 1
+/**
+ * ASN1_DECODE_FLAG_STRICT_DER:
+ *
+ * Used by: asn1_der_decoding2()
+ *
+ * This flag would ensure that no BER decoding takes place.
+ */
+#define ASN1_DECODE_FLAG_STRICT_DER (1<<1)
+/**
+ * ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME:
+ *
+ * Used by: asn1_der_decoding2()
+ *
+ * This flag will tolerate Time encoding errors when in strict DER.
+ */
+#define ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME (1<<2)
+
+
+/**
+ * asn1_data_node_st:
+ * @name: Node name
+ * @value: Node value
+ * @value_len: Node value size
+ * @type: Node value type (ASN1_ETYPE_*)
+ *
+ * Data node inside a #asn1_node structure.
+ */
+struct asn1_data_node_st
+{
+  const char *name;		/* Node name */
+  const void *value;		/* Node value */
+  unsigned int value_len;	/* Node value size */
+  unsigned int type;		/* Node value type (ASN1_ETYPE_*) */
+};
+typedef struct asn1_data_node_st asn1_data_node_st;
+
+/***********************************/
+/*  Fixed constants                */
+/***********************************/
+
+/**
+ * ASN1_MAX_ERROR_DESCRIPTION_SIZE:
+ *
+ * Maximum number of characters
+ * of a description message
+ * (null character included).
+ */
+#define ASN1_MAX_ERROR_DESCRIPTION_SIZE 128
+
+/***********************************/
+/*  Functions definitions          */
+/***********************************/
+
+extern ASN1_API int
+  asn1_parser2tree (const char *file,
+		      asn1_node * definitions, char *error_desc);
+
+extern ASN1_API int
+  asn1_parser2array (const char *inputFileName,
+		       const char *outputFileName,
+		       const char *vectorName, char *error_desc);
+
+extern ASN1_API int
+  asn1_array2tree (const asn1_static_node * array,
+		     asn1_node * definitions, char *errorDescription);
+
+extern ASN1_API void
+  asn1_print_structure (FILE * out, asn1_node_const structure,
+			  const char *name, int mode);
+
+extern ASN1_API int
+  asn1_create_element (asn1_node_const definitions,
+			 const char *source_name, asn1_node * element);
+
+extern ASN1_API int asn1_delete_structure (asn1_node * structure);
+
+extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, unsigned int flags);
+
+extern ASN1_API int
+  asn1_delete_element (asn1_node structure, const char *element_name);
+
+extern ASN1_API int
+  asn1_write_value (asn1_node node_root, const char *name,
+		      const void *ivalue, int len);
+
+extern ASN1_API int
+  asn1_read_value (asn1_node_const root, const char *name,
+		     void *ivalue, int *len);
+
+extern ASN1_API int
+  asn1_read_value_type (asn1_node_const root, const char *name,
+			  void *ivalue, int *len, unsigned int *etype);
+
+extern ASN1_API int
+  asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data);
+
+extern ASN1_API int
+  asn1_number_of_elements (asn1_node_const element, const char *name, int *num);
+
+extern ASN1_API int
+  asn1_der_coding (asn1_node_const element, const char *name,
+		     void *ider, int *len, char *ErrorDescription);
+
+extern ASN1_API int
+  asn1_der_decoding2 (asn1_node *element, const void *ider,
+			int *max_ider_len, unsigned int flags,
+			char *errorDescription);
+
+extern ASN1_API int
+  asn1_der_decoding (asn1_node * element, const void *ider,
+		       int ider_len, char *errorDescription);
+
+/* Do not use. Use asn1_der_decoding() instead. */
+extern ASN1_API int
+  asn1_der_decoding_element (asn1_node * structure,
+			       const char *elementName,
+			       const void *ider, int len,
+			       char *errorDescription) _ASN1_GCC_ATTR_DEPRECATED;
+
+extern ASN1_API int
+  asn1_der_decoding_startEnd (asn1_node element,
+				const void *ider, int ider_len,
+				const char *name_element,
+				int *start, int *end);
+
+extern ASN1_API int
+  asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element);
+
+extern ASN1_API int
+  asn1_expand_octet_string (asn1_node_const definitions,
+			      asn1_node * element,
+			      const char *octetName, const char *objectName);
+
+extern ASN1_API int
+  asn1_read_tag (asn1_node_const root, const char *name,
+		   int *tagValue, int *classValue);
+
+extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const
+							    definitions,
+							    const char
+							    *oidValue);
+
+__LIBTASN1_PURE__
+extern ASN1_API const char *asn1_check_version (const char *req_version);
+
+__LIBTASN1_PURE__
+extern ASN1_API const char *asn1_strerror (int error);
+
+extern ASN1_API void asn1_perror (int error);
+
+#define ASN1_MAX_TAG_SIZE 4
+#define ASN1_MAX_LENGTH_SIZE 9
+#define ASN1_MAX_TL_SIZE (ASN1_MAX_TAG_SIZE+ASN1_MAX_LENGTH_SIZE)
+extern ASN1_API long
+  asn1_get_length_der (const unsigned char *der, int der_len, int *len);
+
+extern ASN1_API long
+  asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len);
+
+extern ASN1_API void
+  asn1_length_der (unsigned long int len, unsigned char *der, int *der_len);
+
+/* Other utility functions. */
+
+extern ASN1_API
+  int asn1_decode_simple_der (unsigned int etype, const unsigned char *der,
+				unsigned int _der_len,
+				const unsigned char **str,
+				unsigned int *str_len);
+
+extern ASN1_API
+  int asn1_decode_simple_ber (unsigned int etype, const unsigned char *der,
+				unsigned int _der_len,
+				unsigned char **str,
+				unsigned int *str_len,
+				unsigned int *ber_len);
+
+extern ASN1_API int
+  asn1_encode_simple_der (unsigned int etype, const unsigned char *str,
+			    unsigned int str_len, unsigned char *tl,
+			    unsigned int *tl_len);
+
+extern ASN1_API asn1_node
+  asn1_find_node (asn1_node_const pointer, const char *name);
+
+extern ASN1_API int
+  asn1_copy_node (asn1_node dst, const char *dst_name,
+		    asn1_node_const src, const char *src_name);
+extern ASN1_API asn1_node
+  asn1_dup_node (asn1_node_const src, const char *src_name);
+
+/* Internal and low-level DER utility functions. */
+
+extern ASN1_API int
+  asn1_get_tag_der (const unsigned char *der, int der_len,
+		      unsigned char *cls, int *len, unsigned long *tag);
+
+extern ASN1_API void
+  asn1_octet_der (const unsigned char *str, int str_len,
+		    unsigned char *der, int *der_len);
+
+extern ASN1_API int
+  asn1_get_octet_der (const unsigned char *der, int der_len,
+			int *ret_len, unsigned char *str,
+			int str_size, int *str_len);
+
+extern ASN1_API void asn1_bit_der (const unsigned char *str, int bit_len,
+				     unsigned char *der, int *der_len);
+
+extern ASN1_API int
+  asn1_get_bit_der (const unsigned char *der, int der_len,
+		      int *ret_len, unsigned char *str,
+		      int str_size, int *bit_len);
+
+extern ASN1_API int
+  asn1_get_object_id_der (const unsigned char *der,
+                          int der_len, int *ret_len,
+                          char *str, int str_size);
+
+extern ASN1_API int
+  asn1_object_id_der (const char *str, unsigned char *der, int *der_len,
+                      unsigned flags);
+
+/* Compatibility types */
+
+/**
+ * asn1_retCode:
+ *
+ * Type formerly returned by libtasn1 functions.
+ *
+ * Deprecated: 3.0: Use int instead.
+ */
+typedef int asn1_retCode;
+
+/**
+ * node_asn_struct:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use #asn1_node instead.
+ */
+#define node_asn_struct asn1_node_st
+
+/**
+ * node_asn:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use #asn1_node instead.
+ */
+#define node_asn asn1_node_st
+
+/**
+ * ASN1_TYPE:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use #asn1_node instead.
+ */
+#define ASN1_TYPE asn1_node
+
+/**
+ * ASN1_TYPE_EMPTY:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use NULL instead.
+ */
+#define ASN1_TYPE_EMPTY NULL
+
+/**
+ * static_struct_asn:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use #asn1_static_node instead.
+ */
+#define static_struct_asn asn1_static_node_st
+
+/**
+ * ASN1_ARRAY_TYPE:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use #asn1_static_node instead.
+ */
+#define ASN1_ARRAY_TYPE asn1_static_node
+
+/**
+ * asn1_static_node_t:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use #asn1_static_node instead.
+ */
+#define asn1_static_node_t asn1_static_node
+
+/**
+ * node_data_struct:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use #asn1_data_node_st instead.
+ */
+#define node_data_struct asn1_data_node_st
+
+/**
+ * ASN1_DATA_NODE:
+ *
+ * Compat #define.
+ *
+ * Deprecated: 3.0: Use #asn1_data_node_st instead.
+ */
+#define ASN1_DATA_NODE asn1_data_node_st
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif				/* LIBTASN1_H */
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 12/22] libtasn1: disable code not needed in grub
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (10 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 11/22] libtasn1: import libtasn1-4.16.0 Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-20 21:47   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 13/22] libtasn1: changes for grub compatibility Daniel Axtens
                   ` (9 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

We don't expect to be able to write ASN.1, only read it,
so we can disable some code.

Do that with #if 0/#endif, rather than deletion. This means
that the difference between upstream and grub is smaller,
which should make updating libtasn1 easier in the future.

With these exclusions we also avoid the need for minmax.h,
which is convenient because it means we don't have to
import it from gnulib.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/lib/libtasn1/lib/coding.c    | 12 ++++++++++--
 grub-core/lib/libtasn1/lib/decoding.c  |  2 ++
 grub-core/lib/libtasn1/lib/element.c   |  4 ++--
 grub-core/lib/libtasn1/lib/errors.c    |  3 +++
 grub-core/lib/libtasn1/lib/structure.c | 10 ++++++----
 include/grub/libtasn1.h                | 15 +++++++++++++++
 6 files changed, 38 insertions(+), 8 deletions(-)

diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c
index 245ea64cf0aa..52def5983685 100644
--- a/grub-core/lib/libtasn1/lib/coding.c
+++ b/grub-core/lib/libtasn1/lib/coding.c
@@ -30,11 +30,11 @@
 #include "parser_aux.h"
 #include <gstr.h>
 #include "element.h"
-#include "minmax.h"
 #include <structure.h>
 
 #define MAX_TAG_LEN 16
 
+#if 0
 /******************************************************/
 /* Function : _asn1_error_description_value_not_found */
 /* Description: creates the ErrorDescription string   */
@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
   Estrcat (ErrorDescription, "' not found");
 
 }
+#endif
 
 /**
  * asn1_length_der:
@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned char *str,
   return ASN1_SUCCESS;
 }
 
+#if 0
 /******************************************************/
 /* Function : _asn1_time_der                          */
 /* Description: creates the DER coding for a TIME     */
@@ -281,7 +283,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned char *der,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 /*
 void
@@ -520,6 +522,7 @@ asn1_bit_der (const unsigned char *str, int bit_len,
 }
 
 
+#if 0
 /******************************************************/
 /* Function : _asn1_complete_explicit_tag             */
 /* Description: add the length coding to the EXPLICIT */
@@ -596,6 +599,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char *der,
 
   return ASN1_SUCCESS;
 }
+#endif
 
 const tag_and_class_st _asn1_tags[] = {
   [ASN1_ETYPE_GENERALSTRING] =
@@ -648,6 +652,8 @@ const tag_and_class_st _asn1_tags[] = {
 
 unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
 
+
+#if 0
 /******************************************************/
 /* Function : _asn1_insert_tag_der                    */
 /* Description: creates the DER coding of tags of one */
@@ -1413,3 +1419,5 @@ error:
   asn1_delete_structure (&node);
   return err;
 }
+
+#endif
\ No newline at end of file
diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c
index ff04eb778cba..42f9a92b5d44 100644
--- a/grub-core/lib/libtasn1/lib/decoding.c
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -1613,6 +1613,7 @@ asn1_der_decoding (asn1_node * element, const void *ider, int ider_len,
   return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription);
 }
 
+#if 0
 /**
  * asn1_der_decoding_element:
  * @structure: pointer to an ASN1 structure
@@ -1643,6 +1644,7 @@ asn1_der_decoding_element (asn1_node * structure, const char *elementName,
 {
   return asn1_der_decoding(structure, ider, len, errorDescription);
 }
+#endif
 
 /**
  * asn1_der_decoding_startEnd:
diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c
index 997eb2725dca..539008d8e949 100644
--- a/grub-core/lib/libtasn1/lib/element.c
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -191,7 +191,7 @@ _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache)
   return ASN1_SUCCESS;
 }
 
-
+#if 0
 /**
  * asn1_write_value:
  * @node_root: pointer to a structure
@@ -645,7 +645,7 @@ asn1_write_value (asn1_node node_root, const char *name,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 #define PUT_VALUE( ptr, ptr_size, data, data_size) \
 	*len = data_size; \
diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c
index cee74daf7959..42785e8622b7 100644
--- a/grub-core/lib/libtasn1/lib/errors.c
+++ b/grub-core/lib/libtasn1/lib/errors.c
@@ -57,6 +57,8 @@ static const libtasn1_error_entry error_algorithms[] = {
   {0, 0}
 };
 
+
+#if 0
 /**
  * asn1_perror:
  * @error: is an error returned by a libtasn1 function.
@@ -73,6 +75,7 @@ asn1_perror (int error)
   const char *str = asn1_strerror (error);
   fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)");
 }
+#endif
 
 /**
  * asn1_strerror:
diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c
index 8189c56a4c93..fcfde01a3930 100644
--- a/grub-core/lib/libtasn1/lib/structure.c
+++ b/grub-core/lib/libtasn1/lib/structure.c
@@ -76,7 +76,7 @@ _asn1_find_left (asn1_node_const node)
   return node->left;
 }
 
-
+#if 0
 int
 _asn1_create_static_structure (asn1_node_const pointer, char *output_file_name,
 			       char *vector_name)
@@ -155,7 +155,7 @@ _asn1_create_static_structure (asn1_node_const pointer, char *output_file_name,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 /**
  * asn1_array2tree:
@@ -718,7 +718,7 @@ asn1_create_element (asn1_node_const definitions, const char *source_name,
   return res;
 }
 
-
+#if 0
 /**
  * asn1_print_structure:
  * @out: pointer to the output file (e.g. stdout).
@@ -1058,7 +1058,7 @@ asn1_print_structure (FILE * out, asn1_node_const structure, const char *name,
 	}
     }
 }
-
+#endif
 
 
 /**
@@ -1153,6 +1153,7 @@ asn1_find_structure_from_oid (asn1_node_const definitions, const char *oidValue)
   return NULL;			/* ASN1_ELEMENT_NOT_FOUND; */
 }
 
+#if 0
 /**
  * asn1_copy_node:
  * @dst: Destination asn1 node.
@@ -1202,6 +1203,7 @@ asn1_copy_node (asn1_node dst, const char *dst_name,
 
   return result;
 }
+#endif
 
 /**
  * asn1_dup_node:
diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
index 6fd7a30dc35c..785eda2ae3f8 100644
--- a/include/grub/libtasn1.h
+++ b/include/grub/libtasn1.h
@@ -319,6 +319,8 @@ typedef struct asn1_data_node_st asn1_data_node_st;
 /*  Functions definitions          */
 /***********************************/
 
+/* These functions are not used in grub and should not be referenced. */
+#if 0
 extern ASN1_API int
   asn1_parser2tree (const char *file,
 		      asn1_node * definitions, char *error_desc);
@@ -327,14 +329,17 @@ extern ASN1_API int
   asn1_parser2array (const char *inputFileName,
 		       const char *outputFileName,
 		       const char *vectorName, char *error_desc);
+#endif
 
 extern ASN1_API int
   asn1_array2tree (const asn1_static_node * array,
 		     asn1_node * definitions, char *errorDescription);
 
+#if 0
 extern ASN1_API void
   asn1_print_structure (FILE * out, asn1_node_const structure,
 			  const char *name, int mode);
+#endif
 
 extern ASN1_API int
   asn1_create_element (asn1_node_const definitions,
@@ -347,9 +352,11 @@ extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, unsigned int
 extern ASN1_API int
   asn1_delete_element (asn1_node structure, const char *element_name);
 
+#if 0
 extern ASN1_API int
   asn1_write_value (asn1_node node_root, const char *name,
 		      const void *ivalue, int len);
+#endif
 
 extern ASN1_API int
   asn1_read_value (asn1_node_const root, const char *name,
@@ -365,9 +372,11 @@ extern ASN1_API int
 extern ASN1_API int
   asn1_number_of_elements (asn1_node_const element, const char *name, int *num);
 
+#if 0
 extern ASN1_API int
   asn1_der_coding (asn1_node_const element, const char *name,
 		     void *ider, int *len, char *ErrorDescription);
+#endif
 
 extern ASN1_API int
   asn1_der_decoding2 (asn1_node *element, const void *ider,
@@ -378,12 +387,14 @@ extern ASN1_API int
   asn1_der_decoding (asn1_node * element, const void *ider,
 		       int ider_len, char *errorDescription);
 
+#if 0
 /* Do not use. Use asn1_der_decoding() instead. */
 extern ASN1_API int
   asn1_der_decoding_element (asn1_node * structure,
 			       const char *elementName,
 			       const void *ider, int len,
 			       char *errorDescription) _ASN1_GCC_ATTR_DEPRECATED;
+#endif
 
 extern ASN1_API int
   asn1_der_decoding_startEnd (asn1_node element,
@@ -408,13 +419,17 @@ extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const
 							    const char
 							    *oidValue);
 
+#if 0
 __LIBTASN1_PURE__
 extern ASN1_API const char *asn1_check_version (const char *req_version);
+#endif
 
 __LIBTASN1_PURE__
 extern ASN1_API const char *asn1_strerror (int error);
 
+#if 0
 extern ASN1_API void asn1_perror (int error);
+#endif
 
 #define ASN1_MAX_TAG_SIZE 4
 #define ASN1_MAX_LENGTH_SIZE 9
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 13/22] libtasn1: changes for grub compatibility
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (11 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 12/22] libtasn1: disable code not needed in grub Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 13:04   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 14/22] libtasn1: compile into asn1 module Daniel Axtens
                   ` (8 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

Do a few things to make libtasn1 compile as part of grub:

 - redefine _asn1_strcat. grub removed strcat so replace it with the
   appropriate calls to memcpy and strlen. Use this internally where
   strcat was used.

 - replace c_isdigit with grub_isdigit (and don't import c-ctype from
   gnulib) grub_isdigit provides the same functionality as c_isdigit: it
   determines if the input is an ASCII digit without regard for locale.

 - replace GL_ATTRIBUTE_PURE with __attribute__((pure)) which been
   supported since gcc-2.96. This avoids messing around with gnulib.

 - adjust libtasn1.h: drop the ASN1_API logic, it's not needed for our
   modules. Unconditionally support const and pure attributes and adjust
   header paths.

 - adjust header paths to "grub/libtasn1.h".

 - replace a 64 bit division with a call to grub_divmod64, preventing
   creation of __udivdi3 calls on 32 bit platforms.

Signed-off-by: Daniel Axtens <dja@axtens.net>

---

v2: Clean up strcat handling, thanks Stefan Berger.
---
 grub-core/lib/libtasn1/lib/decoding.c   | 11 ++++++-----
 grub-core/lib/libtasn1/lib/element.c    |  3 ++-
 grub-core/lib/libtasn1/lib/gstr.c       |  4 ++--
 grub-core/lib/libtasn1/lib/int.h        |  4 ++--
 grub-core/lib/libtasn1/lib/parser_aux.c |  7 ++++---
 include/grub/libtasn1.h                 | 26 ++++++-------------------
 6 files changed, 22 insertions(+), 33 deletions(-)

diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c
index 42f9a92b5d44..3406e1832746 100644
--- a/grub-core/lib/libtasn1/lib/decoding.c
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -32,7 +32,8 @@
 #include <element.h>
 #include <limits.h>
 #include <intprops.h>
-#include <c-ctype.h>
+
+#define c_isdigit grub_isdigit
 
 #ifdef DEBUG
 # define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__)
@@ -2008,8 +2009,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
 	  (p2->type & CONST_ASSIGN))
 	{
 	  strcpy (name, definitions->name);
-	  strcat (name, ".");
-	  strcat (name, p2->name);
+	  _asn1_strcat (name, ".");
+	  _asn1_strcat (name, p2->name);
 
 	  len = sizeof (value);
 	  result = asn1_read_value (definitions, name, value, &len);
@@ -2026,8 +2027,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
 	      if (p2)
 		{
 		  strcpy (name, definitions->name);
-		  strcat (name, ".");
-		  strcat (name, p2->name);
+		  _asn1_strcat (name, ".");
+		  _asn1_strcat (name, p2->name);
 
 		  result = asn1_create_element (definitions, name, &aux);
 		  if (result == ASN1_SUCCESS)
diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c
index 539008d8e949..ed761ff56bd9 100644
--- a/grub-core/lib/libtasn1/lib/element.c
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -30,9 +30,10 @@
 #include "parser_aux.h"
 #include <gstr.h>
 #include "structure.h"
-#include "c-ctype.h"
 #include "element.h"
 
+#define c_isdigit grub_isdigit
+
 void
 _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size)
 {
diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c
index e91a3a151c0d..a092c9a5a24b 100644
--- a/grub-core/lib/libtasn1/lib/gstr.c
+++ b/grub-core/lib/libtasn1/lib/gstr.c
@@ -36,13 +36,13 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src)
 
   if (dest_tot_size - dest_size > str_size)
     {
-      strcat (dest, src);
+      _asn1_strcat (dest, src);
     }
   else
     {
       if (dest_tot_size - dest_size > 0)
 	{
-	  strncat (dest, src, (dest_tot_size - dest_size) - 1);
+	  memcpy (dest + dest_size, src, (dest_tot_size - dest_size) - 1);
 	  dest[dest_tot_size - 1] = 0;
 	}
     }
diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
index ea1625786c1b..4a568efee9c1 100644
--- a/grub-core/lib/libtasn1/lib/int.h
+++ b/grub-core/lib/libtasn1/lib/int.h
@@ -35,7 +35,7 @@
 #include <sys/types.h>
 #endif
 
-#include <libtasn1.h>
+#include "grub/libtasn1.h"
 
 #define ASN1_SMALL_VALUE_SIZE 16
 
@@ -115,7 +115,7 @@ extern const tag_and_class_st _asn1_tags[];
 #define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
 #define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
 #define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
-#define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
+#define _asn1_strcat(a,b) memcpy((char *)a + strlen((const char *)a), (const char *)b, strlen((const char *)b) + 1)
 
 #if SIZEOF_UNSIGNED_LONG_INT == 8
 # define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c
index d5dbbf8765da..89c9be69dc2a 100644
--- a/grub-core/lib/libtasn1/lib/parser_aux.c
+++ b/grub-core/lib/libtasn1/lib/parser_aux.c
@@ -26,7 +26,8 @@
 #include "gstr.h"
 #include "structure.h"
 #include "element.h"
-#include "c-ctype.h"
+
+#define c_isdigit grub_isdigit
 
 char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not found */
 
@@ -40,7 +41,7 @@ char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not fou
 #ifdef __clang__
 __attribute__((no_sanitize("integer")))
 #endif
-_GL_ATTRIBUTE_PURE
+__attribute__((__pure__))
 static unsigned int
 _asn1_hash_name (const char *x)
 {
@@ -634,7 +635,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
   count = 0;
   do
     {
-      d = val / 10;
+      d = grub_divmod64(val, 10, NULL);
       r = val - d * 10;
       temp[start + count] = '0' + (char) r;
       count++;
diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
index 785eda2ae3f8..28dbf16c4e0c 100644
--- a/include/grub/libtasn1.h
+++ b/include/grub/libtasn1.h
@@ -38,29 +38,15 @@
 #ifndef LIBTASN1_H
 #define LIBTASN1_H
 
-#ifndef ASN1_API
-#if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY
-#define ASN1_API __attribute__((__visibility__("default")))
-#elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC
-#define ASN1_API __declspec(dllexport)
-#elif defined _MSC_VER && ! defined ASN1_STATIC
-#define ASN1_API __declspec(dllimport)
-#else
+/* grub: ASN1_API is not used */
 #define ASN1_API
-#endif
-#endif
 
-#ifdef __GNUC__
-# define __LIBTASN1_CONST__  __attribute__((const))
-# define __LIBTASN1_PURE__  __attribute__((pure))
-#else
-# define __LIBTASN1_CONST__
-# define __LIBTASN1_PURE__
-#endif
+/* grub: all our supported compilers support these attributes */
+#define __LIBTASN1_CONST__  __attribute__((const))
+#define __LIBTASN1_PURE__  __attribute__((pure))
 
-#include <sys/types.h>
-#include <time.h>
-#include <stdio.h>		/* for FILE* */
+#include <grub/types.h>
+#include <grub/time.h>
 
 #ifdef __cplusplus
 extern "C"
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 14/22] libtasn1: compile into asn1 module
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (12 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 13/22] libtasn1: changes for grub compatibility Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 13:05   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 15/22] test_asn1: test module for libtasn1 Daniel Axtens
                   ` (7 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

Create a wrapper file that specifies the module license.
Set up the makefile so it is built.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/Makefile.core.def        | 15 +++++++++++++++
 grub-core/lib/libtasn1_wrap/wrap.c | 26 ++++++++++++++++++++++++++
 2 files changed, 41 insertions(+)
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 915287d44c13..6fbae150c687 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2535,3 +2535,18 @@ module = {
   common = commands/i386/wrmsr.c;
   enable = x86;
 };
+
+module = {
+  name = asn1;
+  common = lib/libtasn1/lib/decoding.c;
+  common = lib/libtasn1/lib/coding.c;
+  common = lib/libtasn1/lib/element.c;
+  common = lib/libtasn1/lib/structure.c;
+  common = lib/libtasn1/lib/parser_aux.c;
+  common = lib/libtasn1/lib/gstr.c;
+  common = lib/libtasn1/lib/errors.c;
+  common = lib/libtasn1_wrap/wrap.c;
+  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+  // -Wno-type-limits comes from libtasn1's configure.ac
+  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
+};
diff --git a/grub-core/lib/libtasn1_wrap/wrap.c b/grub-core/lib/libtasn1_wrap/wrap.c
new file mode 100644
index 000000000000..622ba942e33f
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/wrap.c
@@ -0,0 +1,26 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 IBM Corporation
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+
+/*
+ * libtasn1 is provided under LGPL2.1+, which is compatible
+ * with GPL3+. As Grub as a whole is under GPL3+, this module
+ * is therefore under GPL3+ also.
+ */
+GRUB_MOD_LICENSE ("GPLv3+");
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 15/22] test_asn1: test module for libtasn1
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (13 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 14/22] libtasn1: compile into asn1 module Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 19:35   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 16/22] grub-install: support embedding x509 certificates Daniel Axtens
                   ` (6 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

Import tests from libtasn1 that don't use functionality we don't
import. I have put them here rather than in the libtasn1 directory
because:

 -  They need much more significant changes to run in the grub
    context.

 -  I don't expect they will need to be changed when updating
    libtasn1: I expect the old tests will usually continue to pass on
    new versions.

This doesn't test the full decoder but that will be exercised in
test suites for coming patch sets.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 .gitignore                                    |   1 +
 Makefile.util.def                             |   6 +
 grub-core/Makefile.core.def                   |  13 ++
 .../tests/CVE-2018-1000654-1_asn1_tab.h       |  32 +++
 .../tests/CVE-2018-1000654-2_asn1_tab.h       |  36 +++
 .../libtasn1_wrap/tests/CVE-2018-1000654.c    |  61 +++++
 .../lib/libtasn1_wrap/tests/Test_overflow.c   | 138 ++++++++++++
 .../lib/libtasn1_wrap/tests/Test_simple.c     | 207 +++++++++++++++++
 .../lib/libtasn1_wrap/tests/Test_strings.c    | 150 +++++++++++++
 .../libtasn1_wrap/tests/object-id-decoding.c  | 116 ++++++++++
 .../libtasn1_wrap/tests/object-id-encoding.c  | 120 ++++++++++
 .../lib/libtasn1_wrap/tests/octet-string.c    | 211 ++++++++++++++++++
 .../lib/libtasn1_wrap/tests/reproducers.c     |  81 +++++++
 grub-core/lib/libtasn1_wrap/wrap_tests.c      |  75 +++++++
 grub-core/lib/libtasn1_wrap/wrap_tests.h      |  38 ++++
 tests/test_asn1.in                            |  12 +
 16 files changed, 1297 insertions(+)
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_simple.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_strings.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/octet-string.c
 create mode 100644 grub-core/lib/libtasn1_wrap/tests/reproducers.c
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.c
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.h
 create mode 100644 tests/test_asn1.in

diff --git a/.gitignore b/.gitignore
index f6a1bd051752..c8fbf274cf1f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -264,6 +264,7 @@ widthspec.bin
 /stamp-h1
 /syslinux_test
 /tar_test
+/test_asn1
 /test_sha512sum
 /test_unset
 /tests/syslinux/ubuntu10.04_grub.cfg
diff --git a/Makefile.util.def b/Makefile.util.def
index f8b356cc1fa4..3381711c2666 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1211,6 +1211,12 @@ script = {
   common = tests/syslinux_test.in;
 };
 
+script = {
+  testcase;
+  name = test_asn1;
+  common = tests/test_asn1.in;
+};
+
 program = {
   testcase;
   name = example_unit_test;
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 6fbae150c687..d63f216111ca 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2550,3 +2550,16 @@ module = {
   // -Wno-type-limits comes from libtasn1's configure.ac
   cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
 };
+
+module = {
+  name = test_asn1;
+  common = lib/libtasn1_wrap/tests/CVE-2018-1000654.c;
+  common = lib/libtasn1_wrap/tests/object-id-decoding.c;
+  common = lib/libtasn1_wrap/tests/object-id-encoding.c;
+  common = lib/libtasn1_wrap/tests/octet-string.c;
+  common = lib/libtasn1_wrap/tests/reproducers.c;
+  common = lib/libtasn1_wrap/tests/Test_overflow.c;
+  common = lib/libtasn1_wrap/tests/Test_simple.c;
+  common = lib/libtasn1_wrap/tests/Test_strings.c;
+  common = lib/libtasn1_wrap/wrap_tests.c;
+};
diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
new file mode 100644
index 000000000000..1e7d3d64f55d
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
@@ -0,0 +1,32 @@
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <grub/libtasn1.h>
+
+const asn1_static_node CVE_2018_1000654_1_asn1_tab[] = {
+  { "TEST_TREE", 536875024, NULL },
+  { NULL, 1610612748, NULL },
+  { "iso", 1073741825, "1"},
+  { "identified-organization", 1073741825, "3"},
+  { "dod", 1073741825, "6"},
+  { "internet", 1073741825, "1"},
+  { "security", 1073741825, "5"},
+  { "mechanisms", 1073741825, "5"},
+  { "pkix", 1073741825, "7"},
+  { "id-mod", 1073741825, "0"},
+  { "id-pkix1-implicit-88", 1, "2"},
+  { "id-xnyTest", 1879048204, NULL },
+  { NULL, 1073741825, "id-ix"},
+  { NULL, 1073741825, "29"},
+  { NULL, 1, "1"},
+  { "id-ix", 1880096780, "OBJECR"},
+  { NULL, 1073741825, "id-ix"},
+  { NULL, 1073741825, "29"},
+  { NULL, 1, "2"},
+  { "id-xnyTest", 805306380, NULL },
+  { NULL, 1073741825, "id-ix"},
+  { NULL, 1073741825, "29"},
+  { NULL, 1, "1"},
+  { NULL, 0, NULL }
+};
diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
new file mode 100644
index 000000000000..e2561e5ec6dc
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
@@ -0,0 +1,36 @@
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <grub/libtasn1.h>
+
+const asn1_static_node CVE_2018_1000654_2_asn1_tab[] = {
+  { "TEST_TREE", 536875024, NULL },
+  { NULL, 1610612748, NULL },
+  { "iso", 1073741825, "1"},
+  { "identified-organization", 1073741825, "3"},
+  { "dod", 1073741825, "6"},
+  { "internet", 1073741825, "1"},
+  { "security", 1073741825, "5"},
+  { "mechanisms", 1073741825, "5"},
+  { "pkix", 1073741825, "7"},
+  { "id-mod", 1073741825, "0"},
+  { "id-pkix1-implicit-88", 1, "2"},
+  { "id-oneTest", 1879048204, NULL },
+  { NULL, 1073741825, "id-two"},
+  { NULL, 1073741825, "9"},
+  { NULL, 1, "1"},
+  { "id-two", 1879048204, NULL },
+  { NULL, 1073741825, "id-three"},
+  { NULL, 1073741825, "2"},
+  { NULL, 1, "2"},
+  { "id-three", 1879048204, NULL },
+  { NULL, 1073741825, "id-four"},
+  { NULL, 1073741825, "3"},
+  { NULL, 1, "3"},
+  { "id-four", 805306380, NULL },
+  { NULL, 1073741825, "id-two"},
+  { NULL, 1073741825, "3"},
+  { NULL, 1, "3"},
+  { NULL, 0, NULL }
+};
diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
new file mode 100644
index 000000000000..534e304521e0
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2002-2018 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/****************************************************************/
+/* Description: reproducer for CVE-2018-1000654			*/
+/****************************************************************/
+
+#include <grub/libtasn1.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include "../wrap_tests.h"
+
+#include "CVE-2018-1000654-1_asn1_tab.h"
+#include "CVE-2018-1000654-2_asn1_tab.h"
+
+void
+test_CVE_2018_1000654 (void)
+{
+  int result;
+  asn1_node definitions = NULL;
+  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+  result = asn1_array2tree (CVE_2018_1000654_1_asn1_tab, &definitions, errorDescription);
+  if (result != ASN1_RECURSION)
+    {
+      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
+		  asn1_strerror (result), errorDescription);
+      return;
+    }
+
+  asn1_delete_structure (&definitions);
+
+  result = asn1_array2tree (CVE_2018_1000654_2_asn1_tab, &definitions, errorDescription);
+  if (result != ASN1_RECURSION)
+    {
+      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
+		  asn1_strerror (result), errorDescription);
+      return;
+    }
+
+  asn1_delete_structure (&definitions);
+}
diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
new file mode 100644
index 000000000000..f48aea0ef8b8
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/* Written by Simon Josefsson */
+
+#include <grub/libtasn1.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include "../wrap_tests.h"
+
+void
+test_overflow(void)
+{
+  /* Test that values larger than long are rejected.  This has worked
+     fine with all versions of libtasn1. */
+
+  {
+    unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
+    long l;
+    int len;
+
+    l = asn1_get_length_der (der, sizeof der, &len);
+
+    if (l != -2L)
+      {
+	grub_fatal ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len);
+	return;
+      }
+  }
+
+  /* Test that values larger than int but smaller than long are
+     rejected.  This limitation was introduced with libtasn1 2.12. */
+#if (GRUB_LONG_MAX > GRUB_INT_MAX)
+    {
+      unsigned long num = ((long) GRUB_UINT_MAX) << 2;
+      unsigned char der[20];
+      int der_len;
+      long l;
+      int len;
+
+      asn1_length_der (num, der, &der_len);
+
+      l = asn1_get_length_der (der, der_len, &len);
+
+      if (l != -2L)
+	{
+	  grub_fatal ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l,
+		      len);
+	  return;
+	}
+    }
+#endif
+
+  /* Test that values larger than would fit in the input string are
+     rejected.  This problem was fixed in libtasn1 2.12. */
+  {
+    unsigned long num = 64;
+    unsigned char der[20];
+    int der_len;
+    long l;
+    int len;
+
+    asn1_length_der (num, der, &der_len);
+
+    der_len = sizeof (der);
+    l = asn1_get_length_der (der, der_len, &len);
+
+    if (l != -4L)
+      {
+	grub_fatal ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n",
+		    l, len);
+	return;
+      }
+  }
+
+  /* Test that values larger than would fit in the input string are
+     rejected.  This problem was fixed in libtasn1 2.12. */
+  {
+    unsigned long num = 1073741824;
+    unsigned char der[20];
+    int der_len;
+    long l;
+    int len;
+
+    asn1_length_der (num, der, &der_len);
+
+    der_len = sizeof (der);
+    l = asn1_get_length_der (der, der_len, &len);
+
+    if (l != -4L)
+      {
+	grub_fatal ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n",
+		    l, len);
+	return;
+      }
+  }
+
+  /* Test that values larger than would fit in the input string are
+     rejected.  This problem was fixed in libtasn1 2.12. */
+  {
+    unsigned long num = 2147483649;
+    unsigned char der[20];
+    int der_len;
+    long l;
+    int len;
+
+    asn1_length_der (num, der, &der_len);
+
+    der_len = sizeof (der);
+    l = asn1_get_length_der (der, der_len, &len);
+
+    if (l != -2L)
+      {
+	grub_fatal ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n",
+		    l, len);
+	return;
+      }
+  }
+}
diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_simple.c b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c
new file mode 100644
index 000000000000..9f01006ddf47
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by Simon Josefsson
+ *
+ */
+
+#include <grub/libtasn1.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include "../wrap_tests.h"
+
+struct tv
+{
+  int bitlen;
+  const char *bitstr;
+  int derlen;
+  const char *der;
+};
+
+static const struct tv tv[] = {
+  {0, "", 2, "\x01\x00"},
+  {1, "\x00", 3, "\x02\x07\x00"},
+  {2, "\x00", 3, "\x02\x06\x00"},
+  {3, "\x00", 3, "\x02\x05\x00"},
+  {4, "\x00", 3, "\x02\x04\x00"},
+  {5, "\x00", 3, "\x02\x03\x00"},
+  {6, "\x00", 3, "\x02\x02\x00"},
+  {7, "\x00", 3, "\x02\x01\x00"},
+  {8, "\x00\x00", 3, "\x02\x00\x00"},
+  {9, "\x00\x00", 4, "\x03\x07\x00\x00"},
+  {10, "\x00\x00", 4, "\x03\x06\x00\x00"},
+  {11, "\x00\x00", 4, "\x03\x05\x00\x00"},
+  {12, "\x00\x00", 4, "\x03\x04\x00\x00"},
+  {13, "\x00\x00", 4, "\x03\x03\x00\x00"},
+  {14, "\x00\x00", 4, "\x03\x02\x00\x00"},
+  {15, "\x00\x00", 4, "\x03\x01\x00\x00"},
+  {16, "\x00\x00", 4, "\x03\x00\x00\x00"},
+  {17, "\x00\x00\x00", 5, "\x04\x07\x00\x00\x00"},
+  {18, "\x00\x00\x00", 5, "\x04\x06\x00\x00\x00"},
+  {19, "\x00\x00\x00", 5, "\x04\x05\x00\x00\x00"},
+  {1, "\xFF", 3, "\x02\x07\x80"},
+  {2, "\xFF", 3, "\x02\x06\xc0"},
+  {3, "\xFF", 3, "\x02\x05\xe0"},
+  {4, "\xFF", 3, "\x02\x04\xf0"},
+  {5, "\xFF", 3, "\x02\x03\xf8"},
+  {6, "\xFF", 3, "\x02\x02\xfc"},
+  {7, "\xFF", 3, "\x02\x01\xfe"},
+  {8, "\xFF\xFF", 3, "\x02\x00\xff"},
+  {9, "\xFF\xFF", 4, "\x03\x07\xff\x80"},
+  {10, "\xFF\xFF", 4, "\x03\x06\xff\xc0"},
+  {11, "\xFF\xFF", 4, "\x03\x05\xff\xe0"},
+  {12, "\xFF\xFF", 4, "\x03\x04\xff\xf0"},
+  {13, "\xFF\xFF", 4, "\x03\x03\xff\xf8"},
+  {14, "\xFF\xFF", 4, "\x03\x02\xff\xfc"},
+  {15, "\xFF\xFF", 4, "\x03\x01\xff\xfe"},
+  {16, "\xFF\xFF", 4, "\x03\x00\xff\xff"},
+  {17, "\xFF\xFF\xFF", 5, "\x04\x07\xff\xff\x80"},
+  {18, "\xFF\xFF\xFF", 5, "\x04\x06\xff\xff\xc0"},
+  {19, "\xFF\xFF\xFF", 5, "\x04\x05\xff\xff\xe0"},
+};
+
+void
+test_simple (void)
+{
+  int result;
+  unsigned char der[100];
+  unsigned char str[100];
+  int der_len = sizeof (der);
+  int str_size = sizeof (str);
+  int ret_len, bit_len;
+  grub_size_t i;
+
+  /* Dummy test */
+
+  asn1_bit_der (NULL, 0, der, &der_len);
+  result = asn1_get_bit_der (der, 0, &ret_len, str, str_size, &bit_len);
+  if (result != ASN1_GENERIC_ERROR)
+    {
+      grub_fatal ("asn1_get_bit_der zero\n");
+      return;
+    }
+
+  /* Encode short strings with increasing bit lengths */
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      /* Encode */
+
+      asn1_bit_der ((const unsigned char *) tv[i].bitstr, tv[i].bitlen,
+		    der, &der_len);
+
+#if 0
+      {
+	size_t j;
+	for (j = 0; j < der_len; j++)
+	  printf ("\\x%02x", der[j]);
+	printf ("\n");
+      }
+#endif
+
+      if (der_len != tv[i].derlen || grub_memcmp (der, tv[i].der, der_len) != 0)
+	{
+	  grub_fatal ("asn1_bit_der iter %lu\n", (unsigned long) i);
+	  return;
+	}
+
+      /* Decode it */
+
+      result = asn1_get_bit_der (der, der_len, &ret_len, str,
+				 str_size, &bit_len);
+      if (result != ASN1_SUCCESS || ret_len != tv[i].derlen
+	  || bit_len != tv[i].bitlen)
+	{
+	  grub_fatal ("asn1_get_bit_der iter %lu, err: %d\n", (unsigned long) i, result);
+	  return;
+	}
+    }
+
+
+  /* Decode sample from "A Layman's Guide to a Subset of ASN.1, BER,
+     and DER" section 5.4 "BIT STRING": "The BER encoding of the BIT
+     STRING value "011011100101110111" can be any of the following,
+     among others, depending on the choice of padding bits, the form
+     of length octets [...]".
+   */
+
+  /* 03 04 06 6e 5d c0  DER encoding */
+
+  grub_memcpy (der, "\x04\x06\x6e\x5d\xc0", 5);
+  der_len = 5;
+
+  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
+  if (result != ASN1_SUCCESS || ret_len != 5
+      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0)
+    {
+      grub_fatal ("asn1_get_bit_der example\n");
+      return;
+    }
+
+  der_len = sizeof (der);
+  asn1_bit_der (str, bit_len, der, &der_len);
+  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
+    {
+      grub_fatal ("asn1_bit_der example roundtrip\n");
+      return;
+    }
+
+  /* 03 04 06 6e 5d e0 padded with "100000" */
+
+  grub_memcpy (der, "\x04\x06\x6e\x5d\xe0", 5);
+  der_len = 5;
+
+  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
+  if (result != ASN1_SUCCESS || ret_len != 5
+      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xe0", 3) != 0)
+    {
+      grub_fatal ("asn1_get_bit_der example padded\n");
+      return;
+    }
+
+  der_len = sizeof (der);
+  asn1_bit_der (str, bit_len, der, &der_len);
+  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
+    {
+      grub_fatal ("asn1_bit_der example roundtrip\n");
+      return;
+    }
+
+  /* 03 81 04 06 6e 5d c0 long form of length octets */
+
+  grub_memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6);
+  der_len = 6;
+
+  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
+
+  if (result != ASN1_SUCCESS || ret_len != 6
+      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0)
+    {
+      grub_fatal ("asn1_get_bit_der example long form\n");
+      return;
+    }
+
+  der_len = sizeof (der);
+  asn1_bit_der (str, bit_len, der, &der_len);
+  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
+    {
+      grub_fatal ("asn1_bit_der example roundtrip\n");
+      return;
+    }
+}
diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_strings.c b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c
new file mode 100644
index 000000000000..dbe1474b204a
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by Simon Josefsson
+ *
+ */
+
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/libtasn1.h>
+#include "../wrap_tests.h"
+
+struct tv
+{
+  unsigned int etype;
+  unsigned int str_len;
+  const void *str;
+  unsigned int der_len;
+  const void *der;
+};
+
+static const struct tv tv[] = {
+  {ASN1_ETYPE_IA5_STRING, 20,
+   "\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72",
+   22,
+   "\x16\x14\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72"},
+  {ASN1_ETYPE_PRINTABLE_STRING, 5, "\x4e\x69\x6b\x6f\x73",
+   7, "\x13\x05\x4e\x69\x6b\x6f\x73"},
+  {ASN1_ETYPE_UTF8_STRING, 12, "Αττική",
+   14, "\x0c\x0c\xce\x91\xcf\x84\xcf\x84\xce\xb9\xce\xba\xce\xae"},
+  {ASN1_ETYPE_TELETEX_STRING, 15,
+   "\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e",
+   17,
+   "\x14\x0f\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e"},
+  {ASN1_ETYPE_OCTET_STRING, 36,
+   "\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A",
+   38,
+   "\x04\x24\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A"}
+};
+
+#define SSTR(x) sizeof(x)-1,x
+static const struct tv ber[] = {
+  {ASN1_ETYPE_OCTET_STRING,
+   SSTR("\xa0\xa0"),
+   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x00\x00")},
+  {ASN1_ETYPE_OCTET_STRING,
+   SSTR("\xa0\xa0\xb0\xb0\xb0"),
+   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x00\x00")},
+  {ASN1_ETYPE_OCTET_STRING,
+   SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1"),
+   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x00\x00\x00\x00")},
+  {ASN1_ETYPE_OCTET_STRING,
+   SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1\xc1"),
+   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x04\x82\x00\x01\xc1\x00\x00\x00\x00")},
+};
+
+void
+test_strings ()
+{
+  int ret;
+  unsigned char tl[ASN1_MAX_TL_SIZE];
+  unsigned int tl_len, der_len, str_len;
+  const unsigned char *str;
+  unsigned char *b;
+  unsigned int i;
+
+  /* Dummy test */
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      /* Encode */
+      tl_len = sizeof (tl);
+      ret = asn1_encode_simple_der (tv[i].etype, tv[i].str, tv[i].str_len,
+				    tl, &tl_len);
+      if (ret != ASN1_SUCCESS)
+	{
+	  grub_fatal ("Encoding error in %u: %s\n", i,
+		   asn1_strerror (ret));
+	  return;
+	}
+      der_len = tl_len + tv[i].str_len;
+
+      if (der_len != tv[i].der_len || grub_memcmp (tl, tv[i].der, tl_len) != 0)
+	{
+	  grub_fatal (
+		   "DER encoding differs in %u! (size: %u, expected: %u)\n",
+		   i, der_len, tv[i].der_len);
+	  return;
+	}
+
+      /* decoding */
+      ret =
+	asn1_decode_simple_der (tv[i].etype, tv[i].der, tv[i].der_len, &str,
+				&str_len);
+      if (ret != ASN1_SUCCESS)
+	{
+	  grub_fatal ("Decoding error in %u: %s\n", i,
+		   asn1_strerror (ret));
+	  return;
+	}
+
+      if (str_len != tv[i].str_len || grub_memcmp (str, tv[i].str, str_len) != 0)
+	{
+	  grub_fatal (
+		   "DER decoded data differ in %u! (size: %u, expected: %u)\n",
+		   i, der_len, tv[i].str_len);
+	  return;
+	}
+    }
+
+  /* BER decoding */
+  for (i = 0; i < sizeof (ber) / sizeof (ber[0]); i++)
+    {
+      /* decoding */
+      ret =
+	asn1_decode_simple_ber (ber[i].etype, ber[i].der, ber[i].der_len, &b,
+				&str_len, NULL);
+      if (ret != ASN1_SUCCESS)
+	{
+	  grub_fatal ("BER decoding error in %u: %s\n", i,
+		   asn1_strerror (ret));
+	  return;
+	}
+
+      if (str_len != ber[i].str_len || grub_memcmp (b, ber[i].str, str_len) != 0)
+	{
+	  grub_fatal (
+		   "BER decoded data differ in %u! (size: %u, expected: %u)\n",
+		   i, str_len, ber[i].str_len);
+	  return;
+	}
+      grub_free(b);
+    }
+}
diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
new file mode 100644
index 000000000000..d367bbfb5a7b
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <grub/libtasn1.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include "../wrap_tests.h"
+
+struct tv
+{
+  int der_len;
+  const unsigned char *der;
+  const char *oid;
+  int expected_error;
+};
+
+static const struct tv tv[] = {
+  {.der_len = 5,
+   .der = (void *) "\x06\x03\x80\x37\x03",
+   .oid = "2.999.3",
+   .expected_error = ASN1_DER_ERROR /* leading 0x80 */
+  },
+  {.der_len = 12,
+   .der = (void *) "\x06\x0a\x2b\x06\x01\x80\x01\x92\x08\x09\x05\x01",
+   .oid = "1.3.6.1.4.1.2312.9.5.1",
+   .expected_error = ASN1_DER_ERROR /* leading 0x80 */
+  },
+  {.der_len = 6,
+   .der = (void *) "\x06\x04\x01\x02\x03\x04",
+   .oid = "0.1.2.3.4",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 5,
+   .der = (void *) "\x06\x03\x51\x02\x03",
+   .oid = "2.1.2.3",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 5,
+   .der = (void *) "\x06\x03\x88\x37\x03",
+   .oid = "2.999.3",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 12,
+   .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01",
+   .oid = "1.3.6.1.4.1.2312.9.5.1",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 19,
+   .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d",
+   .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 19,
+   .der =
+   (void *)
+   "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07",
+   .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7",
+   .expected_error = ASN1_SUCCESS},
+};
+
+void
+test_object_id_decoding (void)
+{
+  char str[128];
+  int ret, ret_len;
+  grub_size_t i;
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      /* decode */
+      ret =
+	asn1_get_object_id_der (tv[i].der+1,
+				tv[i].der_len-1, &ret_len, str,
+				sizeof (str));
+      if (ret != tv[i].expected_error)
+	{
+	  grub_fatal (
+		   "%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n",
+		   __LINE__, (unsigned long) i, asn1_strerror(ret), tv[i].expected_error);
+	  return;
+	}
+
+      if (tv[i].expected_error != ASN1_SUCCESS)
+        continue;
+
+      if (ret_len != tv[i].der_len-1)
+	{
+	  grub_fatal (
+		   "%d: iter %lu: error in DER, length returned is %d, had %d\n",
+		   __LINE__, (unsigned long)i, ret_len, tv[i].der_len-1);
+	  return;
+	}
+
+      if (grub_strcmp (tv[i].oid, str) != 0)
+	{
+	  grub_fatal (
+		   "%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n",
+		   __LINE__, (unsigned long) i, str, tv[i].oid);
+	  return;
+	}
+
+    }
+}
diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
new file mode 100644
index 000000000000..3a83b58c59f4
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 Nikos Mavrogiannopoulos
+ *
+ * This file is part of LIBTASN1.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <grub/libtasn1.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include "../wrap_tests.h"
+
+struct tv
+{
+  int der_len;
+  const unsigned char *der;
+  const char *oid;
+  int expected_error;
+};
+
+static const struct tv tv[] = {
+  {.der_len = 0,
+   .der = (void *) "",
+   .oid = "5.999.3",
+   .expected_error = ASN1_VALUE_NOT_VALID /* cannot start with 5 */
+  },
+  {.der_len = 0,
+   .der = (void *) "",
+   .oid = "0.48.9",
+   .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 48 */
+  },
+  {.der_len = 0,
+   .der = (void *) "",
+   .oid = "1.40.9",
+   .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 40 */
+  },
+  {.der_len = 4,
+   .der = (void *) "\x06\x02\x4f\x63",
+   .oid = "1.39.99",
+   .expected_error = ASN1_SUCCESS,
+  },
+  {.der_len = 6,
+   .der = (void *) "\x06\x04\x01\x02\x03\x04",
+   .oid = "0.1.2.3.4",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 5,
+   .der = (void *) "\x06\x03\x51\x02\x03",
+   .oid = "2.1.2.3",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 5,
+   .der = (void *) "\x06\x03\x88\x37\x03",
+   .oid = "2.999.3",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 12,
+   .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01",
+   .oid = "1.3.6.1.4.1.2312.9.5.1",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 19,
+   .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d",
+   .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109",
+   .expected_error = ASN1_SUCCESS},
+  {.der_len = 19,
+   .der =
+   (void *)
+   "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07",
+   .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7",
+   .expected_error = ASN1_SUCCESS},
+};
+
+void
+test_object_id_encoding(void)
+{
+  unsigned char der[128];
+  int ret, der_len, i;
+
+  for (i = 0; i < (int)(sizeof (tv) / sizeof (tv[0])); i++)
+    {
+      der_len = sizeof(der);
+      ret = asn1_object_id_der(tv[i].oid, der, &der_len, 0);
+      if (ret != ASN1_SUCCESS)
+	{
+	  if (ret == tv[i].expected_error)
+	    continue;
+	  grub_fatal (
+		   "%d: iter %lu, encoding of OID failed: %s\n",
+		   __LINE__, (unsigned long) i, asn1_strerror(ret));
+	  return;
+	}
+      else if (ret != tv[i].expected_error)
+        {
+	  grub_fatal (
+		   "%d: iter %lu, encoding of OID %s succeeded when expecting failure\n",
+		   __LINE__, (unsigned long) i, tv[i].oid);
+          return;
+        }
+
+      if (der_len != tv[i].der_len || grub_memcmp(der, tv[i].der, der_len) != 0)
+	{
+	  grub_fatal (
+		   "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n",
+		   __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len);
+
+	  return;
+	}
+    }
+}
diff --git a/grub-core/lib/libtasn1_wrap/tests/octet-string.c b/grub-core/lib/libtasn1_wrap/tests/octet-string.c
new file mode 100644
index 000000000000..d8a049e8df06
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/octet-string.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2011-2020 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Written by Simon Josefsson and Nikos Mavrogiannopoulos
+ *
+ */
+
+#include <grub/libtasn1.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include "../wrap_tests.h"
+
+
+struct tv
+{
+  const char *name;
+  int der_len;
+  const unsigned char *der_str;
+  int len;
+  const unsigned char *string;
+  int expected_error;
+  int ber;
+};
+
+static const struct tv tv[] = {
+  {.name = "primitive octet strings",
+   .der_len = 10,
+   .der_str =
+   (void*)"\x04\x08\x01\x23\x45\x67\x89\xab\xcd\xef",
+   .len = 8,
+   .string =
+   (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef",
+   .ber = 0},
+  {.der_len = 22,
+   .der_str =
+   (void*)"\x04\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27",
+   .len = 20,
+   .string =
+   (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27"},
+
+  {.name = "long type of length",
+   .der_len = 23,
+   .der_str =
+   (void*)"\x04\x81\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27",
+   .len = 20,
+   .string =
+   (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27",
+   .ber = 1},
+  {.der_len = 11,
+   .der_str =
+   (void*)"\x04\x81\x08\x01\x23\x45\x67\x89\xab\xcd\xef",
+   .len = 8,
+   .string =
+   (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef",
+   .ber = 1},
+
+  {.name = "constructed - indefinite",
+   .der_len = 11,
+   .der_str = (void*)"\x24\x80\x04\x05\x01\x02\x03\x04\x05\x00\x00",
+   .len = 5,
+   .string = (void*)"\x01\x02\x03\x04\x05",
+   .ber = 1,
+   },
+
+  {.name = "constructed - definite - concat",
+   .der_len = 12,
+   .der_str = (void*)"\x24\x0a\x04\x04\x0a\x0b\x0c\x0d\x04\x02\x0e\x0f",
+   .len = 6,
+   .string = (void*)"\x0a\x0b\x0c\x0d\x0e\x0f",
+   .ber = 1,
+   },
+  {.name = "constructed - definite - recursive",
+   .der_len = 15,
+   .der_str = (void*)"\x24\x0d\x04\x04\x0a\x0b\x0c\x0d\x24\x05\x04\x00\x04\x01\x0f",
+   .len = 5,
+   .string = (void*)"\x0a\x0b\x0c\x0d\x0f",
+   .ber = 1,
+   },
+  {.name = "constructed - definite - single",
+   .der_len = 7,
+   .der_str = (void*)"\x24\x05\x04\x03\x01\x02\x03",
+   .len = 3,
+   .string = (void*)"\x01\x02\x03",
+   .ber = 1,
+   },
+
+  /* a large amount of recursive indefinite encoding */
+  {.name = "a large amount of recursive indefinite encoding",
+   .der_len = 29325,
+   .der_str = (void*)"\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80",
+   .len = 0,
+   .ber = 1,
+   .expected_error = ASN1_DER_ERROR
+   }
+};
+
+void
+test_octet_string (void)
+{
+  unsigned char str[100];
+  unsigned char der[100];
+  int der_len = sizeof (der);
+  int str_size = sizeof (str);
+  unsigned char *tmp = NULL;
+  int ret, ret_len;
+  grub_size_t i;
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      /* Decode */
+
+      if (tv[i].ber == 0)
+	{
+	  str_size = sizeof (str);
+	  ret =
+	    asn1_get_octet_der (tv[i].der_str + 1,
+				tv[i].der_len - 1, &ret_len, str,
+				sizeof (str), &str_size);
+	  if (ret != tv[i].expected_error)
+	    {
+	      grub_fatal (
+		       "%d: asn1_get_octet_der: %s: got %d expected %d\n",
+		       __LINE__, tv[i].name, ret,
+		       tv[i].expected_error);
+	      return;
+	    }
+	  if (tv[i].expected_error)
+	    continue;
+
+	  if (ret_len != tv[i].der_len - 1)
+	    {
+	      grub_fatal (
+		       "%d: error in DER, length returned is %d, had %d\n",
+		       __LINE__, ret_len, tv[i].der_len - 1);
+	      return;
+	    }
+
+	  if (str_size != tv[i].len
+	      || grub_memcmp (tv[i].string, str, tv[i].len) != 0)
+	    {
+	      grub_fatal (
+		       "%d: memcmp: %s: got invalid decoding\n",
+		       __LINE__, tv[i].name);
+
+              return;
+	    }
+
+	  /* Encode */
+	  der_len = sizeof (der);
+	  asn1_octet_der (str, str_size, der, &der_len);
+
+	  if (der_len != tv[i].der_len - 1
+	      || grub_memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0)
+	    {
+	      grub_fatal (
+		       "encoding: %s: got invalid encoding\n",
+		       tv[i].name);
+	      return;
+	    }
+	}
+
+      ret =
+	asn1_decode_simple_ber (ASN1_ETYPE_OCTET_STRING,
+				tv[i].der_str, tv[i].der_len,
+				&tmp, (unsigned int*)&str_size, (unsigned int*)&der_len);
+      if (ret != tv[i].expected_error)
+	{
+	  grub_fatal (
+		   "%d: asn1_decode_simple_ber: %s: got %s expected %s\n",
+		   __LINE__, tv[i].name, asn1_strerror(ret), asn1_strerror(tv[i].expected_error));
+	  return;
+	}
+      if (tv[i].expected_error)
+        continue;
+
+      if (der_len != tv[i].der_len)
+	{
+	  grub_fatal (
+		   "%d: error: %s: DER, length returned is %d, had %d\n",
+		   __LINE__, tv[i].name, der_len, tv[i].der_len);
+	  return;
+	}
+
+      if (str_size != tv[i].len || grub_memcmp (tv[i].string, tmp, tv[i].len) != 0)
+	{
+	  grub_fatal (
+		   "%d: memcmp: %s: got invalid decoding\n",
+		   __LINE__, tv[i].name);
+          return;
+	}
+      grub_free (tmp);
+      tmp = NULL;
+
+    }
+}
diff --git a/grub-core/lib/libtasn1_wrap/tests/reproducers.c b/grub-core/lib/libtasn1_wrap/tests/reproducers.c
new file mode 100644
index 000000000000..dc7268d4c6c6
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/tests/reproducers.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 Free Software Foundation, Inc.
+ *
+ * This file is part of LIBTASN1.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/****************************************************************/
+/* Description: run reproducers for several fixed issues        */
+/****************************************************************/
+
+#include <grub/libtasn1.h>
+#include <grub/err.h>
+#include <grub/mm.h>
+#include "../wrap_tests.h"
+
+#define CONST_DOWN        (1U<<29)
+
+/* produces endless loop (fixed by d4b624b2):
+ * The following translates into a single node with all pointers
+ * (right,left,down) set to NULL. */
+const asn1_static_node endless_asn1_tab[] = {
+  { "TEST_TREE", 536875024, NULL },
+  { NULL, 0, NULL }
+};
+
+/* produces memory leak (fixed by f16d1ff9):
+ * 152 bytes in 1 blocks are definitely lost in loss record 1 of 1
+ *    at 0x4837B65: calloc (vg_replace_malloc.c:762)
+ *    by 0x4851C0D: _asn1_add_static_node (parser_aux.c:71)
+ *    by 0x4853AAC: asn1_array2tree (structure.c:200)
+ *    by 0x10923B: main (single_node.c:67)
+ */
+const asn1_static_node tab[] = {
+{ "a", CONST_DOWN, "" },
+{ "b", 0, "" },
+{ "c", 0, "" },
+{ NULL, 0, NULL }
+};
+
+void
+test_reproducers (void)
+{
+  int result;
+  asn1_node definitions = NULL;
+  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
+
+  result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription);
+  if (result != ASN1_SUCCESS)
+    {
+      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
+		  asn1_strerror (result), errorDescription);
+      return;
+    }
+
+  asn1_delete_structure (&definitions);
+
+  definitions = NULL;
+  result = asn1_array2tree (tab, &definitions, errorDescription);
+  if (result != ASN1_SUCCESS)
+    {
+      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
+		  asn1_strerror (result), errorDescription);
+      return;
+    }
+
+  asn1_delete_structure (&definitions);
+}
diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.c b/grub-core/lib/libtasn1_wrap/wrap_tests.c
new file mode 100644
index 000000000000..75fcd21f0d5d
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/wrap_tests.c
@@ -0,0 +1,75 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 IBM Corporation
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/mm.h>
+#include "wrap_tests.h"
+
+/*
+ * libtasn1 tests - from which this is derived - are provided under GPL3+.
+ */
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_command_t cmd;
+
+static grub_err_t
+grub_cmd_asn1test (grub_command_t cmdd __attribute__((unused)),
+		   int argc __attribute__((unused)),
+		   char **args __attribute__((unused)))
+{
+  grub_printf ("test_CVE_2018_1000654\n");
+  test_CVE_2018_1000654 ();
+
+  grub_printf ("test_object_id_decoding\n");
+  test_object_id_decoding ();
+
+  grub_printf ("test_object_id_encoding\n");
+  test_object_id_encoding ();
+
+  grub_printf ("test_octet_string\n");
+  test_octet_string ();
+
+  grub_printf ("test_overflow\n");
+  test_overflow ();
+
+  grub_printf ("test_reproducers\n");
+  test_overflow ();
+
+  grub_printf ("test_simple\n");
+  test_simple ();
+
+  grub_printf ("test_strings\n");
+  test_strings ();
+
+  grub_printf ("ASN.1 self-tests passed\n");
+
+  return GRUB_ERR_NONE;
+}
+
+
+GRUB_MOD_INIT(test_asn1)
+{
+  cmd = grub_register_command ("test_asn1", grub_cmd_asn1test, NULL,
+			       "Run self-tests for the ASN.1 parser.");
+}
+
+GRUB_MOD_FINI(test_asn1)
+{
+  grub_unregister_command (cmd);
+}
diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.h b/grub-core/lib/libtasn1_wrap/wrap_tests.h
new file mode 100644
index 000000000000..555e56dd2022
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/wrap_tests.h
@@ -0,0 +1,38 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 IBM Corporation
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBTASN1_WRAP_TESTS_H
+#define LIBTASN1_WRAP_TESTS_H
+
+void test_CVE_2018_1000654 (void);
+
+void test_object_id_encoding (void);
+
+void test_object_id_decoding (void);
+
+void test_octet_string (void);
+
+void test_overflow (void);
+
+void test_reproducers (void);
+
+void test_simple (void);
+
+void test_strings (void);
+
+#endif
diff --git a/tests/test_asn1.in b/tests/test_asn1.in
new file mode 100644
index 000000000000..8173c5c270e5
--- /dev/null
+++ b/tests/test_asn1.in
@@ -0,0 +1,12 @@
+#! @BUILD_SHEBANG@
+set -e
+
+. "@builddir@/grub-core/modinfo.sh"
+
+out=`echo test_asn1 | @builddir@/grub-shell`
+
+if [ "$(echo "$out" | tail -n 1)" != "ASN.1 self-tests passed" ]; then
+  echo "ASN.1 test failure: $out"
+  exit 1
+fi
+
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 16/22] grub-install: support embedding x509 certificates
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (14 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 15/22] test_asn1: test module for libtasn1 Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-12 20:24   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 17/22] appended signatures: import GNUTLS's ASN.1 description files Daniel Axtens
                   ` (5 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

From: Alastair D'Silva <alastair@d-silva.org>

To support verification of appended signatures, we need a way to
embed the necessary public keys. Existing appended signature schemes
in the Linux kernel use X.509 certificates, so allow certificates to
be embedded in the grub core image in the same way as PGP keys.

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/commands/pgp.c    |  2 +-
 include/grub/kernel.h       |  3 ++-
 include/grub/util/install.h |  7 +++++--
 util/grub-install-common.c  | 22 +++++++++++++++++++-
 util/grub-mkimage.c         | 15 ++++++++++++--
 util/mkimage.c              | 41 ++++++++++++++++++++++++++++++++++---
 6 files changed, 80 insertions(+), 10 deletions(-)

diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
index 355a43844acc..b81ac0ae46ce 100644
--- a/grub-core/commands/pgp.c
+++ b/grub-core/commands/pgp.c
@@ -944,7 +944,7 @@ GRUB_MOD_INIT(pgp)
     grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
 
     /* Not an ELF module, skip.  */
-    if (header->type != OBJ_TYPE_PUBKEY)
+    if (header->type != OBJ_TYPE_GPG_PUBKEY)
       continue;
 
     pseudo_file.fs = &pseudo_fs;
diff --git a/include/grub/kernel.h b/include/grub/kernel.h
index abbca5ea3359..d3aafc8848d2 100644
--- a/include/grub/kernel.h
+++ b/include/grub/kernel.h
@@ -28,7 +28,8 @@ enum
   OBJ_TYPE_MEMDISK,
   OBJ_TYPE_CONFIG,
   OBJ_TYPE_PREFIX,
-  OBJ_TYPE_PUBKEY,
+  OBJ_TYPE_GPG_PUBKEY,
+  OBJ_TYPE_X509_PUBKEY,
   OBJ_TYPE_DTB,
   OBJ_TYPE_DISABLE_SHIM_LOCK
 };
diff --git a/include/grub/util/install.h b/include/grub/util/install.h
index cf4531e02b66..51f3b13ac130 100644
--- a/include/grub/util/install.h
+++ b/include/grub/util/install.h
@@ -67,6 +67,8 @@
       N_("SBAT metadata"), 0 },						\
   { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
       N_("disable shim_lock verifier"), 0 },				\
+  { "x509key",   'x', N_("FILE"), 0,					\
+      N_("embed FILE as an x509 certificate for signature checking"), 0}, \
   { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\
     "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \
     1},                                                                 \
@@ -188,8 +190,9 @@ void
 grub_install_generate_image (const char *dir, const char *prefix,
 			     FILE *out,
 			     const char *outname, char *mods[],
-			     char *memdisk_path, char **pubkey_paths,
-			     size_t npubkeys,
+			     char *memdisk_path,
+			     char **pubkey_paths, size_t npubkeys,
+			     char **x509key_paths, size_t nx509keys,
 			     char *config_path,
 			     const struct grub_install_image_target_desc *image_target,
 			     int note, size_t appsig_size,
diff --git a/util/grub-install-common.c b/util/grub-install-common.c
index 1216a203c292..7bfa9752a031 100644
--- a/util/grub-install-common.c
+++ b/util/grub-install-common.c
@@ -460,6 +460,8 @@ static char **pubkeys;
 static size_t npubkeys;
 static char *sbat;
 static int disable_shim_lock;
+static char **x509keys;
+static size_t nx509keys;
 static grub_compression_t compression;
 static size_t appsig_size;
 
@@ -501,6 +503,12 @@ grub_install_parse (int key, char *arg)
     case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK:
       disable_shim_lock = 1;
       return 1;
+    case 'x':
+      x509keys = xrealloc (x509keys,
+			  sizeof (x509keys[0])
+			  * (nx509keys + 1));
+      x509keys[nx509keys++] = xstrdup (arg);
+      return 1;
 
     case GRUB_INSTALL_OPTIONS_VERBOSITY:
       verbosity++;
@@ -627,6 +635,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
   for (pk = pubkeys; pk < pubkeys + npubkeys; pk++)
     slen += 20 + grub_strlen (*pk);
 
+  for (pk = x509keys; pk < x509keys + nx509keys; pk++)
+    slen += 10 + grub_strlen (*pk);
+
   for (md = modules.entries; *md; md++)
     {
       slen += 10 + grub_strlen (*md);
@@ -655,6 +666,14 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
       *p++ = ' ';
     }
 
+  for (pk = x509keys; pk < x509keys + nx509keys; pk++)
+    {
+      p = grub_stpcpy (p, "--x509 '");
+      p = grub_stpcpy (p, *pk);
+      *p++ = '\'';
+      *p++ = ' ';
+    }
+
   for (md = modules.entries; *md; md++)
     {
       *p++ = '\'';
@@ -683,7 +702,8 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
 
   grub_install_generate_image (dir, prefix, fp, outname,
 			       modules.entries, memdisk_path,
-			       pubkeys, npubkeys, config_path, tgt,
+			       pubkeys, npubkeys, x509keys, nx509keys,
+			       config_path, tgt,
 			       note, appsig_size, compression, dtb, sbat,
 			       disable_shim_lock);
   while (dc--)
diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
index d01eaeb8443a..7d61ef3ea046 100644
--- a/util/grub-mkimage.c
+++ b/util/grub-mkimage.c
@@ -75,7 +75,8 @@ static struct argp_option options[] = {
    /* TRANSLATORS: "embed" is a verb (command description).  "*/
   {"config",   'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0},
    /* TRANSLATORS: "embed" is a verb (command description).  "*/
-  {"pubkey",   'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0},
+  {"pubkey",   'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0},
+  {"x509",     'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended signature checking"), 0},
   /* TRANSLATORS: NOTE is a name of segment.  */
   {"note",   'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0},
   {"output",  'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0},
@@ -124,6 +125,8 @@ struct arguments
   char *dtb;
   char **pubkeys;
   size_t npubkeys;
+  char **x509keys;
+  size_t nx509keys;
   char *font;
   char *config;
   char *sbat;
@@ -206,6 +209,13 @@ argp_parser (int key, char *arg, struct argp_state *state)
       arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg);
       break;
 
+    case 'x':
+      arguments->x509keys = xrealloc (arguments->x509keys,
+				      sizeof (arguments->x509keys[0])
+				      * (arguments->nx509keys + 1));
+      arguments->x509keys[arguments->nx509keys++] = xstrdup (arg);
+      break;
+
     case 'c':
       if (arguments->config)
 	free (arguments->config);
@@ -332,7 +342,8 @@ main (int argc, char *argv[])
   grub_install_generate_image (arguments.dir, arguments.prefix, fp,
 			       arguments.output, arguments.modules,
 			       arguments.memdisk, arguments.pubkeys,
-			       arguments.npubkeys, arguments.config,
+			       arguments.npubkeys, arguments.x509keys,
+			       arguments.nx509keys, arguments.config,
 			       arguments.image_target, arguments.note,
 			       arguments.appsig_size,
 			       arguments.comp, arguments.dtb,
diff --git a/util/mkimage.c b/util/mkimage.c
index d2cb33883557..5a8021a213cf 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -866,8 +866,10 @@ init_pe_section(const struct grub_install_image_target_desc *image_target,
 void
 grub_install_generate_image (const char *dir, const char *prefix,
 			     FILE *out, const char *outname, char *mods[],
-			     char *memdisk_path, char **pubkey_paths,
-			     size_t npubkeys, char *config_path,
+			     char *memdisk_path,
+			     char **pubkey_paths, size_t npubkeys,
+			     char **x509key_paths, size_t nx509keys,
+			     char *config_path,
 			     const struct grub_install_image_target_desc *image_target,
 			     int note, size_t appsig_size, grub_compression_t comp,
 			     const char *dtb_path, const char *sbat_path, 
@@ -913,6 +915,19 @@ grub_install_generate_image (const char *dir, const char *prefix,
       }
   }
 
+  {
+    size_t i;
+    for (i = 0; i < nx509keys; i++)
+      {
+	size_t curs;
+	curs = ALIGN_ADDR (grub_util_get_image_size (x509key_paths[i]));
+	grub_util_info ("the size of x509 public key %u is 0x%"
+			GRUB_HOST_PRIxLONG_LONG,
+			(unsigned) i, (unsigned long long) curs);
+	total_module_size += curs + sizeof (struct grub_module_header);
+      }
+  }
+
   if (memdisk_path)
     {
       memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512);
@@ -1034,7 +1049,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
 	curs = grub_util_get_image_size (pubkey_paths[i]);
 
 	header = (struct grub_module_header *) (kernel_img + offset);
-	header->type = grub_host_to_target32 (OBJ_TYPE_PUBKEY);
+	header->type = grub_host_to_target32 (OBJ_TYPE_GPG_PUBKEY);
 	header->size = grub_host_to_target32 (curs + sizeof (*header));
 	offset += sizeof (*header);
 
@@ -1043,6 +1058,26 @@ grub_install_generate_image (const char *dir, const char *prefix,
       }
   }
 
+  {
+    size_t i;
+    for (i = 0; i < nx509keys; i++)
+      {
+	size_t curs;
+	struct grub_module_header *header;
+
+	curs = grub_util_get_image_size (x509key_paths[i]);
+
+	header = (struct grub_module_header *) (kernel_img + offset);
+	header->type = grub_host_to_target32 (OBJ_TYPE_X509_PUBKEY);
+	header->size = grub_host_to_target32 (curs + sizeof (*header));
+	offset += sizeof (*header);
+
+	grub_util_load_image (x509key_paths[i], kernel_img + offset);
+	offset += ALIGN_ADDR (curs);
+      }
+  }
+
+
   if (memdisk_path)
     {
       struct grub_module_header *header;
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 17/22] appended signatures: import GNUTLS's ASN.1 description files
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (15 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 16/22] grub-install: support embedding x509 certificates Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-19 21:09   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 18/22] appended signatures: parse PKCS#7 signedData and X.509 certificates Daniel Axtens
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

In order to parse PKCS#7 messages and X.509 certificates with libtasn1,
we need some information about how they are encoded.

We get these from GNUTLS, which has the benefit that they support the
features we need and are well tested.

The GNUTLS license is LGPLv2.1+, which is GPLv3 compatible, allowing
us to import it without issue.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 .../commands/appendedsig/gnutls_asn1_tab.c    | 121 +++++
 .../commands/appendedsig/pkix_asn1_tab.c      | 484 ++++++++++++++++++
 2 files changed, 605 insertions(+)
 create mode 100644 grub-core/commands/appendedsig/gnutls_asn1_tab.c
 create mode 100644 grub-core/commands/appendedsig/pkix_asn1_tab.c

diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
new file mode 100644
index 000000000000..ddd1314e63b6
--- /dev/null
+++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
@@ -0,0 +1,121 @@
+#include <grub/mm.h>
+#include <grub/libtasn1.h>
+
+const asn1_static_node gnutls_asn1_tab[] = {
+  { "GNUTLS", 536872976, NULL },
+  { NULL, 1073741836, NULL },
+  { "RSAPublicKey", 1610612741, NULL },
+  { "modulus", 1073741827, NULL },
+  { "publicExponent", 3, NULL },
+  { "RSAPrivateKey", 1610612741, NULL },
+  { "version", 1073741827, NULL },
+  { "modulus", 1073741827, NULL },
+  { "publicExponent", 1073741827, NULL },
+  { "privateExponent", 1073741827, NULL },
+  { "prime1", 1073741827, NULL },
+  { "prime2", 1073741827, NULL },
+  { "exponent1", 1073741827, NULL },
+  { "exponent2", 1073741827, NULL },
+  { "coefficient", 1073741827, NULL },
+  { "otherPrimeInfos", 16386, "OtherPrimeInfos"},
+  { "ProvableSeed", 1610612741, NULL },
+  { "algorithm", 1073741836, NULL },
+  { "seed", 7, NULL },
+  { "OtherPrimeInfos", 1612709899, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "OtherPrimeInfo"},
+  { "OtherPrimeInfo", 1610612741, NULL },
+  { "prime", 1073741827, NULL },
+  { "exponent", 1073741827, NULL },
+  { "coefficient", 3, NULL },
+  { "AlgorithmIdentifier", 1610612741, NULL },
+  { "algorithm", 1073741836, NULL },
+  { "parameters", 541081613, NULL },
+  { "algorithm", 1, NULL },
+  { "DigestInfo", 1610612741, NULL },
+  { "digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"},
+  { "digest", 7, NULL },
+  { "DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
+  { "DSAPublicKey", 1073741827, NULL },
+  { "DSAParameters", 1610612741, NULL },
+  { "p", 1073741827, NULL },
+  { "q", 1073741827, NULL },
+  { "g", 3, NULL },
+  { "DSASignatureValue", 1610612741, NULL },
+  { "r", 1073741827, NULL },
+  { "s", 3, NULL },
+  { "DSAPrivateKey", 1610612741, NULL },
+  { "version", 1073741827, NULL },
+  { "p", 1073741827, NULL },
+  { "q", 1073741827, NULL },
+  { "g", 1073741827, NULL },
+  { "Y", 1073741827, NULL },
+  { "priv", 3, NULL },
+  { "DHParameter", 1610612741, NULL },
+  { "prime", 1073741827, NULL },
+  { "base", 1073741827, NULL },
+  { "privateValueLength", 16387, NULL },
+  { "ECParameters", 1610612754, NULL },
+  { "namedCurve", 12, NULL },
+  { "ECPrivateKey", 1610612741, NULL },
+  { "Version", 1073741827, NULL },
+  { "privateKey", 1073741831, NULL },
+  { "parameters", 1610637314, "ECParameters"},
+  { NULL, 2056, "0"},
+  { "publicKey", 536895494, NULL },
+  { NULL, 2056, "1"},
+  { "PrincipalName", 1610612741, NULL },
+  { "name-type", 1610620931, NULL },
+  { NULL, 2056, "0"},
+  { "name-string", 536879115, NULL },
+  { NULL, 1073743880, "1"},
+  { NULL, 27, NULL },
+  { "KRB5PrincipalName", 1610612741, NULL },
+  { "realm", 1610620955, NULL },
+  { NULL, 2056, "0"},
+  { "principalName", 536879106, "PrincipalName"},
+  { NULL, 2056, "1"},
+  { "RSAPSSParameters", 1610612741, NULL },
+  { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"},
+  { NULL, 2056, "0"},
+  { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"},
+  { NULL, 2056, "1"},
+  { "saltLength", 1610653699, NULL },
+  { NULL, 1073741833, "20"},
+  { NULL, 2056, "2"},
+  { "trailerField", 536911875, NULL },
+  { NULL, 1073741833, "1"},
+  { NULL, 2056, "3"},
+  { "GOSTParameters", 1610612741, NULL },
+  { "publicKeyParamSet", 1073741836, NULL },
+  { "digestParamSet", 16396, NULL },
+  { "GOSTParametersOld", 1610612741, NULL },
+  { "publicKeyParamSet", 1073741836, NULL },
+  { "digestParamSet", 1073741836, NULL },
+  { "encryptionParamSet", 16396, NULL },
+  { "GOSTPrivateKey", 1073741831, NULL },
+  { "GOSTPrivateKeyOld", 1073741827, NULL },
+  { "IssuerSignTool", 1610612741, NULL },
+  { "signTool", 1073741858, NULL },
+  { "cATool", 1073741858, NULL },
+  { "signToolCert", 1073741858, NULL },
+  { "cAToolCert", 34, NULL },
+  { "Gost28147-89-EncryptedKey", 1610612741, NULL },
+  { "encryptedKey", 1073741831, NULL },
+  { "maskKey", 1610637319, NULL },
+  { NULL, 4104, "0"},
+  { "macKey", 7, NULL },
+  { "SubjectPublicKeyInfo", 1610612741, NULL },
+  { "algorithm", 1073741826, "AlgorithmIdentifier"},
+  { "subjectPublicKey", 6, NULL },
+  { "GostR3410-TransportParameters", 1610612741, NULL },
+  { "encryptionParamSet", 1073741836, NULL },
+  { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"},
+  { NULL, 4104, "0"},
+  { "ukm", 7, NULL },
+  { "GostR3410-KeyTransport", 536870917, NULL },
+  { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"},
+  { "transportParameters", 536895490, "GostR3410-TransportParameters"},
+  { NULL, 4104, "0"},
+  { NULL, 0, NULL }
+};
diff --git a/grub-core/commands/appendedsig/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c
new file mode 100644
index 000000000000..adef69d95ced
--- /dev/null
+++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c
@@ -0,0 +1,484 @@
+#include <grub/mm.h>
+#include <grub/libtasn1.h>
+
+const asn1_static_node pkix_asn1_tab[] = {
+  { "PKIX1", 536875024, NULL },
+  { NULL, 1073741836, NULL },
+  { "PrivateKeyUsagePeriod", 1610612741, NULL },
+  { "notBefore", 1610637349, NULL },
+  { NULL, 4104, "0"},
+  { "notAfter", 536895525, NULL },
+  { NULL, 4104, "1"},
+  { "AuthorityKeyIdentifier", 1610612741, NULL },
+  { "keyIdentifier", 1610637319, NULL },
+  { NULL, 4104, "0"},
+  { "authorityCertIssuer", 1610637314, "GeneralNames"},
+  { NULL, 4104, "1"},
+  { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"},
+  { NULL, 4104, "2"},
+  { "SubjectKeyIdentifier", 1073741831, NULL },
+  { "KeyUsage", 1073741830, NULL },
+  { "DirectoryString", 1610612754, NULL },
+  { "teletexString", 1612709918, NULL },
+  { "MAX", 524298, "1"},
+  { "printableString", 1612709919, NULL },
+  { "MAX", 524298, "1"},
+  { "universalString", 1612709920, NULL },
+  { "MAX", 524298, "1"},
+  { "utf8String", 1612709922, NULL },
+  { "MAX", 524298, "1"},
+  { "bmpString", 1612709921, NULL },
+  { "MAX", 524298, "1"},
+  { "ia5String", 538968093, NULL },
+  { "MAX", 524298, "1"},
+  { "SubjectAltName", 1073741826, "GeneralNames"},
+  { "GeneralNames", 1612709899, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "GeneralName"},
+  { "GeneralName", 1610612754, NULL },
+  { "otherName", 1610620930, "AnotherName"},
+  { NULL, 4104, "0"},
+  { "rfc822Name", 1610620957, NULL },
+  { NULL, 4104, "1"},
+  { "dNSName", 1610620957, NULL },
+  { NULL, 4104, "2"},
+  { "x400Address", 1610620941, NULL },
+  { NULL, 4104, "3"},
+  { "directoryName", 1610620939, NULL },
+  { NULL, 1073743880, "4"},
+  { NULL, 2, "RelativeDistinguishedName"},
+  { "ediPartyName", 1610620941, NULL },
+  { NULL, 4104, "5"},
+  { "uniformResourceIdentifier", 1610620957, NULL },
+  { NULL, 4104, "6"},
+  { "iPAddress", 1610620935, NULL },
+  { NULL, 4104, "7"},
+  { "registeredID", 536879116, NULL },
+  { NULL, 4104, "8"},
+  { "AnotherName", 1610612741, NULL },
+  { "type-id", 1073741836, NULL },
+  { "value", 541073421, NULL },
+  { NULL, 1073743880, "0"},
+  { "type-id", 1, NULL },
+  { "IssuerAltName", 1073741826, "GeneralNames"},
+  { "BasicConstraints", 1610612741, NULL },
+  { "cA", 1610645508, NULL },
+  { NULL, 131081, NULL },
+  { "pathLenConstraint", 537411587, NULL },
+  { "0", 10, "MAX"},
+  { "CRLDistributionPoints", 1612709899, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "DistributionPoint"},
+  { "DistributionPoint", 1610612741, NULL },
+  { "distributionPoint", 1610637314, "DistributionPointName"},
+  { NULL, 2056, "0"},
+  { "reasons", 1610637314, "ReasonFlags"},
+  { NULL, 4104, "1"},
+  { "cRLIssuer", 536895490, "GeneralNames"},
+  { NULL, 4104, "2"},
+  { "DistributionPointName", 1610612754, NULL },
+  { "fullName", 1610620930, "GeneralNames"},
+  { NULL, 4104, "0"},
+  { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"},
+  { NULL, 4104, "1"},
+  { "ReasonFlags", 1073741830, NULL },
+  { "ExtKeyUsageSyntax", 1612709899, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 12, NULL },
+  { "AuthorityInfoAccessSyntax", 1612709899, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "AccessDescription"},
+  { "AccessDescription", 1610612741, NULL },
+  { "accessMethod", 1073741836, NULL },
+  { "accessLocation", 2, "GeneralName"},
+  { "Attribute", 1610612741, NULL },
+  { "type", 1073741836, NULL },
+  { "values", 536870927, NULL },
+  { NULL, 13, NULL },
+  { "AttributeTypeAndValue", 1610612741, NULL },
+  { "type", 1073741836, NULL },
+  { "value", 13, NULL },
+  { "Name", 1610612754, NULL },
+  { "rdnSequence", 536870923, NULL },
+  { NULL, 2, "RelativeDistinguishedName"},
+  { "DistinguishedName", 1610612747, NULL },
+  { NULL, 2, "RelativeDistinguishedName"},
+  { "RelativeDistinguishedName", 1612709903, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "AttributeTypeAndValue"},
+  { "Certificate", 1610612741, NULL },
+  { "tbsCertificate", 1073741826, "TBSCertificate"},
+  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signature", 6, NULL },
+  { "TBSCertificate", 1610612741, NULL },
+  { "version", 1610653699, NULL },
+  { NULL, 1073741833, "0"},
+  { NULL, 2056, "0"},
+  { "serialNumber", 1073741826, "CertificateSerialNumber"},
+  { "signature", 1073741826, "AlgorithmIdentifier"},
+  { "issuer", 1073741826, "Name"},
+  { "validity", 1073741826, "Validity"},
+  { "subject", 1073741826, "Name"},
+  { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"},
+  { "issuerUniqueID", 1610637314, "UniqueIdentifier"},
+  { NULL, 4104, "1"},
+  { "subjectUniqueID", 1610637314, "UniqueIdentifier"},
+  { NULL, 4104, "2"},
+  { "extensions", 536895490, "Extensions"},
+  { NULL, 2056, "3"},
+  { "CertificateSerialNumber", 1073741827, NULL },
+  { "Validity", 1610612741, NULL },
+  { "notBefore", 1073741826, "Time"},
+  { "notAfter", 2, "Time"},
+  { "Time", 1610612754, NULL },
+  { "utcTime", 1073741860, NULL },
+  { "generalTime", 37, NULL },
+  { "UniqueIdentifier", 1073741830, NULL },
+  { "SubjectPublicKeyInfo", 1610612741, NULL },
+  { "algorithm", 1073741826, "AlgorithmIdentifier"},
+  { "subjectPublicKey", 6, NULL },
+  { "Extensions", 1612709899, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "Extension"},
+  { "Extension", 1610612741, NULL },
+  { "extnID", 1073741836, NULL },
+  { "critical", 1610645508, NULL },
+  { NULL, 131081, NULL },
+  { "extnValue", 7, NULL },
+  { "CertificateList", 1610612741, NULL },
+  { "tbsCertList", 1073741826, "TBSCertList"},
+  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signature", 6, NULL },
+  { "TBSCertList", 1610612741, NULL },
+  { "version", 1073758211, NULL },
+  { "signature", 1073741826, "AlgorithmIdentifier"},
+  { "issuer", 1073741826, "Name"},
+  { "thisUpdate", 1073741826, "Time"},
+  { "nextUpdate", 1073758210, "Time"},
+  { "revokedCertificates", 1610629131, NULL },
+  { NULL, 536870917, NULL },
+  { "userCertificate", 1073741826, "CertificateSerialNumber"},
+  { "revocationDate", 1073741826, "Time"},
+  { "crlEntryExtensions", 16386, "Extensions"},
+  { "crlExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "0"},
+  { "AlgorithmIdentifier", 1610612741, NULL },
+  { "algorithm", 1073741836, NULL },
+  { "parameters", 541081613, NULL },
+  { "algorithm", 1, NULL },
+  { "Dss-Sig-Value", 1610612741, NULL },
+  { "r", 1073741827, NULL },
+  { "s", 3, NULL },
+  { "Dss-Parms", 1610612741, NULL },
+  { "p", 1073741827, NULL },
+  { "q", 1073741827, NULL },
+  { "g", 3, NULL },
+  { "pkcs-7-ContentInfo", 1610612741, NULL },
+  { "contentType", 1073741836, NULL },
+  { "content", 541073421, NULL },
+  { NULL, 1073743880, "0"},
+  { "contentType", 1, NULL },
+  { "pkcs-7-DigestInfo", 1610612741, NULL },
+  { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "digest", 7, NULL },
+  { "pkcs-7-SignedData", 1610612741, NULL },
+  { "version", 1073741827, NULL },
+  { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"},
+  { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"},
+  { "certificates", 1610637314, "pkcs-7-CertificateSet"},
+  { NULL, 4104, "0"},
+  { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"},
+  { NULL, 4104, "1"},
+  { "signerInfos", 2, "pkcs-7-SignerInfos"},
+  { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL },
+  { NULL, 2, "AlgorithmIdentifier"},
+  { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL },
+  { "eContentType", 1073741836, NULL },
+  { "eContent", 536895501, NULL },
+  { NULL, 2056, "0"},
+  { "pkcs-7-CertificateRevocationLists", 1610612751, NULL },
+  { NULL, 13, NULL },
+  { "pkcs-7-CertificateChoices", 1610612754, NULL },
+  { "certificate", 13, NULL },
+  { "pkcs-7-CertificateSet", 1610612751, NULL },
+  { NULL, 2, "pkcs-7-CertificateChoices"},
+  { "IssuerAndSerialNumber", 1610612741, NULL },
+  { "issuer", 1073741826, "Name"},
+  { "serialNumber", 2, "CertificateSerialNumber"},
+  { "pkcs-7-SignerInfo", 1610612741, NULL },
+  { "version", 1073741827, NULL },
+  { "sid", 1073741826, "SignerIdentifier"},
+  { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signedAttrs", 1610637314, "SignedAttributes"},
+  { NULL, 4104, "0"},
+  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signature", 1073741831, NULL },
+  { "unsignedAttrs", 536895490, "SignedAttributes"},
+  { NULL, 4104, "1"},
+  { "SignedAttributes", 1612709903, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "Attribute"},
+  { "SignerIdentifier", 1610612754, NULL },
+  { "issuerAndSerialNumber", 1073741826, "IssuerAndSerialNumber"},
+  { "subjectKeyIdentifier", 536879111, NULL },
+  { NULL, 4104, "0"},
+  { "pkcs-7-SignerInfos", 1610612751, NULL },
+  { NULL, 2, "pkcs-7-SignerInfo"},
+  { "pkcs-10-CertificationRequestInfo", 1610612741, NULL },
+  { "version", 1073741827, NULL },
+  { "subject", 1073741826, "Name"},
+  { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"},
+  { "attributes", 536879106, "Attributes"},
+  { NULL, 4104, "0"},
+  { "Attributes", 1610612751, NULL },
+  { NULL, 2, "Attribute"},
+  { "pkcs-10-CertificationRequest", 1610612741, NULL },
+  { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"},
+  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signature", 6, NULL },
+  { "pkcs-9-at-challengePassword", 1879048204, NULL },
+  { "iso", 1073741825, "1"},
+  { "member-body", 1073741825, "2"},
+  { "us", 1073741825, "840"},
+  { "rsadsi", 1073741825, "113549"},
+  { "pkcs", 1073741825, "1"},
+  { NULL, 1073741825, "9"},
+  { NULL, 1, "7"},
+  { "pkcs-9-challengePassword", 1610612754, NULL },
+  { "printableString", 1073741855, NULL },
+  { "utf8String", 34, NULL },
+  { "pkcs-9-localKeyId", 1073741831, NULL },
+  { "pkcs-8-PrivateKeyInfo", 1610612741, NULL },
+  { "version", 1073741827, NULL },
+  { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "privateKey", 1073741831, NULL },
+  { "attributes", 536895490, "Attributes"},
+  { NULL, 4104, "0"},
+  { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL },
+  { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "encryptedData", 2, "pkcs-8-EncryptedData"},
+  { "pkcs-8-EncryptedData", 1073741831, NULL },
+  { "pkcs-5-des-CBC-params", 1612709895, NULL },
+  { NULL, 1048586, "8"},
+  { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL },
+  { NULL, 1048586, "8"},
+  { "pkcs-5-aes128-CBC-params", 1612709895, NULL },
+  { NULL, 1048586, "16"},
+  { "pkcs-5-aes192-CBC-params", 1612709895, NULL },
+  { NULL, 1048586, "16"},
+  { "pkcs-5-aes256-CBC-params", 1612709895, NULL },
+  { NULL, 1048586, "16"},
+  { "Gost28147-89-Parameters", 1610612741, NULL },
+  { "iv", 1073741831, NULL },
+  { "encryptionParamSet", 12, NULL },
+  { "pkcs-5-PBE-params", 1610612741, NULL },
+  { "salt", 1073741831, NULL },
+  { "iterationCount", 3, NULL },
+  { "pkcs-5-PBES2-params", 1610612741, NULL },
+  { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"},
+  { "encryptionScheme", 2, "AlgorithmIdentifier"},
+  { "pkcs-5-PBKDF2-params", 1610612741, NULL },
+  { "salt", 1610612754, NULL },
+  { "specified", 1073741831, NULL },
+  { "otherSource", 2, "AlgorithmIdentifier"},
+  { "iterationCount", 1611137027, NULL },
+  { "1", 10, "MAX"},
+  { "keyLength", 1611153411, NULL },
+  { "1", 10, "MAX"},
+  { "prf", 16386, "AlgorithmIdentifier"},
+  { "pkcs-12-PFX", 1610612741, NULL },
+  { "version", 1610874883, NULL },
+  { "v3", 1, "3"},
+  { "authSafe", 1073741826, "pkcs-7-ContentInfo"},
+  { "macData", 16386, "pkcs-12-MacData"},
+  { "pkcs-12-PbeParams", 1610612741, NULL },
+  { "salt", 1073741831, NULL },
+  { "iterations", 3, NULL },
+  { "pkcs-12-MacData", 1610612741, NULL },
+  { "mac", 1073741826, "pkcs-7-DigestInfo"},
+  { "macSalt", 1073741831, NULL },
+  { "iterations", 536903683, NULL },
+  { NULL, 9, "1"},
+  { "pkcs-12-AuthenticatedSafe", 1610612747, NULL },
+  { NULL, 2, "pkcs-7-ContentInfo"},
+  { "pkcs-12-SafeContents", 1610612747, NULL },
+  { NULL, 2, "pkcs-12-SafeBag"},
+  { "pkcs-12-SafeBag", 1610612741, NULL },
+  { "bagId", 1073741836, NULL },
+  { "bagValue", 1614815245, NULL },
+  { NULL, 1073743880, "0"},
+  { "badId", 1, NULL },
+  { "bagAttributes", 536887311, NULL },
+  { NULL, 2, "Attribute"},
+  { "pkcs-12-CertBag", 1610612741, NULL },
+  { "certId", 1073741836, NULL },
+  { "certValue", 541073421, NULL },
+  { NULL, 1073743880, "0"},
+  { "certId", 1, NULL },
+  { "pkcs-12-CRLBag", 1610612741, NULL },
+  { "crlId", 1073741836, NULL },
+  { "crlValue", 541073421, NULL },
+  { NULL, 1073743880, "0"},
+  { "crlId", 1, NULL },
+  { "pkcs-12-SecretBag", 1610612741, NULL },
+  { "secretTypeId", 1073741836, NULL },
+  { "secretValue", 541073421, NULL },
+  { NULL, 1073743880, "0"},
+  { "secretTypeId", 1, NULL },
+  { "pkcs-7-Data", 1073741831, NULL },
+  { "pkcs-7-EncryptedData", 1610612741, NULL },
+  { "version", 1073741827, NULL },
+  { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"},
+  { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"},
+  { NULL, 4104, "1"},
+  { "pkcs-7-EncryptedContentInfo", 1610612741, NULL },
+  { "contentType", 1073741836, NULL },
+  { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"},
+  { "encryptedContent", 536895495, NULL },
+  { NULL, 4104, "0"},
+  { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
+  { "pkcs-7-UnprotectedAttributes", 1612709903, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "Attribute"},
+  { "ProxyCertInfo", 1610612741, NULL },
+  { "pCPathLenConstraint", 1611153411, NULL },
+  { "0", 10, "MAX"},
+  { "proxyPolicy", 2, "ProxyPolicy"},
+  { "ProxyPolicy", 1610612741, NULL },
+  { "policyLanguage", 1073741836, NULL },
+  { "policy", 16391, NULL },
+  { "certificatePolicies", 1612709899, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "PolicyInformation"},
+  { "PolicyInformation", 1610612741, NULL },
+  { "policyIdentifier", 1073741836, NULL },
+  { "policyQualifiers", 538984459, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "PolicyQualifierInfo"},
+  { "PolicyQualifierInfo", 1610612741, NULL },
+  { "policyQualifierId", 1073741836, NULL },
+  { "qualifier", 541065229, NULL },
+  { "policyQualifierId", 1, NULL },
+  { "CPSuri", 1073741853, NULL },
+  { "UserNotice", 1610612741, NULL },
+  { "noticeRef", 1073758210, "NoticeReference"},
+  { "explicitText", 16386, "DisplayText"},
+  { "NoticeReference", 1610612741, NULL },
+  { "organization", 1073741826, "DisplayText"},
+  { "noticeNumbers", 536870923, NULL },
+  { NULL, 3, NULL },
+  { "DisplayText", 1610612754, NULL },
+  { "ia5String", 1612709917, NULL },
+  { "200", 524298, "1"},
+  { "visibleString", 1612709923, NULL },
+  { "200", 524298, "1"},
+  { "bmpString", 1612709921, NULL },
+  { "200", 524298, "1"},
+  { "utf8String", 538968098, NULL },
+  { "200", 524298, "1"},
+  { "OCSPRequest", 1610612741, NULL },
+  { "tbsRequest", 1073741826, "TBSRequest"},
+  { "optionalSignature", 536895490, "Signature"},
+  { NULL, 2056, "0"},
+  { "TBSRequest", 1610612741, NULL },
+  { "version", 1610653699, NULL },
+  { NULL, 1073741833, "0"},
+  { NULL, 2056, "0"},
+  { "requestorName", 1610637314, "GeneralName"},
+  { NULL, 2056, "1"},
+  { "requestList", 1610612747, NULL },
+  { NULL, 2, "Request"},
+  { "requestExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "2"},
+  { "Signature", 1610612741, NULL },
+  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signature", 1073741830, NULL },
+  { "certs", 536895499, NULL },
+  { NULL, 1073743880, "0"},
+  { NULL, 2, "Certificate"},
+  { "Request", 1610612741, NULL },
+  { "reqCert", 1073741826, "CertID"},
+  { "singleRequestExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "0"},
+  { "CertID", 1610612741, NULL },
+  { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "issuerNameHash", 1073741831, NULL },
+  { "issuerKeyHash", 1073741831, NULL },
+  { "serialNumber", 2, "CertificateSerialNumber"},
+  { "OCSPResponse", 1610612741, NULL },
+  { "responseStatus", 1073741826, "OCSPResponseStatus"},
+  { "responseBytes", 536895490, "ResponseBytes"},
+  { NULL, 2056, "0"},
+  { "OCSPResponseStatus", 1610874901, NULL },
+  { "successful", 1073741825, "0"},
+  { "malformedRequest", 1073741825, "1"},
+  { "internalError", 1073741825, "2"},
+  { "tryLater", 1073741825, "3"},
+  { "sigRequired", 1073741825, "5"},
+  { "unauthorized", 1, "6"},
+  { "ResponseBytes", 1610612741, NULL },
+  { "responseType", 1073741836, NULL },
+  { "response", 7, NULL },
+  { "BasicOCSPResponse", 1610612741, NULL },
+  { "tbsResponseData", 1073741826, "ResponseData"},
+  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
+  { "signature", 1073741830, NULL },
+  { "certs", 536895499, NULL },
+  { NULL, 1073743880, "0"},
+  { NULL, 2, "Certificate"},
+  { "ResponseData", 1610612741, NULL },
+  { "version", 1610653699, NULL },
+  { NULL, 1073741833, "0"},
+  { NULL, 2056, "0"},
+  { "responderID", 1073741826, "ResponderID"},
+  { "producedAt", 1073741861, NULL },
+  { "responses", 1610612747, NULL },
+  { NULL, 2, "SingleResponse"},
+  { "responseExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "1"},
+  { "ResponderID", 1610612754, NULL },
+  { "byName", 1610620939, NULL },
+  { NULL, 1073743880, "1"},
+  { NULL, 2, "RelativeDistinguishedName"},
+  { "byKey", 536879111, NULL },
+  { NULL, 2056, "2"},
+  { "SingleResponse", 1610612741, NULL },
+  { "certID", 1073741826, "CertID"},
+  { "certStatus", 1073741826, "CertStatus"},
+  { "thisUpdate", 1073741861, NULL },
+  { "nextUpdate", 1610637349, NULL },
+  { NULL, 2056, "0"},
+  { "singleExtensions", 536895490, "Extensions"},
+  { NULL, 2056, "1"},
+  { "CertStatus", 1610612754, NULL },
+  { "good", 1610620948, NULL },
+  { NULL, 4104, "0"},
+  { "revoked", 1610620930, "RevokedInfo"},
+  { NULL, 4104, "1"},
+  { "unknown", 536879106, "UnknownInfo"},
+  { NULL, 4104, "2"},
+  { "RevokedInfo", 1610612741, NULL },
+  { "revocationTime", 1073741861, NULL },
+  { "revocationReason", 537157653, NULL },
+  { NULL, 1073743880, "0"},
+  { "unspecified", 1, "0"},
+  { "UnknownInfo", 1073741844, NULL },
+  { "NameConstraints", 1610612741, NULL },
+  { "permittedSubtrees", 1610637314, "GeneralSubtrees"},
+  { NULL, 4104, "0"},
+  { "excludedSubtrees", 536895490, "GeneralSubtrees"},
+  { NULL, 4104, "1"},
+  { "GeneralSubtrees", 1612709899, NULL },
+  { "MAX", 1074266122, "1"},
+  { NULL, 2, "GeneralSubtree"},
+  { "GeneralSubtree", 1610612741, NULL },
+  { "base", 1073741826, "GeneralName"},
+  { "minimum", 1610653699, NULL },
+  { NULL, 1073741833, "0"},
+  { NULL, 4104, "0"},
+  { "maximum", 536895491, NULL },
+  { NULL, 4104, "1"},
+  { "TlsFeatures", 536870923, NULL },
+  { NULL, 3, NULL },
+  { NULL, 0, NULL }
+};
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 18/22] appended signatures: parse PKCS#7 signedData and X.509 certificates
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (16 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 17/22] appended signatures: import GNUTLS's ASN.1 description files Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-19 22:02   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 19/22] appended signatures: support verifying appended signatures Daniel Axtens
                   ` (3 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel
  Cc: rashmica.g, alastair, nayna, Daniel Axtens, Javier Martinez Canillas

This code allows us to parse:

 - PKCS#7 signedData messages. Only a single signerInfo is supported,
   which is all that the Linux sign-file utility supports creating
   out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported.
   Any certificate embedded in the PKCS#7 message will be ignored.

 - X.509 certificates: at least enough to verify the signatures on the
   PKCS#7 messages. We expect that the certificates embedded in grub will
   be leaf certificates, not CA certificates. The parser enforces this.

 - X.509 certificates support the Extended Key Usage extension and handle
   it by verifying that the certificate has a single purpose, that is code
   signing. This is required because Red Hat certificates have both Key
   Usage and Extended Key Usage extensions present.

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> # EKU support
Signed-off-by: Daniel Axtens <dja@axtens.net>

---

v2 changes:

 - Handle the Extended Key Usage extension
 - Fix 2 leaks in x509 cert parsing
 - Improve x509 parser function name
 - Constify the data parameter in parser signatures
 - Support multiple signers in a pkcs7 message. Accept any passing sig.
 - Allow padding after a pkcs7 message in an appended signature, required
    to support my model for signers separated in time.
 - Fix a test that used GRUB_ERR_NONE rather than ASN1_SUCCESS. They're
    both 0 so no harm was done, but better to be correct.
 - Various code and comment cleanups.

Thanks to Nayna Jain and Stefan Berger for their reviews.

revert

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 grub-core/commands/appendedsig/appendedsig.h |  118 ++
 grub-core/commands/appendedsig/asn1util.c    |  103 ++
 grub-core/commands/appendedsig/pkcs7.c       |  509 +++++++++
 grub-core/commands/appendedsig/x509.c        | 1079 ++++++++++++++++++
 4 files changed, 1809 insertions(+)
 create mode 100644 grub-core/commands/appendedsig/appendedsig.h
 create mode 100644 grub-core/commands/appendedsig/asn1util.c
 create mode 100644 grub-core/commands/appendedsig/pkcs7.c
 create mode 100644 grub-core/commands/appendedsig/x509.c

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



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 19/22] appended signatures: support verifying appended signatures
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (17 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 18/22] appended signatures: parse PKCS#7 signedData and X.509 certificates Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-20  1:31   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 20/22] appended signatures: verification tests Daniel Axtens
                   ` (2 subsequent siblings)
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

Building on the parsers and the ability to embed x509 certificates, as
well as the existing gcrypt functionality, add a module for verifying
appended signatures.

This includes a verifier that requires that Linux kernels and grub modules
have appended signatures, and commands to manage the list of trusted
certificates for verification.

Verification must be enabled by setting check_appended_signatures. If
GRUB is locked down when the module is loaded, verification will be
enabled and locked automatically.

As with the PGP verifier, it is not a complete secure-boot solution:
other mechanisms, such as a password or lockdown, must be used to ensure
that a user cannot drop to the grub shell and disable verification.

Signed-off-by: Daniel Axtens <dja@axtens.net>

---

v2 changes:

 - Improve x509 parser function name
 - Constify data parameters in function signatures
 - Support multiple signers
 - Use an enum rather than 0, 1 and 2 for various signature
    enforcement states.
 - Spin out a file reading function that was duplicated.
 - Fix some code style and clarity issues.

Thanks to Nayna Jain and Stefan Berger for their reviews.

Revert "fixups so that you can build pkcs7 without posixly"

This reverts commit 676a19fa8a7f9cca7a58ce2180110f609185b2bd.
---
 grub-core/Makefile.core.def                  |  14 +
 grub-core/commands/appendedsig/appendedsig.c | 669 +++++++++++++++++++
 include/grub/file.h                          |   2 +
 3 files changed, 685 insertions(+)
 create mode 100644 grub-core/commands/appendedsig/appendedsig.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index d63f216111ca..beeedd8ca356 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -946,6 +946,20 @@ module = {
   cppflags = '-I$(srcdir)/lib/posix_wrap';
 };
 
+module = {
+  name = appendedsig;
+  common = commands/appendedsig/appendedsig.c;
+  common = commands/appendedsig/x509.c;
+  common = commands/appendedsig/pkcs7.c;
+  common = commands/appendedsig/asn1util.c;
+  common = commands/appendedsig/gnutls_asn1_tab.c;
+  common = commands/appendedsig/pkix_asn1_tab.c;
+
+  // posix wrapper required for gcry to get sys/types.h
+  cflags = '$(CFLAGS_POSIX)';
+  cppflags = '-I$(srcdir)/lib/posix_wrap';
+};
+
 module = {
   name = hdparm;
   common = commands/hdparm.c;
diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
new file mode 100644
index 000000000000..e63ad1ac645a
--- /dev/null
+++ b/grub-core/commands/appendedsig/appendedsig.c
@@ -0,0 +1,669 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020-2021  IBM Corporation.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/file.h>
+#include <grub/command.h>
+#include <grub/crypto.h>
+#include <grub/pkcs1_v15.h>
+#include <grub/i18n.h>
+#include <grub/gcrypt/gcrypt.h>
+#include <grub/kernel.h>
+#include <grub/extcmd.h>
+#include <grub/verify.h>
+#include <grub/libtasn1.h>
+#include <grub/env.h>
+#include <grub/lockdown.h>
+
+#include "appendedsig.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+const char magic[] = "~Module signature appended~\n";
+
+/*
+ * This structure is extracted from scripts/sign-file.c in the linux kernel
+ * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible.
+ */
+struct module_signature
+{
+  grub_uint8_t algo;		/* Public-key crypto algorithm [0] */
+  grub_uint8_t hash;		/* Digest algorithm [0] */
+  grub_uint8_t id_type;		/* Key identifier type [PKEY_ID_PKCS7] */
+  grub_uint8_t signer_len;	/* Length of signer's name [0] */
+  grub_uint8_t key_id_len;	/* Length of key identifier [0] */
+  grub_uint8_t __pad[3];
+  grub_uint32_t sig_len;	/* Length of signature data */
+} GRUB_PACKED;
+
+
+/* This represents an entire, parsed, appended signature */
+struct grub_appended_signature
+{
+  grub_size_t signature_len;		/* Length of PKCS#7 data +
+                                         * metadata + magic */
+
+  struct module_signature sig_metadata;	/* Module signature metadata */
+  struct pkcs7_signedData pkcs7;	/* Parsed PKCS#7 data */
+};
+
+/* Trusted certificates for verifying appended signatures */
+struct x509_certificate *grub_trusted_key;
+
+/*
+ * Force gcry_rsa to be a module dependency.
+ *
+ * If we use grub_crypto_pk_rsa, then then the gcry_rsa module won't be built
+ * in if you add 'appendedsig' to grub-install --modules. You would need to
+ * add 'gcry_rsa' too. That's confusing and seems suboptimal, especially when
+ * we only support RSA.
+ *
+ * Dynamic loading also causes some concerns. We can't load gcry_rsa from the
+ * the filesystem after we install the verifier - we won't be able to verify
+ * it without having it already present. We also shouldn't load it before we
+ * install the verifier, because that would mean it wouldn't be verified - an
+ * attacker could insert any code they wanted into the module.
+ *
+ * So instead, reference the internal symbol from gcry_rsa. That creates a
+ * direct dependency on gcry_rsa, so it will be built in when this module
+ * is built in. Being built in (assuming the core image is itself signed!)
+ * also resolves our concerns about loading from the filesystem.
+ */
+extern gcry_pk_spec_t _gcry_pubkey_spec_rsa;
+
+static enum
+{ check_sigs_no = 0,
+  check_sigs_enforce = 1,
+  check_sigs_forced = 2
+} check_sigs = check_sigs_no;
+
+static const char *
+grub_env_read_sec (struct grub_env_var *var __attribute__((unused)),
+		   const char *val __attribute__((unused)))
+{
+  if (check_sigs == check_sigs_forced)
+    return "forced";
+  else if (check_sigs == check_sigs_enforce)
+    return "enforce";
+  else
+    return "no";
+}
+
+static char *
+grub_env_write_sec (struct grub_env_var *var __attribute__((unused)),
+		    const char *val)
+{
+  /* Do not allow the value to be changed if set to forced */
+  if (check_sigs == check_sigs_forced)
+    return grub_strdup ("forced");
+
+  if ((*val == '2') || (*val == 'f'))
+    check_sigs = check_sigs_forced;
+  else if ((*val == '1') || (*val == 'e'))
+    check_sigs = check_sigs_enforce;
+  else if ((*val == '0') || (*val == 'n'))
+    check_sigs = check_sigs_no;
+
+  return grub_strdup (grub_env_read_sec (NULL, NULL));
+}
+
+static grub_err_t
+file_read_all (grub_file_t file, grub_uint8_t **buf, grub_size_t *len)
+{
+  grub_off_t full_file_size;
+  grub_size_t file_size, total_read_size = 0;
+  grub_ssize_t read_size;
+
+  full_file_size = grub_file_size (file);
+  if (full_file_size == GRUB_FILE_SIZE_UNKNOWN)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+		       N_("Cannot read a file of unknown size into a buffer"));
+
+  if (full_file_size > GRUB_SIZE_MAX)
+    return grub_error (GRUB_ERR_OUT_OF_RANGE,
+		       N_("File is too large to read: %" PRIuGRUB_UINT64_T
+			  " bytes"), full_file_size);
+
+  file_size = (grub_size_t) full_file_size;
+
+  *buf = grub_malloc (file_size);
+  if (!*buf)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+		       N_("Could not allocate file data buffer size %"
+			  PRIuGRUB_SIZE), file_size);
+
+  while (total_read_size < file_size)
+    {
+      read_size =
+	grub_file_read (file, *buf + total_read_size,
+			file_size - total_read_size);
+
+      if (read_size < 0)
+	{
+	  grub_free (*buf);
+	  return grub_errno;
+	}
+      else if (read_size == 0)
+	{
+	  grub_free (*buf);
+	  return grub_error (GRUB_ERR_IO,
+			     N_("Could not read full file size (%"
+				PRIuGRUB_SIZE "), only %" PRIuGRUB_SIZE
+				" bytes read"), file_size, total_read_size);
+	}
+
+      total_read_size += read_size;
+    }
+  *len = file_size;
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+read_cert_from_file (grub_file_t f, struct x509_certificate *certificate)
+{
+  grub_err_t err;
+  grub_uint8_t *buf;
+  grub_size_t file_size;
+
+  err = file_read_all (f, &buf, &file_size);
+  if (err != GRUB_ERR_NONE)
+    return err;
+
+  err = parse_x509_certificate (buf, file_size, certificate);
+  if (err != GRUB_ERR_NONE)
+    {
+      grub_free (buf);
+      return err;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize,
+			    struct grub_appended_signature *sig)
+{
+  grub_err_t err;
+  grub_size_t pkcs7_size;
+  grub_size_t remaining_len;
+  const grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic);
+
+  if (bufsize < grub_strlen (magic))
+    return grub_error (GRUB_ERR_BAD_SIGNATURE,
+		       N_("File too short for signature magic"));
+
+  if (grub_memcmp (appsigdata, (grub_uint8_t *) magic, grub_strlen (magic)))
+    return grub_error (GRUB_ERR_BAD_SIGNATURE,
+		       N_("Missing or invalid signature magic"));
+
+  remaining_len = bufsize - grub_strlen (magic);
+
+  if (remaining_len < sizeof (struct module_signature))
+    return grub_error (GRUB_ERR_BAD_SIGNATURE,
+		       N_("File too short for signature metadata"));
+
+  appsigdata -= sizeof (struct module_signature);
+
+  /* extract the metadata */
+  grub_memcpy (&(sig->sig_metadata), appsigdata,
+	       sizeof (struct module_signature));
+
+  remaining_len -= sizeof (struct module_signature);
+
+  if (sig->sig_metadata.id_type != 2)
+    return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Wrong signature type"));
+
+  pkcs7_size = grub_be_to_cpu32 (sig->sig_metadata.sig_len);
+
+  if (pkcs7_size > remaining_len)
+    return grub_error (GRUB_ERR_BAD_SIGNATURE,
+		       N_("File too short for PKCS#7 message"));
+
+  grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", pkcs7_size);
+
+  sig->signature_len =
+    grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size;
+
+  /* rewind pointer and parse pkcs7 data */
+  appsigdata -= pkcs7_size;
+
+  err = parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7);
+  if (err != GRUB_ERR_NONE)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
+{
+  grub_err_t err = GRUB_ERR_NONE;
+  grub_size_t datasize;
+  void *context;
+  unsigned char *hash;
+  gcry_mpi_t hashmpi;
+  gcry_err_code_t rc;
+  struct x509_certificate *pk;
+  struct grub_appended_signature sig;
+  struct pkcs7_signerInfo *si;
+  int i;
+
+  if (!grub_trusted_key)
+    return grub_error (GRUB_ERR_BAD_SIGNATURE,
+		       N_("No trusted keys to verify against"));
+
+  err = extract_appended_signature (buf, bufsize, &sig);
+  if (err != GRUB_ERR_NONE)
+    return err;
+
+  datasize = bufsize - sig.signature_len;
+
+  for (i = 0; i < sig.pkcs7.signerInfo_count; i++)
+    {
+      /* This could be optimised in a couple of ways:
+	  - we could only compute hashes once per hash type
+	  - we could track signer information and only verify where IDs match
+	 For now we do the naive O(trusted keys * pkcs7 signers) approach.
+	*/
+      si = &sig.pkcs7.signerInfos[i];
+      context = grub_zalloc (si->hash->contextsize);
+      if (!context)
+	return grub_errno;
+    
+      si->hash->init (context);
+      si->hash->write (context, buf, datasize);
+      si->hash->final (context);
+      hash = si->hash->read (context);
+
+      grub_dprintf ("appendedsig",
+		    "data size %" PRIxGRUB_SIZE ", signer %d hash %02x%02x%02x%02x...\n",
+		    datasize, i, hash[0], hash[1], hash[2], hash[3]);
+    
+      err = GRUB_ERR_BAD_SIGNATURE;
+      for (pk = grub_trusted_key; pk; pk = pk->next)
+	{
+	  rc = grub_crypto_rsa_pad (&hashmpi, hash, si->hash, pk->mpis[0]);
+	  if (rc)
+	    {
+	      err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+    				N_("Error padding hash for RSA verification: %d"),
+    				rc);
+	      grub_free (context);
+	      goto cleanup;
+	    }
+
+	  rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &si->sig_mpi,
+					     pk->mpis, NULL, NULL);
+	  gcry_mpi_release (hashmpi);
+
+	  if (rc == 0)
+	    {
+	      grub_dprintf ("appendedsig",
+			    "verify signer %d with key '%s' succeeded\n", i,
+			    pk->subject);
+	      err = GRUB_ERR_NONE;
+	      break;
+	    }
+
+	  grub_dprintf ("appendedsig",
+			"verify signer %d with key '%s' failed with %d\n", i,
+			pk->subject, rc);
+	}
+
+      grub_free (context);
+
+      if (err == GRUB_ERR_NONE)
+	break;
+    }
+
+  /* If we didn't verify, provide a neat message */
+  if (err != GRUB_ERR_NONE)
+    err = grub_error (GRUB_ERR_BAD_SIGNATURE,
+		      N_("Failed to verify signature against a trusted key"));
+
+cleanup:
+  pkcs7_signedData_release (&sig.pkcs7);
+
+  return err;
+}
+
+static grub_err_t
+grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)),
+			   int argc, char **args)
+{
+  grub_file_t f;
+  grub_err_t err = GRUB_ERR_NONE;
+  grub_uint8_t *data;
+  grub_size_t file_size;
+
+  if (argc < 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
+
+  grub_dprintf ("appendedsig", "verifying %s\n", args[0]);
+
+  f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE);
+  if (!f)
+    {
+      err = grub_errno;
+      goto cleanup;
+    }
+
+  err = file_read_all (f, &data, &file_size);
+  if (err != GRUB_ERR_NONE)
+    goto cleanup;
+
+  err = grub_verify_appended_signature (data, file_size);
+
+  grub_free (data);
+
+cleanup:
+  if (f)
+    grub_file_close (f);
+  return err;
+}
+
+static grub_err_t
+grub_cmd_distrust (grub_command_t cmd __attribute__((unused)),
+		   int argc, char **args)
+{
+  unsigned long cert_num, i;
+  struct x509_certificate *cert, *prev;
+
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("One argument expected"));
+
+  grub_errno = GRUB_ERR_NONE;
+  cert_num = grub_strtoul (args[0], NULL, 10);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
+
+  if (cert_num < 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+		       N_("Certificate number too small - numbers start at 1"));
+
+  if (cert_num == 1)
+    {
+      cert = grub_trusted_key;
+      grub_trusted_key = cert->next;
+
+      certificate_release (cert);
+      grub_free (cert);
+      return GRUB_ERR_NONE;
+    }
+  i = 2;
+  prev = grub_trusted_key;
+  cert = grub_trusted_key->next;
+  while (cert)
+    {
+      if (i == cert_num)
+	{
+	  prev->next = cert->next;
+	  certificate_release (cert);
+	  grub_free (cert);
+	  return GRUB_ERR_NONE;
+	}
+      i++;
+      prev = cert;
+      cert = cert->next;
+    }
+
+  return grub_error (GRUB_ERR_BAD_ARGUMENT,
+		     N_("No certificate number %lu found - only %lu certificates in the store"),
+		     cert_num, i - 1);
+}
+
+static grub_err_t
+grub_cmd_trust (grub_command_t cmd __attribute__((unused)),
+		int argc, char **args)
+{
+  grub_file_t certf;
+  struct x509_certificate *cert = NULL;
+  grub_err_t err;
+
+  if (argc != 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
+
+  certf = grub_file_open (args[0],
+			  GRUB_FILE_TYPE_CERTIFICATE_TRUST
+			  | GRUB_FILE_TYPE_NO_DECOMPRESS);
+  if (!certf)
+    return grub_errno;
+
+
+  cert = grub_zalloc (sizeof (struct x509_certificate));
+  if (!cert)
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
+		       N_("Could not allocate memory for certificate"));
+
+  err = read_cert_from_file (certf, cert);
+  grub_file_close (certf);
+  if (err != GRUB_ERR_NONE)
+    {
+      grub_free (cert);
+      return err;
+    }
+  grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n",
+		cert->subject);
+
+  cert->next = grub_trusted_key;
+  grub_trusted_key = cert;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_list (grub_command_t cmd __attribute__((unused)),
+	       int argc __attribute__((unused)),
+	       char **args __attribute__((unused)))
+{
+  struct x509_certificate *cert;
+  int cert_num = 1;
+  grub_size_t i;
+
+  for (cert = grub_trusted_key; cert; cert = cert->next)
+    {
+      grub_printf (N_("Certificate %d:\n"), cert_num);
+
+      grub_printf (N_("\tSerial: "));
+      for (i = 0; i < cert->serial_len - 1; i++)
+	{
+	  grub_printf ("%02x:", cert->serial[i]);
+	}
+      grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]);
+
+      grub_printf ("\tCN: %s\n\n", cert->subject);
+      cert_num++;
+
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+appendedsig_init (grub_file_t io __attribute__((unused)),
+		  enum grub_file_type type,
+		  void **context __attribute__((unused)),
+		  enum grub_verify_flags *flags)
+{
+  if (check_sigs == check_sigs_no)
+    {
+      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+      return GRUB_ERR_NONE;
+    }
+
+  switch (type & GRUB_FILE_TYPE_MASK)
+    {
+    case GRUB_FILE_TYPE_CERTIFICATE_TRUST:
+      /*
+       * This is a certificate to add to trusted keychain.
+       *
+       * This needs to be verified or blocked. Ideally we'd write an x509
+       * verifier, but we lack the hubris required to take this on. Instead,
+       * require that it have an appended signature.
+       */
+
+      /* Fall through */
+
+    case GRUB_FILE_TYPE_LINUX_KERNEL:
+    case GRUB_FILE_TYPE_GRUB_MODULE:
+      /*
+       * Appended signatures are only defined for ELF binaries.
+       * Out of an abundance of caution, we only verify Linux kernels and
+       * GRUB modules at this point.
+       */
+      *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
+      return GRUB_ERR_NONE;
+
+    case GRUB_FILE_TYPE_ACPI_TABLE:
+    case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE:
+      /*
+       * It is possible to use appended signature verification without
+       * lockdown - like the PGP verifier. When combined with an embedded
+       * config file in a signed grub binary, this could still be a meaningful
+       * secure-boot chain - so long as it isn't subverted by something like a
+       * rouge ACPI table or DT image. Defer them explicitly.
+       */
+      *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;
+      return GRUB_ERR_NONE;
+
+    default:
+      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+      return GRUB_ERR_NONE;
+    }
+}
+
+static grub_err_t
+appendedsig_write (void *ctxt __attribute__((unused)),
+		   void *buf, grub_size_t size)
+{
+  return grub_verify_appended_signature (buf, size);
+}
+
+struct grub_file_verifier grub_appendedsig_verifier = {
+  .name = "appendedsig",
+  .init = appendedsig_init,
+  .write = appendedsig_write,
+};
+
+static grub_ssize_t
+pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
+{
+  grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len);
+  return len;
+}
+
+/* Filesystem descriptor.  */
+static struct grub_fs pseudo_fs = {
+  .name = "pseudo",
+  .fs_read = pseudo_read
+};
+
+static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust;
+
+GRUB_MOD_INIT (appendedsig)
+{
+  int rc;
+  struct grub_module_header *header;
+
+  /* If in lockdown, immediately enter forced mode */
+  if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
+    check_sigs = check_sigs_forced;
+
+  grub_trusted_key = NULL;
+
+  grub_register_variable_hook ("check_appended_signatures",
+			       grub_env_read_sec, grub_env_write_sec);
+  grub_env_export ("check_appended_signatures");
+
+  rc = asn1_init ();
+  if (rc)
+    grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc,
+		asn1_strerror (rc));
+
+  FOR_MODULES (header)
+  {
+    struct grub_file pseudo_file;
+    struct x509_certificate *pk = NULL;
+    grub_err_t err;
+
+    /* Not an ELF module, skip.  */
+    if (header->type != OBJ_TYPE_X509_PUBKEY)
+      continue;
+
+    grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
+    pseudo_file.fs = &pseudo_fs;
+    pseudo_file.size = header->size - sizeof (struct grub_module_header);
+    pseudo_file.data = (char *) header + sizeof (struct grub_module_header);
+
+    grub_dprintf ("appendedsig",
+		  "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n",
+		  pseudo_file.size);
+
+    pk = grub_zalloc (sizeof (struct x509_certificate));
+    if (!pk)
+      {
+	grub_fatal ("Out of memory loading initial certificates");
+      }
+
+    err = read_cert_from_file (&pseudo_file, pk);
+    if (err != GRUB_ERR_NONE)
+      grub_fatal ("Error loading initial key: %s", grub_errmsg);
+
+    grub_dprintf ("appendedsig", "loaded certificate CN='%s'\n", pk->subject);
+
+    pk->next = grub_trusted_key;
+    grub_trusted_key = pk;
+  }
+
+  cmd_trust =
+    grub_register_command ("trust_certificate", grub_cmd_trust,
+			   N_("X509_CERTIFICATE"),
+			   N_("Add X509_CERTIFICATE to trusted certificates."));
+  cmd_list =
+    grub_register_command ("list_certificates", grub_cmd_list, 0,
+			   N_("Show the list of trusted x509 certificates."));
+  cmd_verify =
+    grub_register_command ("verify_appended", grub_cmd_verify_signature,
+			   N_("FILE"),
+			   N_("Verify FILE against the trusted x509 certificates."));
+  cmd_distrust =
+    grub_register_command ("distrust_certificate", grub_cmd_distrust,
+			   N_("CERT_NUMBER"),
+			   N_("Remove CERT_NUMBER (as listed by list_certificates) from trusted certificates."));
+
+  grub_verifier_register (&grub_appendedsig_verifier);
+  grub_dl_set_persistent (mod);
+}
+
+GRUB_MOD_FINI (appendedsig)
+{
+  /*
+   * grub_dl_set_persistent should prevent this from actually running, but
+   * it does still run under emu.
+   */
+
+  grub_verifier_unregister (&grub_appendedsig_verifier);
+  grub_unregister_command (cmd_verify);
+  grub_unregister_command (cmd_list);
+  grub_unregister_command (cmd_trust);
+  grub_unregister_command (cmd_distrust);
+}
diff --git a/include/grub/file.h b/include/grub/file.h
index 31567483ccfc..96827a4f8961 100644
--- a/include/grub/file.h
+++ b/include/grub/file.h
@@ -80,6 +80,8 @@ enum grub_file_type
     GRUB_FILE_TYPE_PUBLIC_KEY,
     /* File holding public key to add to trused keys.  */
     GRUB_FILE_TYPE_PUBLIC_KEY_TRUST,
+    /* File holding x509 certificiate to add to trusted keys.  */
+    GRUB_FILE_TYPE_CERTIFICATE_TRUST,
     /* File of which we intend to print a blocklist to the user.  */
     GRUB_FILE_TYPE_PRINT_BLOCKLIST,
     /* File we intend to use for test loading or testing speed.  */
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 20/22] appended signatures: verification tests
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (18 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 19/22] appended signatures: support verifying appended signatures Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-20 12:49   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 21/22] appended signatures: documentation Daniel Axtens
  2021-06-30  8:40 ` [PATCH v2 22/22] ieee1275: enter lockdown based on /ibm,secure-boot Daniel Axtens
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

These tests are run through all_functional_test and test a range
of commands and behaviours.

Signed-off-by: Daniel Axtens <dja@axtens.net>

---

v2 changes:

  - add a test for EKU
  - add tests for files signed with multiple signers
  - add a test of padded PKCS#7 messages
  - use macros to reduce duplication for exposing certificate files
     to the test via procfs
  - more useful comments
---
 grub-core/Makefile.core.def               |   6 +
 grub-core/tests/appended_signature_test.c | 273 ++++++
 grub-core/tests/appended_signatures.h     | 975 ++++++++++++++++++++++
 grub-core/tests/lib/functional_test.c     |   1 +
 4 files changed, 1255 insertions(+)
 create mode 100644 grub-core/tests/appended_signature_test.c
 create mode 100644 grub-core/tests/appended_signatures.h

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index beeedd8ca356..4aa4cf263d94 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2131,6 +2131,12 @@ module = {
   common = tests/setjmp_test.c;
 };
 
+module = {
+  name = appended_signature_test;
+  common = tests/appended_signature_test.c;
+  common = tests/appended_signatures.h;
+};
+
 module = {
   name = signature_test;
   common = tests/signature_test.c;
diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c
new file mode 100644
index 000000000000..5365185cb5cc
--- /dev/null
+++ b/grub-core/tests/appended_signature_test.c
@@ -0,0 +1,273 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020  IBM Corporation.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/time.h>
+#include <grub/misc.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/env.h>
+#include <grub/test.h>
+#include <grub/mm.h>
+#include <grub/procfs.h>
+#include <grub/file.h>
+
+#include "appended_signatures.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define PROC_FILE(identifier, file_name) \
+static char * \
+get_ ## identifier (grub_size_t *sz) \
+{ \
+  char *ret; \
+  \
+  *sz = identifier ## _len; \
+  ret = grub_malloc (*sz); \
+  if (ret) \
+    grub_memcpy (ret, identifier, *sz); \
+  return ret; \
+} \
+\
+static struct grub_procfs_entry identifier ## _entry = \
+{ \
+  .name = file_name, \
+  .get_contents = get_ ## identifier \
+};
+
+#define DEFINE_TEST_CASE(case_name) PROC_FILE(case_name, #case_name)
+
+#define DO_TEST(case_name, is_valid) \
+{ \
+  grub_procfs_register (#case_name, &case_name ## _entry); \
+  do_verify ("(proc)/" #case_name, is_valid); \
+  grub_procfs_unregister (&case_name ## _entry); \
+}
+
+
+DEFINE_TEST_CASE (hi_signed);
+DEFINE_TEST_CASE (hi_signed_sha256);
+DEFINE_TEST_CASE (hj_signed);
+DEFINE_TEST_CASE (short_msg);
+DEFINE_TEST_CASE (unsigned_msg);
+DEFINE_TEST_CASE (hi_signed_2nd);
+DEFINE_TEST_CASE (hi_double);
+DEFINE_TEST_CASE (hi_double_extended);
+
+PROC_FILE (certificate_der, "certificate.der")
+PROC_FILE (certificate2_der, "certificate2.der")
+PROC_FILE (certificate_printable_der, "certificate_printable.der")
+PROC_FILE (certificate_eku_der, "certificate_eku.der")
+
+static void
+do_verify (const char *f, int is_valid)
+{
+  grub_command_t cmd;
+  char *args[] = { (char *) f, NULL };
+  grub_err_t err;
+
+  cmd = grub_command_find ("verify_appended");
+  if (!cmd)
+    {
+      grub_test_assert (0, "can't find command `%s'", "verify_appended");
+      return;
+    }
+  err = (cmd->func) (cmd, 1, args);
+  if (is_valid)
+    {
+      grub_test_assert (err == GRUB_ERR_NONE,
+			"verification of %s failed: %d: %s", f, grub_errno,
+			grub_errmsg);
+    }
+  else
+    {
+      grub_test_assert (err == GRUB_ERR_BAD_SIGNATURE,
+			"verification of %s unexpectedly succeeded", f);
+    }
+  grub_errno = GRUB_ERR_NONE;
+
+}
+
+static void
+appended_signature_test (void)
+{
+  grub_command_t cmd_trust, cmd_distrust;
+  char *trust_args[] = { (char *) "(proc)/certificate.der", NULL };
+  char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL };
+  char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der",
+				   NULL };
+  char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL };
+  char *distrust_args[] = { (char *) "1", NULL };
+  char *distrust2_args[] = { (char *) "2", NULL };
+  grub_err_t err;
+
+  grub_procfs_register ("certificate.der", &certificate_der_entry);
+  grub_procfs_register ("certificate2.der", &certificate2_der_entry);
+  grub_procfs_register ("certificate_printable.der",
+			&certificate_printable_der_entry);
+  grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry);
+
+  cmd_trust = grub_command_find ("trust_certificate");
+  if (!cmd_trust)
+    {
+      grub_test_assert (0, "can't find command `%s'", "trust_certificate");
+      return;
+    }
+  err = (cmd_trust->func) (cmd_trust, 1, trust_args);
+
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "loading certificate failed: %d: %s", grub_errno,
+		    grub_errmsg);
+
+  /* If we have no certificate the remainder of the tests are meaningless */
+  if (err != GRUB_ERR_NONE)
+    return;
+
+  /*
+   * Reload the command: this works around some 'interesting' behaviour in the
+   * dynamic command dispatcher. The first time you call cmd->func you get a
+   * dispatcher that loads the module, finds the real cmd, calls it, and then
+   * releases some internal storage. This means it's not safe to call a second
+   * time and we need to reload it.
+   */
+  cmd_trust = grub_command_find ("trust_certificate");
+
+  /* hi, signed with key 1, SHA-512 */
+  DO_TEST (hi_signed, 1);
+
+  /* hi, signed with key 1, SHA-256 */
+  DO_TEST (hi_signed_sha256, 1);
+
+  /* hi, key 1, SHA-512, second byte corrupted */
+  DO_TEST (hj_signed, 0);
+
+  /* message too short for a signature */
+  DO_TEST (short_msg, 0);
+
+  /* lorem ipsum */
+  DO_TEST (unsigned_msg, 0);
+
+  /* hi, signed with both keys, SHA-512 */
+  DO_TEST (hi_double, 1);
+
+  /*
+   * hi, signed with both keys and with empty space to test we haven't broken
+   * support for adding more signatures after the fact
+   */
+  DO_TEST (hi_double_extended, 1);
+
+  /*
+   * in enforcing mode, we shouldn't be able to load a certificate that isn't
+   * signed by an existing trusted key.
+   *
+   * However, procfs files automatically skip the verification test, so we can't
+   * easily test this.
+   */
+
+  /*
+   * verify that testing with 2 trusted certs works
+   */
+  DO_TEST (hi_signed_2nd, 0);
+
+  err = (cmd_trust->func) (cmd_trust, 1, trust_args2);
+
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "loading certificate 2 failed: %d: %s", grub_errno,
+		    grub_errmsg);
+
+  if (err != GRUB_ERR_NONE)
+    return;
+
+  DO_TEST (hi_signed_2nd, 1);
+  DO_TEST (hi_signed, 1);
+  DO_TEST (hi_double, 1);
+  DO_TEST (hi_double_extended, 1);
+
+  /*
+   * Check certificate removal. They're added to the _top_ of the list and
+   * removed by position in the list. Current the list looks like [#2, #1].
+   *
+   * First test removing the second certificate in the list, which is
+   * certificate #1, giving us just [#2].
+   */
+  cmd_distrust = grub_command_find ("distrust_certificate");
+  if (!cmd_distrust)
+    {
+      grub_test_assert (0, "can't find command `%s'", "distrust_certificate");
+      return;
+    }
+
+  err = (cmd_distrust->func) (cmd_distrust, 1, distrust2_args);
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "distrusting certificate 1 failed: %d: %s", grub_errno,
+		    grub_errmsg);
+  DO_TEST (hi_signed_2nd, 1);
+  DO_TEST (hi_signed, 0);
+  DO_TEST (hi_double, 1);
+
+  /*
+   * Now reload certificate #1. This will make the list look like [#1, #2]
+   */
+  err = (cmd_trust->func) (cmd_trust, 1, trust_args);
+
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "reloading certificate 1 failed: %d: %s", grub_errno,
+		    grub_errmsg);
+  DO_TEST (hi_signed, 1);
+
+  /* Remove the first certificate in the list, giving us just [#2] */
+  err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args);
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "distrusting certificate 1 (first time) failed: %d: %s",
+		    grub_errno, grub_errmsg);
+  DO_TEST (hi_signed_2nd, 1);
+  DO_TEST (hi_signed, 0);
+
+  /*
+   * Remove the first certificate again, giving an empty list.
+   *
+   * verify_appended should fail if there are no certificates to verify against.
+   */
+  err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args);
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "distrusting certificate 1 (second time) failed: %d: %s",
+		    grub_errno, grub_errmsg);
+  DO_TEST (hi_signed_2nd, 0);
+  DO_TEST (hi_double, 0);
+
+  /*
+   * Lastly, check a certificate that uses printableString rather than
+   * utf8String loads properly, and that a certificate with an appropriate
+   * extended key usage loads.
+   */
+  err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable);
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "trusting printable certificate failed: %d: %s",
+		    grub_errno, grub_errmsg);
+
+  err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku);
+  grub_test_assert (err == GRUB_ERR_NONE,
+		    "trusting certificate with extended key usage failed: %d: %s",
+		    grub_errno, grub_errmsg);
+
+  grub_procfs_unregister (&certificate_der_entry);
+  grub_procfs_unregister (&certificate2_der_entry);
+  grub_procfs_unregister (&certificate_printable_der_entry);
+  grub_procfs_unregister (&certificate_eku_der_entry);
+}
+
+GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test);
diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h
new file mode 100644
index 000000000000..c6aa12d86ac7
--- /dev/null
+++ b/grub-core/tests/appended_signatures.h
@@ -0,0 +1,975 @@
+unsigned char certificate_der[] = {
+  0x30, 0x82, 0x05, 0x5d, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01,
+  0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
+  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+  0x05, 0x00, 0x30, 0x3d, 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04,
+  0x03, 0x0c, 0x32, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65,
+  0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
+  0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74,
+  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
+  0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x30,
+  0x36, 0x32, 0x39, 0x30, 0x38, 0x33, 0x36, 0x31, 0x33, 0x5a, 0x18, 0x0f,
+  0x32, 0x31, 0x32, 0x31, 0x30, 0x36, 0x30, 0x35, 0x30, 0x38, 0x33, 0x36,
+  0x31, 0x33, 0x5a, 0x30, 0x33, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55,
+  0x04, 0x03, 0x0c, 0x28, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70,
+  0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74,
+  0x75, 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x69, 0x67,
+  0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, 0x22,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a,
+  0x02, 0x82, 0x02, 0x01, 0x00, 0xb9, 0x09, 0xb2, 0xf6, 0x24, 0x34, 0xdc,
+  0x62, 0xe6, 0x4e, 0xee, 0x04, 0xdb, 0x29, 0xdc, 0x94, 0xcc, 0xee, 0x8a,
+  0x5b, 0xc3, 0x9e, 0x06, 0xba, 0xa7, 0x9b, 0xa4, 0x5f, 0x15, 0x59, 0x8e,
+  0xb8, 0x6e, 0x3c, 0xeb, 0x2e, 0xf2, 0xac, 0x21, 0x42, 0xbd, 0x30, 0xa1,
+  0x39, 0xe5, 0xb9, 0x4f, 0xa0, 0x53, 0xd5, 0x42, 0xdc, 0x8a, 0x87, 0x30,
+  0x38, 0x93, 0x44, 0x80, 0x3b, 0x1a, 0x7e, 0x9e, 0x8e, 0x3e, 0xea, 0x45,
+  0xa0, 0x11, 0x8b, 0xfb, 0x78, 0xe4, 0xbc, 0x65, 0x6b, 0x73, 0xea, 0x6e,
+  0xdf, 0x7c, 0x5b, 0x63, 0x7e, 0x5b, 0x0a, 0x1c, 0xe6, 0x76, 0x19, 0xb5,
+  0x01, 0xde, 0xf6, 0x65, 0x51, 0x30, 0x0a, 0x56, 0x69, 0x69, 0xe8, 0x20,
+  0xf9, 0x13, 0xf1, 0xbf, 0x6f, 0xdd, 0xce, 0x94, 0x96, 0x6e, 0x63, 0xd6,
+  0xfa, 0xa4, 0x91, 0x5f, 0xb3, 0x9c, 0xc7, 0xfa, 0xa9, 0xff, 0x66, 0x5f,
+  0xf3, 0xab, 0x5e, 0xdf, 0x4e, 0xca, 0x11, 0xcf, 0xbf, 0xf8, 0xad, 0x65,
+  0xb1, 0x49, 0x8b, 0xe9, 0x2a, 0xad, 0x7d, 0xf3, 0x0b, 0xfa, 0x5b, 0x6a,
+  0x6a, 0x20, 0x12, 0x77, 0xef, 0x4b, 0xb6, 0xbe, 0x92, 0xba, 0x14, 0x9c,
+  0x5e, 0xea, 0xdc, 0x56, 0x6d, 0x92, 0xd3, 0x64, 0x22, 0xf6, 0x12, 0xe8,
+  0x7d, 0x5e, 0x9c, 0xd6, 0xf9, 0x75, 0x68, 0x7f, 0x8f, 0xd3, 0x6e, 0x05,
+  0x94, 0x91, 0x4f, 0xa1, 0xd6, 0x50, 0x72, 0x3b, 0x11, 0x1f, 0x28, 0x13,
+  0xe8, 0x25, 0x6b, 0xdf, 0xff, 0x72, 0x46, 0x25, 0xe9, 0x05, 0x6f, 0x02,
+  0xc7, 0x1e, 0xc9, 0xcf, 0x99, 0xe9, 0xa7, 0xe2, 0xae, 0xbc, 0xc1, 0x22,
+  0x32, 0x73, 0x2d, 0xa3, 0x70, 0x8f, 0xa7, 0x8d, 0xbf, 0x5f, 0x74, 0x05,
+  0x1b, 0x5e, 0xfe, 0x97, 0x3c, 0xe7, 0x3b, 0x86, 0x0d, 0xf6, 0x38, 0xdb,
+  0xd2, 0x39, 0x47, 0x82, 0x00, 0x44, 0x6c, 0x7b, 0x40, 0x24, 0x0b, 0x3a,
+  0xd4, 0x19, 0x31, 0xba, 0x4e, 0x8e, 0xa3, 0x33, 0xa6, 0x78, 0xef, 0x72,
+  0x9f, 0x06, 0x37, 0x01, 0x9b, 0x79, 0x0d, 0x04, 0xbf, 0xba, 0xd5, 0x1f,
+  0x27, 0xdc, 0x85, 0xbb, 0xef, 0xd2, 0x60, 0xda, 0xa0, 0x3f, 0x66, 0xce,
+  0x9f, 0xa2, 0x7e, 0xa8, 0x8d, 0xee, 0x14, 0x4b, 0xcb, 0x93, 0xf1, 0x38,
+  0xac, 0x4f, 0xd8, 0x29, 0xf3, 0x6f, 0xd4, 0xfd, 0x4d, 0x34, 0x77, 0x58,
+  0x99, 0xdb, 0x16, 0xc1, 0xd0, 0xc7, 0x43, 0x41, 0x70, 0xc4, 0xad, 0x01,
+  0x29, 0x65, 0x22, 0x43, 0x00, 0x6f, 0xb3, 0x00, 0x27, 0x38, 0xc1, 0x4f,
+  0xda, 0x28, 0x96, 0x42, 0xdc, 0xbc, 0x3e, 0x34, 0x8e, 0x14, 0xb8, 0xf3,
+  0x86, 0x4a, 0xea, 0x16, 0x90, 0xf9, 0x0e, 0x9e, 0x8f, 0x66, 0x0c, 0xbf,
+  0x29, 0xd3, 0x8f, 0xfc, 0x4d, 0x38, 0x68, 0xe2, 0xe7, 0x64, 0x32, 0x47,
+  0xdd, 0x56, 0xc9, 0xe4, 0x47, 0x9f, 0x18, 0x89, 0xfc, 0x30, 0x7a, 0xae,
+  0x63, 0xe4, 0xec, 0x93, 0x04, 0xd4, 0x61, 0xe7, 0xbf, 0x0a, 0x06, 0x29,
+  0xc2, 0xa6, 0xd5, 0x53, 0x5d, 0x65, 0x6d, 0x4a, 0xd0, 0xb7, 0x68, 0x4d,
+  0x46, 0x0a, 0xb5, 0xff, 0x52, 0x5e, 0x92, 0x7e, 0x75, 0x08, 0xa4, 0x63,
+  0x0a, 0x6c, 0x31, 0x7a, 0xaa, 0x0c, 0x52, 0xf4, 0x2e, 0xcd, 0x08, 0xeb,
+  0xb3, 0xbd, 0xad, 0x8b, 0x8b, 0x9b, 0x8d, 0x71, 0x42, 0x30, 0x8e, 0xc7,
+  0xfd, 0xec, 0xb7, 0xe6, 0x26, 0x96, 0xf2, 0x74, 0x1b, 0x78, 0x95, 0x22,
+  0x14, 0xf3, 0xc9, 0xd3, 0x79, 0x11, 0xd9, 0xb7, 0x4d, 0x0d, 0x61, 0x60,
+  0x5c, 0x47, 0x50, 0xf3, 0xca, 0x84, 0x4c, 0x5c, 0x30, 0x2c, 0x6a, 0x18,
+  0x26, 0xb0, 0xf3, 0xd1, 0x15, 0x19, 0x39, 0xc3, 0x23, 0x13, 0x0f, 0x9c,
+  0x97, 0x2b, 0x97, 0x93, 0xf9, 0xf8, 0x18, 0x9b, 0x4a, 0x4d, 0xd6, 0xd3,
+  0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c,
+  0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
+  0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07,
+  0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
+  0x8f, 0xba, 0x8b, 0xf5, 0xf4, 0x77, 0xb2, 0xa4, 0x19, 0xef, 0x43, 0xb1,
+  0x8b, 0x03, 0x4b, 0x45, 0x47, 0xb5, 0x2a, 0x48, 0x30, 0x1f, 0x06, 0x03,
+  0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x59, 0x1c, 0xb5,
+  0x52, 0x62, 0x83, 0x05, 0x3b, 0x41, 0x4c, 0x63, 0x4d, 0x5b, 0xf4, 0x8c,
+  0xe6, 0xd7, 0xda, 0x87, 0x54, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
+  0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01,
+  0x00, 0x36, 0x2d, 0x0a, 0xcb, 0x49, 0x54, 0x75, 0xd7, 0xca, 0x21, 0x86,
+  0xae, 0x40, 0x0f, 0x63, 0x10, 0x35, 0xfd, 0xbc, 0xba, 0x28, 0x31, 0x33,
+  0x07, 0x08, 0x64, 0x03, 0x6c, 0xd3, 0xd5, 0xf7, 0xb7, 0x79, 0x11, 0x0c,
+  0xa8, 0x9e, 0xfd, 0x34, 0xa2, 0xba, 0x77, 0x15, 0x15, 0x2d, 0x2c, 0x96,
+  0xae, 0x47, 0xbb, 0x82, 0x89, 0x09, 0x7f, 0xd1, 0x95, 0x69, 0x9b, 0xfe,
+  0xd7, 0x6f, 0x4e, 0x68, 0xf6, 0xe7, 0x5f, 0x54, 0xa1, 0x3a, 0xeb, 0xa4,
+  0xbf, 0x7a, 0xb6, 0x7f, 0xaa, 0xd8, 0xd7, 0x99, 0xcb, 0xae, 0x88, 0x6d,
+  0x7a, 0xf3, 0xfa, 0x9e, 0x44, 0x2f, 0x30, 0xa8, 0xe6, 0xb9, 0x75, 0xa0,
+  0x82, 0xd6, 0xb0, 0xe3, 0x03, 0xb3, 0x12, 0xa3, 0xdc, 0xb9, 0x4d, 0x93,
+  0xd4, 0x30, 0xea, 0xce, 0x96, 0x92, 0x07, 0xf8, 0xba, 0xe4, 0x0f, 0x41,
+  0xe3, 0x04, 0xaa, 0x8c, 0x07, 0x1a, 0x34, 0x60, 0xfc, 0xc0, 0x05, 0xd2,
+  0x5a, 0xa8, 0x66, 0xef, 0xe0, 0x94, 0xc5, 0x2f, 0x0f, 0xff, 0xdc, 0x70,
+  0xfb, 0xe2, 0x9d, 0x61, 0x51, 0x25, 0x02, 0xff, 0x4b, 0x69, 0xfd, 0x66,
+  0xb9, 0xeb, 0x0c, 0xc8, 0x50, 0xd3, 0xb1, 0x08, 0x1e, 0x09, 0x54, 0x87,
+  0xe8, 0xa3, 0x4b, 0xef, 0x0c, 0x32, 0x0a, 0x6c, 0xec, 0x27, 0x22, 0xba,
+  0x7f, 0xdc, 0x52, 0x27, 0x31, 0x14, 0x9a, 0xa8, 0xf7, 0xf9, 0xeb, 0xc8,
+  0xb5, 0x8d, 0x12, 0xed, 0x94, 0xab, 0x3d, 0x9a, 0xfb, 0x4e, 0x04, 0x05,
+  0xd2, 0x3c, 0x7c, 0x8a, 0xed, 0x46, 0x1b, 0x7c, 0xb5, 0x6c, 0x40, 0xb8,
+  0xc1, 0xbf, 0xb0, 0xd2, 0x93, 0x8e, 0xa8, 0x0f, 0xde, 0x78, 0xf3, 0x8c,
+  0xd8, 0x9f, 0xf8, 0xdc, 0xa1, 0x23, 0x20, 0x40, 0x17, 0xb4, 0xdb, 0xb7,
+  0x09, 0x74, 0xa7, 0x80, 0xc2, 0x12, 0xd9, 0x76, 0x79, 0x5b, 0x71, 0xa9,
+  0x6c, 0xd4, 0x57, 0x48, 0xe8, 0xfe, 0xc5, 0xc2, 0x6e, 0xe7, 0x83, 0x5a,
+  0x07, 0xf0, 0x33, 0xc1, 0xc1, 0x1d, 0x34, 0xd4, 0xc8, 0xb0, 0xb7, 0xdb,
+  0xeb, 0xe9, 0xe3, 0x59, 0xdc, 0x7f, 0x36, 0x58, 0xa9, 0xb8, 0x52, 0xdd,
+  0xf9, 0xfd, 0x1c, 0x22, 0x2f, 0x93, 0x3d, 0x53, 0x89, 0x80, 0xde, 0xa2,
+  0xb5, 0xa5, 0x36, 0xbd, 0xc3, 0x92, 0x03, 0xf3, 0x93, 0xc8, 0xc7, 0x4a,
+  0x0b, 0x8b, 0x62, 0xfe, 0xd0, 0xf8, 0x0d, 0x7a, 0x32, 0xb4, 0x39, 0x1a,
+  0xb7, 0x4e, 0xaa, 0xc4, 0x33, 0x32, 0x90, 0x8c, 0xab, 0xd4, 0xae, 0xa5,
+  0xa4, 0x85, 0xcf, 0xba, 0xe1, 0x1b, 0x26, 0x7f, 0x74, 0x02, 0x12, 0x09,
+  0x89, 0x56, 0xe4, 0xe7, 0x9d, 0x91, 0xde, 0x88, 0xe7, 0x1c, 0xed, 0x80,
+  0x05, 0xa8, 0x58, 0x9a, 0x3e, 0x16, 0x97, 0xd5, 0xbc, 0x54, 0xcc, 0xf0,
+  0x32, 0xf2, 0x93, 0x09, 0x94, 0x9f, 0x3c, 0xd9, 0x58, 0xca, 0x68, 0x0b,
+  0xde, 0x3f, 0x73, 0x64, 0xb7, 0xf4, 0xd7, 0x5f, 0x2b, 0xe7, 0x7b, 0x06,
+  0xca, 0xb1, 0x3e, 0xed, 0xd2, 0xb9, 0x29, 0xc1, 0x95, 0x87, 0xad, 0xd6,
+  0x63, 0x69, 0xb8, 0x1f, 0x70, 0xdb, 0xeb, 0xc7, 0x11, 0x7d, 0xe2, 0x99,
+  0x64, 0x6a, 0xf5, 0x3f, 0x30, 0x74, 0x5f, 0x2a, 0x21, 0xda, 0xef, 0x44,
+  0x1d, 0xad, 0x97, 0xa1, 0xfe, 0x14, 0xa7, 0x88, 0x99, 0xd0, 0x1e, 0xb0,
+  0x61, 0x88, 0x09, 0xc9, 0xfa, 0xd1, 0xb3, 0xcb, 0x1d, 0x76, 0x04, 0xbe,
+  0x06, 0x44, 0xd2, 0x30, 0x5e, 0x95, 0x4b, 0x96, 0xc0, 0xd6, 0xbe, 0xd0,
+  0x4d, 0xf2, 0xf4, 0x71, 0x72, 0xa9, 0xbd, 0x07, 0x4f, 0xbc, 0xb3, 0x78,
+  0xb4, 0x8a, 0x44, 0xbd, 0x58, 0xd5, 0x21, 0xb6, 0x47, 0x9c, 0x88, 0x1f,
+  0xbc, 0xbd, 0x54, 0xfa, 0x1d, 0x49, 0xec, 0x51, 0xd9, 0x43, 0x49, 0x9c,
+  0x0c, 0xfa, 0x18, 0xdb, 0xeb, 0x05, 0x77, 0xa2, 0x9a
+};
+unsigned int certificate_der_len = 1377;
+
+unsigned char hi_signed[] = {
+  0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
+  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
+  0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
+  0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
+  0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
+  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
+  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
+  0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+  0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+  0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
+  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77,
+  0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03,
+  0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e,
+  0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52,
+  0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01,
+  0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75,
+  0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1,
+  0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d,
+  0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e,
+  0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f,
+  0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae,
+  0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5,
+  0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a,
+  0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33,
+  0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2,
+  0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9,
+  0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70,
+  0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67,
+  0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a,
+  0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3,
+  0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33,
+  0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4,
+  0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1,
+  0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26,
+  0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41,
+  0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda,
+  0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3,
+  0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98,
+  0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5,
+  0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e,
+  0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe,
+  0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c,
+  0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6,
+  0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5,
+  0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f,
+  0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb,
+  0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0,
+  0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4,
+  0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b,
+  0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52,
+  0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f,
+  0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c,
+  0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17,
+  0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
+  0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
+  0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_signed_len = 739;
+
+unsigned char hj_signed[] = {
+  0x68, 0x6a, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
+  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
+  0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
+  0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
+  0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
+  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
+  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
+  0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+  0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+  0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
+  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77,
+  0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03,
+  0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e,
+  0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52,
+  0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01,
+  0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75,
+  0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1,
+  0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d,
+  0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e,
+  0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f,
+  0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae,
+  0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5,
+  0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a,
+  0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33,
+  0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2,
+  0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9,
+  0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70,
+  0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67,
+  0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a,
+  0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3,
+  0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33,
+  0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4,
+  0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1,
+  0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26,
+  0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41,
+  0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda,
+  0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3,
+  0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98,
+  0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5,
+  0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e,
+  0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe,
+  0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c,
+  0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6,
+  0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5,
+  0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f,
+  0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb,
+  0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0,
+  0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4,
+  0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b,
+  0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52,
+  0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f,
+  0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c,
+  0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17,
+  0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
+  0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
+  0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hj_signed_len = 739;
+
+unsigned char hi_signed_sha256[] = {
+  0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
+  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
+  0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09,
+  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
+  0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
+  0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
+  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
+  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
+  0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
+  0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
+  0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
+  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0x96, 0x02, 0x7b, 0xa4, 0x07,
+  0xa7, 0x39, 0x8d, 0xa6, 0x0b, 0xde, 0x33, 0xdd, 0xf8, 0xec, 0x24, 0x5d,
+  0x06, 0x81, 0xe7, 0x3c, 0x2d, 0x0c, 0x53, 0xfb, 0x7e, 0x5a, 0xf3, 0xee,
+  0xe5, 0x4c, 0x7c, 0xf7, 0xe7, 0x8f, 0x36, 0x62, 0x35, 0xb8, 0x99, 0xc3,
+  0xeb, 0x85, 0x1d, 0x2e, 0x40, 0x6e, 0x2a, 0xb4, 0x3a, 0x76, 0x48, 0x4f,
+  0x8b, 0x29, 0xd4, 0x9e, 0x5c, 0xd2, 0x41, 0x4d, 0xc1, 0x72, 0x0f, 0x97,
+  0xe0, 0x7d, 0x88, 0xed, 0x1a, 0xb0, 0xde, 0x1b, 0x21, 0xa6, 0x0c, 0x19,
+  0xd8, 0xb0, 0x12, 0x54, 0x7b, 0xd8, 0x19, 0x03, 0xbd, 0x77, 0x83, 0x23,
+  0xeb, 0xeb, 0x68, 0x0a, 0x7b, 0x3a, 0x4d, 0x25, 0x44, 0xe1, 0x64, 0x8d,
+  0x43, 0x5a, 0x1c, 0x9f, 0x74, 0x79, 0x31, 0x3f, 0xc7, 0x8e, 0xae, 0xe1,
+  0xf9, 0x1e, 0x54, 0x12, 0x36, 0x85, 0xf2, 0x55, 0xba, 0x42, 0x60, 0x64,
+  0x25, 0x9f, 0x73, 0x62, 0x42, 0xd2, 0x1c, 0x5e, 0x39, 0x4f, 0x7d, 0x91,
+  0xb8, 0xf9, 0x59, 0x3c, 0x13, 0x6b, 0x84, 0x76, 0x6d, 0x8a, 0xc3, 0xcb,
+  0x2d, 0x14, 0x27, 0x16, 0xdc, 0x20, 0x2c, 0xbc, 0x6b, 0xc9, 0xda, 0x9f,
+  0xef, 0xe2, 0x2d, 0xc3, 0x83, 0xd8, 0xf9, 0x94, 0x18, 0xbc, 0xfe, 0x8f,
+  0xa9, 0x44, 0xad, 0xff, 0x1b, 0xcb, 0x86, 0x30, 0x96, 0xa8, 0x3c, 0x7a,
+  0x4b, 0x73, 0x1b, 0xa9, 0xc3, 0x3b, 0xaa, 0xd7, 0x44, 0xa8, 0x4d, 0xd6,
+  0x92, 0xb6, 0x00, 0x04, 0x09, 0x05, 0x4a, 0x95, 0x02, 0x90, 0x19, 0x8c,
+  0x9a, 0xa5, 0xee, 0x58, 0x24, 0xb0, 0xca, 0x5e, 0x6f, 0x73, 0xdb, 0xf5,
+  0xa1, 0xf4, 0xf0, 0xa9, 0xeb, 0xe4, 0xdc, 0x55, 0x9f, 0x8f, 0x7a, 0xd0,
+  0xf7, 0xb6, 0xaa, 0xa6, 0xb5, 0xb4, 0xab, 0xb8, 0x65, 0xad, 0x12, 0x32,
+  0x1c, 0xe6, 0x99, 0x71, 0x93, 0xe8, 0xb4, 0x1e, 0x21, 0x27, 0x52, 0xea,
+  0x8c, 0xc8, 0x79, 0x96, 0x2e, 0x48, 0x60, 0x57, 0x1c, 0x7d, 0x8c, 0x0d,
+  0x07, 0xa7, 0x12, 0x83, 0x0a, 0x76, 0x6a, 0x64, 0xed, 0xbe, 0x8d, 0xaf,
+  0xdf, 0x51, 0x05, 0xdd, 0xf2, 0xd3, 0xb8, 0x93, 0xa9, 0x13, 0xa5, 0x96,
+  0xe8, 0xfa, 0x82, 0x02, 0x18, 0x71, 0x7a, 0x71, 0xbb, 0x39, 0x6f, 0x85,
+  0xee, 0x16, 0x82, 0x27, 0x42, 0x9f, 0x83, 0xc8, 0xab, 0x6a, 0x3b, 0x99,
+  0xba, 0x38, 0x92, 0x38, 0xae, 0x59, 0xfa, 0xaa, 0x40, 0x2b, 0x52, 0x95,
+  0xca, 0x5e, 0xe1, 0x9b, 0x00, 0xbd, 0xb9, 0x63, 0x25, 0x8d, 0xc7, 0x22,
+  0xaf, 0xe5, 0x67, 0x76, 0x91, 0xf4, 0xda, 0xc9, 0x7e, 0x9e, 0xec, 0x9b,
+  0x1f, 0x7d, 0x3b, 0xfe, 0xa1, 0x20, 0x52, 0xac, 0xd0, 0xe5, 0xa6, 0xf1,
+  0xfd, 0x4c, 0x08, 0x59, 0x7d, 0x50, 0xbb, 0x0c, 0xcf, 0xd8, 0xb6, 0x0f,
+  0xc7, 0x19, 0xcb, 0x7a, 0x96, 0x6f, 0x0f, 0x6c, 0x71, 0x56, 0x72, 0xd1,
+  0x06, 0x29, 0x0f, 0x08, 0xa2, 0x46, 0x3e, 0x58, 0x42, 0xc4, 0x8c, 0xe0,
+  0x6e, 0xe9, 0x37, 0xd5, 0x2f, 0x74, 0x36, 0x1d, 0x14, 0xcb, 0x10, 0x0e,
+  0x7d, 0x67, 0xbd, 0x38, 0x0e, 0xa4, 0x27, 0x1d, 0x3c, 0x78, 0x4d, 0x0d,
+  0x15, 0x42, 0x70, 0x20, 0xe0, 0x1d, 0x83, 0x6c, 0x4d, 0xf1, 0x02, 0xa1,
+  0x51, 0xc4, 0xc5, 0x5d, 0x69, 0x90, 0x58, 0x82, 0x94, 0x50, 0x36, 0x22,
+  0xb3, 0xa4, 0x15, 0x77, 0xdc, 0x44, 0xb0, 0x50, 0xa2, 0x3f, 0xd0, 0x0e,
+  0x1b, 0xfc, 0xf4, 0x5b, 0x3b, 0x7d, 0x63, 0x94, 0x22, 0xf3, 0x87, 0x0a,
+  0x41, 0x8a, 0x27, 0x48, 0xcb, 0x6c, 0xfd, 0x70, 0x66, 0x5f, 0x11, 0x6f,
+  0x74, 0x2c, 0x42, 0xaf, 0x74, 0x45, 0x3f, 0x0c, 0x03, 0xc8, 0x80, 0xe2,
+  0x71, 0x08, 0x93, 0xbd, 0x4d, 0x18, 0x78, 0x1e, 0x8e, 0xb9, 0x3a, 0xd6,
+  0x1a, 0xde, 0xf9, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
+  0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
+  0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_signed_sha256_len = 739;
+
+unsigned char short_msg[] = {
+  0x68, 0x69, 0x0a
+};
+unsigned int short_msg_len = 3;
+
+unsigned char unsigned_msg[] = {
+  0x53, 0x65, 0x64, 0x20, 0x75, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x70,
+  0x69, 0x63, 0x69, 0x61, 0x74, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65,
+  0x20, 0x6f, 0x6d, 0x6e, 0x69, 0x73, 0x20, 0x69, 0x73, 0x74, 0x65, 0x20,
+  0x6e, 0x61, 0x74, 0x75, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20,
+  0x73, 0x69, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
+  0x65, 0x6d, 0x20, 0x61, 0x63, 0x63, 0x75, 0x73, 0x61, 0x6e, 0x74, 0x69,
+  0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x71, 0x75,
+  0x65, 0x20, 0x6c, 0x61, 0x75, 0x64, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d,
+  0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6d, 0x20, 0x72, 0x65, 0x6d, 0x20,
+  0x61, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x65, 0x61, 0x71,
+  0x75, 0x65, 0x20, 0x69, 0x70, 0x73, 0x61, 0x20, 0x71, 0x75, 0x61, 0x65,
+  0x20, 0x61, 0x62, 0x20, 0x69, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6e, 0x76,
+  0x65, 0x6e, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x74,
+  0x61, 0x74, 0x69, 0x73, 0x20, 0x65, 0x74, 0x20, 0x71, 0x75, 0x61, 0x73,
+  0x69, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x6f,
+  0x20, 0x62, 0x65, 0x61, 0x74, 0x61, 0x65, 0x20, 0x76, 0x69, 0x74, 0x61,
+  0x65, 0x20, 0x64, 0x69, 0x63, 0x74, 0x61, 0x20, 0x73, 0x75, 0x6e, 0x74,
+  0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6f, 0x2e, 0x20,
+  0x4e, 0x65, 0x6d, 0x6f, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x70,
+  0x73, 0x61, 0x6d, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
+  0x65, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75,
+  0x70, 0x74, 0x61, 0x73, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x73, 0x70,
+  0x65, 0x72, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x20, 0x61, 0x75, 0x74, 0x20,
+  0x6f, 0x64, 0x69, 0x74, 0x20, 0x61, 0x75, 0x74, 0x20, 0x66, 0x75, 0x67,
+  0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x71, 0x75, 0x69, 0x61,
+  0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x75, 0x6e, 0x74, 0x75,
+  0x72, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f,
+  0x72, 0x65, 0x73, 0x20, 0x65, 0x6f, 0x73, 0x20, 0x71, 0x75, 0x69, 0x20,
+  0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x20, 0x76, 0x6f, 0x6c, 0x75,
+  0x70, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x65, 0x71, 0x75, 0x69,
+  0x20, 0x6e, 0x65, 0x73, 0x63, 0x69, 0x75, 0x6e, 0x74, 0x2e, 0x20, 0x4e,
+  0x65, 0x71, 0x75, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x72, 0x6f, 0x20, 0x71,
+  0x75, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x73, 0x74, 0x2c,
+  0x20, 0x71, 0x75, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d,
+  0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20,
+  0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d,
+  0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65,
+  0x74, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63,
+  0x69, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64,
+  0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75,
+  0x6d, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x69, 0x75, 0x73, 0x20, 0x6d,
+  0x6f, 0x64, 0x69, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x20,
+  0x69, 0x6e, 0x63, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20,
+  0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x74, 0x20, 0x64, 0x6f,
+  0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x6d, 0x20,
+  0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x65,
+  0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
+  0x65, 0x6d, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20,
+  0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x20, 0x76, 0x65,
+  0x6e, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e,
+  0x6f, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63,
+  0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x6d, 0x20, 0x75, 0x6c,
+  0x6c, 0x61, 0x6d, 0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x69, 0x73,
+  0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x6c, 0x61,
+  0x62, 0x6f, 0x72, 0x69, 0x6f, 0x73, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x69,
+  0x73, 0x69, 0x20, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69,
+  0x64, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
+  0x6f, 0x64, 0x69, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61,
+  0x74, 0x75, 0x72, 0x3f, 0x20, 0x51, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75,
+  0x74, 0x65, 0x6d, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x65, 0x75, 0x6d, 0x20,
+  0x69, 0x75, 0x72, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65,
+  0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69, 0x20, 0x69,
+  0x6e, 0x20, 0x65, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61,
+  0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x20, 0x65, 0x73, 0x73,
+  0x65, 0x20, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x6e, 0x69, 0x68, 0x69, 0x6c,
+  0x20, 0x6d, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x61, 0x65, 0x20, 0x63,
+  0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x75, 0x72, 0x2c, 0x20,
+  0x76, 0x65, 0x6c, 0x20, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x71, 0x75,
+  0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x65, 0x75,
+  0x6d, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x71, 0x75, 0x6f,
+  0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x73, 0x20, 0x6e, 0x75,
+  0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72,
+  0x3f, 0x0a
+};
+unsigned int unsigned_msg_len = 866;
+
+unsigned char certificate2_der[] = {
+  0x30, 0x82, 0x05, 0x52, 0x30, 0x82, 0x03, 0x3a, 0xa0, 0x03, 0x02, 0x01,
+  0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
+  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30,
+  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+  0x05, 0x00, 0x30, 0x3a, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04,
+  0x03, 0x0c, 0x2f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20,
+  0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
+  0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+  0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+  0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38,
+  0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32,
+  0x30, 0x30, 0x37, 0x30, 0x34, 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a,
+  0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+  0x20, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65,
+  0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69,
+  0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02,
+  0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+  0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02,
+  0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xb0, 0x2f, 0x50, 0x01, 0x9c, 0x0e,
+  0xd6, 0x8c, 0x07, 0xca, 0xc1, 0xcf, 0xbc, 0x03, 0xdd, 0xd3, 0xfa, 0xe3,
+  0x4f, 0x71, 0xc1, 0x30, 0xaa, 0x09, 0x96, 0xe4, 0xd0, 0x6c, 0x42, 0x93,
+  0xdb, 0x35, 0xf6, 0x7e, 0x1b, 0x67, 0xc0, 0xc2, 0x2d, 0x5b, 0xec, 0xca,
+  0x35, 0x06, 0x32, 0x6c, 0x7b, 0x2c, 0xd3, 0x71, 0x2b, 0xe9, 0x7a, 0x19,
+  0xd1, 0xf2, 0xa0, 0x7f, 0xd7, 0x4d, 0x6e, 0x28, 0xbb, 0xae, 0x49, 0x4a,
+  0xbc, 0xea, 0x47, 0x67, 0xb8, 0x36, 0xa6, 0xf5, 0x0d, 0x0e, 0x20, 0x14,
+  0x0c, 0x66, 0x67, 0x28, 0xb5, 0x97, 0x8b, 0x1f, 0x5e, 0x32, 0x06, 0x29,
+  0x9c, 0x99, 0x92, 0x0f, 0x73, 0xac, 0xfd, 0xd2, 0x1d, 0xf2, 0xa8, 0x55,
+  0x9d, 0x1b, 0xd8, 0x3d, 0xb0, 0x76, 0x9a, 0xb6, 0x6c, 0x9f, 0x62, 0x37,
+  0x2f, 0xc0, 0xef, 0x44, 0xb3, 0x0d, 0x4a, 0x3e, 0x4f, 0x7d, 0xbd, 0xdb,
+  0xd8, 0x75, 0x5f, 0x68, 0xe3, 0xf0, 0xec, 0x82, 0x66, 0x7c, 0x31, 0x70,
+  0xa9, 0xa1, 0x6f, 0x38, 0x9f, 0xdf, 0xf5, 0xf0, 0x7d, 0x23, 0x9d, 0x34,
+  0xa5, 0x85, 0xd3, 0xdf, 0x68, 0x41, 0xfc, 0x4f, 0x89, 0x45, 0x3c, 0x24,
+  0x81, 0xa6, 0xf2, 0x3c, 0x02, 0x26, 0x09, 0x48, 0xdd, 0xfe, 0x4b, 0xb6,
+  0x66, 0xbf, 0x8f, 0xe5, 0x5f, 0xf0, 0x5d, 0x8a, 0x61, 0x2e, 0x5f, 0x9f,
+  0x80, 0xd9, 0xd5, 0xe6, 0x41, 0xd8, 0x10, 0x5e, 0x7a, 0xc6, 0xdb, 0x89,
+  0xc7, 0xca, 0x6c, 0x5b, 0xb1, 0x4e, 0x7d, 0x0c, 0x03, 0xfd, 0x50, 0xca,
+  0xbf, 0xbb, 0xe2, 0x69, 0x4b, 0x4e, 0xc2, 0x3d, 0x75, 0xfa, 0xd1, 0xcc,
+  0xd6, 0xf9, 0x39, 0xb9, 0xdc, 0x53, 0xad, 0x62, 0xfb, 0x1b, 0x94, 0x26,
+  0x7f, 0x21, 0x54, 0x5c, 0xb7, 0xdc, 0xe7, 0x96, 0x8c, 0xce, 0x75, 0xe0,
+  0x17, 0x01, 0x3a, 0x3c, 0x77, 0x6e, 0xa4, 0x8b, 0x7a, 0x83, 0x28, 0x7a,
+  0xf7, 0xb0, 0x5f, 0xfc, 0x7f, 0x2d, 0x2e, 0xec, 0xf5, 0xeb, 0x9c, 0x63,
+  0x74, 0xd0, 0xe5, 0xdc, 0x19, 0xe4, 0x71, 0xc5, 0x4a, 0x8a, 0x54, 0xa4,
+  0xe0, 0x7d, 0x4e, 0xbf, 0x53, 0x30, 0xaf, 0xd0, 0xeb, 0x96, 0xc3, 0xbb,
+  0x65, 0xf7, 0x67, 0xf5, 0xae, 0xd3, 0x96, 0xf2, 0x63, 0xc8, 0x69, 0xf7,
+  0x47, 0xcb, 0x27, 0x79, 0xe1, 0xff, 0x2f, 0x68, 0xdf, 0x1e, 0xb3, 0xb8,
+  0x0c, 0xc5, 0x58, 0x73, 0xcc, 0xfe, 0x8c, 0xda, 0x4e, 0x3b, 0x01, 0x04,
+  0xcd, 0xcb, 0xb8, 0x3e, 0x06, 0xfd, 0x4c, 0x0a, 0x9f, 0x5e, 0x76, 0x8c,
+  0x0c, 0x83, 0x75, 0x09, 0x08, 0xb2, 0xdb, 0xf4, 0x49, 0x4e, 0xa0, 0xf2,
+  0x0c, 0x7b, 0x87, 0x38, 0x9e, 0x22, 0x67, 0xbd, 0xd1, 0x97, 0x57, 0x24,
+  0xf1, 0x46, 0x07, 0xf9, 0xd2, 0x1b, 0xec, 0x25, 0x5e, 0x67, 0xd9, 0x66,
+  0x23, 0x1b, 0xd3, 0xe4, 0xaa, 0xec, 0x88, 0xf0, 0x7e, 0x15, 0x83, 0x51,
+  0x31, 0x67, 0x51, 0x76, 0x5f, 0x55, 0xd7, 0x36, 0xdf, 0x4a, 0x84, 0x0b,
+  0x6f, 0x5c, 0xbb, 0x5b, 0x8f, 0x37, 0x23, 0x7f, 0xf8, 0x17, 0x84, 0xa2,
+  0x70, 0x20, 0x07, 0x0c, 0x90, 0x3a, 0x04, 0xfd, 0xf0, 0x08, 0x4a, 0xb1,
+  0x16, 0x0f, 0xe6, 0xf6, 0x40, 0x51, 0x83, 0xd2, 0x87, 0x40, 0x9c, 0x1c,
+  0x9f, 0x13, 0x38, 0x17, 0xd3, 0x34, 0x58, 0xad, 0x05, 0x71, 0xa0, 0x73,
+  0xca, 0x40, 0xa6, 0xa4, 0x81, 0x02, 0xee, 0xa8, 0x72, 0x41, 0xa1, 0x41,
+  0x18, 0x64, 0x8a, 0x86, 0x8a, 0x5d, 0xe6, 0x4f, 0x0a, 0xc5, 0x95, 0x98,
+  0xf9, 0x78, 0xfe, 0x19, 0x0d, 0xc9, 0xb3, 0x89, 0xc1, 0x2b, 0x09, 0xbe,
+  0xf1, 0xd2, 0x04, 0x5d, 0xcc, 0x28, 0xf5, 0x4b, 0xd2, 0x20, 0x4f, 0xc5,
+  0x41, 0x9d, 0x8c, 0x85, 0xd8, 0xb0, 0x68, 0x5e, 0xc1, 0x0c, 0xb7, 0x24,
+  0x4d, 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30,
+  0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30,
+  0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02,
+  0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
+  0x14, 0xac, 0xf5, 0x47, 0x17, 0xd9, 0x7d, 0xc1, 0xb1, 0xc4, 0x41, 0xe1,
+  0x41, 0x60, 0xcb, 0x37, 0x11, 0x60, 0x28, 0x78, 0x5f, 0x30, 0x1f, 0x06,
+  0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x94,
+  0xfb, 0xf9, 0xb2, 0x43, 0xe9, 0x33, 0xd7, 0x50, 0x7d, 0xc7, 0x37, 0xdb,
+  0xd5, 0x82, 0x5a, 0x4e, 0xbe, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
+  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02,
+  0x01, 0x00, 0x96, 0x70, 0x65, 0x26, 0x42, 0xf8, 0xdc, 0x69, 0xde, 0xcf,
+  0x41, 0x3a, 0x2e, 0x7f, 0x5b, 0xf1, 0xf9, 0x3b, 0x9b, 0xd2, 0x4e, 0x64,
+  0x48, 0x81, 0xe4, 0x5d, 0x1e, 0x22, 0xce, 0x68, 0x63, 0x62, 0xe5, 0x1b,
+  0x9b, 0xf2, 0xc7, 0x12, 0xda, 0x1e, 0x9b, 0x90, 0x84, 0x79, 0x48, 0x12,
+  0xe6, 0x21, 0x6f, 0x2f, 0x7e, 0x18, 0x77, 0xdb, 0x8c, 0xc4, 0xd1, 0x0d,
+  0x91, 0xbf, 0x39, 0x22, 0x0f, 0x64, 0xcf, 0x25, 0x2e, 0x8c, 0x1f, 0x91,
+  0x81, 0xb5, 0xe9, 0x6c, 0x02, 0x3a, 0xf8, 0x07, 0xa2, 0x6f, 0x46, 0x5d,
+  0x7b, 0xfd, 0x43, 0xff, 0x41, 0x0f, 0xe2, 0x57, 0x1c, 0xbd, 0x48, 0x60,
+  0x53, 0x11, 0x48, 0x87, 0x88, 0x9d, 0x13, 0x82, 0x40, 0x68, 0x44, 0x2c,
+  0xc6, 0xc8, 0x95, 0x27, 0x4f, 0xb6, 0xb9, 0x4a, 0x22, 0x0a, 0xfd, 0xe4,
+  0x46, 0x8f, 0x35, 0x12, 0x98, 0x5a, 0x34, 0x6f, 0x2b, 0x57, 0x62, 0xa1,
+  0x4d, 0x8d, 0x79, 0x37, 0xe4, 0x6b, 0x8a, 0x32, 0x5b, 0xcb, 0xef, 0x79,
+  0x11, 0xed, 0xa7, 0xf8, 0x7a, 0x1c, 0xbd, 0x86, 0xdc, 0x0e, 0x2e, 0xfd,
+  0xd3, 0x51, 0xbb, 0x73, 0xad, 0x00, 0xa0, 0x1b, 0xf9, 0x1d, 0xd1, 0x4a,
+  0xe4, 0xd4, 0x02, 0x63, 0x2b, 0x39, 0x5f, 0x18, 0x08, 0x2f, 0x42, 0xb7,
+  0x23, 0x4b, 0x48, 0x46, 0x1f, 0x63, 0x87, 0xae, 0x6d, 0xd5, 0xdb, 0x60,
+  0xf8, 0x5f, 0xd3, 0x13, 0xec, 0xca, 0xdd, 0x60, 0x60, 0x79, 0x52, 0x70,
+  0x47, 0xae, 0x1d, 0x38, 0x78, 0x71, 0xcf, 0xb3, 0x04, 0x03, 0xbe, 0xba,
+  0x81, 0xba, 0x74, 0xb1, 0x30, 0x35, 0xdc, 0xea, 0x21, 0x4a, 0x9b, 0x70,
+  0xfb, 0xd6, 0x60, 0x59, 0x78, 0x0c, 0x4d, 0x39, 0x19, 0x1d, 0xe5, 0x75,
+  0xba, 0x07, 0xf4, 0x22, 0x37, 0x64, 0xb7, 0xf2, 0x9a, 0xc9, 0x11, 0x2d,
+  0x8e, 0x58, 0xa6, 0xcf, 0x83, 0xf1, 0xcb, 0x6c, 0x7f, 0x02, 0xbd, 0xda,
+  0x03, 0x92, 0xa9, 0x45, 0x24, 0x56, 0xc5, 0xbd, 0x41, 0xd1, 0x20, 0x86,
+  0xc0, 0xb6, 0xb7, 0xe8, 0xa7, 0xb2, 0x46, 0xf7, 0x8e, 0xa9, 0x38, 0x0e,
+  0x23, 0x77, 0x3c, 0x0d, 0x66, 0x83, 0x6a, 0x1a, 0x6b, 0x7f, 0x54, 0x11,
+  0x58, 0x0d, 0x4a, 0xb5, 0x74, 0x60, 0xca, 0xed, 0xff, 0x91, 0x47, 0xd9,
+  0x29, 0xe0, 0xaa, 0x8c, 0xa8, 0x8f, 0x10, 0x4c, 0x15, 0x7d, 0xce, 0x95,
+  0xf9, 0x87, 0x1e, 0x18, 0x38, 0x18, 0xfc, 0xcc, 0xaf, 0x91, 0x17, 0x3f,
+  0xfa, 0xf0, 0x8a, 0x09, 0x6f, 0xba, 0x4e, 0x53, 0xf7, 0xfa, 0x4f, 0x20,
+  0xa3, 0xf4, 0x4a, 0x5a, 0xde, 0x17, 0x1c, 0x29, 0x6a, 0x6f, 0x03, 0x48,
+  0xdf, 0xad, 0x4f, 0xe4, 0xbc, 0x71, 0xc4, 0x72, 0x32, 0x11, 0x84, 0xac,
+  0x09, 0xd2, 0x18, 0x44, 0x35, 0xf1, 0xcd, 0xaf, 0xa8, 0x98, 0xe0, 0x8b,
+  0xec, 0xa0, 0x83, 0x37, 0xc3, 0x35, 0x85, 0xd6, 0xd8, 0x1b, 0xe0, 0x75,
+  0xdc, 0xfd, 0xde, 0xc9, 0xeb, 0xd5, 0x18, 0x0f, 0xd3, 0x4c, 0x2f, 0x71,
+  0xdc, 0x48, 0xe3, 0x14, 0xeb, 0xda, 0x00, 0x24, 0x24, 0x9e, 0xa3, 0x8e,
+  0x3e, 0x08, 0x6f, 0x22, 0x24, 0xd6, 0xc4, 0x85, 0x8f, 0x68, 0x00, 0x4a,
+  0x82, 0x4c, 0x33, 0x6e, 0xa5, 0x35, 0x7b, 0xeb, 0x4b, 0xdc, 0xa0, 0xa6,
+  0x65, 0x6f, 0x5a, 0x7a, 0xdf, 0x8a, 0x01, 0x52, 0xa1, 0x6c, 0xff, 0x59,
+  0x22, 0x7f, 0xe1, 0x96, 0x1b, 0x19, 0xb8, 0xf9, 0x5d, 0x44, 0x9f, 0x91,
+  0x03, 0x3c, 0x3d, 0xa1, 0x2a, 0xb6, 0x5a, 0x51, 0xa0, 0xce, 0x4a, 0x88,
+  0x22, 0x72, 0x9c, 0xdc, 0xc0, 0x47, 0x76, 0x35, 0x84, 0x75, 0x9b, 0x87,
+  0x5c, 0xd3, 0xcf, 0xe7, 0xdd, 0xa3, 0x57, 0x14, 0xdf, 0x00, 0xfd, 0x19,
+  0x2a, 0x7d, 0x89, 0x27, 0x1c, 0x78, 0x97, 0x04, 0x58, 0x48
+};
+unsigned int certificate2_der_len = 1366;
+
+unsigned char hi_signed_2nd[] = {
+  0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb1, 0x06, 0x09, 0x2a, 0x86, 0x48,
+  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa2, 0x30, 0x82,
+  0x02, 0x9e, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
+  0x7b, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
+  0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
+  0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
+  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
+  0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
+  0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
+  0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
+  0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
+  0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+  0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
+  0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
+  0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
+  0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
+  0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
+  0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
+  0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
+  0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
+  0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
+  0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
+  0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
+  0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
+  0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
+  0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
+  0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
+  0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
+  0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
+  0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
+  0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
+  0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
+  0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
+  0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
+  0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
+  0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
+  0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
+  0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
+  0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
+  0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
+  0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
+  0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
+  0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
+  0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
+  0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
+  0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
+  0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
+  0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
+  0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
+  0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
+  0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
+  0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
+  0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
+  0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
+  0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
+  0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb5,
+  0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e,
+  0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64,
+  0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_signed_2nd_len = 736;
+
+unsigned char hi_double[] = {
+  0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48,
+  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82,
+  0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04,
+  0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
+  0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
+  0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
+  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
+  0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
+  0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
+  0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
+  0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
+  0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+  0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
+  0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
+  0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
+  0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
+  0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
+  0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
+  0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
+  0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
+  0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
+  0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
+  0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
+  0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
+  0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
+  0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
+  0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
+  0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
+  0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
+  0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
+  0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
+  0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
+  0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
+  0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
+  0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
+  0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
+  0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
+  0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
+  0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
+  0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
+  0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
+  0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
+  0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
+  0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
+  0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
+  0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
+  0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
+  0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
+  0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
+  0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
+  0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
+  0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
+  0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
+  0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
+  0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
+  0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31,
+  0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72,
+  0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20,
+  0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65,
+  0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+  0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+  0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a,
+  0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b,
+  0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30,
+  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+  0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97,
+  0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf,
+  0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77,
+  0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55,
+  0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2,
+  0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88,
+  0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1,
+  0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3,
+  0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45,
+  0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86,
+  0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd,
+  0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27,
+  0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1,
+  0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a,
+  0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d,
+  0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5,
+  0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1,
+  0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04,
+  0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a,
+  0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c,
+  0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6,
+  0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9,
+  0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60,
+  0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83,
+  0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7,
+  0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35,
+  0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f,
+  0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0,
+  0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa,
+  0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2,
+  0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3,
+  0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24,
+  0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6,
+  0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e,
+  0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74,
+  0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b,
+  0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a,
+  0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc,
+  0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83,
+  0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21,
+  0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51,
+  0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19,
+  0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35,
+  0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x05, 0x33, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69,
+  0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65,
+  0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_double_len = 1374;
+
+unsigned char hi_double_extended[] = {
+  0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48,
+  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82,
+  0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
+  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
+  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04,
+  0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
+  0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
+  0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
+  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
+  0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
+  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
+  0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
+  0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
+  0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
+  0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
+  0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
+  0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
+  0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
+  0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
+  0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
+  0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
+  0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
+  0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
+  0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
+  0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
+  0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
+  0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
+  0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
+  0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
+  0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
+  0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
+  0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
+  0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
+  0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
+  0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
+  0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
+  0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
+  0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
+  0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
+  0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
+  0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
+  0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
+  0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
+  0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
+  0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
+  0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
+  0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
+  0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
+  0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
+  0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
+  0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
+  0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
+  0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
+  0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
+  0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
+  0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
+  0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
+  0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
+  0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31,
+  0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72,
+  0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20,
+  0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65,
+  0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
+  0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
+  0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a,
+  0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b,
+  0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30,
+  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+  0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97,
+  0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf,
+  0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77,
+  0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55,
+  0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2,
+  0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88,
+  0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1,
+  0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3,
+  0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45,
+  0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86,
+  0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd,
+  0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27,
+  0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1,
+  0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a,
+  0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d,
+  0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5,
+  0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1,
+  0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04,
+  0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a,
+  0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c,
+  0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6,
+  0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9,
+  0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60,
+  0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83,
+  0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7,
+  0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35,
+  0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f,
+  0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0,
+  0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa,
+  0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2,
+  0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3,
+  0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24,
+  0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6,
+  0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e,
+  0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74,
+  0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b,
+  0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a,
+  0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc,
+  0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83,
+  0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21,
+  0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51,
+  0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19,
+  0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35,
+  0x23, 0x2d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x05, 0x34, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
+  0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
+  0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
+};
+unsigned int hi_double_extended_len = 1375;
+
+unsigned char certificate_printable_der[] = {
+  0x30, 0x82, 0x03, 0x39, 0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01,
+  0x02, 0x02, 0x09, 0x00, 0xde, 0xf6, 0x22, 0xc4, 0xf2, 0xf1, 0x86, 0x02,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x0b, 0x05, 0x00, 0x30, 0x2a, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55,
+  0x04, 0x03, 0x13, 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
+  0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
+  0x43, 0x41, 0x20, 0x32, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30,
+  0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33, 0x31, 0x31, 0x34, 0x31,
+  0x39, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x30, 0x32, 0x35,
+  0x31, 0x34, 0x31, 0x39, 0x32, 0x33, 0x5a, 0x30, 0x2f, 0x31, 0x2d, 0x30,
+  0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x52, 0x65, 0x64, 0x20,
+  0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42,
+  0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20,
+  0x33, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, 0x82, 0x01, 0x22,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+  0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0xda, 0xa1, 0xed, 0x8d, 0x8e, 0x15,
+  0x5c, 0xf8, 0x01, 0x77, 0x48, 0x4a, 0x60, 0x96, 0xf9, 0x27, 0xfa, 0xe2,
+  0xb1, 0x69, 0x0f, 0x51, 0x19, 0x52, 0x7e, 0xc4, 0x34, 0x8e, 0xe1, 0x9b,
+  0x9c, 0xa4, 0xb1, 0x5c, 0xd6, 0x81, 0x98, 0x78, 0xfe, 0xa9, 0xe5, 0x0b,
+  0x00, 0xba, 0x9c, 0x64, 0x7e, 0xc7, 0xcc, 0x72, 0xb1, 0x73, 0x4b, 0x11,
+  0x07, 0x52, 0xf0, 0x20, 0x96, 0x8b, 0x99, 0x39, 0xde, 0xdb, 0xfa, 0x3d,
+  0x45, 0xe2, 0x98, 0x7b, 0x0c, 0x41, 0xe4, 0x0c, 0xb5, 0x5d, 0x92, 0x74,
+  0x39, 0x96, 0xe1, 0x97, 0x97, 0xa1, 0xad, 0x2e, 0xcc, 0xd0, 0x1b, 0x4d,
+  0x9d, 0xbd, 0x3e, 0xa9, 0x36, 0x8e, 0xcc, 0xc7, 0x5f, 0x6a, 0x7d, 0x39,
+  0x5e, 0x0b, 0x8d, 0xca, 0xe4, 0x83, 0xe9, 0x3b, 0x5c, 0x86, 0x47, 0xd4,
+  0xba, 0x7d, 0x98, 0x26, 0xa1, 0xf4, 0xe8, 0x90, 0x6b, 0x0f, 0xf1, 0x6b,
+  0x8c, 0xe3, 0xa2, 0x80, 0x3c, 0x96, 0xf1, 0x0a, 0xb6, 0x66, 0xc0, 0x4b,
+  0x61, 0xf7, 0x74, 0xcd, 0xd3, 0x7b, 0x8e, 0x5e, 0x39, 0xda, 0x99, 0x20,
+  0x33, 0x93, 0xd3, 0xf0, 0x7f, 0xad, 0x35, 0xe9, 0x88, 0x8d, 0x9c, 0xbf,
+  0x65, 0xf1, 0x47, 0x02, 0xf9, 0x7c, 0xed, 0x27, 0x5f, 0x4a, 0x65, 0x3c,
+  0xcf, 0x5f, 0x0e, 0x88, 0x95, 0x74, 0xde, 0xfb, 0x9e, 0x2e, 0x91, 0x9b,
+  0x45, 0x37, 0xc8, 0x85, 0xff, 0xe3, 0x41, 0x70, 0xfe, 0xd5, 0xef, 0x0e,
+  0x82, 0x22, 0x08, 0xb7, 0x3b, 0x44, 0x3e, 0xdc, 0x5b, 0x7f, 0xba, 0xbf,
+  0xe6, 0x58, 0x9d, 0x02, 0x6e, 0x75, 0xbf, 0x50, 0xec, 0xcf, 0x3f, 0xa5,
+  0x91, 0x0a, 0xe2, 0x59, 0x2c, 0xc3, 0xe7, 0x05, 0x03, 0xe8, 0xf2, 0x6f,
+  0x2a, 0x04, 0x68, 0x9a, 0x31, 0x32, 0x8f, 0x04, 0x35, 0xcd, 0x1f, 0x34,
+  0xcc, 0x4f, 0x79, 0x5a, 0x99, 0x8d, 0x9d, 0x5c, 0xf5, 0x02, 0x03, 0x01,
+  0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
+  0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03,
+  0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06,
+  0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x65, 0xc5, 0xbe, 0xca,
+  0xe6, 0x59, 0x6a, 0xfd, 0x6c, 0x71, 0xc4, 0xa7, 0x98, 0xc6, 0x25, 0x8d,
+  0x7b, 0x67, 0x05, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+  0x18, 0x30, 0x16, 0x80, 0x14, 0x81, 0xf8, 0xee, 0x47, 0x5c, 0x3e, 0xed,
+  0xfb, 0xce, 0xa5, 0x84, 0xbe, 0xd7, 0xae, 0xdb, 0xd3, 0x7d, 0x64, 0xb3,
+  0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+  0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x1e, 0x3d,
+  0x1d, 0x53, 0x33, 0xde, 0x4e, 0xc7, 0xc4, 0xf4, 0xdf, 0xda, 0x18, 0x19,
+  0x8a, 0xa9, 0xff, 0xe2, 0x63, 0x2b, 0xbe, 0xf2, 0x61, 0x63, 0xe2, 0xf6,
+  0xed, 0x47, 0x1a, 0x71, 0x02, 0xec, 0x2a, 0xef, 0x89, 0x77, 0xe3, 0xfd,
+  0x86, 0x69, 0xf1, 0x3f, 0x0d, 0xf9, 0x6e, 0xf9, 0x3b, 0xad, 0x26, 0x47,
+  0xb7, 0xf2, 0x0d, 0xad, 0x23, 0xa3, 0x67, 0x3b, 0xcb, 0x6d, 0x9e, 0x03,
+  0x0f, 0xbc, 0x69, 0x73, 0x9f, 0xd4, 0xa5, 0x0f, 0x6f, 0xf8, 0xab, 0x4d,
+  0x36, 0xd1, 0xe0, 0xe0, 0x5d, 0x20, 0x43, 0x90, 0xc4, 0x65, 0x61, 0x93,
+  0xe2, 0x0f, 0x51, 0x59, 0x0a, 0xf7, 0x88, 0x70, 0x57, 0xb9, 0x04, 0xa9,
+  0x32, 0x57, 0x9c, 0xb3, 0x57, 0x38, 0x8b, 0x8e, 0x46, 0xc8, 0x32, 0x6c,
+  0xb4, 0xf3, 0x96, 0x7f, 0x4b, 0xf0, 0x88, 0xf9, 0x7f, 0xe2, 0x71, 0xe1,
+  0x8b, 0xe2, 0x14, 0xf1, 0x4b, 0x25, 0x00, 0x48, 0x1c, 0x7e, 0xe5, 0x8d,
+  0x65, 0x2d, 0xeb, 0x72, 0x4f, 0x92, 0x44, 0xf3, 0xe6, 0xe0, 0xd0, 0xdf,
+  0x85, 0xa8, 0x13, 0x4a, 0xfb, 0x99, 0xca, 0x14, 0x2c, 0x97, 0x80, 0x93,
+  0x27, 0xd3, 0x20, 0xf8, 0x6d, 0x29, 0x28, 0x2c, 0xb9, 0x77, 0xea, 0xb1,
+  0x63, 0xbd, 0x7d, 0x53, 0xfd, 0x4a, 0x62, 0x64, 0x0b, 0x98, 0xa8, 0xae,
+  0x11, 0xfc, 0x6e, 0x8d, 0x63, 0xd4, 0x15, 0x55, 0xc6, 0x4c, 0x74, 0xf5,
+  0x5f, 0xa0, 0xb9, 0x2c, 0x2d, 0x9a, 0x7a, 0x87, 0x6e, 0xf0, 0x5e, 0x25,
+  0xed, 0xfc, 0xd8, 0xc4, 0x34, 0x33, 0x32, 0xad, 0x01, 0xd4, 0x4b, 0x49,
+  0x51, 0xc2, 0x07, 0x7f, 0x90, 0x6d, 0xea, 0xf5, 0x4c, 0x41, 0x71, 0x64,
+  0xeb, 0x1f, 0x29, 0xa3, 0x1f, 0x64, 0xa2, 0x1e, 0x0e, 0x6f, 0xa1, 0x67,
+  0x99, 0x8d, 0x98, 0x1c, 0xb8, 0x53, 0x9d, 0x30, 0x1d, 0xae, 0x32, 0x56,
+  0xd2
+};
+unsigned int certificate_printable_der_len = 829;
+
+unsigned char certificate_eku_der[] = {
+  0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01,
+  0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
+  0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
+  0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
+  0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86,
+  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63,
+  0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74,
+  0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32,
+  0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33,
+  0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a,
+  0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
+  0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63,
+  0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67,
+  0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20,
+  0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
+  0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65,
+  0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22,
+  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+  0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+  0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15,
+  0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85,
+  0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06,
+  0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5,
+  0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76,
+  0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd,
+  0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c,
+  0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9,
+  0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78,
+  0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29,
+  0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb,
+  0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09,
+  0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6,
+  0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27,
+  0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7,
+  0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55,
+  0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68,
+  0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90,
+  0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62,
+  0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16,
+  0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b,
+  0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01,
+  0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
+  0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03,
+  0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80,
+  0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c,
+  0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03,
+  0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c,
+  0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f,
+  0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55,
+  0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab,
+  0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4,
+  0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+  0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+  0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9,
+  0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef,
+  0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29,
+  0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b,
+  0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf,
+  0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32,
+  0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9,
+  0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74,
+  0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb,
+  0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0,
+  0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24,
+  0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06,
+  0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57,
+  0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61,
+  0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21,
+  0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1,
+  0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81,
+  0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3,
+  0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e,
+  0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd,
+  0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21,
+  0x89, 0xa0, 0x55, 0xf7
+};
+unsigned int certificate_eku_der_len = 916;
diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c
index 96781fb39b5f..403fa5c789ab 100644
--- a/grub-core/tests/lib/functional_test.c
+++ b/grub-core/tests/lib/functional_test.c
@@ -73,6 +73,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)),
   grub_dl_load ("xnu_uuid_test");
   grub_dl_load ("pbkdf2_test");
   grub_dl_load ("signature_test");
+  grub_dl_load ("appended_signature_test");
   grub_dl_load ("sleep_test");
   grub_dl_load ("bswap_test");
   grub_dl_load ("ctz_test");
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 21/22] appended signatures: documentation
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (19 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 20/22] appended signatures: verification tests Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-19 22:24   ` Stefan Berger
  2021-06-30  8:40 ` [PATCH v2 22/22] ieee1275: enter lockdown based on /ibm,secure-boot Daniel Axtens
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

This explains how appended signatures can be used to form part of
a secure boot chain, and documents the commands and variables
introduced.

Signed-off-by: Daniel Axtens <dja@axtens.net>

---

v2: fix a grammar issue, thanks Stefan Berger.
---
 docs/grub.texi | 193 ++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 176 insertions(+), 17 deletions(-)

diff --git a/docs/grub.texi b/docs/grub.texi
index bed565371460..02fcda11e3bd 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -3209,6 +3209,7 @@ These variables have special meaning to GRUB.
 
 @menu
 * biosnum::
+* check_appended_signatures::
 * check_signatures::
 * chosen::
 * cmdpath::
@@ -3268,11 +3269,18 @@ For an alternative approach which also changes BIOS drive mappings for the
 chain-loaded system, @pxref{drivemap}.
 
 
+@node check_appended_signatures
+@subsection check_appended_signatures
+
+This variable controls whether GRUB enforces appended signature validation on
+certain loaded files. @xref{Using appended signatures}.
+
+
 @node check_signatures
 @subsection check_signatures
 
-This variable controls whether GRUB enforces digital signature
-validation on loaded files. @xref{Using digital signatures}.
+This variable controls whether GRUB enforces GPG-style digital signature
+validation on loaded files. @xref{Using GPG-style digital signatures}.
 
 @node chosen
 @subsection chosen
@@ -3989,6 +3997,7 @@ you forget a command, you can run the command @command{help}
 * date::                        Display or set current date and time
 * devicetree::                  Load a device tree blob
 * distrust::                    Remove a pubkey from trusted keys
+* distrust_certificate::        Remove a certificate from the list of trusted certificates
 * drivemap::                    Map a drive to another
 * echo::                        Display a line of text
 * eval::                        Evaluate agruments as GRUB commands
@@ -4005,6 +4014,7 @@ you forget a command, you can run the command @command{help}
 * keystatus::                   Check key modifier status
 * linux::                       Load a Linux kernel
 * linux16::                     Load a Linux kernel (16-bit mode)
+* list_certificates::           List trusted certificates
 * list_env::                    List variables in environment block
 * list_trusted::                List trusted public keys
 * load_env::                    Load variables from environment block
@@ -4042,8 +4052,10 @@ you forget a command, you can run the command @command{help}
 * test::                        Check file types and compare values
 * true::                        Do nothing, successfully
 * trust::                       Add public key to list of trusted keys
+* trust_certificate::           Add an x509 certificate to the list of trusted certificates
 * unset::                       Unset an environment variable
 @comment * vbeinfo::                     List available video modes
+* verify_appended::             Verify appended digital signature
 * verify_detached::             Verify detached digital signature
 * videoinfo::                   List available video modes
 @comment * xen_*::              Xen boot commands for AArch64
@@ -4371,9 +4383,28 @@ These keys are used to validate signatures when environment variable
 @code{check_signatures} is set to @code{enforce}
 (@pxref{check_signatures}), and by some invocations of
 @command{verify_detached} (@pxref{verify_detached}).  @xref{Using
-digital signatures}, for more information.
+GPG-style digital signatures}, for more information.
+@end deffn
+
+
+@node distrust_certificate
+@subsection distrust_certificate
+
+@deffn Command distrust_certificate cert_number
+Remove the x509 certificate numbered @var{cert_number} from GRUB's keyring of
+trusted x509 certificates for verifying appended signatures.
+
+@var{cert_number} is the certificate number as listed by
+@command{list_certificates} (@pxref{list_certificates}).
+
+These certificates are used to validate appended signatures when environment
+variable @code{check_appended_signatures} is set to @code{enforce}
+(@pxref{check_appended_signatures}), and by @command{verify_appended}
+(@pxref{verify_appended}). See @xref{Using appended signatures} for more
+information.
 @end deffn
 
+
 @node drivemap
 @subsection drivemap
 
@@ -4631,6 +4662,21 @@ This command is only available on x86 systems.
 @end deffn
 
 
+@node list_certificates
+@subsection list_certificates
+
+@deffn Command list_certificates
+List all x509 certificates trusted by GRUB for validating appended signatures.
+The output is a numbered list of certificates, showing the certificate's serial
+number and Common Name.
+
+The certificate number can be used as an argument to
+@command{distrust_certificate} (@pxref{distrust_certificate}).
+
+See @xref{Using appended signatures} for more information.
+@end deffn
+
+
 @node list_env
 @subsection list_env
 
@@ -4650,7 +4696,7 @@ The output is in GPG's v4 key fingerprint format (i.e., the output of
 @code{gpg --fingerprint}).  The least significant four bytes (last
 eight hexadecimal digits) can be used as an argument to
 @command{distrust} (@pxref{distrust}).
-@xref{Using digital signatures}, for more information about uses for
+@xref{Using GPG-style digital signatures}, for more information about uses for
 these keys.
 @end deffn
 
@@ -4685,8 +4731,12 @@ When used with care, @option{--skip-sig} and the whitelist enable an
 administrator to configure a system to boot only signed
 configurations, but to allow the user to select from among multiple
 configurations, and to enable ``one-shot'' boot attempts and
-``savedefault'' behavior.  @xref{Using digital signatures}, for more
+``savedefault'' behavior.  @xref{Using GPG-style digital signatures}, for more
 information.
+
+Extra care should be taken when combining this command with appended signatures
+(@pxref{Using appended signatures}), as this file is not validated by an
+appended signature and could set @code{check_appended_signatures=no}.
 @end deffn
 
 
@@ -4982,7 +5032,7 @@ read.  It is possible to modify a digitally signed environment block
 file from within GRUB using this command, such that its signature will
 no longer be valid on subsequent boots.  Care should be taken in such
 advanced configurations to avoid rendering the system
-unbootable. @xref{Using digital signatures}, for more information.
+unbootable. @xref{Using GPG-style digital signatures}, for more information.
 @end deffn
 
 
@@ -5382,11 +5432,31 @@ signatures when environment variable @code{check_signatures} is set to
 must itself be properly signed.  The @option{--skip-sig} option can be
 used to disable signature-checking when reading @var{pubkey_file}
 itself. It is expected that @option{--skip-sig} is useful for testing
-and manual booting. @xref{Using digital signatures}, for more
+and manual booting. @xref{Using GPG-style digital signatures}, for more
 information.
 @end deffn
 
 
+@node trust_certificate
+@subsection trust_certificate
+
+@deffn Command trust_certificate x509_certificate
+Read a DER-formatted x509 certificate from the file @var{x509_certificate}
+and add it to GRUB's internal list of trusted x509 certificates. These
+certificates are used to validate appended signatures when the environment
+variable @code{check_appended_signatures} is set to @code{enforce}.
+
+Note that if @code{check_appended_signatures} is set to @code{enforce}
+when @command{trust_certificate} is executed, then @var{x509_certificate}
+must itself bear an appended signature. (It is not sufficient that
+@var{x509_certificate} be signed by a trusted certificate according to the
+x509 rules: grub does not include support for validating signatures within x509
+certificates themselves.)
+
+See @xref{Using appended signatures} for more information.
+@end deffn
+
+
 @node unset
 @subsection unset
 
@@ -5405,6 +5475,18 @@ only on PC BIOS platforms.
 @end deffn
 @end ignore
 
+@node verify_appended
+@subsection verify_appended
+
+@deffn Command verify_appended file
+Verifies an appended signature on @var{file} against the trusted certificates
+known to GRUB (See @pxref{list_certificates}, @pxref{trust_certificate}, and
+@pxref{distrust_certificate}).
+
+Exit code @code{$?} is set to 0 if the signature validates
+successfully.  If validation fails, it is set to a non-zero value.
+See @xref{Using appended signatures}, for more information.
+@end deffn
 
 @node verify_detached
 @subsection verify_detached
@@ -5423,7 +5505,7 @@ tried.
 
 Exit code @code{$?} is set to 0 if the signature validates
 successfully.  If validation fails, it is set to a non-zero value.
-@xref{Using digital signatures}, for more information.
+@xref{Using GPG-style digital signatures}, for more information.
 @end deffn
 
 @node videoinfo
@@ -5789,13 +5871,14 @@ environment variables and commands are listed in the same order.
 @chapter Security
 
 @menu
-* Authentication and authorisation:: Users and access control
-* Using digital signatures::         Booting digitally signed code
-* UEFI secure boot and shim::        Booting digitally signed PE files
-* Secure Boot Advanced Targeting::   Embedded information for generation number based revocation
-* Measured Boot::                    Measuring boot components
-* Lockdown::                         Lockdown when booting on a secure setup
-* Signing GRUB itself::              Ensuring the integrity of the GRUB core image
+* Authentication and authorisation::   Users and access control
+* Using GPG-style digital signatures:: Booting digitally signed code
+* Using appended signatures::          An alternative approach to booting digitally signed code
+* UEFI secure boot and shim::          Booting digitally signed PE files
+* Secure Boot Advanced Targeting::     Embedded information for generation number based revocation
+* Measured Boot::                      Measuring boot components
+* Lockdown::                           Lockdown when booting on a secure setup
+* Signing GRUB itself::                Ensuring the integrity of the GRUB core image
 @end menu
 
 @node Authentication and authorisation
@@ -5869,8 +5952,8 @@ generating configuration files with authentication.  You can use
 adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2}
 commands.
 
-@node Using digital signatures
-@section Using digital signatures in GRUB
+@node Using GPG-style digital signatures
+@section Using GPG-style digital signatures in GRUB
 
 GRUB's @file{core.img} can optionally provide enforcement that all files
 subsequently read from disk are covered by a valid digital signature.
@@ -5953,6 +6036,82 @@ or BIOS) configuration to cause the machine to boot from a different
 (attacker-controlled) device.  GRUB is at best only one link in a
 secure boot chain.
 
+@node Using appended signatures
+@section Using appended signatures in GRUB
+
+GRUB supports verifying Linux-style 'appended signatures' for secure boot.
+Appended signatures are PKCS#7 messages containing a signature over the
+contents of a file, plus some metadata, appended to the end of a file. A file
+with an appended signature ends with the magic string:
+
+@example
+~Module signature appended~\n
+@end example
+
+where @code{\n} represents the carriage-return character, @code{0x0a}.
+
+To enable appended signature verification, load the appendedsig module and an
+x509 certificate for verification. Building the appendedsig module into the
+core grub image is recommended.
+
+Certificates can be managed at boot time using the @pxref{trust_certificate},
+@pxref{distrust_certificate} and @pxref{list_certificates} commands.
+Certificates can also be built in to the core image using the @code{--x509}
+parameter to @command{grub-install} or @command{grub-mkimage}.
+
+A file can be explictly verified using the @pxref{verify_appended} command.
+
+Only signatures made with the SHA-256 or SHA-512 hash algorithm are supported,
+and only RSA signatures are supported.
+
+A file can be signed with the @command{sign-file} utility supplied with the
+Linux kernel source. For example, if you have @code{signing.key} as the private
+key and @code{certificate.der} as the x509 certificate containing the public key:
+
+@example
+sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed
+@end example
+
+Enforcement of signature verification is controlled by the
+@code{check_appended_signatures} variable. Verification will only take place
+when files are loaded if the variable is set to @code{enforce}. If a
+certificate is built into the grub core image with the @code{--x509} parameter,
+the variable will be automatically set to @code{enforce} when the appendedsig
+module is loaded.
+
+Unlike GPG-style signatures, not all files loaded by GRUB are required to be
+signed. Once verification is turned on, the following file types must carry
+appended signatures:
+
+@enumerate
+@item Linux, Multiboot, BSD, XNU and Plan9 kernels
+@item Grub modules, except those built in to the core image
+@item Any new certificate files to be trusted
+@end enumerate
+
+ACPI tables and Device Tree images will not be checked for appended signatures
+but must be verified by another mechanism such as GPG-style signatures before
+they will be loaded.
+
+No attempt is made to validate any other file type. In particular,
+chain-loaded binaries are not verified - if your platform supports
+chain-loading and this cannot be disabled, consider an alternative secure
+boot mechanism.
+
+As with GPG-style appended signatures, signature checking does @strong{not}
+stop an attacker with console access from dropping manually to the GRUB
+console and executing:
+
+@example
+set check_appended_signatures=no
+@end example
+
+Refer to the section on password-protecting GRUB (@pxref{Authentication
+and authorisation}) for more information on preventing this.
+
+Additionally, special care must be taken around the @command{loadenv} command,
+which can be used to turn off @code{check_appended_signature}.
+
 @node UEFI secure boot and shim
 @section UEFI secure boot and shim support
 
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* [PATCH v2 22/22] ieee1275: enter lockdown based on /ibm,secure-boot
  2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
                   ` (20 preceding siblings ...)
  2021-06-30  8:40 ` [PATCH v2 21/22] appended signatures: documentation Daniel Axtens
@ 2021-06-30  8:40 ` Daniel Axtens
  2021-07-19 22:08   ` Stefan Berger
  21 siblings, 1 reply; 56+ messages in thread
From: Daniel Axtens @ 2021-06-30  8:40 UTC (permalink / raw)
  To: grub-devel; +Cc: rashmica.g, alastair, nayna, Daniel Axtens

If the 'ibm,secure-boot' property of the root node is 2 or greater,
enter lockdown.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 docs/grub.texi                 |  4 ++--
 grub-core/Makefile.core.def    |  1 +
 grub-core/kern/ieee1275/init.c | 27 +++++++++++++++++++++++++++
 include/grub/lockdown.h        |  3 ++-
 4 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/docs/grub.texi b/docs/grub.texi
index 02fcda11e3bd..b13316cdb491 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6189,8 +6189,8 @@ Measured boot is currently only supported on EFI platforms.
 @section Lockdown when booting on a secure setup
 
 The GRUB can be locked down when booted on a secure boot environment, for example
-if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will
-be restricted and some operations/commands cannot be executed.
+if UEFI or Power secure boot is enabled. On a locked down configuration, the
+GRUB will be restricted and some operations/commands cannot be executed.
 
 The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down.
 Otherwise it does not exit.
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 4aa4cf263d94..775a031f1843 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -317,6 +317,7 @@ kernel = {
   powerpc_ieee1275 = kern/powerpc/cache.S;
   powerpc_ieee1275 = kern/powerpc/dl.c;
   powerpc_ieee1275 = kern/powerpc/compiler-rt.S;
+  powerpc_ieee1275 = kern/lockdown.c;
 
   sparc64_ieee1275 = kern/sparc64/cache.S;
   sparc64_ieee1275 = kern/sparc64/dl.c;
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
index 4586bec939b2..5faf4e736074 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -44,6 +44,7 @@
 #ifdef __sparc__
 #include <grub/machine/kernel.h>
 #endif
+#include <grub/lockdown.h>
 
 /* The maximum heap size we're going to claim. Not used by sparc.
    We allocate 1/4 of the available memory under 4G, up to this limit. */
@@ -442,6 +443,30 @@ grub_parse_cmdline (void)
     }
 }
 
+static void
+grub_get_ieee1275_secure_boot (void)
+{
+  grub_ieee1275_phandle_t root;
+  int rc;
+  grub_uint32_t is_sb;
+
+  grub_ieee1275_finddevice ("/", &root);
+
+  rc = grub_ieee1275_get_integer_property (root, "ibm,secure-boot", &is_sb,
+                                           sizeof (is_sb), 0);
+
+  /* ibm,secure-boot:
+   * 0 - disabled
+   * 1 - audit
+   * 2 - enforce
+   * 3 - enforce + OS-specific behaviour
+   *
+   * We only support enforce.
+   */
+  if (rc >= 0 && is_sb >= 2)
+    grub_lockdown ();
+}
+
 grub_addr_t grub_modbase;
 
 void
@@ -467,6 +492,8 @@ grub_machine_init (void)
 #else
   grub_install_get_time_ms (grub_rtc_get_time_ms);
 #endif
+
+  grub_get_ieee1275_secure_boot ();
 }
 
 void
diff --git a/include/grub/lockdown.h b/include/grub/lockdown.h
index 40531fa823bf..ebfee4bf06e7 100644
--- a/include/grub/lockdown.h
+++ b/include/grub/lockdown.h
@@ -24,7 +24,8 @@
 #define GRUB_LOCKDOWN_DISABLED       0
 #define GRUB_LOCKDOWN_ENABLED        1
 
-#ifdef GRUB_MACHINE_EFI
+#if defined(GRUB_MACHINE_EFI) || \
+    (defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275))
 extern void
 EXPORT_FUNC (grub_lockdown) (void);
 extern int
-- 
2.30.2



^ permalink raw reply related	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE
  2021-06-30  8:40 ` [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE Daniel Axtens
@ 2021-07-12 12:33   ` Stefan Berger
  2021-07-14 16:21   ` Daniel Kiper
  1 sibling, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:33 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> HEAP_MAX_ADDR is confusing. Currently it is set to 32MB, except
> on ieee1275 on x86, where it is 64MB.
>
> There is a comment which purports to explain it:
>
> /* If possible, we will avoid claiming heap above this address, because it
>     seems to cause relocation problems with OSes that link at 4 MiB */
>
> This doesn't make a lot of sense when the constants are well above 4MB
> already. It was not always this way. Prior to
> commit 7b5d0fe4440c ("Increase heap limit") in 2010, HEAP_MAX_SIZE and
> HEAP_MAX_ADDR were indeed 4MB. However, when the constants were increased
> the comment was left unchanged.
>
> It's been over a decade. It doesn't seem like we have problems with
> claims over 4MB on powerpc or x86 ieee1275. (sparc does things completely
> differently and never used the constant.)
>
> Drop the constant and the check.
>
> The only use of HEAP_MIN_SIZE was to potentially override the
> HEAP_MAX_ADDR check. It is now unused. Remove it.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>


I tested this patch and the other 2 memory related patches in a ppc64 
QEMU VM where I need them for getting more memory for trusted boot 
enablement on ppc64. Without these patches it runs out of memory. From 
what I can see they work fine.

Tested-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   grub-core/kern/ieee1275/init.c | 17 -----------------
>   1 file changed, 17 deletions(-)
>
> diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
> index d483e35eed2b..c5d091689f29 100644
> --- a/grub-core/kern/ieee1275/init.c
> +++ b/grub-core/kern/ieee1275/init.c
> @@ -45,9 +45,6 @@
>   #include <grub/machine/kernel.h>
>   #endif
>   
> -/* The minimal heap size we can live with. */
> -#define HEAP_MIN_SIZE		(unsigned long) (2 * 1024 * 1024)
> -
>   /* The maximum heap size we're going to claim */
>   #ifdef __i386__
>   #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
> @@ -55,14 +52,6 @@
>   #define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
>   #endif
>   
> -/* If possible, we will avoid claiming heap above this address, because it
> -   seems to cause relocation problems with OSes that link at 4 MiB */
> -#ifdef __i386__
> -#define HEAP_MAX_ADDR		(unsigned long) (64 * 1024 * 1024)
> -#else
> -#define HEAP_MAX_ADDR		(unsigned long) (32 * 1024 * 1024)
> -#endif
> -
>   extern char _start[];
>   extern char _end[];
>   
> @@ -183,12 +172,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>     if (*total + len > HEAP_MAX_SIZE)
>       len = HEAP_MAX_SIZE - *total;
>   
> -  /* Avoid claiming anything above HEAP_MAX_ADDR, if possible. */
> -  if ((addr < HEAP_MAX_ADDR) &&				/* if it's too late, don't bother */
> -      (addr + len > HEAP_MAX_ADDR) &&				/* if it wasn't available anyway, don't bother */
> -      (*total + (HEAP_MAX_ADDR - addr) > HEAP_MIN_SIZE))	/* only limit ourselves when we can afford to */
> -     len = HEAP_MAX_ADDR - addr;
> -
>     /* In theory, firmware should already prevent this from happening by not
>        listing our own image in /memory/available.  The check below is intended
>        as a safeguard in case that doesn't happen.  However, it doesn't protect


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 02/22] ieee1275: claim more memory
  2021-06-30  8:40 ` [PATCH v2 02/22] ieee1275: claim more memory Daniel Axtens
@ 2021-07-12 12:35   ` Stefan Berger
  2021-07-15 21:51   ` Daniel Kiper
  1 sibling, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:35 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> On powerpc-ieee1275, we are running out of memory trying to verify
> anything. This is because:
>
>   - we have to load an entire file into memory to verify it. This is
>     extremely difficult to change with appended signatures.
>   - We only have 32MB of heap.
>   - Distro kernels are now often around 30MB.
>
> So we want to claim more memory from OpenFirmware for our heap.
>
> There are some complications:
>
>   - The grub mm code isn't the only thing that will make claims on
>     memory from OpenFirmware:
>
>      * PFW/SLOF will have claimed some for their own use.
>
>      * The ieee1275 loader will try to find other bits of memory that we
>        haven't claimed to place the kernel and initrd when we go to boot.
>
>      * Once we load Linux, it will also try to claim memory. It claims
>        memory without any reference to /memory/available, it just starts
>        at min(top of RMO, 768MB) and works down. So we need to avoid this
>        area. See arch/powerpc/kernel/prom_init.c as of v5.11.
>
>   - The smallest amount of memory a ppc64 KVM guest can have is 256MB.
>     It doesn't work with distro kernels but can work with custom kernels.
>     We should maintain support for that. (ppc32 can boot with even less,
>     and we shouldn't break that either.)
>
>   - Even if a VM has more memory, the memory OpenFirmware makes available
>     as Real Memory Area can be restricted. A freshly created LPAR on a
>     PowerVM machine is likely to have only 256MB available to OpenFirmware
>     even if it has many gigabytes of memory allocated.
>
> EFI systems will attempt to allocate 1/4th of the available memory,
> clamped to between 1M and 1600M. That seems like a good sort of
> approach, we just need to figure out if 1/4 is the right fraction
> for us.
>
> We don't know in advance how big the kernel and initrd are going to be,
> which makes figuring out how much memory we can take a bit tricky.
>
> To figure out how much memory we should leave unused, I looked at:
>
>   - an Ubuntu 20.04.1 ppc64le pseries KVM guest:
>      vmlinux: ~30MB
>      initrd:  ~50MB
>
>   - a RHEL8.2 ppc64le pseries KVM guest:
>      vmlinux: ~30MB
>      initrd:  ~30MB
>
> Ubuntu VMs struggle to boot with just 256MB under SLOF.
> RHEL likewise has a higher minimum supported memory figure.
> So lets first consider a distro kernel and 512MB of addressible memory.
> (This is the default case for anything booting under PFW.) Say we lose
> 131MB to PFW (based on some tests). This leaves us 381MB. 1/4 of 381MB
> is ~95MB. That should be enough to verify a 30MB vmlinux and should
> leave plenty of space to load Linux and the initrd.
>
> If we consider 256MB of RMA under PFW, we have just 125MB remaining. 1/4
> of that is a smidge under 32MB, which gives us very poor odds of verifying
> a distro-sized kernel. However, if we need 80MB just to put the kernel
> and initrd in memory, we can't claim any more than 45MB anyway. So 1/4
> will do. We'll come back to this later.
>
> grub is always built as a 32-bit binary, even if it's loading a ppc64
> kernel. So we can't address memory beyond 4GB. This gives a natural cap
> of 1GB for powerpc-ieee1275.
>
> Also apply this 1/4 approach to i386-ieee1275, but keep the 32MB cap.
>
> make check still works for both i386 and powerpc and I've booted
> powerpc grub with this change under SLOF and PFW.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Tested-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   docs/grub-dev.texi             |  6 ++-
>   grub-core/kern/ieee1275/init.c | 81 +++++++++++++++++++++++++++-------
>   2 files changed, 69 insertions(+), 18 deletions(-)
>
> diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
> index 6c629a23e2dc..c11f1ac46de2 100644
> --- a/docs/grub-dev.texi
> +++ b/docs/grub-dev.texi
> @@ -1047,7 +1047,9 @@ space is limited to 4GiB. GRUB allocates pages from EFI for its heap, at most
>   1.6 GiB.
>   
>   On i386-ieee1275 and powerpc-ieee1275 GRUB uses same stack as IEEE1275.
> -It allocates at most 32MiB for its heap.
> +
> +On i386-ieee1275, GRUB allocates at most 32MiB for its heap. On
> +powerpc-ieee1275, GRUB allocates up to 1GiB.
>   
>   On sparc64-ieee1275 stack is 256KiB and heap is 2MiB.
>   
> @@ -1075,7 +1077,7 @@ In short:
>   @item i386-qemu               @tab 60 KiB  @tab < 4 GiB
>   @item *-efi                   @tab ?       @tab < 1.6 GiB
>   @item i386-ieee1275           @tab ?       @tab < 32 MiB
> -@item powerpc-ieee1275        @tab ?       @tab < 32 MiB
> +@item powerpc-ieee1275        @tab ?       @tab < 1 GiB
>   @item sparc64-ieee1275        @tab 256KiB  @tab 2 MiB
>   @item arm-uboot               @tab 256KiB  @tab 2 MiB
>   @item mips(el)-qemu_mips      @tab 2MiB    @tab 253 MiB
> diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
> index c5d091689f29..4162b5949df4 100644
> --- a/grub-core/kern/ieee1275/init.c
> +++ b/grub-core/kern/ieee1275/init.c
> @@ -45,11 +45,12 @@
>   #include <grub/machine/kernel.h>
>   #endif
>   
> -/* The maximum heap size we're going to claim */
> +/* The maximum heap size we're going to claim. Not used by sparc.
> +   We allocate 1/4 of the available memory under 4G, up to this limit. */
>   #ifdef __i386__
>   #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
> -#else
> -#define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
> +#else // __powerpc__
> +#define HEAP_MAX_SIZE		(unsigned long) (1 * 1024 * 1024 * 1024)
>   #endif
>   
>   extern char _start[];
> @@ -145,16 +146,45 @@ grub_claim_heap (void)
>   				 + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000);
>   }
>   #else
> -/* Helper for grub_claim_heap.  */
> +/* Helper for grub_claim_heap on powerpc. */
> +static int
> +heap_size (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
> +	   void *data)
> +{
> +  grub_uint32_t total = *(grub_uint32_t *)data;
> +
> +  if (type != GRUB_MEMORY_AVAILABLE)
> +    return 0;
> +
> +  /* Do not consider memory beyond 4GB */
> +  if (addr > 0xffffffffUL)
> +    return 0;
> +
> +  if (addr + len > 0xffffffffUL)
> +    len = 0xffffffffUL - addr;
> +
> +  total += len;
> +  *(grub_uint32_t *)data = total;
> +
> +  return 0;
> +}
> +
>   static int
>   heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>   	   void *data)
>   {
> -  unsigned long *total = data;
> +  grub_uint32_t total = *(grub_uint32_t *)data;
>   
>     if (type != GRUB_MEMORY_AVAILABLE)
>       return 0;
>   
> +  /* Do not consider memory beyond 4GB */
> +  if (addr > 0xffffffffUL)
> +    return 0;
> +
> +  if (addr + len > 0xffffffffUL)
> +    len = 0xffffffffUL - addr;
> +
>     if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM))
>       {
>         if (addr + len <= 0x180000)
> @@ -168,10 +198,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>       }
>     len -= 1; /* Required for some firmware.  */
>   
> -  /* Never exceed HEAP_MAX_SIZE  */
> -  if (*total + len > HEAP_MAX_SIZE)
> -    len = HEAP_MAX_SIZE - *total;
> -
>     /* In theory, firmware should already prevent this from happening by not
>        listing our own image in /memory/available.  The check below is intended
>        as a safeguard in case that doesn't happen.  However, it doesn't protect
> @@ -183,6 +209,18 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>         len = 0;
>       }
>   
> +  /* If this block contains 0x30000000 (768MB), do not claim below that.
> +     Linux likes to claim memory at min(RMO top, 768MB) and works down
> +     without reference to /memory/available. */
> +  if ((addr < 0x30000000) && ((addr + len) > 0x30000000))
> +    {
> +      len = len - (0x30000000 - addr);
> +      addr = 0x30000000;
> +    }
> +
> +  if (len > total)
> +    len = total;
> +
>     if (len)
>       {
>         grub_err_t err;
> @@ -191,10 +229,12 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>         if (err)
>   	return err;
>         grub_mm_init_region ((void *) (grub_addr_t) addr, len);
> +      total -= len;
>       }
>   
> -  *total += len;
> -  if (*total >= HEAP_MAX_SIZE)
> +  *(grub_uint32_t *)data = total;
> +
> +  if (total == 0)
>       return 1;
>   
>     return 0;
> @@ -203,13 +243,22 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>   static void
>   grub_claim_heap (void)
>   {
> -  unsigned long total = 0;
> +  grub_uint32_t total = 0;
>   
>     if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
> -    heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
> -	       1, &total);
> -  else
> -    grub_machine_mmap_iterate (heap_init, &total);
> +    {
> +      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
> +		 1, &total);
> +      return;
> +    }
> +
> +  grub_machine_mmap_iterate (heap_size, &total);
> +
> +  total = total / 4;
> +  if (total > HEAP_MAX_SIZE)
> +    total = HEAP_MAX_SIZE;
> +
> +  grub_machine_mmap_iterate (heap_init, &total);
>   }
>   #endif
>   


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 03/22] ieee1275: request memory with ibm, client-architecture-support
  2021-06-30  8:40 ` [PATCH v2 03/22] ieee1275: request memory with ibm, client-architecture-support Daniel Axtens
@ 2021-07-12 12:40   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:40 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> On PowerVM, the first time we boot a Linux partition, we may only get
> 256MB of real memory area, even if the partition has more memory.
>
> This isn't really enough. Fortunately, the Power Architecture Platform
> Reference (PAPR) defines a method we can call to ask for more memory.
> This is part of the broad and powerful ibm,client-architecture-support
> (CAS) method.
>
> CAS can do an enormous amount of things on a PAPR platform: as well as
> asking for memory, you can set the supported processor level, the interrupt
> controller, hash vs radix mmu, and so on. We want to touch as little of
> this as possible because we don't want to step on the toes of the future OS.
>
> If:
>
>   - we are running under what we think is PowerVM (compatible property of /
>     begins with "IBM"), and
>
>   - the full amount of RMA is less than 512MB (as determined by the reg
>     property of /memory)
>
> then call CAS as follows: (refer to the Linux on Power Architecture
> Reference, LoPAR, which is public, at B.5.2.3):
>
>   - Use the "any" PVR value and supply 2 option vectors.
>
>   - Set option vector 1 (PowerPC Server Processor Architecture Level)
>     to "ignore".
>
>   - Set option vector 2 with default or Linux-like options, including a
>     min-rma-size of 512MB.
>
> This will cause a CAS reboot and the partition will restart with 512MB
> of RMA. Grub will notice the 512MB and not call CAS again.
>
> (A partition can be configured with only 256MB of memory, which would
> mean this request couldn't be satisfied, but PFW refuses to load with
> only 256MB of memory, so it's a bit moot. SLOF will run fine with 256MB,
> but we will never call CAS under qemu/SLOF because /compatible won't
> begin with "IBM".)
>
> One of the first things Linux does while still running under OpenFirmware
> is to call CAS with a much fuller set of options (including asking for
> 512MB of memory). This includes a much more restrictive set of PVR values
> and processor support levels, and this will induce another reboot. On this
> reboot grub will again notice the higher RMA, and not call CAS. We will get
> to Linux, Linux will call CAS but because the values are now set for Linux
> this will not induce another CAS reboot and we will finally boot.
>
> On all subsequent boots, everything will be configured with 512MB of RMA
> and all the settings Linux likes, so there will be no further CAS reboots.
>
> (phyp is super sticky with the RMA size - it persists even on cold boots.
> So if you've ever booted Linux in a partition, you'll probably never have
> grub call CAS. It'll only ever fire the first time a partition loads grub,
> or if you deliberately lower the amount of memory your partition has below
> 512MB.)
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>


Tested-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   grub-core/kern/ieee1275/cmain.c  |   3 +
>   grub-core/kern/ieee1275/init.c   | 144 ++++++++++++++++++++++++++++++-
>   include/grub/ieee1275/ieee1275.h |   6 ++
>   3 files changed, 151 insertions(+), 2 deletions(-)
>
> diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c
> index 20cbbd761ec3..cc98811f4f99 100644
> --- a/grub-core/kern/ieee1275/cmain.c
> +++ b/grub-core/kern/ieee1275/cmain.c
> @@ -124,6 +124,9 @@ grub_ieee1275_find_options (void)
>   	      break;
>   	    }
>   	}
> +
> +      if (grub_strncmp (tmp, "IBM,", 4) == 0)
> +	grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY);
>       }
>   
>     if (is_smartfirmware)
> diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
> index 4162b5949df4..4586bec939b2 100644
> --- a/grub-core/kern/ieee1275/init.c
> +++ b/grub-core/kern/ieee1275/init.c
> @@ -240,6 +240,135 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>     return 0;
>   }
>   
> +/* How much memory does OF believe it has? (regardless of whether
> +   it's accessible or not) */
> +static grub_err_t
> +grub_ieee1275_total_mem (grub_uint64_t *total)
> +{
> +  grub_ieee1275_phandle_t root;
> +  grub_ieee1275_phandle_t memory;
> +  grub_uint32_t reg[4];
> +  grub_ssize_t reg_size;
> +  grub_uint32_t address_cells = 1;
> +  grub_uint32_t size_cells = 1;
> +  grub_uint64_t size;
> +
> +  /* If we fail to get to the end, report 0. */
> +  *total = 0;
> +
> +  /* Determine the format of each entry in `reg'.  */
> +  grub_ieee1275_finddevice ("/", &root);
> +  grub_ieee1275_get_integer_property (root, "#address-cells", &address_cells,
> +				      sizeof address_cells, 0);
> +  grub_ieee1275_get_integer_property (root, "#size-cells", &size_cells,
> +				      sizeof size_cells, 0);
> +
> +  if (size_cells > address_cells)
> +    address_cells = size_cells;
> +
> +  /* Load `/memory/reg'.  */
> +  if (grub_ieee1275_finddevice ("/memory", &memory))
> +    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
> +		       "couldn't find /memory node");
> +  if (grub_ieee1275_get_integer_property (memory, "reg", reg,
> +					  sizeof reg, &reg_size))
> +    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
> +		       "couldn't examine /memory/reg property");
> +  if (reg_size < 0 || (grub_size_t) reg_size > sizeof (reg))
> +    return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
> +                       "/memory response buffer exceeded");
> +
> +  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_BROKEN_ADDRESS_CELLS))
> +    {
> +      address_cells = 1;
> +      size_cells = 1;
> +    }
> +
> +  /* Decode only the size */
> +  size = reg[address_cells];
> +  if (size_cells == 2)
> +    size = (size << 32) | reg[address_cells + 1];
> +
> +  *total = size;
> +
> +  return grub_errno;
> +}
> +
> +/* Based on linux - arch/powerpc/kernel/prom_init.c */
> +struct option_vector2 {
> +	grub_uint8_t byte1;
> +	grub_uint16_t reserved;
> +	grub_uint32_t real_base;
> +	grub_uint32_t real_size;
> +	grub_uint32_t virt_base;
> +	grub_uint32_t virt_size;
> +	grub_uint32_t load_base;
> +	grub_uint32_t min_rma;
> +	grub_uint32_t min_load;
> +	grub_uint8_t min_rma_percent;
> +	grub_uint8_t max_pft_size;
> +} __attribute__((packed));
> +
> +struct pvr_entry {
> +	  grub_uint32_t mask;
> +	  grub_uint32_t entry;
> +};
> +
> +struct cas_vector {
> +    struct {
> +      struct pvr_entry terminal;
> +    } pvr_list;
> +    grub_uint8_t num_vecs;
> +    grub_uint8_t vec1_size;
> +    grub_uint8_t vec1;
> +    grub_uint8_t vec2_size;
> +    struct option_vector2 vec2;
> +} __attribute__((packed));
> +
> +/* Call ibm,client-architecture-support to try to get more RMA.
> +   We ask for 512MB which should be enough to verify a distro kernel.
> +   We ignore most errors: if we don't succeed we'll proceed with whatever
> +   memory we have. */
> +static void
> +grub_ieee1275_ibm_cas (void)
> +{
> +  int rc;
> +  grub_ieee1275_ihandle_t root;
> +  struct cas_args {
> +    struct grub_ieee1275_common_hdr common;
> +    grub_ieee1275_cell_t method;
> +    grub_ieee1275_ihandle_t ihandle;
> +    grub_ieee1275_cell_t cas_addr;
> +    grub_ieee1275_cell_t result;
> +  } args;
> +  struct cas_vector vector = {
> +    .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */
> +    .num_vecs = 2 - 1,
> +    .vec1_size = 0,
> +    .vec1 = 0x80, /* ignore */
> +    .vec2_size = 1 + sizeof(struct option_vector2) - 2,
> +    .vec2 = {
> +      0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48
> +    },
> +  };
> +
> +  INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2);
> +  args.method = (grub_ieee1275_cell_t)"ibm,client-architecture-support";
> +  rc = grub_ieee1275_open("/", &root);
> +  if (rc) {
> +	  grub_error (GRUB_ERR_IO, "could not open root when trying to call CAS");
> +	  return;
> +  }
> +  args.ihandle = root;
> +  args.cas_addr = (grub_ieee1275_cell_t)&vector;
> +
> +  grub_printf("Calling ibm,client-architecture-support...");
> +  IEEE1275_CALL_ENTRY_FN (&args);
> +  grub_printf("done\n");
> +
> +  grub_ieee1275_close(root);
> +}
> +
>   static void
>   grub_claim_heap (void)
>   {
> @@ -247,11 +376,22 @@ grub_claim_heap (void)
>   
>     if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
>       {
> -      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
> -		 1, &total);
> +      heap_init (GRUB_IEEE1275_STATIC_HEAP_START,
> +		 GRUB_IEEE1275_STATIC_HEAP_LEN, 1, &total);
>         return;
>       }
>   
> +  if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY))
> +    {
> +      grub_uint64_t rma_size;
> +      grub_err_t err;
> +
> +      err = grub_ieee1275_total_mem (&rma_size);
> +      /* if we have an error, don't call CAS, just hope for the best */
> +      if (!err && rma_size < (512 * 1024 * 1024))
> +	grub_ieee1275_ibm_cas();
> +    }
> +
>     grub_machine_mmap_iterate (heap_size, &total);
>   
>     total = total / 4;
> diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
> index 73e2f4644751..18c479b668ce 100644
> --- a/include/grub/ieee1275/ieee1275.h
> +++ b/include/grub/ieee1275/ieee1275.h
> @@ -148,6 +148,12 @@ enum grub_ieee1275_flag
>     GRUB_IEEE1275_FLAG_CURSORONOFF_ANSI_BROKEN,
>   
>     GRUB_IEEE1275_FLAG_RAW_DEVNAMES,
> +
> +  /* On PFW, the first time we boot a Linux partition, we may only get 256MB
> +     of real memory area, even if the partition has more memory. Set this flag
> +     if we think we're running under PFW. Then, if this flag is set, and the
> +     RMA is only 256MB in size, try asking for more with CAS. */
> +  GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY,
>   };
>   
>   extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag);


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 04/22] Add suport for signing grub with an appended signature
  2021-06-30  8:40 ` [PATCH v2 04/22] Add suport for signing grub with an appended signature Daniel Axtens
@ 2021-07-12 12:43   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:43 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna

On 6/30/21 4:40 AM, Daniel Axtens wrote:

> From: Rashmica Gupta <rashmica.g@gmail.com>
>
> Add infrastructure to allow firmware to verify the integrity of grub
> by use of a Linux-kernel-module-style appended signature. We initially
> target powerpc-ieee1275, but the code should be extensible to other
> platforms.
>
> Usually these signatures are appended to a file without modifying the
> ELF file itself. (This is what the 'sign-file' tool does, for example.)
> The verifier loads the signed file from the file system and looks at the
> end of the file for the appended signature. However, on powerpc-ieee1275
> platforms, the bootloader is often stored directly in the PReP partition
> as raw bytes without a file-system. This makes determining the location
> of an appended signature more difficult.
>
> To address this, we add a new ELF note.
>
> The name field of shall be the string "Appended-Signature", zero-padded
> to 4 byte alignment. The type field shall be 0x41536967 (the ASCII values
> for the string "ASig"). It must be the final section in the ELF binary.
>
> The description shall contain the appended signature structure as defined
> by the Linux kernel. The description will also be padded to be a multiple
> of 4 bytes. The padding shall be added before the appended signature
> structure (not at the end) so that the final bytes of a signed ELF file
> are the appended signature magic.
>
> A subsequent patch documents how to create a grub core.img validly signed
> under this scheme.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>
> Signed-off-by: Rashmica Gupta <rashmica.g@gmail.com>
>
> ---
>
> You can experiment with this code with a patched version of SLOF
> that verifies these signatures. You can find one at:
>     https://github.com/daxtens/SLOF
>
> I will be proposing this for inclusion in a future Power Architecture
> Platform Reference (PAPR).
> ---
>   include/grub/util/install.h |  8 ++++++--
>   include/grub/util/mkimage.h |  4 ++--
>   util/grub-install-common.c  | 15 +++++++++++---
>   util/grub-mkimage.c         | 11 +++++++++++
>   util/grub-mkimagexx.c       | 39 ++++++++++++++++++++++++++++++++++++-
>   util/mkimage.c              | 13 +++++++------
>   6 files changed, 76 insertions(+), 14 deletions(-)
>
> diff --git a/include/grub/util/install.h b/include/grub/util/install.h
> index 7df3191f47ef..cf4531e02b66 100644
> --- a/include/grub/util/install.h
> +++ b/include/grub/util/install.h
> @@ -67,6 +67,9 @@
>         N_("SBAT metadata"), 0 },						\
>     { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
>         N_("disable shim_lock verifier"), 0 },				\
> +  { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\
> +    "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \
> +    1},                                                                 \
>     { "verbose", 'v', 0, 0,						\
>       N_("print verbose messages."), 1 }
>   
> @@ -128,7 +131,8 @@ enum grub_install_options {
>     GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS,
>     GRUB_INSTALL_OPTIONS_DTB,
>     GRUB_INSTALL_OPTIONS_SBAT,
> -  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK
> +  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK,
> +  GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE
>   };
>   
>   extern char *grub_install_source_directory;
> @@ -188,7 +192,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
>   			     size_t npubkeys,
>   			     char *config_path,
>   			     const struct grub_install_image_target_desc *image_target,
> -			     int note,
> +			     int note, size_t appsig_size,
>   			     grub_compression_t comp, const char *dtb_file,
>   			     const char *sbat_path, const int disable_shim_lock);
>   
> diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h
> index 3819a67441c8..6f1da89b9b65 100644
> --- a/include/grub/util/mkimage.h
> +++ b/include/grub/util/mkimage.h
> @@ -51,12 +51,12 @@ grub_mkimage_load_image64 (const char *kernel_path,
>   			   const struct grub_install_image_target_desc *image_target);
>   void
>   grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target,
> -			     int note, char **core_img, size_t *core_size,
> +			     int note, size_t appsig_size, char **core_img, size_t *core_size,
>   			     Elf32_Addr target_addr,
>   			     struct grub_mkimage_layout *layout);
>   void
>   grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target,
> -			     int note, char **core_img, size_t *core_size,
> +			     int note, size_t appsig_size, char **core_img, size_t *core_size,
>   			     Elf64_Addr target_addr,
>   			     struct grub_mkimage_layout *layout);
>   
> diff --git a/util/grub-install-common.c b/util/grub-install-common.c
> index 4e212e690c52..1216a203c292 100644
> --- a/util/grub-install-common.c
> +++ b/util/grub-install-common.c
> @@ -461,10 +461,12 @@ static size_t npubkeys;
>   static char *sbat;
>   static int disable_shim_lock;
>   static grub_compression_t compression;
> +static size_t appsig_size;
>   
>   int
>   grub_install_parse (int key, char *arg)
>   {
> +  const char *end;
>     switch (key)
>       {
>       case 'C':
> @@ -562,6 +564,12 @@ grub_install_parse (int key, char *arg)
>         grub_util_error (_("Unrecognized compression `%s'"), arg);
>       case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE:
>         return 1;
> +    case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE:
> +      grub_errno = 0;
> +      appsig_size = grub_strtol(arg, &end, 10);
> +      if (grub_errno)
> +        return 0;
> +      return 1;
>       default:
>         return 0;
>       }
> @@ -661,10 +669,11 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>   		  " --output '%s' "
>   		  " --dtb '%s' "
>   		  "--sbat '%s' "
> -		  "--format '%s' --compression '%s' %s %s %s\n",
> +		  "--format '%s' --compression '%s' "
> +		  "--appended-signature-size %zu %s %s %s\n",
>   		  dir, prefix,
>   		  outname, dtb ? : "", sbat ? : "", mkimage_target,
> -		  compnames[compression], note ? "--note" : "",
> +		  compnames[compression], appsig_size, note ? "--note" : "",
>   		  disable_shim_lock ? "--disable-shim-lock" : "", s);
>     free (s);
>   
> @@ -675,7 +684,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>     grub_install_generate_image (dir, prefix, fp, outname,
>   			       modules.entries, memdisk_path,
>   			       pubkeys, npubkeys, config_path, tgt,
> -			       note, compression, dtb, sbat,
> +			       note, appsig_size, compression, dtb, sbat,
>   			       disable_shim_lock);
>     while (dc--)
>       grub_install_pop_module ();
> diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
> index c0d559937020..d01eaeb8443a 100644
> --- a/util/grub-mkimage.c
> +++ b/util/grub-mkimage.c
> @@ -84,6 +84,7 @@ static struct argp_option options[] = {
>     {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0},
>     {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0},
>     {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
> +  {"appended-signature-size", 'S', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0},
>     { 0, 0, 0, 0, 0, 0 }
>   };
>   
> @@ -128,6 +129,7 @@ struct arguments
>     char *sbat;
>     int note;
>     int disable_shim_lock;
> +  size_t appsig_size;
>     const struct grub_install_image_target_desc *image_target;
>     grub_compression_t comp;
>   };
> @@ -138,6 +140,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
>     /* Get the input argument from argp_parse, which we
>        know is a pointer to our arguments structure. */
>     struct arguments *arguments = state->input;
> +  const char* end;


Nit: char* end -> char *end;

Otherwise looks good to me.


>   
>     switch (key)
>       {
> @@ -170,6 +173,13 @@ argp_parser (int key, char *arg, struct argp_state *state)
>         arguments->note = 1;
>         break;
>   
> +    case 'S':
> +      grub_errno = 0;
> +      arguments->appsig_size = grub_strtol(arg, &end, 10);
> +      if (grub_errno)
> +        return 0;
> +      break;
> +
>       case 'm':
>         if (arguments->memdisk)
>   	free (arguments->memdisk);
> @@ -324,6 +334,7 @@ main (int argc, char *argv[])
>   			       arguments.memdisk, arguments.pubkeys,
>   			       arguments.npubkeys, arguments.config,
>   			       arguments.image_target, arguments.note,
> +			       arguments.appsig_size,
>   			       arguments.comp, arguments.dtb,
>   			       arguments.sbat, arguments.disable_shim_lock);
>   
> diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
> index d78fa3e53308..393119486d3f 100644
> --- a/util/grub-mkimagexx.c
> +++ b/util/grub-mkimagexx.c
> @@ -84,6 +84,15 @@ struct grub_ieee1275_note
>     struct grub_ieee1275_note_desc descriptor;
>   };
>   
> +#define GRUB_APPENDED_SIGNATURE_NOTE_NAME "Appended-Signature"
> +#define GRUB_APPENDED_SIGNATURE_NOTE_TYPE 0x41536967 /* "ASig" */
> +
> +struct grub_appended_signature_note
> +{
> +  Elf32_Nhdr header;
> +  char name[ALIGN_UP(sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME), 4)];
> +};
> +
>   #define GRUB_XEN_NOTE_NAME "Xen"
>   
>   struct fixup_block_list
> @@ -207,7 +216,7 @@ grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr)
>   
>   void
>   SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target,
> -				    int note, char **core_img, size_t *core_size,
> +				    int note, size_t appsig_size, char **core_img, size_t *core_size,
>   				    Elf_Addr target_addr,
>   				    struct grub_mkimage_layout *layout)
>   {
> @@ -221,6 +230,12 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
>     int shnum = 4;
>     int string_size = sizeof (".text") + sizeof ("mods") + 1;
>   
> +  if (appsig_size)
> +    {
> +      phnum++;
> +      footer_size += ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4);
> +    }
> +
>     if (image_target->id != IMAGE_LOONGSON_ELF)
>       phnum += 2;
>   
> @@ -484,6 +499,28 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc
>         phdr->p_offset = grub_host_to_target32 (header_size + program_size);
>       }
>   
> +  if (appsig_size) {
> +    int note_size = ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4);
> +    struct grub_appended_signature_note *note_ptr = (struct grub_appended_signature_note *)
> +      (elf_img + program_size + header_size + (note ? sizeof (struct grub_ieee1275_note) : 0));
> +
> +    note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME));
> +    /* needs to sit at the end, so we round this up and sign some zero padding */
> +    note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(appsig_size, 4));
> +    note_ptr->header.n_type = grub_host_to_target32 (GRUB_APPENDED_SIGNATURE_NOTE_TYPE);
> +    strcpy (note_ptr->name, GRUB_APPENDED_SIGNATURE_NOTE_NAME);
> +
> +    phdr++;
> +    phdr->p_type = grub_host_to_target32 (PT_NOTE);
> +    phdr->p_flags = grub_host_to_target32 (PF_R);
> +    phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof);
> +    phdr->p_vaddr = 0;
> +    phdr->p_paddr = 0;
> +    phdr->p_filesz = grub_host_to_target32 (note_size);
> +    phdr->p_memsz = 0;
> +    phdr->p_offset = grub_host_to_target32 (header_size + program_size + (note ? sizeof (struct grub_ieee1275_note) : 0));
> +  }
> +
>     {
>       char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)
>   		       + shnum * sizeof (*shdr));
> diff --git a/util/mkimage.c b/util/mkimage.c
> index a26cf76f72f2..d2cb33883557 100644
> --- a/util/mkimage.c
> +++ b/util/mkimage.c
> @@ -869,8 +869,9 @@ grub_install_generate_image (const char *dir, const char *prefix,
>   			     char *memdisk_path, char **pubkey_paths,
>   			     size_t npubkeys, char *config_path,
>   			     const struct grub_install_image_target_desc *image_target,
> -			     int note, grub_compression_t comp, const char *dtb_path,
> -			     const char *sbat_path, int disable_shim_lock)
> +			     int note, size_t appsig_size, grub_compression_t comp,
> +			     const char *dtb_path, const char *sbat_path,
> +			     int disable_shim_lock)
>   {
>     char *kernel_img, *core_img;
>     size_t total_module_size, core_size;
> @@ -1773,11 +1774,11 @@ grub_install_generate_image (const char *dir, const char *prefix,
>   	else
>   	  target_addr = image_target->link_addr;
>   	if (image_target->voidp_sizeof == 4)
> -	  grub_mkimage_generate_elf32 (image_target, note, &core_img, &core_size,
> -				       target_addr, &layout);
> +	  grub_mkimage_generate_elf32 (image_target, note, appsig_size, &core_img,
> +				       &core_size, target_addr, &layout);
>   	else
> -	  grub_mkimage_generate_elf64 (image_target, note, &core_img, &core_size,
> -				       target_addr, &layout);
> +	  grub_mkimage_generate_elf64 (image_target, note, appsig_size, &core_img,
> +				       &core_size, target_addr, &layout);
>         }
>         break;
>       }


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 05/22] docs/grub: Document signing grub under UEFI
  2021-06-30  8:40 ` [PATCH v2 05/22] docs/grub: Document signing grub under UEFI Daniel Axtens
@ 2021-07-12 12:44   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:44 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> Before adding information about how grub is signed with an appended
> signature scheme, it's worth adding some information about how it
> can currently be signed for UEFI.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   docs/grub.texi | 22 +++++++++++++++++++++-
>   1 file changed, 21 insertions(+), 1 deletion(-)
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index f8b4b3b21a7f..2ffc3b417312 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -5795,6 +5795,7 @@ environment variables and commands are listed in the same order.
>   * Secure Boot Advanced Targeting::   Embedded information for generation number based revocation
>   * Measured Boot::                    Measuring boot components
>   * Lockdown::                         Lockdown when booting on a secure setup
> +* Signing GRUB itself::              Ensuring the integrity of the GRUB core image
>   @end menu
>   
>   @node Authentication and authorisation
> @@ -5873,7 +5874,7 @@ commands.
>   
>   GRUB's @file{core.img} can optionally provide enforcement that all files
>   subsequently read from disk are covered by a valid digital signature.
> -This document does @strong{not} cover how to ensure that your
> +This section does @strong{not} cover how to ensure that your
>   platform's firmware (e.g., Coreboot) validates @file{core.img}.
>   
>   If environment variable @code{check_signatures}
> @@ -6035,6 +6036,25 @@ be restricted and some operations/commands cannot be executed.
>   The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down.
>   Otherwise it does not exit.
>   
> +@node Signing GRUB itself
> +@section Signing GRUB itself
> +
> +To ensure a complete secure-boot chain, there must be a way for the code that
> +loads GRUB to verify the integrity of the core image.
> +
> +This is ultimately platform-specific and individual platforms can define their
> +own mechanisms. However, there are general-purpose mechanisms that can be used
> +with GRUB.
> +
> +@section Signing GRUB for UEFI secure boot
> +
> +On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed
> +with a tool such as @command{pesign} or @command{sbsign}. Refer to the
> +suggestions in @pxref{UEFI secure boot and shim} to ensure that the final
> +image works under UEFI secure boot and can maintain the secure-boot chain. It
> +will also be necessary to enrol the public key used into a relevant firmware
> +key database.
> +
>   @node Platform limitations
>   @chapter Platform limitations
>   


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 06/22] docs/grub: Document signing grub with an appended signature
  2021-06-30  8:40 ` [PATCH v2 06/22] docs/grub: Document signing grub with an appended signature Daniel Axtens
@ 2021-07-12 12:46   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:46 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> Signing grub for firmware that verifies an appended signature is a
> bit fiddly. I don't want people to have to figure it out from scratch
> so document it here.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>
> ---
>   docs/grub.texi | 42 ++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 42 insertions(+)
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index 2ffc3b417312..bed565371460 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -6055,6 +6055,48 @@ image works under UEFI secure boot and can maintain the secure-boot chain. It
>   will also be necessary to enrol the public key used into a relevant firmware
>   key database.
>   
> +@section Signing GRUB with an appended signature
> +
> +The @file{core.img} itself can be signed with a Linux kernel module-style
> +appended signature.
> +
> +To support IEEE1275 platforms where the boot image is often loaded directly
> +from a disk partition rather than from a file system, the @file{core.img}
> +can specify the size and location of the appended signature with an ELF
> +note added by @command{grub-install}.
> +
> +An image can be signed this way using the @command{sign-file} command from
> +the Linux kernel:
> +
> +@example
> +@group
> +# grub.key is your private key and certificate.der is your public key
> +
> +# Determine the size of the appended signature. It depends on the signing
> +# certificate and the hash algorithm
> +touch empty
> +sign-file SHA256 grub.key certificate.der empty empty.sig
> +SIG_SIZE=`stat -c '%s' empty.sig`
> +rm empty empty.sig
> +
> +# Build a grub image with $SIG_SIZE reserved for the signature
> +grub-install --appended-signature-size $SIG_SIZE --modules="..." ...
> +
> +# Replace the reserved size with a signature:
> +# cut off the last $SIG_SIZE bytes with truncate's minus modifier
> +truncate -s -$SIG_SIZE /boot/grub/powerpc-ieee1275/core.elf core.elf.unsigned
> +# sign the trimmed file with an appended signature, restoring the correct size
> +sign-file SHA256 grub.key certificate.der core.elf.unsigned core.elf.signed
> +
> +# Don't forget to install the signed image as required
> +# (e.g. on powerpc-ieee1275, to the PReP partition)
> +@end group
> +@end example
> +
> +As with UEFI secure boot, it is necessary to build in the required modules,
> +or sign them separately.
> +
> +
>   @node Platform limitations
>   @chapter Platform limitations
>   


Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>




^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 07/22] dl: provide a fake grub_dl_set_persistent for the emu target
  2021-06-30  8:40 ` [PATCH v2 07/22] dl: provide a fake grub_dl_set_persistent for the emu target Daniel Axtens
@ 2021-07-12 12:48   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:48 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> Trying to start grub-emu with a module that calls grub_dl_set_persistent
> will crash because grub-emu fakes modules and passes NULL to the module
> init function.
>
> Provide an empty function for the emu case.
>
> Fixes: ee7808e2197c (dl: Add support for persistent modules)
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   include/grub/dl.h | 11 +++++++++++
>   1 file changed, 11 insertions(+)
>
> diff --git a/include/grub/dl.h b/include/grub/dl.h
> index b3753c9ca262..5decbe2f2fb9 100644
> --- a/include/grub/dl.h
> +++ b/include/grub/dl.h
> @@ -243,11 +243,22 @@ grub_dl_get (const char *name)
>     return 0;
>   }
>   
> +#ifdef GRUB_MACHINE_EMU
> +/*
> + * Under grub-emu, modules are faked and NULL is passed to GRUB_MOD_INIT.
> + * So we fake this out to avoid a NULL deref.
> + */
> +static inline void
> +grub_dl_set_persistent (grub_dl_t mod __attribute__((unused)))
> +{
> +}
> +#else
>   static inline void
>   grub_dl_set_persistent (grub_dl_t mod)
>   {
>     mod->persistent = 1;
>   }
> +#endif
>   
>   static inline int
>   grub_dl_is_persistent (grub_dl_t mod)


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 08/22] pgp: factor out rsa_pad
  2021-06-30  8:40 ` [PATCH v2 08/22] pgp: factor out rsa_pad Daniel Axtens
@ 2021-07-12 12:52   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:52 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> rsa_pad does the PKCS#1 v1.5 padding for the RSA signature scheme.
> We want to use it in other RSA signature verification applications.
>
> I considered and rejected putting it in lib/crypto.c. That file doesn't
> currently require any MPI functions, but rsa_pad does. That's not so
> much of a problem for the grub kernel and modules, but crypto.c also
> gets built into all the grub utilities. So - despite the utils not
> using any asymmetric ciphers -  we would need to built the entire MPI
> infrastructure in to them.
>
> A better and simpler solution is just to spin rsa_pad out into its own
> PKCS#1 v1.5 module.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

This an almost straight move of code from one function into another one:

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   grub-core/Makefile.core.def |  8 +++++
>   grub-core/commands/pgp.c    | 28 ++----------------
>   grub-core/lib/pkcs1_v15.c   | 59 +++++++++++++++++++++++++++++++++++++
>   include/grub/pkcs1_v15.h    | 27 +++++++++++++++++
>   4 files changed, 96 insertions(+), 26 deletions(-)
>   create mode 100644 grub-core/lib/pkcs1_v15.c
>   create mode 100644 include/grub/pkcs1_v15.h
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 8022e1c0a794..915287d44c13 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -2469,6 +2469,14 @@ module = {
>     cppflags = '$(CPPFLAGS_GCRY)';
>   };
>   
> +module = {
> +  name = pkcs1_v15;
> +  common = lib/pkcs1_v15.c;
> +
> +  cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
> +  cppflags = '$(CPPFLAGS_GCRY)';
> +};
> +
>   module = {
>     name = all_video;
>     common = lib/fake_module.c;
> diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
> index 5daa1e9d00c7..2408db4994f6 100644
> --- a/grub-core/commands/pgp.c
> +++ b/grub-core/commands/pgp.c
> @@ -24,6 +24,7 @@
>   #include <grub/file.h>
>   #include <grub/command.h>
>   #include <grub/crypto.h>
> +#include <grub/pkcs1_v15.h>
>   #include <grub/i18n.h>
>   #include <grub/gcrypt/gcrypt.h>
>   #include <grub/pubkey.h>
> @@ -411,32 +412,7 @@ static int
>   rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
>   	 const gcry_md_spec_t *hash, struct grub_public_subkey *sk)
>   {
> -  grub_size_t tlen, emlen, fflen;
> -  grub_uint8_t *em, *emptr;
> -  unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]);
> -  int ret;
> -  tlen = hash->mdlen + hash->asnlen;
> -  emlen = (nbits + 7) / 8;
> -  if (emlen < tlen + 11)
> -    return 1;
> -
> -  em = grub_malloc (emlen);
> -  if (!em)
> -    return 1;
> -
> -  em[0] = 0x00;
> -  em[1] = 0x01;
> -  fflen = emlen - tlen - 3;
> -  for (emptr = em + 2; emptr < em + 2 + fflen; emptr++)
> -    *emptr = 0xff;
> -  *emptr++ = 0x00;
> -  grub_memcpy (emptr, hash->asnoid, hash->asnlen);
> -  emptr += hash->asnlen;
> -  grub_memcpy (emptr, hval, hash->mdlen);
> -
> -  ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0);
> -  grub_free (em);
> -  return ret;
> +  return grub_crypto_rsa_pad(hmpi, hval, hash, sk->mpis[0]);
>   }
>   
>   struct grub_pubkey_context
> diff --git a/grub-core/lib/pkcs1_v15.c b/grub-core/lib/pkcs1_v15.c
> new file mode 100644
> index 000000000000..dbacd563d014
> --- /dev/null
> +++ b/grub-core/lib/pkcs1_v15.c
> @@ -0,0 +1,59 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2013  Free Software Foundation, Inc.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/dl.h>
> +#include <grub/gcrypt/gcrypt.h>
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +/*
> + * Given a hash value 'hval', of hash specification 'hash', perform
> + * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod'
> + * (see RFC 8017 s 9.2) and place the result in 'hmpi'.
> + */
> +gcry_err_code_t
> +grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval,
> +		     const gcry_md_spec_t * hash, gcry_mpi_t mod)
> +{
> +  grub_size_t tlen, emlen, fflen;
> +  grub_uint8_t *em, *emptr;
> +  unsigned nbits = gcry_mpi_get_nbits (mod);
> +  int ret;
> +  tlen = hash->mdlen + hash->asnlen;
> +  emlen = (nbits + 7) / 8;
> +  if (emlen < tlen + 11)
> +    return GPG_ERR_TOO_SHORT;
> +
> +  em = grub_malloc (emlen);
> +  if (!em)
> +    return 1;
> +
> +  em[0] = 0x00;
> +  em[1] = 0x01;
> +  fflen = emlen - tlen - 3;
> +  for (emptr = em + 2; emptr < em + 2 + fflen; emptr++)
> +    *emptr = 0xff;
> +  *emptr++ = 0x00;
> +  grub_memcpy (emptr, hash->asnoid, hash->asnlen);
> +  emptr += hash->asnlen;
> +  grub_memcpy (emptr, hval, hash->mdlen);
> +
> +  ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0);
> +  grub_free (em);
> +  return ret;
> +}
> diff --git a/include/grub/pkcs1_v15.h b/include/grub/pkcs1_v15.h
> new file mode 100644
> index 000000000000..5c338c84a158
> --- /dev/null
> +++ b/include/grub/pkcs1_v15.h
> @@ -0,0 +1,27 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2013  Free Software Foundation, Inc.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +/*
> + * Given a hash value 'hval', of hash specification 'hash', perform
> + * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod'
> + * (See RFC 8017 s 9.2)
> + */
> +gcry_err_code_t
> +grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval,
> +		     const gcry_md_spec_t * hash, gcry_mpi_t mod);
> +


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 09/22] crypto: move storage for grub_crypto_pk_* to crypto.c
  2021-06-30  8:40 ` [PATCH v2 09/22] crypto: move storage for grub_crypto_pk_* to crypto.c Daniel Axtens
@ 2021-07-12 12:54   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:54 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> The way gcry_rsa and friends (the asymmetric ciphers) are loaded for the
> pgp module is a bit quirky.
>
> include/grub/crypto.h contains:
>    extern struct gcry_pk_spec *grub_crypto_pk_rsa;
>
> commands/pgp.c contains the actual storage:
>    struct gcry_pk_spec *grub_crypto_pk_rsa;
>
> And the module itself saves to the storage in pgp.c:
>    GRUB_MOD_INIT(gcry_rsa)
>    {
>      grub_crypto_pk_rsa = &_gcry_pubkey_spec_rsa;
>    }
>
> This is annoying: gcry_rsa now has a dependency on pgp!
>
> We want to be able to bring in gcry_rsa without bringing in PGP,
> so move the storage to crypto.c.
>
> Previously, gcry_rsa depended on pgp and mpi. Now it depends on
> crypto and mpi. As pgp depends on crypto, this doesn't add any new
> module dependencies using the PGP verfier.
>
> [FWIW, the story is different for the symmetric ciphers. cryptodisk
> and friends (zfs encryption etc) use grub_crypto_lookup_cipher_by_name()
> to get a cipher handle. That depends on grub_ciphers being populated
> by people calling grub_cipher_register. import_gcry.py ensures that the
> symmetric ciphers call it.]
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   grub-core/commands/pgp.c | 4 ----
>   grub-core/lib/crypto.c   | 4 ++++
>   2 files changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
> index 2408db4994f6..355a43844acc 100644
> --- a/grub-core/commands/pgp.c
> +++ b/grub-core/commands/pgp.c
> @@ -147,10 +147,6 @@ const char *hashes[] = {
>     [0x0b] = "sha224"
>   };
>   
> -struct gcry_pk_spec *grub_crypto_pk_dsa;
> -struct gcry_pk_spec *grub_crypto_pk_ecdsa;
> -struct gcry_pk_spec *grub_crypto_pk_rsa;
> -
>   static int
>   dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval,
>   	 const gcry_md_spec_t *hash, struct grub_public_subkey *sk);
> diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c
> index ca334d5a40e0..c578128a59db 100644
> --- a/grub-core/lib/crypto.c
> +++ b/grub-core/lib/crypto.c
> @@ -121,6 +121,10 @@ grub_md_unregister (gcry_md_spec_t *cipher)
>         }
>   }
>   
> +struct gcry_pk_spec *grub_crypto_pk_dsa;
> +struct gcry_pk_spec *grub_crypto_pk_ecdsa;
> +struct gcry_pk_spec *grub_crypto_pk_rsa;
> +
>   void
>   grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in,
>   		  grub_size_t inlen)


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 10/22] posix_wrap: tweaks in preparation for libtasn1
  2021-06-30  8:40 ` [PATCH v2 10/22] posix_wrap: tweaks in preparation for libtasn1 Daniel Axtens
@ 2021-07-12 12:56   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 12:56 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
>   - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
>     SIZEOF_UNSIGNED_LONG.
>
>   - Define WORD_BIT, the size in bits of an int. This is a defined
>     in the Single Unix Specification and in gnulib's limits.h. gnulib
>     assumes it's 32 bits on all our platforms, including 64 bit
>     platforms, so we also use that value.
>
>   - Provide strto[u]l[l] preprocessor macros that resolve to
>     grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
>     also define HAVE_STRTOUL here.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>


Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   grub-core/lib/posix_wrap/limits.h    | 1 +
>   grub-core/lib/posix_wrap/stdlib.h    | 8 ++++++++
>   grub-core/lib/posix_wrap/sys/types.h | 1 +
>   3 files changed, 10 insertions(+)
>
> diff --git a/grub-core/lib/posix_wrap/limits.h b/grub-core/lib/posix_wrap/limits.h
> index 7217138ffd6b..591dbf3289d8 100644
> --- a/grub-core/lib/posix_wrap/limits.h
> +++ b/grub-core/lib/posix_wrap/limits.h
> @@ -37,5 +37,6 @@
>   #define LONG_MAX GRUB_LONG_MAX
>   
>   #define CHAR_BIT 8
> +#define WORD_BIT 32
>   
>   #endif
> diff --git a/grub-core/lib/posix_wrap/stdlib.h b/grub-core/lib/posix_wrap/stdlib.h
> index 7a8d385e973a..4634db09f294 100644
> --- a/grub-core/lib/posix_wrap/stdlib.h
> +++ b/grub-core/lib/posix_wrap/stdlib.h
> @@ -58,4 +58,12 @@ abs (int c)
>     return (c >= 0) ? c : -c;
>   }
>   
> +#define strtol grub_strtol
> +
> +/* for libgcrypt */
> +#define HAVE_STRTOUL
> +#define strtoul grub_strtoul
> +
> +#define strtoull grub_strtoull
> +
>   #endif
> diff --git a/grub-core/lib/posix_wrap/sys/types.h b/grub-core/lib/posix_wrap/sys/types.h
> index 854eb0122efa..f63412c8da06 100644
> --- a/grub-core/lib/posix_wrap/sys/types.h
> +++ b/grub-core/lib/posix_wrap/sys/types.h
> @@ -51,6 +51,7 @@ typedef grub_uint8_t byte;
>   typedef grub_addr_t uintptr_t;
>   
>   #define SIZEOF_UNSIGNED_LONG GRUB_CPU_SIZEOF_LONG
> +#define SIZEOF_UNSIGNED_LONG_INT GRUB_CPU_SIZEOF_LONG
>   #define SIZEOF_UNSIGNED_INT 4
>   #define SIZEOF_UNSIGNED_LONG_LONG 8
>   #define SIZEOF_UNSIGNED_SHORT 2


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 13/22] libtasn1: changes for grub compatibility
  2021-06-30  8:40 ` [PATCH v2 13/22] libtasn1: changes for grub compatibility Daniel Axtens
@ 2021-07-12 13:04   ` Stefan Berger
  2022-04-21  6:16     ` Daniel Axtens
  0 siblings, 1 reply; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 13:04 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> Do a few things to make libtasn1 compile as part of grub:
>
>   - redefine _asn1_strcat. grub removed strcat so replace it with the
>     appropriate calls to memcpy and strlen. Use this internally where
>     strcat was used.
>
>   - replace c_isdigit with grub_isdigit (and don't import c-ctype from
>     gnulib) grub_isdigit provides the same functionality as c_isdigit: it
>     determines if the input is an ASCII digit without regard for locale.
>
>   - replace GL_ATTRIBUTE_PURE with __attribute__((pure)) which been
>     supported since gcc-2.96. This avoids messing around with gnulib.
>
>   - adjust libtasn1.h: drop the ASN1_API logic, it's not needed for our
>     modules. Unconditionally support const and pure attributes and adjust
>     header paths.
>
>   - adjust header paths to "grub/libtasn1.h".
>
>   - replace a 64 bit division with a call to grub_divmod64, preventing
>     creation of __udivdi3 calls on 32 bit platforms.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>
>
> ---
>
> v2: Clean up strcat handling, thanks Stefan Berger.
> ---
>   grub-core/lib/libtasn1/lib/decoding.c   | 11 ++++++-----
>   grub-core/lib/libtasn1/lib/element.c    |  3 ++-
>   grub-core/lib/libtasn1/lib/gstr.c       |  4 ++--
>   grub-core/lib/libtasn1/lib/int.h        |  4 ++--
>   grub-core/lib/libtasn1/lib/parser_aux.c |  7 ++++---
>   include/grub/libtasn1.h                 | 26 ++++++-------------------
>   6 files changed, 22 insertions(+), 33 deletions(-)
>
> diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c
> index 42f9a92b5d44..3406e1832746 100644
> --- a/grub-core/lib/libtasn1/lib/decoding.c
> +++ b/grub-core/lib/libtasn1/lib/decoding.c
> @@ -32,7 +32,8 @@
>   #include <element.h>
>   #include <limits.h>
>   #include <intprops.h>
> -#include <c-ctype.h>
> +
> +#define c_isdigit grub_isdigit
>   
>   #ifdef DEBUG
>   # define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__)
> @@ -2008,8 +2009,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
>   	  (p2->type & CONST_ASSIGN))
>   	{
>   	  strcpy (name, definitions->name);
> -	  strcat (name, ".");
> -	  strcat (name, p2->name);
> +	  _asn1_strcat (name, ".");
> +	  _asn1_strcat (name, p2->name);
>   
>   	  len = sizeof (value);
>   	  result = asn1_read_value (definitions, name, value, &len);
> @@ -2026,8 +2027,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
>   	      if (p2)
>   		{
>   		  strcpy (name, definitions->name);
> -		  strcat (name, ".");
> -		  strcat (name, p2->name);
> +		  _asn1_strcat (name, ".");
> +		  _asn1_strcat (name, p2->name);
>   
>   		  result = asn1_create_element (definitions, name, &aux);
>   		  if (result == ASN1_SUCCESS)
> diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c
> index 539008d8e949..ed761ff56bd9 100644
> --- a/grub-core/lib/libtasn1/lib/element.c
> +++ b/grub-core/lib/libtasn1/lib/element.c
> @@ -30,9 +30,10 @@
>   #include "parser_aux.h"
>   #include <gstr.h>
>   #include "structure.h"
> -#include "c-ctype.h"
>   #include "element.h"
>   
> +#define c_isdigit grub_isdigit
> +
>   void
>   _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size)
>   {
> diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c
> index e91a3a151c0d..a092c9a5a24b 100644
> --- a/grub-core/lib/libtasn1/lib/gstr.c
> +++ b/grub-core/lib/libtasn1/lib/gstr.c
> @@ -36,13 +36,13 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src)
>   
>     if (dest_tot_size - dest_size > str_size)
>       {
> -      strcat (dest, src);
> +      _asn1_strcat (dest, src);
>       }
>     else
>       {
>         if (dest_tot_size - dest_size > 0)
>   	{
> -	  strncat (dest, src, (dest_tot_size - dest_size) - 1);
> +	  memcpy (dest + dest_size, src, (dest_tot_size - dest_size) - 1);


With dest_size = strlen(dest) this is following the 'pattern' of the 
#define below.


>   	  dest[dest_tot_size - 1] = 0;
>   	}
>       }
> diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
> index ea1625786c1b..4a568efee9c1 100644
> --- a/grub-core/lib/libtasn1/lib/int.h
> +++ b/grub-core/lib/libtasn1/lib/int.h
> @@ -35,7 +35,7 @@
>   #include <sys/types.h>
>   #endif
>   
> -#include <libtasn1.h>
> +#include "grub/libtasn1.h"
>   
>   #define ASN1_SMALL_VALUE_SIZE 16
>   
> @@ -115,7 +115,7 @@ extern const tag_and_class_st _asn1_tags[];
>   #define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
>   #define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
>   #define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
> -#define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
> +#define _asn1_strcat(a,b) memcpy((char *)a + strlen((const char *)a), (const char *)b, strlen((const char *)b) + 1)
>   
>   #if SIZEOF_UNSIGNED_LONG_INT == 8
>   # define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
> diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c
> index d5dbbf8765da..89c9be69dc2a 100644
> --- a/grub-core/lib/libtasn1/lib/parser_aux.c
> +++ b/grub-core/lib/libtasn1/lib/parser_aux.c
> @@ -26,7 +26,8 @@
>   #include "gstr.h"
>   #include "structure.h"
>   #include "element.h"
> -#include "c-ctype.h"
> +
> +#define c_isdigit grub_isdigit
>   
>   char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not found */
>   
> @@ -40,7 +41,7 @@ char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not fou
>   #ifdef __clang__
>   __attribute__((no_sanitize("integer")))
>   #endif
> -_GL_ATTRIBUTE_PURE
> +__attribute__((__pure__))
>   static unsigned int
>   _asn1_hash_name (const char *x)
>   {
> @@ -634,7 +635,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
>     count = 0;
>     do
>       {
> -      d = val / 10;
> +      d = grub_divmod64(val, 10, NULL);
>         r = val - d * 10;
>         temp[start + count] = '0' + (char) r;
>         count++;
> diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
> index 785eda2ae3f8..28dbf16c4e0c 100644
> --- a/include/grub/libtasn1.h
> +++ b/include/grub/libtasn1.h
> @@ -38,29 +38,15 @@
>   #ifndef LIBTASN1_H
>   #define LIBTASN1_H
>   
> -#ifndef ASN1_API
> -#if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY
> -#define ASN1_API __attribute__((__visibility__("default")))
> -#elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC
> -#define ASN1_API __declspec(dllexport)
> -#elif defined _MSC_VER && ! defined ASN1_STATIC
> -#define ASN1_API __declspec(dllimport)
> -#else
> +/* grub: ASN1_API is not used */
>   #define ASN1_API
> -#endif
> -#endif
>   
> -#ifdef __GNUC__
> -# define __LIBTASN1_CONST__  __attribute__((const))
> -# define __LIBTASN1_PURE__  __attribute__((pure))
> -#else
> -# define __LIBTASN1_CONST__
> -# define __LIBTASN1_PURE__
> -#endif
> +/* grub: all our supported compilers support these attributes */
> +#define __LIBTASN1_CONST__  __attribute__((const))
> +#define __LIBTASN1_PURE__  __attribute__((pure))
>   
> -#include <sys/types.h>
> -#include <time.h>
> -#include <stdio.h>		/* for FILE* */
> +#include <grub/types.h>
> +#include <grub/time.h>
>   
>   #ifdef __cplusplus
>   extern "C"


Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>




^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 14/22] libtasn1: compile into asn1 module
  2021-06-30  8:40 ` [PATCH v2 14/22] libtasn1: compile into asn1 module Daniel Axtens
@ 2021-07-12 13:05   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 13:05 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> Create a wrapper file that specifies the module license.
> Set up the makefile so it is built.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   grub-core/Makefile.core.def        | 15 +++++++++++++++
>   grub-core/lib/libtasn1_wrap/wrap.c | 26 ++++++++++++++++++++++++++
>   2 files changed, 41 insertions(+)
>   create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 915287d44c13..6fbae150c687 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -2535,3 +2535,18 @@ module = {
>     common = commands/i386/wrmsr.c;
>     enable = x86;
>   };
> +
> +module = {
> +  name = asn1;
> +  common = lib/libtasn1/lib/decoding.c;
> +  common = lib/libtasn1/lib/coding.c;
> +  common = lib/libtasn1/lib/element.c;
> +  common = lib/libtasn1/lib/structure.c;
> +  common = lib/libtasn1/lib/parser_aux.c;
> +  common = lib/libtasn1/lib/gstr.c;
> +  common = lib/libtasn1/lib/errors.c;
> +  common = lib/libtasn1_wrap/wrap.c;
> +  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
> +  // -Wno-type-limits comes from libtasn1's configure.ac
> +  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
> +};
> diff --git a/grub-core/lib/libtasn1_wrap/wrap.c b/grub-core/lib/libtasn1_wrap/wrap.c
> new file mode 100644
> index 000000000000..622ba942e33f
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/wrap.c
> @@ -0,0 +1,26 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020 IBM Corporation
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/dl.h>
> +
> +/*
> + * libtasn1 is provided under LGPL2.1+, which is compatible
> + * with GPL3+. As Grub as a whole is under GPL3+, this module
> + * is therefore under GPL3+ also.
> + */
> +GRUB_MOD_LICENSE ("GPLv3+");


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 15/22] test_asn1: test module for libtasn1
  2021-06-30  8:40 ` [PATCH v2 15/22] test_asn1: test module for libtasn1 Daniel Axtens
@ 2021-07-12 19:35   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 19:35 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> Import tests from libtasn1 that don't use functionality we don't
> import. I have put them here rather than in the libtasn1 directory
> because:
>
>   -  They need much more significant changes to run in the grub
>      context.
>
>   -  I don't expect they will need to be changed when updating
>      libtasn1: I expect the old tests will usually continue to pass on
>      new versions.
>
> This doesn't test the full decoder but that will be exercised in
> test suites for coming patch sets.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

You had to make some adjustments to the test cases since you don't want 
them to be invoked each via main anymore but there's a test harness now 
calling one after the other.

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>



> ---
>   .gitignore                                    |   1 +
>   Makefile.util.def                             |   6 +
>   grub-core/Makefile.core.def                   |  13 ++
>   .../tests/CVE-2018-1000654-1_asn1_tab.h       |  32 +++
>   .../tests/CVE-2018-1000654-2_asn1_tab.h       |  36 +++
>   .../libtasn1_wrap/tests/CVE-2018-1000654.c    |  61 +++++
>   .../lib/libtasn1_wrap/tests/Test_overflow.c   | 138 ++++++++++++
>   .../lib/libtasn1_wrap/tests/Test_simple.c     | 207 +++++++++++++++++
>   .../lib/libtasn1_wrap/tests/Test_strings.c    | 150 +++++++++++++
>   .../libtasn1_wrap/tests/object-id-decoding.c  | 116 ++++++++++
>   .../libtasn1_wrap/tests/object-id-encoding.c  | 120 ++++++++++
>   .../lib/libtasn1_wrap/tests/octet-string.c    | 211 ++++++++++++++++++
>   .../lib/libtasn1_wrap/tests/reproducers.c     |  81 +++++++
>   grub-core/lib/libtasn1_wrap/wrap_tests.c      |  75 +++++++
>   grub-core/lib/libtasn1_wrap/wrap_tests.h      |  38 ++++
>   tests/test_asn1.in                            |  12 +
>   16 files changed, 1297 insertions(+)
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_simple.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/Test_strings.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/octet-string.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/tests/reproducers.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.c
>   create mode 100644 grub-core/lib/libtasn1_wrap/wrap_tests.h
>   create mode 100644 tests/test_asn1.in
>
> diff --git a/.gitignore b/.gitignore
> index f6a1bd051752..c8fbf274cf1f 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -264,6 +264,7 @@ widthspec.bin
>   /stamp-h1
>   /syslinux_test
>   /tar_test
> +/test_asn1
>   /test_sha512sum
>   /test_unset
>   /tests/syslinux/ubuntu10.04_grub.cfg
> diff --git a/Makefile.util.def b/Makefile.util.def
> index f8b356cc1fa4..3381711c2666 100644
> --- a/Makefile.util.def
> +++ b/Makefile.util.def
> @@ -1211,6 +1211,12 @@ script = {
>     common = tests/syslinux_test.in;
>   };
>   
> +script = {
> +  testcase;
> +  name = test_asn1;
> +  common = tests/test_asn1.in;
> +};
> +
>   program = {
>     testcase;
>     name = example_unit_test;
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 6fbae150c687..d63f216111ca 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -2550,3 +2550,16 @@ module = {
>     // -Wno-type-limits comes from libtasn1's configure.ac
>     cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) -I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
>   };
> +
> +module = {
> +  name = test_asn1;
> +  common = lib/libtasn1_wrap/tests/CVE-2018-1000654.c;
> +  common = lib/libtasn1_wrap/tests/object-id-decoding.c;
> +  common = lib/libtasn1_wrap/tests/object-id-encoding.c;
> +  common = lib/libtasn1_wrap/tests/octet-string.c;
> +  common = lib/libtasn1_wrap/tests/reproducers.c;
> +  common = lib/libtasn1_wrap/tests/Test_overflow.c;
> +  common = lib/libtasn1_wrap/tests/Test_simple.c;
> +  common = lib/libtasn1_wrap/tests/Test_strings.c;
> +  common = lib/libtasn1_wrap/wrap_tests.c;
> +};
> diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
> new file mode 100644
> index 000000000000..1e7d3d64f55d
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h
> @@ -0,0 +1,32 @@
> +#if HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +
> +#include <grub/libtasn1.h>
> +
> +const asn1_static_node CVE_2018_1000654_1_asn1_tab[] = {
> +  { "TEST_TREE", 536875024, NULL },
> +  { NULL, 1610612748, NULL },
> +  { "iso", 1073741825, "1"},
> +  { "identified-organization", 1073741825, "3"},
> +  { "dod", 1073741825, "6"},
> +  { "internet", 1073741825, "1"},
> +  { "security", 1073741825, "5"},
> +  { "mechanisms", 1073741825, "5"},
> +  { "pkix", 1073741825, "7"},
> +  { "id-mod", 1073741825, "0"},
> +  { "id-pkix1-implicit-88", 1, "2"},
> +  { "id-xnyTest", 1879048204, NULL },
> +  { NULL, 1073741825, "id-ix"},
> +  { NULL, 1073741825, "29"},
> +  { NULL, 1, "1"},
> +  { "id-ix", 1880096780, "OBJECR"},
> +  { NULL, 1073741825, "id-ix"},
> +  { NULL, 1073741825, "29"},
> +  { NULL, 1, "2"},
> +  { "id-xnyTest", 805306380, NULL },
> +  { NULL, 1073741825, "id-ix"},
> +  { NULL, 1073741825, "29"},
> +  { NULL, 1, "1"},
> +  { NULL, 0, NULL }
> +};
> diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
> new file mode 100644
> index 000000000000..e2561e5ec6dc
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h
> @@ -0,0 +1,36 @@
> +#if HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +
> +#include <grub/libtasn1.h>
> +
> +const asn1_static_node CVE_2018_1000654_2_asn1_tab[] = {
> +  { "TEST_TREE", 536875024, NULL },
> +  { NULL, 1610612748, NULL },
> +  { "iso", 1073741825, "1"},
> +  { "identified-organization", 1073741825, "3"},
> +  { "dod", 1073741825, "6"},
> +  { "internet", 1073741825, "1"},
> +  { "security", 1073741825, "5"},
> +  { "mechanisms", 1073741825, "5"},
> +  { "pkix", 1073741825, "7"},
> +  { "id-mod", 1073741825, "0"},
> +  { "id-pkix1-implicit-88", 1, "2"},
> +  { "id-oneTest", 1879048204, NULL },
> +  { NULL, 1073741825, "id-two"},
> +  { NULL, 1073741825, "9"},
> +  { NULL, 1, "1"},
> +  { "id-two", 1879048204, NULL },
> +  { NULL, 1073741825, "id-three"},
> +  { NULL, 1073741825, "2"},
> +  { NULL, 1, "2"},
> +  { "id-three", 1879048204, NULL },
> +  { NULL, 1073741825, "id-four"},
> +  { NULL, 1073741825, "3"},
> +  { NULL, 1, "3"},
> +  { "id-four", 805306380, NULL },
> +  { NULL, 1073741825, "id-two"},
> +  { NULL, 1073741825, "3"},
> +  { NULL, 1, "3"},
> +  { NULL, 0, NULL }
> +};
> diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
> new file mode 100644
> index 000000000000..534e304521e0
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c
> @@ -0,0 +1,61 @@
> +/*
> + * Copyright (C) 2002-2018 Free Software Foundation, Inc.
> + *
> + * This file is part of LIBTASN1.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +/****************************************************************/
> +/* Description: reproducer for CVE-2018-1000654			*/
> +/****************************************************************/
> +
> +#include <grub/libtasn1.h>
> +#include <grub/err.h>
> +#include <grub/mm.h>
> +#include <grub/misc.h>
> +#include <grub/types.h>
> +#include "../wrap_tests.h"
> +
> +#include "CVE-2018-1000654-1_asn1_tab.h"
> +#include "CVE-2018-1000654-2_asn1_tab.h"
> +
> +void
> +test_CVE_2018_1000654 (void)
> +{
> +  int result;
> +  asn1_node definitions = NULL;
> +  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
> +
> +  result = asn1_array2tree (CVE_2018_1000654_1_asn1_tab, &definitions, errorDescription);
> +  if (result != ASN1_RECURSION)
> +    {
> +      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
> +		  asn1_strerror (result), errorDescription);
> +      return;
> +    }
> +
> +  asn1_delete_structure (&definitions);
> +
> +  result = asn1_array2tree (CVE_2018_1000654_2_asn1_tab, &definitions, errorDescription);
> +  if (result != ASN1_RECURSION)
> +    {
> +      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
> +		  asn1_strerror (result), errorDescription);
> +      return;
> +    }
> +
> +  asn1_delete_structure (&definitions);
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
> new file mode 100644
> index 000000000000..f48aea0ef8b8
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c
> @@ -0,0 +1,138 @@
> +/*
> + * Copyright (C) 2012-2014 Free Software Foundation, Inc.
> + *
> + * This file is part of LIBTASN1.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +/* Written by Simon Josefsson */
> +
> +#include <grub/libtasn1.h>
> +#include <grub/err.h>
> +#include <grub/mm.h>
> +#include <grub/misc.h>
> +#include <grub/types.h>
> +#include "../wrap_tests.h"
> +
> +void
> +test_overflow(void)
> +{
> +  /* Test that values larger than long are rejected.  This has worked
> +     fine with all versions of libtasn1. */
> +
> +  {
> +    unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF";
> +    long l;
> +    int len;
> +
> +    l = asn1_get_length_der (der, sizeof der, &len);
> +
> +    if (l != -2L)
> +      {
> +	grub_fatal ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len);
> +	return;
> +      }
> +  }
> +
> +  /* Test that values larger than int but smaller than long are
> +     rejected.  This limitation was introduced with libtasn1 2.12. */
> +#if (GRUB_LONG_MAX > GRUB_INT_MAX)
> +    {
> +      unsigned long num = ((long) GRUB_UINT_MAX) << 2;
> +      unsigned char der[20];
> +      int der_len;
> +      long l;
> +      int len;
> +
> +      asn1_length_der (num, der, &der_len);
> +
> +      l = asn1_get_length_der (der, der_len, &len);
> +
> +      if (l != -2L)
> +	{
> +	  grub_fatal ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l,
> +		      len);
> +	  return;
> +	}
> +    }
> +#endif
> +
> +  /* Test that values larger than would fit in the input string are
> +     rejected.  This problem was fixed in libtasn1 2.12. */
> +  {
> +    unsigned long num = 64;
> +    unsigned char der[20];
> +    int der_len;
> +    long l;
> +    int len;
> +
> +    asn1_length_der (num, der, &der_len);
> +
> +    der_len = sizeof (der);
> +    l = asn1_get_length_der (der, der_len, &len);
> +
> +    if (l != -4L)
> +      {
> +	grub_fatal ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n",
> +		    l, len);
> +	return;
> +      }
> +  }
> +
> +  /* Test that values larger than would fit in the input string are
> +     rejected.  This problem was fixed in libtasn1 2.12. */
> +  {
> +    unsigned long num = 1073741824;
> +    unsigned char der[20];
> +    int der_len;
> +    long l;
> +    int len;
> +
> +    asn1_length_der (num, der, &der_len);
> +
> +    der_len = sizeof (der);
> +    l = asn1_get_length_der (der, der_len, &len);
> +
> +    if (l != -4L)
> +      {
> +	grub_fatal ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n",
> +		    l, len);
> +	return;
> +      }
> +  }
> +
> +  /* Test that values larger than would fit in the input string are
> +     rejected.  This problem was fixed in libtasn1 2.12. */
> +  {
> +    unsigned long num = 2147483649;
> +    unsigned char der[20];
> +    int der_len;
> +    long l;
> +    int len;
> +
> +    asn1_length_der (num, der, &der_len);
> +
> +    der_len = sizeof (der);
> +    l = asn1_get_length_der (der, der_len, &len);
> +
> +    if (l != -2L)
> +      {
> +	grub_fatal ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n",
> +		    l, len);
> +	return;
> +      }
> +  }
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_simple.c b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c
> new file mode 100644
> index 000000000000..9f01006ddf47
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c
> @@ -0,0 +1,207 @@
> +/*
> + * Copyright (C) 2011-2014 Free Software Foundation, Inc.
> + *
> + * This file is part of LIBTASN1.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Written by Simon Josefsson
> + *
> + */
> +
> +#include <grub/libtasn1.h>
> +#include <grub/mm.h>
> +#include <grub/misc.h>
> +#include <grub/err.h>
> +#include "../wrap_tests.h"
> +
> +struct tv
> +{
> +  int bitlen;
> +  const char *bitstr;
> +  int derlen;
> +  const char *der;
> +};
> +
> +static const struct tv tv[] = {
> +  {0, "", 2, "\x01\x00"},
> +  {1, "\x00", 3, "\x02\x07\x00"},
> +  {2, "\x00", 3, "\x02\x06\x00"},
> +  {3, "\x00", 3, "\x02\x05\x00"},
> +  {4, "\x00", 3, "\x02\x04\x00"},
> +  {5, "\x00", 3, "\x02\x03\x00"},
> +  {6, "\x00", 3, "\x02\x02\x00"},
> +  {7, "\x00", 3, "\x02\x01\x00"},
> +  {8, "\x00\x00", 3, "\x02\x00\x00"},
> +  {9, "\x00\x00", 4, "\x03\x07\x00\x00"},
> +  {10, "\x00\x00", 4, "\x03\x06\x00\x00"},
> +  {11, "\x00\x00", 4, "\x03\x05\x00\x00"},
> +  {12, "\x00\x00", 4, "\x03\x04\x00\x00"},
> +  {13, "\x00\x00", 4, "\x03\x03\x00\x00"},
> +  {14, "\x00\x00", 4, "\x03\x02\x00\x00"},
> +  {15, "\x00\x00", 4, "\x03\x01\x00\x00"},
> +  {16, "\x00\x00", 4, "\x03\x00\x00\x00"},
> +  {17, "\x00\x00\x00", 5, "\x04\x07\x00\x00\x00"},
> +  {18, "\x00\x00\x00", 5, "\x04\x06\x00\x00\x00"},
> +  {19, "\x00\x00\x00", 5, "\x04\x05\x00\x00\x00"},
> +  {1, "\xFF", 3, "\x02\x07\x80"},
> +  {2, "\xFF", 3, "\x02\x06\xc0"},
> +  {3, "\xFF", 3, "\x02\x05\xe0"},
> +  {4, "\xFF", 3, "\x02\x04\xf0"},
> +  {5, "\xFF", 3, "\x02\x03\xf8"},
> +  {6, "\xFF", 3, "\x02\x02\xfc"},
> +  {7, "\xFF", 3, "\x02\x01\xfe"},
> +  {8, "\xFF\xFF", 3, "\x02\x00\xff"},
> +  {9, "\xFF\xFF", 4, "\x03\x07\xff\x80"},
> +  {10, "\xFF\xFF", 4, "\x03\x06\xff\xc0"},
> +  {11, "\xFF\xFF", 4, "\x03\x05\xff\xe0"},
> +  {12, "\xFF\xFF", 4, "\x03\x04\xff\xf0"},
> +  {13, "\xFF\xFF", 4, "\x03\x03\xff\xf8"},
> +  {14, "\xFF\xFF", 4, "\x03\x02\xff\xfc"},
> +  {15, "\xFF\xFF", 4, "\x03\x01\xff\xfe"},
> +  {16, "\xFF\xFF", 4, "\x03\x00\xff\xff"},
> +  {17, "\xFF\xFF\xFF", 5, "\x04\x07\xff\xff\x80"},
> +  {18, "\xFF\xFF\xFF", 5, "\x04\x06\xff\xff\xc0"},
> +  {19, "\xFF\xFF\xFF", 5, "\x04\x05\xff\xff\xe0"},
> +};
> +
> +void
> +test_simple (void)
> +{
> +  int result;
> +  unsigned char der[100];
> +  unsigned char str[100];
> +  int der_len = sizeof (der);
> +  int str_size = sizeof (str);
> +  int ret_len, bit_len;
> +  grub_size_t i;
> +
> +  /* Dummy test */
> +
> +  asn1_bit_der (NULL, 0, der, &der_len);
> +  result = asn1_get_bit_der (der, 0, &ret_len, str, str_size, &bit_len);
> +  if (result != ASN1_GENERIC_ERROR)
> +    {
> +      grub_fatal ("asn1_get_bit_der zero\n");
> +      return;
> +    }
> +
> +  /* Encode short strings with increasing bit lengths */
> +
> +  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
> +    {
> +      /* Encode */
> +
> +      asn1_bit_der ((const unsigned char *) tv[i].bitstr, tv[i].bitlen,
> +		    der, &der_len);
> +
> +#if 0
> +      {
> +	size_t j;
> +	for (j = 0; j < der_len; j++)
> +	  printf ("\\x%02x", der[j]);
> +	printf ("\n");
> +      }
> +#endif
> +
> +      if (der_len != tv[i].derlen || grub_memcmp (der, tv[i].der, der_len) != 0)
> +	{
> +	  grub_fatal ("asn1_bit_der iter %lu\n", (unsigned long) i);
> +	  return;
> +	}
> +
> +      /* Decode it */
> +
> +      result = asn1_get_bit_der (der, der_len, &ret_len, str,
> +				 str_size, &bit_len);
> +      if (result != ASN1_SUCCESS || ret_len != tv[i].derlen
> +	  || bit_len != tv[i].bitlen)
> +	{
> +	  grub_fatal ("asn1_get_bit_der iter %lu, err: %d\n", (unsigned long) i, result);
> +	  return;
> +	}
> +    }
> +
> +
> +  /* Decode sample from "A Layman's Guide to a Subset of ASN.1, BER,
> +     and DER" section 5.4 "BIT STRING": "The BER encoding of the BIT
> +     STRING value "011011100101110111" can be any of the following,
> +     among others, depending on the choice of padding bits, the form
> +     of length octets [...]".
> +   */
> +
> +  /* 03 04 06 6e 5d c0  DER encoding */
> +
> +  grub_memcpy (der, "\x04\x06\x6e\x5d\xc0", 5);
> +  der_len = 5;
> +
> +  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
> +  if (result != ASN1_SUCCESS || ret_len != 5
> +      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0)
> +    {
> +      grub_fatal ("asn1_get_bit_der example\n");
> +      return;
> +    }
> +
> +  der_len = sizeof (der);
> +  asn1_bit_der (str, bit_len, der, &der_len);
> +  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
> +    {
> +      grub_fatal ("asn1_bit_der example roundtrip\n");
> +      return;
> +    }
> +
> +  /* 03 04 06 6e 5d e0 padded with "100000" */
> +
> +  grub_memcpy (der, "\x04\x06\x6e\x5d\xe0", 5);
> +  der_len = 5;
> +
> +  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
> +  if (result != ASN1_SUCCESS || ret_len != 5
> +      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xe0", 3) != 0)
> +    {
> +      grub_fatal ("asn1_get_bit_der example padded\n");
> +      return;
> +    }
> +
> +  der_len = sizeof (der);
> +  asn1_bit_der (str, bit_len, der, &der_len);
> +  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
> +    {
> +      grub_fatal ("asn1_bit_der example roundtrip\n");
> +      return;
> +    }
> +
> +  /* 03 81 04 06 6e 5d c0 long form of length octets */
> +
> +  grub_memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6);
> +  der_len = 6;
> +
> +  result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len);
> +
> +  if (result != ASN1_SUCCESS || ret_len != 6
> +      || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0)
> +    {
> +      grub_fatal ("asn1_get_bit_der example long form\n");
> +      return;
> +    }
> +
> +  der_len = sizeof (der);
> +  asn1_bit_der (str, bit_len, der, &der_len);
> +  if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0)
> +    {
> +      grub_fatal ("asn1_bit_der example roundtrip\n");
> +      return;
> +    }
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_strings.c b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c
> new file mode 100644
> index 000000000000..dbe1474b204a
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright (C) 2012-2014 Free Software Foundation, Inc.
> + *
> + * This file is part of LIBTASN1.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Written by Simon Josefsson
> + *
> + */
> +
> +#include <grub/mm.h>
> +#include <grub/err.h>
> +#include <grub/misc.h>
> +#include <grub/libtasn1.h>
> +#include "../wrap_tests.h"
> +
> +struct tv
> +{
> +  unsigned int etype;
> +  unsigned int str_len;
> +  const void *str;
> +  unsigned int der_len;
> +  const void *der;
> +};
> +
> +static const struct tv tv[] = {
> +  {ASN1_ETYPE_IA5_STRING, 20,
> +   "\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72",
> +   22,
> +   "\x16\x14\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72"},
> +  {ASN1_ETYPE_PRINTABLE_STRING, 5, "\x4e\x69\x6b\x6f\x73",
> +   7, "\x13\x05\x4e\x69\x6b\x6f\x73"},
> +  {ASN1_ETYPE_UTF8_STRING, 12, "Αττική",
> +   14, "\x0c\x0c\xce\x91\xcf\x84\xcf\x84\xce\xb9\xce\xba\xce\xae"},
> +  {ASN1_ETYPE_TELETEX_STRING, 15,
> +   "\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e",
> +   17,
> +   "\x14\x0f\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e"},
> +  {ASN1_ETYPE_OCTET_STRING, 36,
> +   "\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A",
> +   38,
> +   "\x04\x24\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A"}
> +};
> +
> +#define SSTR(x) sizeof(x)-1,x
> +static const struct tv ber[] = {
> +  {ASN1_ETYPE_OCTET_STRING,
> +   SSTR("\xa0\xa0"),
> +   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x00\x00")},
> +  {ASN1_ETYPE_OCTET_STRING,
> +   SSTR("\xa0\xa0\xb0\xb0\xb0"),
> +   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x00\x00")},
> +  {ASN1_ETYPE_OCTET_STRING,
> +   SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1"),
> +   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x00\x00\x00\x00")},
> +  {ASN1_ETYPE_OCTET_STRING,
> +   SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1\xc1"),
> +   SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x04\x82\x00\x01\xc1\x00\x00\x00\x00")},
> +};
> +
> +void
> +test_strings ()
> +{
> +  int ret;
> +  unsigned char tl[ASN1_MAX_TL_SIZE];
> +  unsigned int tl_len, der_len, str_len;
> +  const unsigned char *str;
> +  unsigned char *b;
> +  unsigned int i;
> +
> +  /* Dummy test */
> +
> +  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
> +    {
> +      /* Encode */
> +      tl_len = sizeof (tl);
> +      ret = asn1_encode_simple_der (tv[i].etype, tv[i].str, tv[i].str_len,
> +				    tl, &tl_len);
> +      if (ret != ASN1_SUCCESS)
> +	{
> +	  grub_fatal ("Encoding error in %u: %s\n", i,
> +		   asn1_strerror (ret));
> +	  return;
> +	}
> +      der_len = tl_len + tv[i].str_len;
> +
> +      if (der_len != tv[i].der_len || grub_memcmp (tl, tv[i].der, tl_len) != 0)
> +	{
> +	  grub_fatal (
> +		   "DER encoding differs in %u! (size: %u, expected: %u)\n",
> +		   i, der_len, tv[i].der_len);
> +	  return;
> +	}
> +
> +      /* decoding */
> +      ret =
> +	asn1_decode_simple_der (tv[i].etype, tv[i].der, tv[i].der_len, &str,
> +				&str_len);
> +      if (ret != ASN1_SUCCESS)
> +	{
> +	  grub_fatal ("Decoding error in %u: %s\n", i,
> +		   asn1_strerror (ret));
> +	  return;
> +	}
> +
> +      if (str_len != tv[i].str_len || grub_memcmp (str, tv[i].str, str_len) != 0)
> +	{
> +	  grub_fatal (
> +		   "DER decoded data differ in %u! (size: %u, expected: %u)\n",
> +		   i, der_len, tv[i].str_len);
> +	  return;
> +	}
> +    }
> +
> +  /* BER decoding */
> +  for (i = 0; i < sizeof (ber) / sizeof (ber[0]); i++)
> +    {
> +      /* decoding */
> +      ret =
> +	asn1_decode_simple_ber (ber[i].etype, ber[i].der, ber[i].der_len, &b,
> +				&str_len, NULL);
> +      if (ret != ASN1_SUCCESS)
> +	{
> +	  grub_fatal ("BER decoding error in %u: %s\n", i,
> +		   asn1_strerror (ret));
> +	  return;
> +	}
> +
> +      if (str_len != ber[i].str_len || grub_memcmp (b, ber[i].str, str_len) != 0)
> +	{
> +	  grub_fatal (
> +		   "BER decoded data differ in %u! (size: %u, expected: %u)\n",
> +		   i, str_len, ber[i].str_len);
> +	  return;
> +	}
> +      grub_free(b);
> +    }
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
> new file mode 100644
> index 000000000000..d367bbfb5a7b
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c
> @@ -0,0 +1,116 @@
> +/*
> + * Copyright (C) 2016 Red Hat, Inc.
> + *
> + * This file is part of LIBTASN1.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include <grub/libtasn1.h>
> +#include <grub/types.h>
> +#include <grub/misc.h>
> +#include <grub/err.h>
> +#include "../wrap_tests.h"
> +
> +struct tv
> +{
> +  int der_len;
> +  const unsigned char *der;
> +  const char *oid;
> +  int expected_error;
> +};
> +
> +static const struct tv tv[] = {
> +  {.der_len = 5,
> +   .der = (void *) "\x06\x03\x80\x37\x03",
> +   .oid = "2.999.3",
> +   .expected_error = ASN1_DER_ERROR /* leading 0x80 */
> +  },
> +  {.der_len = 12,
> +   .der = (void *) "\x06\x0a\x2b\x06\x01\x80\x01\x92\x08\x09\x05\x01",
> +   .oid = "1.3.6.1.4.1.2312.9.5.1",
> +   .expected_error = ASN1_DER_ERROR /* leading 0x80 */
> +  },
> +  {.der_len = 6,
> +   .der = (void *) "\x06\x04\x01\x02\x03\x04",
> +   .oid = "0.1.2.3.4",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 5,
> +   .der = (void *) "\x06\x03\x51\x02\x03",
> +   .oid = "2.1.2.3",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 5,
> +   .der = (void *) "\x06\x03\x88\x37\x03",
> +   .oid = "2.999.3",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 12,
> +   .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01",
> +   .oid = "1.3.6.1.4.1.2312.9.5.1",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 19,
> +   .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d",
> +   .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 19,
> +   .der =
> +   (void *)
> +   "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07",
> +   .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7",
> +   .expected_error = ASN1_SUCCESS},
> +};
> +
> +void
> +test_object_id_decoding (void)
> +{
> +  char str[128];
> +  int ret, ret_len;
> +  grub_size_t i;
> +
> +  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
> +    {
> +      /* decode */
> +      ret =
> +	asn1_get_object_id_der (tv[i].der+1,
> +				tv[i].der_len-1, &ret_len, str,
> +				sizeof (str));
> +      if (ret != tv[i].expected_error)
> +	{
> +	  grub_fatal (
> +		   "%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n",
> +		   __LINE__, (unsigned long) i, asn1_strerror(ret), tv[i].expected_error);
> +	  return;
> +	}
> +
> +      if (tv[i].expected_error != ASN1_SUCCESS)
> +        continue;
> +
> +      if (ret_len != tv[i].der_len-1)
> +	{
> +	  grub_fatal (
> +		   "%d: iter %lu: error in DER, length returned is %d, had %d\n",
> +		   __LINE__, (unsigned long)i, ret_len, tv[i].der_len-1);
> +	  return;
> +	}
> +
> +      if (grub_strcmp (tv[i].oid, str) != 0)
> +	{
> +	  grub_fatal (
> +		   "%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n",
> +		   __LINE__, (unsigned long) i, str, tv[i].oid);
> +	  return;
> +	}
> +
> +    }
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
> new file mode 100644
> index 000000000000..3a83b58c59f4
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c
> @@ -0,0 +1,120 @@
> +/*
> + * Copyright (C) 2019 Nikos Mavrogiannopoulos
> + *
> + * This file is part of LIBTASN1.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include <grub/libtasn1.h>
> +#include <grub/types.h>
> +#include <grub/misc.h>
> +#include <grub/err.h>
> +#include "../wrap_tests.h"
> +
> +struct tv
> +{
> +  int der_len;
> +  const unsigned char *der;
> +  const char *oid;
> +  int expected_error;
> +};
> +
> +static const struct tv tv[] = {
> +  {.der_len = 0,
> +   .der = (void *) "",
> +   .oid = "5.999.3",
> +   .expected_error = ASN1_VALUE_NOT_VALID /* cannot start with 5 */
> +  },
> +  {.der_len = 0,
> +   .der = (void *) "",
> +   .oid = "0.48.9",
> +   .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 48 */
> +  },
> +  {.der_len = 0,
> +   .der = (void *) "",
> +   .oid = "1.40.9",
> +   .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 40 */
> +  },
> +  {.der_len = 4,
> +   .der = (void *) "\x06\x02\x4f\x63",
> +   .oid = "1.39.99",
> +   .expected_error = ASN1_SUCCESS,
> +  },
> +  {.der_len = 6,
> +   .der = (void *) "\x06\x04\x01\x02\x03\x04",
> +   .oid = "0.1.2.3.4",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 5,
> +   .der = (void *) "\x06\x03\x51\x02\x03",
> +   .oid = "2.1.2.3",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 5,
> +   .der = (void *) "\x06\x03\x88\x37\x03",
> +   .oid = "2.999.3",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 12,
> +   .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01",
> +   .oid = "1.3.6.1.4.1.2312.9.5.1",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 19,
> +   .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d",
> +   .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109",
> +   .expected_error = ASN1_SUCCESS},
> +  {.der_len = 19,
> +   .der =
> +   (void *)
> +   "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07",
> +   .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7",
> +   .expected_error = ASN1_SUCCESS},
> +};
> +
> +void
> +test_object_id_encoding(void)
> +{
> +  unsigned char der[128];
> +  int ret, der_len, i;
> +
> +  for (i = 0; i < (int)(sizeof (tv) / sizeof (tv[0])); i++)
> +    {
> +      der_len = sizeof(der);
> +      ret = asn1_object_id_der(tv[i].oid, der, &der_len, 0);
> +      if (ret != ASN1_SUCCESS)
> +	{
> +	  if (ret == tv[i].expected_error)
> +	    continue;
> +	  grub_fatal (
> +		   "%d: iter %lu, encoding of OID failed: %s\n",
> +		   __LINE__, (unsigned long) i, asn1_strerror(ret));
> +	  return;
> +	}
> +      else if (ret != tv[i].expected_error)
> +        {
> +	  grub_fatal (
> +		   "%d: iter %lu, encoding of OID %s succeeded when expecting failure\n",
> +		   __LINE__, (unsigned long) i, tv[i].oid);
> +          return;
> +        }
> +
> +      if (der_len != tv[i].der_len || grub_memcmp(der, tv[i].der, der_len) != 0)
> +	{
> +	  grub_fatal (
> +		   "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n",
> +		   __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len);
> +
> +	  return;
> +	}
> +    }
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/tests/octet-string.c b/grub-core/lib/libtasn1_wrap/tests/octet-string.c
> new file mode 100644
> index 000000000000..d8a049e8df06
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/octet-string.c
> @@ -0,0 +1,211 @@
> +/*
> + * Copyright (C) 2011-2020 Free Software Foundation, Inc.
> + *
> + * This file is part of LIBTASN1.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Written by Simon Josefsson and Nikos Mavrogiannopoulos
> + *
> + */
> +
> +#include <grub/libtasn1.h>
> +#include <grub/err.h>
> +#include <grub/mm.h>
> +#include <grub/misc.h>
> +#include "../wrap_tests.h"
> +
> +
> +struct tv
> +{
> +  const char *name;
> +  int der_len;
> +  const unsigned char *der_str;
> +  int len;
> +  const unsigned char *string;
> +  int expected_error;
> +  int ber;
> +};
> +
> +static const struct tv tv[] = {
> +  {.name = "primitive octet strings",
> +   .der_len = 10,
> +   .der_str =
> +   (void*)"\x04\x08\x01\x23\x45\x67\x89\xab\xcd\xef",
> +   .len = 8,
> +   .string =
> +   (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef",
> +   .ber = 0},
> +  {.der_len = 22,
> +   .der_str =
> +   (void*)"\x04\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27",
> +   .len = 20,
> +   .string =
> +   (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27"},
> +
> +  {.name = "long type of length",
> +   .der_len = 23,
> +   .der_str =
> +   (void*)"\x04\x81\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27",
> +   .len = 20,
> +   .string =
> +   (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27",
> +   .ber = 1},
> +  {.der_len = 11,
> +   .der_str =
> +   (void*)"\x04\x81\x08\x01\x23\x45\x67\x89\xab\xcd\xef",
> +   .len = 8,
> +   .string =
> +   (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef",
> +   .ber = 1},
> +
> +  {.name = "constructed - indefinite",
> +   .der_len = 11,
> +   .der_str = (void*)"\x24\x80\x04\x05\x01\x02\x03\x04\x05\x00\x00",
> +   .len = 5,
> +   .string = (void*)"\x01\x02\x03\x04\x05",
> +   .ber = 1,
> +   },
> +
> +  {.name = "constructed - definite - concat",
> +   .der_len = 12,
> +   .der_str = (void*)"\x24\x0a\x04\x04\x0a\x0b\x0c\x0d\x04\x02\x0e\x0f",
> +   .len = 6,
> +   .string = (void*)"\x0a\x0b\x0c\x0d\x0e\x0f",
> +   .ber = 1,
> +   },
> +  {.name = "constructed - definite - recursive",
> +   .der_len = 15,
> +   .der_str = (void*)"\x24\x0d\x04\x04\x0a\x0b\x0c\x0d\x24\x05\x04\x00\x04\x01\x0f",
> +   .len = 5,
> +   .string = (void*)"\x0a\x0b\x0c\x0d\x0f",
> +   .ber = 1,
> +   },
> +  {.name = "constructed - definite - single",
> +   .der_len = 7,
> +   .der_str = (void*)"\x24\x05\x04\x03\x01\x02\x03",
> +   .len = 3,
> +   .string = (void*)"\x01\x02\x03",
> +   .ber = 1,
> +   },
> +
> +  /* a large amount of recursive indefinite encoding */
> +  {.name = "a large amount of recursive indefinite encoding",
> +   .der_len = 29325,
> +   .der_str = (void*)"\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80",
> +   .len = 0,
> +   .ber = 1,
> +   .expected_error = ASN1_DER_ERROR
> +   }
> +};
> +
> +void
> +test_octet_string (void)
> +{
> +  unsigned char str[100];
> +  unsigned char der[100];
> +  int der_len = sizeof (der);
> +  int str_size = sizeof (str);
> +  unsigned char *tmp = NULL;
> +  int ret, ret_len;
> +  grub_size_t i;
> +
> +  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
> +    {
> +      /* Decode */
> +
> +      if (tv[i].ber == 0)
> +	{
> +	  str_size = sizeof (str);
> +	  ret =
> +	    asn1_get_octet_der (tv[i].der_str + 1,
> +				tv[i].der_len - 1, &ret_len, str,
> +				sizeof (str), &str_size);
> +	  if (ret != tv[i].expected_error)
> +	    {
> +	      grub_fatal (
> +		       "%d: asn1_get_octet_der: %s: got %d expected %d\n",
> +		       __LINE__, tv[i].name, ret,
> +		       tv[i].expected_error);
> +	      return;
> +	    }
> +	  if (tv[i].expected_error)
> +	    continue;
> +
> +	  if (ret_len != tv[i].der_len - 1)
> +	    {
> +	      grub_fatal (
> +		       "%d: error in DER, length returned is %d, had %d\n",
> +		       __LINE__, ret_len, tv[i].der_len - 1);
> +	      return;
> +	    }
> +
> +	  if (str_size != tv[i].len
> +	      || grub_memcmp (tv[i].string, str, tv[i].len) != 0)
> +	    {
> +	      grub_fatal (
> +		       "%d: memcmp: %s: got invalid decoding\n",
> +		       __LINE__, tv[i].name);
> +
> +              return;
> +	    }
> +
> +	  /* Encode */
> +	  der_len = sizeof (der);
> +	  asn1_octet_der (str, str_size, der, &der_len);
> +
> +	  if (der_len != tv[i].der_len - 1
> +	      || grub_memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0)
> +	    {
> +	      grub_fatal (
> +		       "encoding: %s: got invalid encoding\n",
> +		       tv[i].name);
> +	      return;
> +	    }
> +	}
> +
> +      ret =
> +	asn1_decode_simple_ber (ASN1_ETYPE_OCTET_STRING,
> +				tv[i].der_str, tv[i].der_len,
> +				&tmp, (unsigned int*)&str_size, (unsigned int*)&der_len);
> +      if (ret != tv[i].expected_error)
> +	{
> +	  grub_fatal (
> +		   "%d: asn1_decode_simple_ber: %s: got %s expected %s\n",
> +		   __LINE__, tv[i].name, asn1_strerror(ret), asn1_strerror(tv[i].expected_error));
> +	  return;
> +	}
> +      if (tv[i].expected_error)
> +        continue;
> +
> +      if (der_len != tv[i].der_len)
> +	{
> +	  grub_fatal (
> +		   "%d: error: %s: DER, length returned is %d, had %d\n",
> +		   __LINE__, tv[i].name, der_len, tv[i].der_len);
> +	  return;
> +	}
> +
> +      if (str_size != tv[i].len || grub_memcmp (tv[i].string, tmp, tv[i].len) != 0)
> +	{
> +	  grub_fatal (
> +		   "%d: memcmp: %s: got invalid decoding\n",
> +		   __LINE__, tv[i].name);
> +          return;
> +	}
> +      grub_free (tmp);
> +      tmp = NULL;
> +
> +    }
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/tests/reproducers.c b/grub-core/lib/libtasn1_wrap/tests/reproducers.c
> new file mode 100644
> index 000000000000..dc7268d4c6c6
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/tests/reproducers.c
> @@ -0,0 +1,81 @@
> +/*
> + * Copyright (C) 2019 Free Software Foundation, Inc.
> + *
> + * This file is part of LIBTASN1.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +/****************************************************************/
> +/* Description: run reproducers for several fixed issues        */
> +/****************************************************************/
> +
> +#include <grub/libtasn1.h>
> +#include <grub/err.h>
> +#include <grub/mm.h>
> +#include "../wrap_tests.h"
> +
> +#define CONST_DOWN        (1U<<29)
> +
> +/* produces endless loop (fixed by d4b624b2):
> + * The following translates into a single node with all pointers
> + * (right,left,down) set to NULL. */
> +const asn1_static_node endless_asn1_tab[] = {
> +  { "TEST_TREE", 536875024, NULL },
> +  { NULL, 0, NULL }
> +};
> +
> +/* produces memory leak (fixed by f16d1ff9):
> + * 152 bytes in 1 blocks are definitely lost in loss record 1 of 1
> + *    at 0x4837B65: calloc (vg_replace_malloc.c:762)
> + *    by 0x4851C0D: _asn1_add_static_node (parser_aux.c:71)
> + *    by 0x4853AAC: asn1_array2tree (structure.c:200)
> + *    by 0x10923B: main (single_node.c:67)
> + */
> +const asn1_static_node tab[] = {
> +{ "a", CONST_DOWN, "" },
> +{ "b", 0, "" },
> +{ "c", 0, "" },
> +{ NULL, 0, NULL }
> +};
> +
> +void
> +test_reproducers (void)
> +{
> +  int result;
> +  asn1_node definitions = NULL;
> +  char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
> +
> +  result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
> +		  asn1_strerror (result), errorDescription);
> +      return;
> +    }
> +
> +  asn1_delete_structure (&definitions);
> +
> +  definitions = NULL;
> +  result = asn1_array2tree (tab, &definitions, errorDescription);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      grub_fatal ("Error: %s\nErrorDescription = %s\n\n",
> +		  asn1_strerror (result), errorDescription);
> +      return;
> +    }
> +
> +  asn1_delete_structure (&definitions);
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.c b/grub-core/lib/libtasn1_wrap/wrap_tests.c
> new file mode 100644
> index 000000000000..75fcd21f0d5d
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/wrap_tests.c
> @@ -0,0 +1,75 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020 IBM Corporation
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/dl.h>
> +#include <grub/command.h>
> +#include <grub/mm.h>
> +#include "wrap_tests.h"
> +
> +/*
> + * libtasn1 tests - from which this is derived - are provided under GPL3+.
> + */
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +static grub_command_t cmd;
> +
> +static grub_err_t
> +grub_cmd_asn1test (grub_command_t cmdd __attribute__((unused)),
> +		   int argc __attribute__((unused)),
> +		   char **args __attribute__((unused)))
> +{
> +  grub_printf ("test_CVE_2018_1000654\n");
> +  test_CVE_2018_1000654 ();
> +
> +  grub_printf ("test_object_id_decoding\n");
> +  test_object_id_decoding ();
> +
> +  grub_printf ("test_object_id_encoding\n");
> +  test_object_id_encoding ();
> +
> +  grub_printf ("test_octet_string\n");
> +  test_octet_string ();
> +
> +  grub_printf ("test_overflow\n");
> +  test_overflow ();
> +
> +  grub_printf ("test_reproducers\n");
> +  test_overflow ();
> +
> +  grub_printf ("test_simple\n");
> +  test_simple ();
> +
> +  grub_printf ("test_strings\n");
> +  test_strings ();
> +
> +  grub_printf ("ASN.1 self-tests passed\n");
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +
> +GRUB_MOD_INIT(test_asn1)
> +{
> +  cmd = grub_register_command ("test_asn1", grub_cmd_asn1test, NULL,
> +			       "Run self-tests for the ASN.1 parser.");
> +}
> +
> +GRUB_MOD_FINI(test_asn1)
> +{
> +  grub_unregister_command (cmd);
> +}
> diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.h b/grub-core/lib/libtasn1_wrap/wrap_tests.h
> new file mode 100644
> index 000000000000..555e56dd2022
> --- /dev/null
> +++ b/grub-core/lib/libtasn1_wrap/wrap_tests.h
> @@ -0,0 +1,38 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020 IBM Corporation
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef LIBTASN1_WRAP_TESTS_H
> +#define LIBTASN1_WRAP_TESTS_H
> +
> +void test_CVE_2018_1000654 (void);
> +
> +void test_object_id_encoding (void);
> +
> +void test_object_id_decoding (void);
> +
> +void test_octet_string (void);
> +
> +void test_overflow (void);
> +
> +void test_reproducers (void);
> +
> +void test_simple (void);
> +
> +void test_strings (void);
> +
> +#endif
> diff --git a/tests/test_asn1.in b/tests/test_asn1.in
> new file mode 100644
> index 000000000000..8173c5c270e5
> --- /dev/null
> +++ b/tests/test_asn1.in
> @@ -0,0 +1,12 @@
> +#! @BUILD_SHEBANG@
> +set -e
> +
> +. "@builddir@/grub-core/modinfo.sh"
> +
> +out=`echo test_asn1 | @builddir@/grub-shell`
> +
> +if [ "$(echo "$out" | tail -n 1)" != "ASN.1 self-tests passed" ]; then
> +  echo "ASN.1 test failure: $out"
> +  exit 1
> +fi
> +



^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 16/22] grub-install: support embedding x509 certificates
  2021-06-30  8:40 ` [PATCH v2 16/22] grub-install: support embedding x509 certificates Daniel Axtens
@ 2021-07-12 20:24   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-12 20:24 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> From: Alastair D'Silva <alastair@d-silva.org>
>
> To support verification of appended signatures, we need a way to
> embed the necessary public keys. Existing appended signature schemes
> in the Linux kernel use X.509 certificates, so allow certificates to
> be embedded in the grub core image in the same way as PGP keys.
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Signed-off-by: Daniel Axtens <dja@axtens.net>


Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   grub-core/commands/pgp.c    |  2 +-
>   include/grub/kernel.h       |  3 ++-
>   include/grub/util/install.h |  7 +++++--
>   util/grub-install-common.c  | 22 +++++++++++++++++++-
>   util/grub-mkimage.c         | 15 ++++++++++++--
>   util/mkimage.c              | 41 ++++++++++++++++++++++++++++++++++---
>   6 files changed, 80 insertions(+), 10 deletions(-)
>
> diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c
> index 355a43844acc..b81ac0ae46ce 100644
> --- a/grub-core/commands/pgp.c
> +++ b/grub-core/commands/pgp.c
> @@ -944,7 +944,7 @@ GRUB_MOD_INIT(pgp)
>       grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
>   
>       /* Not an ELF module, skip.  */
> -    if (header->type != OBJ_TYPE_PUBKEY)
> +    if (header->type != OBJ_TYPE_GPG_PUBKEY)
>         continue;
>   
>       pseudo_file.fs = &pseudo_fs;
> diff --git a/include/grub/kernel.h b/include/grub/kernel.h
> index abbca5ea3359..d3aafc8848d2 100644
> --- a/include/grub/kernel.h
> +++ b/include/grub/kernel.h
> @@ -28,7 +28,8 @@ enum
>     OBJ_TYPE_MEMDISK,
>     OBJ_TYPE_CONFIG,
>     OBJ_TYPE_PREFIX,
> -  OBJ_TYPE_PUBKEY,
> +  OBJ_TYPE_GPG_PUBKEY,
> +  OBJ_TYPE_X509_PUBKEY,
>     OBJ_TYPE_DTB,
>     OBJ_TYPE_DISABLE_SHIM_LOCK
>   };
> diff --git a/include/grub/util/install.h b/include/grub/util/install.h
> index cf4531e02b66..51f3b13ac130 100644
> --- a/include/grub/util/install.h
> +++ b/include/grub/util/install.h
> @@ -67,6 +67,8 @@
>         N_("SBAT metadata"), 0 },						\
>     { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
>         N_("disable shim_lock verifier"), 0 },				\
> +  { "x509key",   'x', N_("FILE"), 0,					\
> +      N_("embed FILE as an x509 certificate for signature checking"), 0}, \
>     { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\
>       "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \
>       1},                                                                 \
> @@ -188,8 +190,9 @@ void
>   grub_install_generate_image (const char *dir, const char *prefix,
>   			     FILE *out,
>   			     const char *outname, char *mods[],
> -			     char *memdisk_path, char **pubkey_paths,
> -			     size_t npubkeys,
> +			     char *memdisk_path,
> +			     char **pubkey_paths, size_t npubkeys,
> +			     char **x509key_paths, size_t nx509keys,
>   			     char *config_path,
>   			     const struct grub_install_image_target_desc *image_target,
>   			     int note, size_t appsig_size,
> diff --git a/util/grub-install-common.c b/util/grub-install-common.c
> index 1216a203c292..7bfa9752a031 100644
> --- a/util/grub-install-common.c
> +++ b/util/grub-install-common.c
> @@ -460,6 +460,8 @@ static char **pubkeys;
>   static size_t npubkeys;
>   static char *sbat;
>   static int disable_shim_lock;
> +static char **x509keys;
> +static size_t nx509keys;
>   static grub_compression_t compression;
>   static size_t appsig_size;
>   
> @@ -501,6 +503,12 @@ grub_install_parse (int key, char *arg)
>       case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK:
>         disable_shim_lock = 1;
>         return 1;
> +    case 'x':
> +      x509keys = xrealloc (x509keys,
> +			  sizeof (x509keys[0])
> +			  * (nx509keys + 1));
> +      x509keys[nx509keys++] = xstrdup (arg);
> +      return 1;
>   
>       case GRUB_INSTALL_OPTIONS_VERBOSITY:
>         verbosity++;
> @@ -627,6 +635,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>     for (pk = pubkeys; pk < pubkeys + npubkeys; pk++)
>       slen += 20 + grub_strlen (*pk);
>   
> +  for (pk = x509keys; pk < x509keys + nx509keys; pk++)
> +    slen += 10 + grub_strlen (*pk);
> +
>     for (md = modules.entries; *md; md++)
>       {
>         slen += 10 + grub_strlen (*md);
> @@ -655,6 +666,14 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>         *p++ = ' ';
>       }
>   
> +  for (pk = x509keys; pk < x509keys + nx509keys; pk++)
> +    {
> +      p = grub_stpcpy (p, "--x509 '");
> +      p = grub_stpcpy (p, *pk);
> +      *p++ = '\'';
> +      *p++ = ' ';
> +    }
> +
>     for (md = modules.entries; *md; md++)
>       {
>         *p++ = '\'';
> @@ -683,7 +702,8 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>   
>     grub_install_generate_image (dir, prefix, fp, outname,
>   			       modules.entries, memdisk_path,
> -			       pubkeys, npubkeys, config_path, tgt,
> +			       pubkeys, npubkeys, x509keys, nx509keys,
> +			       config_path, tgt,
>   			       note, appsig_size, compression, dtb, sbat,
>   			       disable_shim_lock);
>     while (dc--)
> diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
> index d01eaeb8443a..7d61ef3ea046 100644
> --- a/util/grub-mkimage.c
> +++ b/util/grub-mkimage.c
> @@ -75,7 +75,8 @@ static struct argp_option options[] = {
>      /* TRANSLATORS: "embed" is a verb (command description).  "*/
>     {"config",   'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0},
>      /* TRANSLATORS: "embed" is a verb (command description).  "*/
> -  {"pubkey",   'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0},
> +  {"pubkey",   'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0},
> +  {"x509",     'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended signature checking"), 0},
>     /* TRANSLATORS: NOTE is a name of segment.  */
>     {"note",   'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0},
>     {"output",  'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0},
> @@ -124,6 +125,8 @@ struct arguments
>     char *dtb;
>     char **pubkeys;
>     size_t npubkeys;
> +  char **x509keys;
> +  size_t nx509keys;
>     char *font;
>     char *config;
>     char *sbat;
> @@ -206,6 +209,13 @@ argp_parser (int key, char *arg, struct argp_state *state)
>         arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg);
>         break;
>   
> +    case 'x':
> +      arguments->x509keys = xrealloc (arguments->x509keys,
> +				      sizeof (arguments->x509keys[0])
> +				      * (arguments->nx509keys + 1));
> +      arguments->x509keys[arguments->nx509keys++] = xstrdup (arg);
> +      break;
> +
>       case 'c':
>         if (arguments->config)
>   	free (arguments->config);
> @@ -332,7 +342,8 @@ main (int argc, char *argv[])
>     grub_install_generate_image (arguments.dir, arguments.prefix, fp,
>   			       arguments.output, arguments.modules,
>   			       arguments.memdisk, arguments.pubkeys,
> -			       arguments.npubkeys, arguments.config,
> +			       arguments.npubkeys, arguments.x509keys,
> +			       arguments.nx509keys, arguments.config,
>   			       arguments.image_target, arguments.note,
>   			       arguments.appsig_size,
>   			       arguments.comp, arguments.dtb,
> diff --git a/util/mkimage.c b/util/mkimage.c
> index d2cb33883557..5a8021a213cf 100644
> --- a/util/mkimage.c
> +++ b/util/mkimage.c
> @@ -866,8 +866,10 @@ init_pe_section(const struct grub_install_image_target_desc *image_target,
>   void
>   grub_install_generate_image (const char *dir, const char *prefix,
>   			     FILE *out, const char *outname, char *mods[],
> -			     char *memdisk_path, char **pubkey_paths,
> -			     size_t npubkeys, char *config_path,
> +			     char *memdisk_path,
> +			     char **pubkey_paths, size_t npubkeys,
> +			     char **x509key_paths, size_t nx509keys,
> +			     char *config_path,
>   			     const struct grub_install_image_target_desc *image_target,
>   			     int note, size_t appsig_size, grub_compression_t comp,
>   			     const char *dtb_path, const char *sbat_path,
> @@ -913,6 +915,19 @@ grub_install_generate_image (const char *dir, const char *prefix,
>         }
>     }
>   
> +  {
> +    size_t i;
> +    for (i = 0; i < nx509keys; i++)
> +      {
> +	size_t curs;
> +	curs = ALIGN_ADDR (grub_util_get_image_size (x509key_paths[i]));
> +	grub_util_info ("the size of x509 public key %u is 0x%"
> +			GRUB_HOST_PRIxLONG_LONG,
> +			(unsigned) i, (unsigned long long) curs);
> +	total_module_size += curs + sizeof (struct grub_module_header);
> +      }
> +  }
> +
>     if (memdisk_path)
>       {
>         memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512);
> @@ -1034,7 +1049,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
>   	curs = grub_util_get_image_size (pubkey_paths[i]);
>   
>   	header = (struct grub_module_header *) (kernel_img + offset);
> -	header->type = grub_host_to_target32 (OBJ_TYPE_PUBKEY);
> +	header->type = grub_host_to_target32 (OBJ_TYPE_GPG_PUBKEY);
>   	header->size = grub_host_to_target32 (curs + sizeof (*header));
>   	offset += sizeof (*header);
>   
> @@ -1043,6 +1058,26 @@ grub_install_generate_image (const char *dir, const char *prefix,
>         }
>     }
>   
> +  {
> +    size_t i;
> +    for (i = 0; i < nx509keys; i++)
> +      {
> +	size_t curs;
> +	struct grub_module_header *header;
> +
> +	curs = grub_util_get_image_size (x509key_paths[i]);
> +
> +	header = (struct grub_module_header *) (kernel_img + offset);
> +	header->type = grub_host_to_target32 (OBJ_TYPE_X509_PUBKEY);
> +	header->size = grub_host_to_target32 (curs + sizeof (*header));
> +	offset += sizeof (*header);
> +
> +	grub_util_load_image (x509key_paths[i], kernel_img + offset);
> +	offset += ALIGN_ADDR (curs);
> +      }
> +  }
> +
> +
>     if (memdisk_path)
>       {
>         struct grub_module_header *header;


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE
  2021-06-30  8:40 ` [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE Daniel Axtens
  2021-07-12 12:33   ` Stefan Berger
@ 2021-07-14 16:21   ` Daniel Kiper
  1 sibling, 0 replies; 56+ messages in thread
From: Daniel Kiper @ 2021-07-14 16:21 UTC (permalink / raw)
  To: Daniel Axtens
  Cc: grub-devel, rashmica.g, alastair, nayna, stefanb, eric.snowberg

On Wed, Jun 30, 2021 at 06:40:10PM +1000, Daniel Axtens wrote:
> HEAP_MAX_ADDR is confusing. Currently it is set to 32MB, except
> on ieee1275 on x86, where it is 64MB.
>
> There is a comment which purports to explain it:
>
> /* If possible, we will avoid claiming heap above this address, because it
>    seems to cause relocation problems with OSes that link at 4 MiB */
>
> This doesn't make a lot of sense when the constants are well above 4MB
> already. It was not always this way. Prior to
> commit 7b5d0fe4440c ("Increase heap limit") in 2010, HEAP_MAX_SIZE and
> HEAP_MAX_ADDR were indeed 4MB. However, when the constants were increased
> the comment was left unchanged.
>
> It's been over a decade. It doesn't seem like we have problems with
> claims over 4MB on powerpc or x86 ieee1275. (sparc does things completely
> differently and never used the constant.)
>
> Drop the constant and the check.
>
> The only use of HEAP_MIN_SIZE was to potentially override the
> HEAP_MAX_ADDR check. It is now unused. Remove it.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>

> ---
>  grub-core/kern/ieee1275/init.c | 17 -----------------
>  1 file changed, 17 deletions(-)
>
> diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
> index d483e35eed2b..c5d091689f29 100644
> --- a/grub-core/kern/ieee1275/init.c
> +++ b/grub-core/kern/ieee1275/init.c
> @@ -45,9 +45,6 @@
>  #include <grub/machine/kernel.h>
>  #endif
>
> -/* The minimal heap size we can live with. */
> -#define HEAP_MIN_SIZE		(unsigned long) (2 * 1024 * 1024)
> -
>  /* The maximum heap size we're going to claim */
>  #ifdef __i386__
>  #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
> @@ -55,14 +52,6 @@
>  #define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
>  #endif
>
> -/* If possible, we will avoid claiming heap above this address, because it
> -   seems to cause relocation problems with OSes that link at 4 MiB */
> -#ifdef __i386__
> -#define HEAP_MAX_ADDR		(unsigned long) (64 * 1024 * 1024)
> -#else
> -#define HEAP_MAX_ADDR		(unsigned long) (32 * 1024 * 1024)
> -#endif
> -
>  extern char _start[];
>  extern char _end[];
>
> @@ -183,12 +172,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>    if (*total + len > HEAP_MAX_SIZE)
>      len = HEAP_MAX_SIZE - *total;
>
> -  /* Avoid claiming anything above HEAP_MAX_ADDR, if possible. */
> -  if ((addr < HEAP_MAX_ADDR) &&				/* if it's too late, don't bother */
> -      (addr + len > HEAP_MAX_ADDR) &&				/* if it wasn't available anyway, don't bother */
> -      (*total + (HEAP_MAX_ADDR - addr) > HEAP_MIN_SIZE))	/* only limit ourselves when we can afford to */
> -     len = HEAP_MAX_ADDR - addr;
> -
>    /* In theory, firmware should already prevent this from happening by not
>       listing our own image in /memory/available.  The check below is intended
>       as a safeguard in case that doesn't happen.  However, it doesn't protect
> --
> 2.30.2

Daniel


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 02/22] ieee1275: claim more memory
  2021-06-30  8:40 ` [PATCH v2 02/22] ieee1275: claim more memory Daniel Axtens
  2021-07-12 12:35   ` Stefan Berger
@ 2021-07-15 21:51   ` Daniel Kiper
  2021-07-16  3:59     ` Patrick Steinhardt
  1 sibling, 1 reply; 56+ messages in thread
From: Daniel Kiper @ 2021-07-15 21:51 UTC (permalink / raw)
  To: Daniel Axtens
  Cc: grub-devel, rashmica.g, alastair, nayna, chris.coulson,
	hanson.char, hchar, javierm, leif, phcoder, pjones, pmenzel, ps,
	stefanb, xnox

CC-in a few people who can be interested in this...

On Wed, Jun 30, 2021 at 06:40:11PM +1000, Daniel Axtens wrote:
> On powerpc-ieee1275, we are running out of memory trying to verify
> anything. This is because:
>
>  - we have to load an entire file into memory to verify it. This is
>    extremely difficult to change with appended signatures.
>  - We only have 32MB of heap.
>  - Distro kernels are now often around 30MB.
>
> So we want to claim more memory from OpenFirmware for our heap.

AFAICT it is common problem in the GRUB right now. Please take a look at
[1], [2]. It would be nice to find general solution for all. Of course
if possible. So, if somebody could take a look at memory management in
the GRUB and propose a solution for the problem that would be perfect.
Any volunteers?

Daniel

[1] https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00009.html
[2] https://lists.gnu.org/archive/html/grub-devel/2021-01/msg00031.html

> There are some complications:
>
>  - The grub mm code isn't the only thing that will make claims on
>    memory from OpenFirmware:
>
>     * PFW/SLOF will have claimed some for their own use.
>
>     * The ieee1275 loader will try to find other bits of memory that we
>       haven't claimed to place the kernel and initrd when we go to boot.
>
>     * Once we load Linux, it will also try to claim memory. It claims
>       memory without any reference to /memory/available, it just starts
>       at min(top of RMO, 768MB) and works down. So we need to avoid this
>       area. See arch/powerpc/kernel/prom_init.c as of v5.11.
>
>  - The smallest amount of memory a ppc64 KVM guest can have is 256MB.
>    It doesn't work with distro kernels but can work with custom kernels.
>    We should maintain support for that. (ppc32 can boot with even less,
>    and we shouldn't break that either.)
>
>  - Even if a VM has more memory, the memory OpenFirmware makes available
>    as Real Memory Area can be restricted. A freshly created LPAR on a
>    PowerVM machine is likely to have only 256MB available to OpenFirmware
>    even if it has many gigabytes of memory allocated.
>
> EFI systems will attempt to allocate 1/4th of the available memory,
> clamped to between 1M and 1600M. That seems like a good sort of
> approach, we just need to figure out if 1/4 is the right fraction
> for us.
>
> We don't know in advance how big the kernel and initrd are going to be,
> which makes figuring out how much memory we can take a bit tricky.
>
> To figure out how much memory we should leave unused, I looked at:
>
>  - an Ubuntu 20.04.1 ppc64le pseries KVM guest:
>     vmlinux: ~30MB
>     initrd:  ~50MB
>
>  - a RHEL8.2 ppc64le pseries KVM guest:
>     vmlinux: ~30MB
>     initrd:  ~30MB
>
> Ubuntu VMs struggle to boot with just 256MB under SLOF.
> RHEL likewise has a higher minimum supported memory figure.
> So lets first consider a distro kernel and 512MB of addressible memory.
> (This is the default case for anything booting under PFW.) Say we lose
> 131MB to PFW (based on some tests). This leaves us 381MB. 1/4 of 381MB
> is ~95MB. That should be enough to verify a 30MB vmlinux and should
> leave plenty of space to load Linux and the initrd.
>
> If we consider 256MB of RMA under PFW, we have just 125MB remaining. 1/4
> of that is a smidge under 32MB, which gives us very poor odds of verifying
> a distro-sized kernel. However, if we need 80MB just to put the kernel
> and initrd in memory, we can't claim any more than 45MB anyway. So 1/4
> will do. We'll come back to this later.
>
> grub is always built as a 32-bit binary, even if it's loading a ppc64
> kernel. So we can't address memory beyond 4GB. This gives a natural cap
> of 1GB for powerpc-ieee1275.
>
> Also apply this 1/4 approach to i386-ieee1275, but keep the 32MB cap.
>
> make check still works for both i386 and powerpc and I've booted
> powerpc grub with this change under SLOF and PFW.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>
> ---
>  docs/grub-dev.texi             |  6 ++-
>  grub-core/kern/ieee1275/init.c | 81 +++++++++++++++++++++++++++-------
>  2 files changed, 69 insertions(+), 18 deletions(-)
>
> diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
> index 6c629a23e2dc..c11f1ac46de2 100644
> --- a/docs/grub-dev.texi
> +++ b/docs/grub-dev.texi
> @@ -1047,7 +1047,9 @@ space is limited to 4GiB. GRUB allocates pages from EFI for its heap, at most
>  1.6 GiB.
>
>  On i386-ieee1275 and powerpc-ieee1275 GRUB uses same stack as IEEE1275.
> -It allocates at most 32MiB for its heap.
> +
> +On i386-ieee1275, GRUB allocates at most 32MiB for its heap. On
> +powerpc-ieee1275, GRUB allocates up to 1GiB.
>
>  On sparc64-ieee1275 stack is 256KiB and heap is 2MiB.
>
> @@ -1075,7 +1077,7 @@ In short:
>  @item i386-qemu               @tab 60 KiB  @tab < 4 GiB
>  @item *-efi                   @tab ?       @tab < 1.6 GiB
>  @item i386-ieee1275           @tab ?       @tab < 32 MiB
> -@item powerpc-ieee1275        @tab ?       @tab < 32 MiB
> +@item powerpc-ieee1275        @tab ?       @tab < 1 GiB
>  @item sparc64-ieee1275        @tab 256KiB  @tab 2 MiB
>  @item arm-uboot               @tab 256KiB  @tab 2 MiB
>  @item mips(el)-qemu_mips      @tab 2MiB    @tab 253 MiB
> diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
> index c5d091689f29..4162b5949df4 100644
> --- a/grub-core/kern/ieee1275/init.c
> +++ b/grub-core/kern/ieee1275/init.c
> @@ -45,11 +45,12 @@
>  #include <grub/machine/kernel.h>
>  #endif
>
> -/* The maximum heap size we're going to claim */
> +/* The maximum heap size we're going to claim. Not used by sparc.
> +   We allocate 1/4 of the available memory under 4G, up to this limit. */
>  #ifdef __i386__
>  #define HEAP_MAX_SIZE		(unsigned long) (64 * 1024 * 1024)
> -#else
> -#define HEAP_MAX_SIZE		(unsigned long) (32 * 1024 * 1024)
> +#else // __powerpc__
> +#define HEAP_MAX_SIZE		(unsigned long) (1 * 1024 * 1024 * 1024)
>  #endif
>
>  extern char _start[];
> @@ -145,16 +146,45 @@ grub_claim_heap (void)
>  				 + GRUB_KERNEL_MACHINE_STACK_SIZE), 0x200000);
>  }
>  #else
> -/* Helper for grub_claim_heap.  */
> +/* Helper for grub_claim_heap on powerpc. */
> +static int
> +heap_size (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
> +	   void *data)
> +{
> +  grub_uint32_t total = *(grub_uint32_t *)data;
> +
> +  if (type != GRUB_MEMORY_AVAILABLE)
> +    return 0;
> +
> +  /* Do not consider memory beyond 4GB */
> +  if (addr > 0xffffffffUL)
> +    return 0;
> +
> +  if (addr + len > 0xffffffffUL)
> +    len = 0xffffffffUL - addr;
> +
> +  total += len;
> +  *(grub_uint32_t *)data = total;
> +
> +  return 0;
> +}
> +
>  static int
>  heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>  	   void *data)
>  {
> -  unsigned long *total = data;
> +  grub_uint32_t total = *(grub_uint32_t *)data;
>
>    if (type != GRUB_MEMORY_AVAILABLE)
>      return 0;
>
> +  /* Do not consider memory beyond 4GB */
> +  if (addr > 0xffffffffUL)
> +    return 0;
> +
> +  if (addr + len > 0xffffffffUL)
> +    len = 0xffffffffUL - addr;
> +
>    if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PRE1_5M_CLAIM))
>      {
>        if (addr + len <= 0x180000)
> @@ -168,10 +198,6 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>      }
>    len -= 1; /* Required for some firmware.  */
>
> -  /* Never exceed HEAP_MAX_SIZE  */
> -  if (*total + len > HEAP_MAX_SIZE)
> -    len = HEAP_MAX_SIZE - *total;
> -
>    /* In theory, firmware should already prevent this from happening by not
>       listing our own image in /memory/available.  The check below is intended
>       as a safeguard in case that doesn't happen.  However, it doesn't protect
> @@ -183,6 +209,18 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>        len = 0;
>      }
>
> +  /* If this block contains 0x30000000 (768MB), do not claim below that.
> +     Linux likes to claim memory at min(RMO top, 768MB) and works down
> +     without reference to /memory/available. */
> +  if ((addr < 0x30000000) && ((addr + len) > 0x30000000))
> +    {
> +      len = len - (0x30000000 - addr);
> +      addr = 0x30000000;
> +    }
> +
> +  if (len > total)
> +    len = total;
> +
>    if (len)
>      {
>        grub_err_t err;
> @@ -191,10 +229,12 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>        if (err)
>  	return err;
>        grub_mm_init_region ((void *) (grub_addr_t) addr, len);
> +      total -= len;
>      }
>
> -  *total += len;
> -  if (*total >= HEAP_MAX_SIZE)
> +  *(grub_uint32_t *)data = total;
> +
> +  if (total == 0)
>      return 1;
>
>    return 0;
> @@ -203,13 +243,22 @@ heap_init (grub_uint64_t addr, grub_uint64_t len, grub_memory_type_t type,
>  static void
>  grub_claim_heap (void)
>  {
> -  unsigned long total = 0;
> +  grub_uint32_t total = 0;
>
>    if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_FORCE_CLAIM))
> -    heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
> -	       1, &total);
> -  else
> -    grub_machine_mmap_iterate (heap_init, &total);
> +    {
> +      heap_init (GRUB_IEEE1275_STATIC_HEAP_START, GRUB_IEEE1275_STATIC_HEAP_LEN,
> +		 1, &total);
> +      return;
> +    }
> +
> +  grub_machine_mmap_iterate (heap_size, &total);
> +
> +  total = total / 4;
> +  if (total > HEAP_MAX_SIZE)
> +    total = HEAP_MAX_SIZE;
> +
> +  grub_machine_mmap_iterate (heap_init, &total);
>  }
>  #endif
>
> --
> 2.30.2


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 02/22] ieee1275: claim more memory
  2021-07-15 21:51   ` Daniel Kiper
@ 2021-07-16  3:59     ` Patrick Steinhardt
  2021-07-21 14:45       ` Daniel Kiper
  2021-07-28 11:17       ` Daniel Kiper
  0 siblings, 2 replies; 56+ messages in thread
From: Patrick Steinhardt @ 2021-07-16  3:59 UTC (permalink / raw)
  To: Daniel Kiper
  Cc: Daniel Axtens, grub-devel, rashmica.g, alastair, nayna,
	chris.coulson, hanson.char, hchar, javierm, leif, phcoder,
	pjones, pmenzel, stefanb, xnox

[-- Attachment #1: Type: text/plain, Size: 1747 bytes --]

On Thu, Jul 15, 2021 at 11:51:04PM +0200, Daniel Kiper wrote:
> CC-in a few people who can be interested in this...
> 
> On Wed, Jun 30, 2021 at 06:40:11PM +1000, Daniel Axtens wrote:
> > On powerpc-ieee1275, we are running out of memory trying to verify
> > anything. This is because:
> >
> >  - we have to load an entire file into memory to verify it. This is
> >    extremely difficult to change with appended signatures.
> >  - We only have 32MB of heap.
> >  - Distro kernels are now often around 30MB.
> >
> > So we want to claim more memory from OpenFirmware for our heap.
> 
> AFAICT it is common problem in the GRUB right now. Please take a look at
> [1], [2]. It would be nice to find general solution for all. Of course
> if possible. So, if somebody could take a look at memory management in
> the GRUB and propose a solution for the problem that would be perfect.
> Any volunteers?
> 
> Daniel
> 
> [1] https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00009.html
> [2] https://lists.gnu.org/archive/html/grub-devel/2021-01/msg00031.html

I think that my [1] should solve the issue generically. Instead of
bumping any of the static limits we have in place, we just drop them
altogether in favor of dynamically requesting additional EFI regions
whenever we realize that the currently mapped regions cannot satisfy our
needs. Like this, we can lower the initially requested regions, but
scale them to the specific needs if need be.

I had planned to revisit this patch series much earlier, but somehow I
didn't yet find the time. Any comments on my approach would be welcome
though, and if we agree that this may be a viable route to go down then
I'd be happy to further pursue it.

Patrick

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 17/22] appended signatures: import GNUTLS's ASN.1 description files
  2021-06-30  8:40 ` [PATCH v2 17/22] appended signatures: import GNUTLS's ASN.1 description files Daniel Axtens
@ 2021-07-19 21:09   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-19 21:09 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> In order to parse PKCS#7 messages and X.509 certificates with libtasn1,
> we need some information about how they are encoded.
>
> We get these from GNUTLS, which has the benefit that they support the
> features we need and are well tested.
>
> The GNUTLS license is LGPLv2.1+, which is GPLv3 compatible, allowing
> us to import it without issue.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

The GNUTLS files are here:

- 
https://github.com/gnutls/gnutls/blob/857543cc24114431dd5dde0e83c2c44b9b7e6050/lib/gnutls_asn1_tab.c

- 
https://github.com/gnutls/gnutls/blob/857543cc24114431dd5dde0e83c2c44b9b7e6050/lib/pkix_asn1_tab.c


Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   .../commands/appendedsig/gnutls_asn1_tab.c    | 121 +++++
>   .../commands/appendedsig/pkix_asn1_tab.c      | 484 ++++++++++++++++++
>   2 files changed, 605 insertions(+)
>   create mode 100644 grub-core/commands/appendedsig/gnutls_asn1_tab.c
>   create mode 100644 grub-core/commands/appendedsig/pkix_asn1_tab.c
>
> diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
> new file mode 100644
> index 000000000000..ddd1314e63b6
> --- /dev/null
> +++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c
> @@ -0,0 +1,121 @@
> +#include <grub/mm.h>
> +#include <grub/libtasn1.h>
> +
> +const asn1_static_node gnutls_asn1_tab[] = {
> +  { "GNUTLS", 536872976, NULL },
> +  { NULL, 1073741836, NULL },
> +  { "RSAPublicKey", 1610612741, NULL },
> +  { "modulus", 1073741827, NULL },
> +  { "publicExponent", 3, NULL },
> +  { "RSAPrivateKey", 1610612741, NULL },
> +  { "version", 1073741827, NULL },
> +  { "modulus", 1073741827, NULL },
> +  { "publicExponent", 1073741827, NULL },
> +  { "privateExponent", 1073741827, NULL },
> +  { "prime1", 1073741827, NULL },
> +  { "prime2", 1073741827, NULL },
> +  { "exponent1", 1073741827, NULL },
> +  { "exponent2", 1073741827, NULL },
> +  { "coefficient", 1073741827, NULL },
> +  { "otherPrimeInfos", 16386, "OtherPrimeInfos"},
> +  { "ProvableSeed", 1610612741, NULL },
> +  { "algorithm", 1073741836, NULL },
> +  { "seed", 7, NULL },
> +  { "OtherPrimeInfos", 1612709899, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "OtherPrimeInfo"},
> +  { "OtherPrimeInfo", 1610612741, NULL },
> +  { "prime", 1073741827, NULL },
> +  { "exponent", 1073741827, NULL },
> +  { "coefficient", 3, NULL },
> +  { "AlgorithmIdentifier", 1610612741, NULL },
> +  { "algorithm", 1073741836, NULL },
> +  { "parameters", 541081613, NULL },
> +  { "algorithm", 1, NULL },
> +  { "DigestInfo", 1610612741, NULL },
> +  { "digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"},
> +  { "digest", 7, NULL },
> +  { "DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
> +  { "DSAPublicKey", 1073741827, NULL },
> +  { "DSAParameters", 1610612741, NULL },
> +  { "p", 1073741827, NULL },
> +  { "q", 1073741827, NULL },
> +  { "g", 3, NULL },
> +  { "DSASignatureValue", 1610612741, NULL },
> +  { "r", 1073741827, NULL },
> +  { "s", 3, NULL },
> +  { "DSAPrivateKey", 1610612741, NULL },
> +  { "version", 1073741827, NULL },
> +  { "p", 1073741827, NULL },
> +  { "q", 1073741827, NULL },
> +  { "g", 1073741827, NULL },
> +  { "Y", 1073741827, NULL },
> +  { "priv", 3, NULL },
> +  { "DHParameter", 1610612741, NULL },
> +  { "prime", 1073741827, NULL },
> +  { "base", 1073741827, NULL },
> +  { "privateValueLength", 16387, NULL },
> +  { "ECParameters", 1610612754, NULL },
> +  { "namedCurve", 12, NULL },
> +  { "ECPrivateKey", 1610612741, NULL },
> +  { "Version", 1073741827, NULL },
> +  { "privateKey", 1073741831, NULL },
> +  { "parameters", 1610637314, "ECParameters"},
> +  { NULL, 2056, "0"},
> +  { "publicKey", 536895494, NULL },
> +  { NULL, 2056, "1"},
> +  { "PrincipalName", 1610612741, NULL },
> +  { "name-type", 1610620931, NULL },
> +  { NULL, 2056, "0"},
> +  { "name-string", 536879115, NULL },
> +  { NULL, 1073743880, "1"},
> +  { NULL, 27, NULL },
> +  { "KRB5PrincipalName", 1610612741, NULL },
> +  { "realm", 1610620955, NULL },
> +  { NULL, 2056, "0"},
> +  { "principalName", 536879106, "PrincipalName"},
> +  { NULL, 2056, "1"},
> +  { "RSAPSSParameters", 1610612741, NULL },
> +  { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"},
> +  { NULL, 2056, "0"},
> +  { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"},
> +  { NULL, 2056, "1"},
> +  { "saltLength", 1610653699, NULL },
> +  { NULL, 1073741833, "20"},
> +  { NULL, 2056, "2"},
> +  { "trailerField", 536911875, NULL },
> +  { NULL, 1073741833, "1"},
> +  { NULL, 2056, "3"},
> +  { "GOSTParameters", 1610612741, NULL },
> +  { "publicKeyParamSet", 1073741836, NULL },
> +  { "digestParamSet", 16396, NULL },
> +  { "GOSTParametersOld", 1610612741, NULL },
> +  { "publicKeyParamSet", 1073741836, NULL },
> +  { "digestParamSet", 1073741836, NULL },
> +  { "encryptionParamSet", 16396, NULL },
> +  { "GOSTPrivateKey", 1073741831, NULL },
> +  { "GOSTPrivateKeyOld", 1073741827, NULL },
> +  { "IssuerSignTool", 1610612741, NULL },
> +  { "signTool", 1073741858, NULL },
> +  { "cATool", 1073741858, NULL },
> +  { "signToolCert", 1073741858, NULL },
> +  { "cAToolCert", 34, NULL },
> +  { "Gost28147-89-EncryptedKey", 1610612741, NULL },
> +  { "encryptedKey", 1073741831, NULL },
> +  { "maskKey", 1610637319, NULL },
> +  { NULL, 4104, "0"},
> +  { "macKey", 7, NULL },
> +  { "SubjectPublicKeyInfo", 1610612741, NULL },
> +  { "algorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "subjectPublicKey", 6, NULL },
> +  { "GostR3410-TransportParameters", 1610612741, NULL },
> +  { "encryptionParamSet", 1073741836, NULL },
> +  { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"},
> +  { NULL, 4104, "0"},
> +  { "ukm", 7, NULL },
> +  { "GostR3410-KeyTransport", 536870917, NULL },
> +  { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"},
> +  { "transportParameters", 536895490, "GostR3410-TransportParameters"},
> +  { NULL, 4104, "0"},
> +  { NULL, 0, NULL }
> +};
> diff --git a/grub-core/commands/appendedsig/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c
> new file mode 100644
> index 000000000000..adef69d95ced
> --- /dev/null
> +++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c
> @@ -0,0 +1,484 @@
> +#include <grub/mm.h>
> +#include <grub/libtasn1.h>
> +
> +const asn1_static_node pkix_asn1_tab[] = {
> +  { "PKIX1", 536875024, NULL },
> +  { NULL, 1073741836, NULL },
> +  { "PrivateKeyUsagePeriod", 1610612741, NULL },
> +  { "notBefore", 1610637349, NULL },
> +  { NULL, 4104, "0"},
> +  { "notAfter", 536895525, NULL },
> +  { NULL, 4104, "1"},
> +  { "AuthorityKeyIdentifier", 1610612741, NULL },
> +  { "keyIdentifier", 1610637319, NULL },
> +  { NULL, 4104, "0"},
> +  { "authorityCertIssuer", 1610637314, "GeneralNames"},
> +  { NULL, 4104, "1"},
> +  { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"},
> +  { NULL, 4104, "2"},
> +  { "SubjectKeyIdentifier", 1073741831, NULL },
> +  { "KeyUsage", 1073741830, NULL },
> +  { "DirectoryString", 1610612754, NULL },
> +  { "teletexString", 1612709918, NULL },
> +  { "MAX", 524298, "1"},
> +  { "printableString", 1612709919, NULL },
> +  { "MAX", 524298, "1"},
> +  { "universalString", 1612709920, NULL },
> +  { "MAX", 524298, "1"},
> +  { "utf8String", 1612709922, NULL },
> +  { "MAX", 524298, "1"},
> +  { "bmpString", 1612709921, NULL },
> +  { "MAX", 524298, "1"},
> +  { "ia5String", 538968093, NULL },
> +  { "MAX", 524298, "1"},
> +  { "SubjectAltName", 1073741826, "GeneralNames"},
> +  { "GeneralNames", 1612709899, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "GeneralName"},
> +  { "GeneralName", 1610612754, NULL },
> +  { "otherName", 1610620930, "AnotherName"},
> +  { NULL, 4104, "0"},
> +  { "rfc822Name", 1610620957, NULL },
> +  { NULL, 4104, "1"},
> +  { "dNSName", 1610620957, NULL },
> +  { NULL, 4104, "2"},
> +  { "x400Address", 1610620941, NULL },
> +  { NULL, 4104, "3"},
> +  { "directoryName", 1610620939, NULL },
> +  { NULL, 1073743880, "4"},
> +  { NULL, 2, "RelativeDistinguishedName"},
> +  { "ediPartyName", 1610620941, NULL },
> +  { NULL, 4104, "5"},
> +  { "uniformResourceIdentifier", 1610620957, NULL },
> +  { NULL, 4104, "6"},
> +  { "iPAddress", 1610620935, NULL },
> +  { NULL, 4104, "7"},
> +  { "registeredID", 536879116, NULL },
> +  { NULL, 4104, "8"},
> +  { "AnotherName", 1610612741, NULL },
> +  { "type-id", 1073741836, NULL },
> +  { "value", 541073421, NULL },
> +  { NULL, 1073743880, "0"},
> +  { "type-id", 1, NULL },
> +  { "IssuerAltName", 1073741826, "GeneralNames"},
> +  { "BasicConstraints", 1610612741, NULL },
> +  { "cA", 1610645508, NULL },
> +  { NULL, 131081, NULL },
> +  { "pathLenConstraint", 537411587, NULL },
> +  { "0", 10, "MAX"},
> +  { "CRLDistributionPoints", 1612709899, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "DistributionPoint"},
> +  { "DistributionPoint", 1610612741, NULL },
> +  { "distributionPoint", 1610637314, "DistributionPointName"},
> +  { NULL, 2056, "0"},
> +  { "reasons", 1610637314, "ReasonFlags"},
> +  { NULL, 4104, "1"},
> +  { "cRLIssuer", 536895490, "GeneralNames"},
> +  { NULL, 4104, "2"},
> +  { "DistributionPointName", 1610612754, NULL },
> +  { "fullName", 1610620930, "GeneralNames"},
> +  { NULL, 4104, "0"},
> +  { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"},
> +  { NULL, 4104, "1"},
> +  { "ReasonFlags", 1073741830, NULL },
> +  { "ExtKeyUsageSyntax", 1612709899, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 12, NULL },
> +  { "AuthorityInfoAccessSyntax", 1612709899, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "AccessDescription"},
> +  { "AccessDescription", 1610612741, NULL },
> +  { "accessMethod", 1073741836, NULL },
> +  { "accessLocation", 2, "GeneralName"},
> +  { "Attribute", 1610612741, NULL },
> +  { "type", 1073741836, NULL },
> +  { "values", 536870927, NULL },
> +  { NULL, 13, NULL },
> +  { "AttributeTypeAndValue", 1610612741, NULL },
> +  { "type", 1073741836, NULL },
> +  { "value", 13, NULL },
> +  { "Name", 1610612754, NULL },
> +  { "rdnSequence", 536870923, NULL },
> +  { NULL, 2, "RelativeDistinguishedName"},
> +  { "DistinguishedName", 1610612747, NULL },
> +  { NULL, 2, "RelativeDistinguishedName"},
> +  { "RelativeDistinguishedName", 1612709903, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "AttributeTypeAndValue"},
> +  { "Certificate", 1610612741, NULL },
> +  { "tbsCertificate", 1073741826, "TBSCertificate"},
> +  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "signature", 6, NULL },
> +  { "TBSCertificate", 1610612741, NULL },
> +  { "version", 1610653699, NULL },
> +  { NULL, 1073741833, "0"},
> +  { NULL, 2056, "0"},
> +  { "serialNumber", 1073741826, "CertificateSerialNumber"},
> +  { "signature", 1073741826, "AlgorithmIdentifier"},
> +  { "issuer", 1073741826, "Name"},
> +  { "validity", 1073741826, "Validity"},
> +  { "subject", 1073741826, "Name"},
> +  { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"},
> +  { "issuerUniqueID", 1610637314, "UniqueIdentifier"},
> +  { NULL, 4104, "1"},
> +  { "subjectUniqueID", 1610637314, "UniqueIdentifier"},
> +  { NULL, 4104, "2"},
> +  { "extensions", 536895490, "Extensions"},
> +  { NULL, 2056, "3"},
> +  { "CertificateSerialNumber", 1073741827, NULL },
> +  { "Validity", 1610612741, NULL },
> +  { "notBefore", 1073741826, "Time"},
> +  { "notAfter", 2, "Time"},
> +  { "Time", 1610612754, NULL },
> +  { "utcTime", 1073741860, NULL },
> +  { "generalTime", 37, NULL },
> +  { "UniqueIdentifier", 1073741830, NULL },
> +  { "SubjectPublicKeyInfo", 1610612741, NULL },
> +  { "algorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "subjectPublicKey", 6, NULL },
> +  { "Extensions", 1612709899, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "Extension"},
> +  { "Extension", 1610612741, NULL },
> +  { "extnID", 1073741836, NULL },
> +  { "critical", 1610645508, NULL },
> +  { NULL, 131081, NULL },
> +  { "extnValue", 7, NULL },
> +  { "CertificateList", 1610612741, NULL },
> +  { "tbsCertList", 1073741826, "TBSCertList"},
> +  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "signature", 6, NULL },
> +  { "TBSCertList", 1610612741, NULL },
> +  { "version", 1073758211, NULL },
> +  { "signature", 1073741826, "AlgorithmIdentifier"},
> +  { "issuer", 1073741826, "Name"},
> +  { "thisUpdate", 1073741826, "Time"},
> +  { "nextUpdate", 1073758210, "Time"},
> +  { "revokedCertificates", 1610629131, NULL },
> +  { NULL, 536870917, NULL },
> +  { "userCertificate", 1073741826, "CertificateSerialNumber"},
> +  { "revocationDate", 1073741826, "Time"},
> +  { "crlEntryExtensions", 16386, "Extensions"},
> +  { "crlExtensions", 536895490, "Extensions"},
> +  { NULL, 2056, "0"},
> +  { "AlgorithmIdentifier", 1610612741, NULL },
> +  { "algorithm", 1073741836, NULL },
> +  { "parameters", 541081613, NULL },
> +  { "algorithm", 1, NULL },
> +  { "Dss-Sig-Value", 1610612741, NULL },
> +  { "r", 1073741827, NULL },
> +  { "s", 3, NULL },
> +  { "Dss-Parms", 1610612741, NULL },
> +  { "p", 1073741827, NULL },
> +  { "q", 1073741827, NULL },
> +  { "g", 3, NULL },
> +  { "pkcs-7-ContentInfo", 1610612741, NULL },
> +  { "contentType", 1073741836, NULL },
> +  { "content", 541073421, NULL },
> +  { NULL, 1073743880, "0"},
> +  { "contentType", 1, NULL },
> +  { "pkcs-7-DigestInfo", 1610612741, NULL },
> +  { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "digest", 7, NULL },
> +  { "pkcs-7-SignedData", 1610612741, NULL },
> +  { "version", 1073741827, NULL },
> +  { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"},
> +  { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"},
> +  { "certificates", 1610637314, "pkcs-7-CertificateSet"},
> +  { NULL, 4104, "0"},
> +  { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"},
> +  { NULL, 4104, "1"},
> +  { "signerInfos", 2, "pkcs-7-SignerInfos"},
> +  { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL },
> +  { NULL, 2, "AlgorithmIdentifier"},
> +  { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL },
> +  { "eContentType", 1073741836, NULL },
> +  { "eContent", 536895501, NULL },
> +  { NULL, 2056, "0"},
> +  { "pkcs-7-CertificateRevocationLists", 1610612751, NULL },
> +  { NULL, 13, NULL },
> +  { "pkcs-7-CertificateChoices", 1610612754, NULL },
> +  { "certificate", 13, NULL },
> +  { "pkcs-7-CertificateSet", 1610612751, NULL },
> +  { NULL, 2, "pkcs-7-CertificateChoices"},
> +  { "IssuerAndSerialNumber", 1610612741, NULL },
> +  { "issuer", 1073741826, "Name"},
> +  { "serialNumber", 2, "CertificateSerialNumber"},
> +  { "pkcs-7-SignerInfo", 1610612741, NULL },
> +  { "version", 1073741827, NULL },
> +  { "sid", 1073741826, "SignerIdentifier"},
> +  { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "signedAttrs", 1610637314, "SignedAttributes"},
> +  { NULL, 4104, "0"},
> +  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "signature", 1073741831, NULL },
> +  { "unsignedAttrs", 536895490, "SignedAttributes"},
> +  { NULL, 4104, "1"},
> +  { "SignedAttributes", 1612709903, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "Attribute"},
> +  { "SignerIdentifier", 1610612754, NULL },
> +  { "issuerAndSerialNumber", 1073741826, "IssuerAndSerialNumber"},
> +  { "subjectKeyIdentifier", 536879111, NULL },
> +  { NULL, 4104, "0"},
> +  { "pkcs-7-SignerInfos", 1610612751, NULL },
> +  { NULL, 2, "pkcs-7-SignerInfo"},
> +  { "pkcs-10-CertificationRequestInfo", 1610612741, NULL },
> +  { "version", 1073741827, NULL },
> +  { "subject", 1073741826, "Name"},
> +  { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"},
> +  { "attributes", 536879106, "Attributes"},
> +  { NULL, 4104, "0"},
> +  { "Attributes", 1610612751, NULL },
> +  { NULL, 2, "Attribute"},
> +  { "pkcs-10-CertificationRequest", 1610612741, NULL },
> +  { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"},
> +  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "signature", 6, NULL },
> +  { "pkcs-9-at-challengePassword", 1879048204, NULL },
> +  { "iso", 1073741825, "1"},
> +  { "member-body", 1073741825, "2"},
> +  { "us", 1073741825, "840"},
> +  { "rsadsi", 1073741825, "113549"},
> +  { "pkcs", 1073741825, "1"},
> +  { NULL, 1073741825, "9"},
> +  { NULL, 1, "7"},
> +  { "pkcs-9-challengePassword", 1610612754, NULL },
> +  { "printableString", 1073741855, NULL },
> +  { "utf8String", 34, NULL },
> +  { "pkcs-9-localKeyId", 1073741831, NULL },
> +  { "pkcs-8-PrivateKeyInfo", 1610612741, NULL },
> +  { "version", 1073741827, NULL },
> +  { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "privateKey", 1073741831, NULL },
> +  { "attributes", 536895490, "Attributes"},
> +  { NULL, 4104, "0"},
> +  { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL },
> +  { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "encryptedData", 2, "pkcs-8-EncryptedData"},
> +  { "pkcs-8-EncryptedData", 1073741831, NULL },
> +  { "pkcs-5-des-CBC-params", 1612709895, NULL },
> +  { NULL, 1048586, "8"},
> +  { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL },
> +  { NULL, 1048586, "8"},
> +  { "pkcs-5-aes128-CBC-params", 1612709895, NULL },
> +  { NULL, 1048586, "16"},
> +  { "pkcs-5-aes192-CBC-params", 1612709895, NULL },
> +  { NULL, 1048586, "16"},
> +  { "pkcs-5-aes256-CBC-params", 1612709895, NULL },
> +  { NULL, 1048586, "16"},
> +  { "Gost28147-89-Parameters", 1610612741, NULL },
> +  { "iv", 1073741831, NULL },
> +  { "encryptionParamSet", 12, NULL },
> +  { "pkcs-5-PBE-params", 1610612741, NULL },
> +  { "salt", 1073741831, NULL },
> +  { "iterationCount", 3, NULL },
> +  { "pkcs-5-PBES2-params", 1610612741, NULL },
> +  { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"},
> +  { "encryptionScheme", 2, "AlgorithmIdentifier"},
> +  { "pkcs-5-PBKDF2-params", 1610612741, NULL },
> +  { "salt", 1610612754, NULL },
> +  { "specified", 1073741831, NULL },
> +  { "otherSource", 2, "AlgorithmIdentifier"},
> +  { "iterationCount", 1611137027, NULL },
> +  { "1", 10, "MAX"},
> +  { "keyLength", 1611153411, NULL },
> +  { "1", 10, "MAX"},
> +  { "prf", 16386, "AlgorithmIdentifier"},
> +  { "pkcs-12-PFX", 1610612741, NULL },
> +  { "version", 1610874883, NULL },
> +  { "v3", 1, "3"},
> +  { "authSafe", 1073741826, "pkcs-7-ContentInfo"},
> +  { "macData", 16386, "pkcs-12-MacData"},
> +  { "pkcs-12-PbeParams", 1610612741, NULL },
> +  { "salt", 1073741831, NULL },
> +  { "iterations", 3, NULL },
> +  { "pkcs-12-MacData", 1610612741, NULL },
> +  { "mac", 1073741826, "pkcs-7-DigestInfo"},
> +  { "macSalt", 1073741831, NULL },
> +  { "iterations", 536903683, NULL },
> +  { NULL, 9, "1"},
> +  { "pkcs-12-AuthenticatedSafe", 1610612747, NULL },
> +  { NULL, 2, "pkcs-7-ContentInfo"},
> +  { "pkcs-12-SafeContents", 1610612747, NULL },
> +  { NULL, 2, "pkcs-12-SafeBag"},
> +  { "pkcs-12-SafeBag", 1610612741, NULL },
> +  { "bagId", 1073741836, NULL },
> +  { "bagValue", 1614815245, NULL },
> +  { NULL, 1073743880, "0"},
> +  { "badId", 1, NULL },
> +  { "bagAttributes", 536887311, NULL },
> +  { NULL, 2, "Attribute"},
> +  { "pkcs-12-CertBag", 1610612741, NULL },
> +  { "certId", 1073741836, NULL },
> +  { "certValue", 541073421, NULL },
> +  { NULL, 1073743880, "0"},
> +  { "certId", 1, NULL },
> +  { "pkcs-12-CRLBag", 1610612741, NULL },
> +  { "crlId", 1073741836, NULL },
> +  { "crlValue", 541073421, NULL },
> +  { NULL, 1073743880, "0"},
> +  { "crlId", 1, NULL },
> +  { "pkcs-12-SecretBag", 1610612741, NULL },
> +  { "secretTypeId", 1073741836, NULL },
> +  { "secretValue", 541073421, NULL },
> +  { NULL, 1073743880, "0"},
> +  { "secretTypeId", 1, NULL },
> +  { "pkcs-7-Data", 1073741831, NULL },
> +  { "pkcs-7-EncryptedData", 1610612741, NULL },
> +  { "version", 1073741827, NULL },
> +  { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"},
> +  { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"},
> +  { NULL, 4104, "1"},
> +  { "pkcs-7-EncryptedContentInfo", 1610612741, NULL },
> +  { "contentType", 1073741836, NULL },
> +  { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"},
> +  { "encryptedContent", 536895495, NULL },
> +  { NULL, 4104, "0"},
> +  { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"},
> +  { "pkcs-7-UnprotectedAttributes", 1612709903, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "Attribute"},
> +  { "ProxyCertInfo", 1610612741, NULL },
> +  { "pCPathLenConstraint", 1611153411, NULL },
> +  { "0", 10, "MAX"},
> +  { "proxyPolicy", 2, "ProxyPolicy"},
> +  { "ProxyPolicy", 1610612741, NULL },
> +  { "policyLanguage", 1073741836, NULL },
> +  { "policy", 16391, NULL },
> +  { "certificatePolicies", 1612709899, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "PolicyInformation"},
> +  { "PolicyInformation", 1610612741, NULL },
> +  { "policyIdentifier", 1073741836, NULL },
> +  { "policyQualifiers", 538984459, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "PolicyQualifierInfo"},
> +  { "PolicyQualifierInfo", 1610612741, NULL },
> +  { "policyQualifierId", 1073741836, NULL },
> +  { "qualifier", 541065229, NULL },
> +  { "policyQualifierId", 1, NULL },
> +  { "CPSuri", 1073741853, NULL },
> +  { "UserNotice", 1610612741, NULL },
> +  { "noticeRef", 1073758210, "NoticeReference"},
> +  { "explicitText", 16386, "DisplayText"},
> +  { "NoticeReference", 1610612741, NULL },
> +  { "organization", 1073741826, "DisplayText"},
> +  { "noticeNumbers", 536870923, NULL },
> +  { NULL, 3, NULL },
> +  { "DisplayText", 1610612754, NULL },
> +  { "ia5String", 1612709917, NULL },
> +  { "200", 524298, "1"},
> +  { "visibleString", 1612709923, NULL },
> +  { "200", 524298, "1"},
> +  { "bmpString", 1612709921, NULL },
> +  { "200", 524298, "1"},
> +  { "utf8String", 538968098, NULL },
> +  { "200", 524298, "1"},
> +  { "OCSPRequest", 1610612741, NULL },
> +  { "tbsRequest", 1073741826, "TBSRequest"},
> +  { "optionalSignature", 536895490, "Signature"},
> +  { NULL, 2056, "0"},
> +  { "TBSRequest", 1610612741, NULL },
> +  { "version", 1610653699, NULL },
> +  { NULL, 1073741833, "0"},
> +  { NULL, 2056, "0"},
> +  { "requestorName", 1610637314, "GeneralName"},
> +  { NULL, 2056, "1"},
> +  { "requestList", 1610612747, NULL },
> +  { NULL, 2, "Request"},
> +  { "requestExtensions", 536895490, "Extensions"},
> +  { NULL, 2056, "2"},
> +  { "Signature", 1610612741, NULL },
> +  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "signature", 1073741830, NULL },
> +  { "certs", 536895499, NULL },
> +  { NULL, 1073743880, "0"},
> +  { NULL, 2, "Certificate"},
> +  { "Request", 1610612741, NULL },
> +  { "reqCert", 1073741826, "CertID"},
> +  { "singleRequestExtensions", 536895490, "Extensions"},
> +  { NULL, 2056, "0"},
> +  { "CertID", 1610612741, NULL },
> +  { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "issuerNameHash", 1073741831, NULL },
> +  { "issuerKeyHash", 1073741831, NULL },
> +  { "serialNumber", 2, "CertificateSerialNumber"},
> +  { "OCSPResponse", 1610612741, NULL },
> +  { "responseStatus", 1073741826, "OCSPResponseStatus"},
> +  { "responseBytes", 536895490, "ResponseBytes"},
> +  { NULL, 2056, "0"},
> +  { "OCSPResponseStatus", 1610874901, NULL },
> +  { "successful", 1073741825, "0"},
> +  { "malformedRequest", 1073741825, "1"},
> +  { "internalError", 1073741825, "2"},
> +  { "tryLater", 1073741825, "3"},
> +  { "sigRequired", 1073741825, "5"},
> +  { "unauthorized", 1, "6"},
> +  { "ResponseBytes", 1610612741, NULL },
> +  { "responseType", 1073741836, NULL },
> +  { "response", 7, NULL },
> +  { "BasicOCSPResponse", 1610612741, NULL },
> +  { "tbsResponseData", 1073741826, "ResponseData"},
> +  { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"},
> +  { "signature", 1073741830, NULL },
> +  { "certs", 536895499, NULL },
> +  { NULL, 1073743880, "0"},
> +  { NULL, 2, "Certificate"},
> +  { "ResponseData", 1610612741, NULL },
> +  { "version", 1610653699, NULL },
> +  { NULL, 1073741833, "0"},
> +  { NULL, 2056, "0"},
> +  { "responderID", 1073741826, "ResponderID"},
> +  { "producedAt", 1073741861, NULL },
> +  { "responses", 1610612747, NULL },
> +  { NULL, 2, "SingleResponse"},
> +  { "responseExtensions", 536895490, "Extensions"},
> +  { NULL, 2056, "1"},
> +  { "ResponderID", 1610612754, NULL },
> +  { "byName", 1610620939, NULL },
> +  { NULL, 1073743880, "1"},
> +  { NULL, 2, "RelativeDistinguishedName"},
> +  { "byKey", 536879111, NULL },
> +  { NULL, 2056, "2"},
> +  { "SingleResponse", 1610612741, NULL },
> +  { "certID", 1073741826, "CertID"},
> +  { "certStatus", 1073741826, "CertStatus"},
> +  { "thisUpdate", 1073741861, NULL },
> +  { "nextUpdate", 1610637349, NULL },
> +  { NULL, 2056, "0"},
> +  { "singleExtensions", 536895490, "Extensions"},
> +  { NULL, 2056, "1"},
> +  { "CertStatus", 1610612754, NULL },
> +  { "good", 1610620948, NULL },
> +  { NULL, 4104, "0"},
> +  { "revoked", 1610620930, "RevokedInfo"},
> +  { NULL, 4104, "1"},
> +  { "unknown", 536879106, "UnknownInfo"},
> +  { NULL, 4104, "2"},
> +  { "RevokedInfo", 1610612741, NULL },
> +  { "revocationTime", 1073741861, NULL },
> +  { "revocationReason", 537157653, NULL },
> +  { NULL, 1073743880, "0"},
> +  { "unspecified", 1, "0"},
> +  { "UnknownInfo", 1073741844, NULL },
> +  { "NameConstraints", 1610612741, NULL },
> +  { "permittedSubtrees", 1610637314, "GeneralSubtrees"},
> +  { NULL, 4104, "0"},
> +  { "excludedSubtrees", 536895490, "GeneralSubtrees"},
> +  { NULL, 4104, "1"},
> +  { "GeneralSubtrees", 1612709899, NULL },
> +  { "MAX", 1074266122, "1"},
> +  { NULL, 2, "GeneralSubtree"},
> +  { "GeneralSubtree", 1610612741, NULL },
> +  { "base", 1073741826, "GeneralName"},
> +  { "minimum", 1610653699, NULL },
> +  { NULL, 1073741833, "0"},
> +  { NULL, 4104, "0"},
> +  { "maximum", 536895491, NULL },
> +  { NULL, 4104, "1"},
> +  { "TlsFeatures", 536870923, NULL },
> +  { NULL, 3, NULL },
> +  { NULL, 0, NULL }
> +};


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 18/22] appended signatures: parse PKCS#7 signedData and X.509 certificates
  2021-06-30  8:40 ` [PATCH v2 18/22] appended signatures: parse PKCS#7 signedData and X.509 certificates Daniel Axtens
@ 2021-07-19 22:02   ` Stefan Berger
  2022-04-21  6:36     ` Daniel Axtens
  0 siblings, 1 reply; 56+ messages in thread
From: Stefan Berger @ 2021-07-19 22:02 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens
  Cc: rashmica.g, alastair, nayna, Javier Martinez Canillas

On 6/30/21 4:40 AM, Daniel Axtens wrote:

> This code allows us to parse:
>
>   - PKCS#7 signedData messages. Only a single signerInfo is supported,
>     which is all that the Linux sign-file utility supports creating
>     out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported.
>     Any certificate embedded in the PKCS#7 message will be ignored.
>
>   - X.509 certificates: at least enough to verify the signatures on the
>     PKCS#7 messages. We expect that the certificates embedded in grub will
>     be leaf certificates, not CA certificates. The parser enforces this.
>
>   - X.509 certificates support the Extended Key Usage extension and handle
>     it by verifying that the certificate has a single purpose, that is code
>     signing. This is required because Red Hat certificates have both Key
>     Usage and Extended Key Usage extensions present.
>
> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> # EKU support
> Signed-off-by: Daniel Axtens <dja@axtens.net>

A few comments below.


>
> ---
>
> v2 changes:
>
>   - Handle the Extended Key Usage extension
>   - Fix 2 leaks in x509 cert parsing
>   - Improve x509 parser function name
>   - Constify the data parameter in parser signatures
>   - Support multiple signers in a pkcs7 message. Accept any passing sig.
>   - Allow padding after a pkcs7 message in an appended signature, required
>      to support my model for signers separated in time.
>   - Fix a test that used GRUB_ERR_NONE rather than ASN1_SUCCESS. They're
>      both 0 so no harm was done, but better to be correct.
>   - Various code and comment cleanups.
>
> Thanks to Nayna Jain and Stefan Berger for their reviews.
>
> revert
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>
> ---
>   grub-core/commands/appendedsig/appendedsig.h |  118 ++
>   grub-core/commands/appendedsig/asn1util.c    |  103 ++
>   grub-core/commands/appendedsig/pkcs7.c       |  509 +++++++++
>   grub-core/commands/appendedsig/x509.c        | 1079 ++++++++++++++++++
>   4 files changed, 1809 insertions(+)
>   create mode 100644 grub-core/commands/appendedsig/appendedsig.h
>   create mode 100644 grub-core/commands/appendedsig/asn1util.c
>   create mode 100644 grub-core/commands/appendedsig/pkcs7.c
>   create mode 100644 grub-core/commands/appendedsig/x509.c
>
> diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
> new file mode 100644
> index 000000000000..327d68ddb1b7
> --- /dev/null
> +++ b/grub-core/commands/appendedsig/appendedsig.h
> @@ -0,0 +1,118 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020  IBM Corporation.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/crypto.h>
> +#include <grub/libtasn1.h>
> +
> +extern asn1_node _gnutls_gnutls_asn;
> +extern asn1_node _gnutls_pkix_asn;
> +
> +#define MAX_OID_LEN 32
> +
> +/*
> + * One or more x509 certificates.
> + *
> + * We do limited parsing: extracting only the serial, CN and RSA public key.
> + */
> +struct x509_certificate
> +{
> +  struct x509_certificate *next;
> +
> +  grub_uint8_t *serial;
> +  grub_size_t serial_len;
> +
> +  char *subject;
> +  grub_size_t subject_len;
> +
> +  /* We only support RSA public keys. This encodes [modulus, publicExponent] */
> +  gcry_mpi_t mpis[2];
> +};
> +
> +/*
> + * A PKCS#7 signedData signerInfo.
> + */
> +struct pkcs7_signerInfo
> +{
> +  const gcry_md_spec_t *hash;
> +  gcry_mpi_t sig_mpi;
> +};
> +
> +/*
> + * A PKCS#7 signedData message.
> + *
> + * We make no attempt to match intelligently, so we don't save any info about
> + * the signer.
> + */
> +struct pkcs7_signedData
> +{
> +  int signerInfo_count;
> +  struct pkcs7_signerInfo *signerInfos;
> +};
> +
> +
> +/* Do libtasn1 init */
> +int asn1_init (void);
> +
> +/*
> + * Import a DER-encoded certificate at 'data', of size 'size'.
> + *
> + * Place the results into 'results', which must be already allocated.
> + */
> +grub_err_t
> +parse_x509_certificate (const void *data, grub_size_t size,
> +			struct x509_certificate *results);
> +
> +/*
> + * Release all the storage associated with the x509 certificate.
> + * If the caller dynamically allocated the certificate, it must free it.
> + * The caller is also responsible for maintenance of the linked list.
> + */
> +void certificate_release (struct x509_certificate *cert);
> +
> +/*
> + * Parse a PKCS#7 message, which must be a signedData message.
> + *
> + * The message must be in 'sigbuf' and of size 'data_size'. The result is
> + * placed in 'msg', which must already be allocated.
> + */
> +grub_err_t
> +parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size,
> +			struct pkcs7_signedData *msg);
> +
> +/*
> + * Release all the storage associated with the PKCS#7 message.
> + * If the caller dynamically allocated the message, it must free it.
> + */
> +void pkcs7_signedData_release (struct pkcs7_signedData *msg);
> +
> +/*
> + * Read a value from an ASN1 node, allocating memory to store it.
> + *
> + * It will work for anything where the size libtasn1 returns is right:
> + *  - Integers
> + *  - Octet strings
> + *  - DER encoding of other structures
> + * It will _not_ work for things where libtasn1 size requires adjustment:
> + *  - Strings that require an extra NULL byte at the end
> + *  - Bit strings because libtasn1 returns the length in bits, not bytes.
> + *
> + * If the function returns a non-NULL value, the caller must free it.
> + */
> +void *grub_asn1_allocate_and_read (asn1_node node, const char *name,
> +				   const char *friendly_name,
> +				   int *content_size);
> diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c
> new file mode 100644
> index 000000000000..6b508222a081
> --- /dev/null
> +++ b/grub-core/commands/appendedsig/asn1util.c
> @@ -0,0 +1,103 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020  IBM Corporation.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/libtasn1.h>
> +#include <grub/types.h>
> +#include <grub/err.h>
> +#include <grub/mm.h>
> +#include <grub/crypto.h>
> +#include <grub/misc.h>
> +#include <grub/gcrypt/gcrypt.h>
> +
> +#include "appendedsig.h"
> +
> +asn1_node _gnutls_gnutls_asn = ASN1_TYPE_EMPTY;
> +asn1_node _gnutls_pkix_asn = ASN1_TYPE_EMPTY;
> +
> +extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[];
> +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
> +
> +/*
> + * Read a value from an ASN1 node, allocating memory to store it.
> + *
> + * It will work for anything where the size libtasn1 returns is right:
> + *  - Integers
> + *  - Octet strings
> + *  - DER encoding of other structures
> + * It will _not_ work for things where libtasn1 size requires adjustment:
> + *  - Strings that require an extra NULL byte at the end
> + *  - Bit strings because libtasn1 returns the length in bits, not bytes.
> + *
> + * If the function returns a non-NULL value, the caller must free it.
> + */
> +void *
> +grub_asn1_allocate_and_read (asn1_node node, const char *name,
> +			     const char *friendly_name, int *content_size)
> +{
> +  int result;
> +  grub_uint8_t *tmpstr = NULL;
> +  int tmpstr_size = 0;
> +
> +  result = asn1_read_value (node, name, NULL, &tmpstr_size);
> +  if (result != ASN1_MEM_ERROR)
> +    {
> +      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
> +		     _
> +		     ("Reading size of %s did not return expected status: %s"),
> +		     friendly_name, asn1_strerror (result));
> +      grub_errno = GRUB_ERR_BAD_FILE_TYPE;
> +      return NULL;
> +    }
> +
> +  tmpstr = grub_malloc (tmpstr_size);
> +  if (tmpstr == NULL)
> +    {
> +      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
> +		     "Could not allocate memory to store %s", friendly_name);
> +      grub_errno = GRUB_ERR_OUT_OF_MEMORY;
> +      return NULL;
> +    }
> +
> +  result = asn1_read_value (node, name, tmpstr, &tmpstr_size);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      grub_free (tmpstr);
> +      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
> +		     "Error reading %s: %s",
> +		     friendly_name, asn1_strerror (result));
> +      grub_errno = GRUB_ERR_BAD_FILE_TYPE;
> +      return NULL;
> +    }
> +
> +  *content_size = tmpstr_size;
> +
> +  return tmpstr;
> +}
> +
> +int
> +asn1_init (void)
> +{
> +  int res;
> +  res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      return res;
> +    }
> +  res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL);
> +  return res;
> +}
> diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c
> new file mode 100644
> index 000000000000..845f58a53e83
> --- /dev/null
> +++ b/grub-core/commands/appendedsig/pkcs7.c
> @@ -0,0 +1,509 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020  IBM Corporation.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "appendedsig.h"
> +#include <grub/misc.h>
> +#include <grub/crypto.h>
> +#include <grub/gcrypt/gcrypt.h>
> +#include <sys/types.h>
> +
> +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
> +
> +/*
> + * RFC 5652 s 5.1
> + */
> +const char *signedData_oid = "1.2.840.113549.1.7.2";
> +
> +/*
> + * RFC 4055 s 2.1
> + */
> +const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
> +const char *sha512_oid = "2.16.840.1.101.3.4.2.3";
> +
> +static grub_err_t
> +process_content (grub_uint8_t * content, int size,
> +		 struct pkcs7_signedData *msg)
> +{
> +  int res;
> +  asn1_node signed_part;
> +  grub_err_t err = GRUB_ERR_NONE;
> +  char algo_oid[MAX_OID_LEN];
> +  int algo_oid_size = sizeof (algo_oid);
> +  int algo_count;
> +  int signer_count;
> +  int i;
> +  char version;
> +  int version_size = sizeof (version);
> +  grub_uint8_t *result_buf;
> +  int result_size = 0;
> +  int crls_size = 0;
> +  gcry_error_t gcry_err;
> +  bool sha256_in_da, sha256_in_si, sha512_in_da, sha512_in_si;
> +  char *da_path;
> +  char *si_sig_path;
> +  char *si_da_path;
> +
> +  res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData",
> +			     &signed_part);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Could not create ASN.1 structure for PKCS#7 signed part.");
> +    }
> +
> +  res = asn1_der_decoding2 (&signed_part, content, &size,
> +			    ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "Error reading PKCS#7 signed data: %s", asn1_error);
> +      goto cleanup_signed_part;
> +    }
> +
> +  /* SignedData ::= SEQUENCE {
> +   *     version CMSVersion,
> +   *     digestAlgorithms DigestAlgorithmIdentifiers,
> +   *     encapContentInfo EncapsulatedContentInfo,
> +   *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
> +   *     crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
> +   *     signerInfos SignerInfos }
> +   */
> +
> +  /* version per the algo in 5.1, must be 1 */
> +  res = asn1_read_value (signed_part, "version", &version, &version_size);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "Error reading signedData version: %s",
> +		    asn1_strerror (res));
> +      goto cleanup_signed_part;
> +    }
> +
> +  if (version != 1)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "Unexpected signature version v%d, only v1 supported",
> +		    version);
> +      goto cleanup_signed_part;
> +    }
> +
> +  /*
> +   * digestAlgorithms DigestAlgorithmIdentifiers
> +   *
> +   * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
> +   * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1)
> +   *
> +   * RFC 4055 s 2.1:
> +   * sha256Identifier  AlgorithmIdentifier  ::=  { id-sha256, NULL }
> +   * sha512Identifier  AlgorithmIdentifier  ::=  { id-sha512, NULL }
> +   *
> +   * We only support 1 element in the set, and we do not check parameters atm.
> +   */
> +  res =
> +    asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "Error counting number of digest algorithms: %s",
> +		    asn1_strerror (res));
> +      goto cleanup_signed_part;
> +    }
> +
> +  if (algo_count <= 0)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "A minimum of 1 digest algorithm is required");
> +      goto cleanup_signed_part;
> +    }
> +
> +  if (algo_count > 2)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
> +		    "A maximum of 2 digest algorithms are supported");
> +      goto cleanup_signed_part;
> +    }
> +
> +  sha256_in_da = false;
> +  sha512_in_da = false;
> +
> +  for (i = 0; i < algo_count; i++)
> +    {
> +      da_path = grub_xasprintf ("digestAlgorithms.?%d.algorithm", i + 1);
> +      if (!da_path)
> +	{
> +	  err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			    "Could not allocate path for digest algorithm parsing path");
> +	  goto cleanup_signed_part;
> +	}
> +
> +      algo_oid_size = sizeof (algo_oid);
> +      res = asn1_read_value (signed_part, da_path, algo_oid, &algo_oid_size);
> +      if (res != ASN1_SUCCESS)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			"Error reading digest algorithm: %s",
> +			asn1_strerror (res));
> +	  grub_free (da_path);
> +	  goto cleanup_signed_part;
> +	}
> +
> +      if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
> +	{
> +	  if (!sha512_in_da)
> +	    {
> +	      sha512_in_da = true;
> +	    }
> +	  else
> +	    {
> +	      err =
> +		grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			    "SHA-512 specified twice in digest algorithm list");
> +	      grub_free (da_path);
> +	      goto cleanup_signed_part;
> +	    }
> +	}
> +      else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
> +	{
> +	  if (!sha256_in_da)
> +	    {
> +	      sha256_in_da = true;
> +	    }
> +	  else
> +	    {
> +	      err =
> +		grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			    "SHA-256 specified twice in digest algorithm list");
> +	      grub_free (da_path);
> +	      goto cleanup_signed_part;
> +	    }
> +	}
> +      else
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
> +			"Only SHA-256 and SHA-512 hashes are supported, found OID %s",
> +			algo_oid);
> +	  grub_free (da_path);
> +	  goto cleanup_signed_part;
> +	}
> +
> +      grub_free (da_path);
> +    }
> +
> +  /* at this point, at least one of sha{256,512}_in_da must be true */
> +
> +  /*
> +   * We ignore the certificates, but we don't permit CRLs.
> +   * A CRL entry might be revoking the certificate we're using, and we have
> +   * no way of dealing with that at the moment.
> +   */
> +  res = asn1_read_value (signed_part, "crls", NULL, &crls_size);
> +  if (res != ASN1_ELEMENT_NOT_FOUND)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
> +		    "PKCS#7 messages with embedded CRLs are not supported");
> +      goto cleanup_signed_part;
> +    }
> +
> +  /* read the signatures */
> +
> +  res = asn1_number_of_elements (signed_part, "signerInfos", &signer_count);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "Error counting number of signers: %s",
> +		    asn1_strerror (res));
> +      goto cleanup_signed_part;
> +    }
> +
> +  if (signer_count <= 0)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "A minimum of 1 signer is required");
> +      goto cleanup_signed_part;
> +    }
> +
> +  msg->signerInfos = grub_calloc (signer_count,
> +				  sizeof (struct pkcs7_signerInfo));
> +  if (!msg->signerInfos)
> +    {
> +      err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			"Could not allocate space for %d signers",
> +			signer_count);
> +      goto cleanup_signed_part;
> +    }
> +
> +  msg->signerInfo_count = 0;
> +  for (i = 0; i < signer_count; i++)
> +    {
> +      si_da_path =
> +	grub_xasprintf ("signerInfos.?%d.digestAlgorithm.algorithm", i + 1);
> +      if (!si_da_path)
> +	{
> +	  err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			    "Could not allocate path for signer %d's digest algorithm parsing path",
> +			    i);
> +	  goto cleanup_signerInfos;
> +	}
> +
> +      algo_oid_size = sizeof (algo_oid);
> +      res =
> +	asn1_read_value (signed_part, si_da_path, algo_oid, &algo_oid_size);
> +      if (res != ASN1_SUCCESS)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			"Error reading signer %d's digest algorithm: %s",
> +			i, asn1_strerror (res));
> +	  grub_free (si_da_path);
> +	  goto cleanup_signerInfos;
> +	}
> +
> +      grub_free (si_da_path);
> +
> +      if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0)
> +	{
> +	  if (!sha512_in_da)
> +	    {
> +	      err =
> +		grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			    "Signer %d claims a SHA-512 signature which was not specified in the outer DigestAlgorithms",
> +			    i);
> +	      goto cleanup_signerInfos;
> +	    }
> +	  else
> +	    {
> +	      sha512_in_si = true;
> +	      msg->signerInfos[i].hash =
> +		grub_crypto_lookup_md_by_name ("sha512");
> +	    }
> +	}
> +      else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0)
> +	{
> +	  if (!sha256_in_da)
> +	    {
> +	      err =
> +		grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			    "Signer %d claims a SHA-256 signature which was not specified in the outer DigestAlgorithms",
> +			    i);
> +	      goto cleanup_signerInfos;
> +	    }
> +	  else
> +	    {
> +	      sha256_in_si = true;
> +	      msg->signerInfos[i].hash =
> +		grub_crypto_lookup_md_by_name ("sha256");
> +	    }
> +	}
> +      else
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
> +			"Only SHA-256 and SHA-512 hashes are supported, found OID %s",
> +			algo_oid);
> +	  goto cleanup_signerInfos;
> +	}
> +
> +      if (!msg->signerInfos[i].hash)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			"Hash algorithm for signer %d (OID %s) not loaded", i,
> +			algo_oid);
> +	  goto cleanup_signerInfos;
> +	}
> +
> +      si_sig_path = grub_xasprintf ("signerInfos.?%d.signature", i + 1);
> +      if (!si_sig_path)
> +	{
> +	  err = grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			    "Could not allocate path for signer %d's signature parsing path",
> +			    i);
> +	  goto cleanup_signerInfos;
> +	}
> +
> +      result_buf =
> +	grub_asn1_allocate_and_read (signed_part, si_sig_path,
> +				     "signature data", &result_size);
> +      if (!result_buf)
> +	{
> +	  err = grub_errno;
> +	  grub_free (si_sig_path);
> +	  goto cleanup_signerInfos;
> +	}
> +      grub_free (si_sig_path);

Nit: You could probably move this before the if statement so you only 
have to write this once.


> +
> +      gcry_err =
> +	gcry_mpi_scan (&(msg->signerInfos[i].sig_mpi), GCRYMPI_FMT_USG,
> +		       result_buf, result_size, NULL);
> +      if (gcry_err != GPG_ERR_NO_ERROR)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			"Error loading signature %d into MPI structure: %d",
> +			i, gcry_err);
> +	  grub_free (result_buf);
> +	  goto cleanup_signerInfos;
> +	}
> +
> +      grub_free (result_buf);

Same with this one.


> +
> +      /* use msg->signerInfo_count to track fully populated signerInfos so we
> +         know how many we need to clean up */
> +      msg->signerInfo_count++;
> +    }
> +
> +  /* Final consistency check of signerInfo.*.digestAlgorithm vs
> +     digestAlgorithms.*.algorithm. An algorithm must be present in both
> +     digestAlgorithms and signerInfo or in neither. We have already checked
> +     for an algorithm in signerInfo that is not in digestAlgorithms, here we
> +     check for algorithms in digestAlgorithms but not in signerInfos. */
> +  if (sha512_in_da && !sha512_in_si)
> +    {
> +      err = grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			"SHA-512 specified in DigestAlgorithms but did not appear in SignerInfos");
> +      goto cleanup_signerInfos;
> +    }
> +
> +  if (sha256_in_da && !sha256_in_si)
> +    {
> +      err = grub_error (GRUB_ERR_BAD_SIGNATURE,
> +			"SHA-256 specified in DigestAlgorithms but did not appear in SignerInfos");
> +      goto cleanup_signerInfos;
> +    }
> +
> +  asn1_delete_structure (&signed_part);
> +  return GRUB_ERR_NONE;
> +
> +cleanup_signerInfos:
> +  for (i = 0; i < msg->signerInfo_count; i++)
> +    gcry_mpi_release (msg->signerInfos[i].sig_mpi);
> +  grub_free (msg->signerInfos);
> +cleanup_signed_part:
> +  asn1_delete_structure (&signed_part);
> +  return err;
> +}
> +
> +grub_err_t
> +parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size,
> +			struct pkcs7_signedData *msg)
> +{
> +  int res;
> +  asn1_node content_info;
> +  grub_err_t err = GRUB_ERR_NONE;
> +  char content_oid[MAX_OID_LEN];
> +  grub_uint8_t *content;
> +  int content_size;
> +  int content_oid_size = sizeof (content_oid);
> +  int size;
> +
> +  if (data_size > GRUB_INT_MAX)
> +    return grub_error (GRUB_ERR_OUT_OF_RANGE,
> +		       "Cannot parse a PKCS#7 message where data size > INT_MAX");
> +  size = (int) data_size;
> +
> +  res = asn1_create_element (_gnutls_pkix_asn,
> +			     "PKIX1.pkcs-7-ContentInfo", &content_info);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Could not create ASN.1 structure for PKCS#7 data: %s",
> +			 asn1_strerror (res));
> +    }
> +
> +  res = asn1_der_decoding2 (&content_info, sigbuf, &size,
> +			    ASN1_DECODE_FLAG_STRICT_DER |
> +			    ASN1_DECODE_FLAG_ALLOW_PADDING, asn1_error);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "Error decoding PKCS#7 message DER: %s", asn1_error);
> +      goto cleanup;
> +    }
> +
> +  /*
> +   * ContentInfo ::= SEQUENCE {
> +   *     contentType ContentType,
> +   *     content [0] EXPLICIT ANY DEFINED BY contentType }
> +   *
> +   * ContentType ::= OBJECT IDENTIFIER
> +   */
> +  res =
> +    asn1_read_value (content_info, "contentType", content_oid,
> +		     &content_oid_size);
> +  if (res != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "Error reading PKCS#7 content type: %s",
> +		    asn1_strerror (res));
> +      goto cleanup;
> +    }
> +
> +  /* OID for SignedData defined in 5.1 */
> +  if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		    "Unexpected content type in PKCS#7 message: OID %s",
> +		    content_oid);
> +      goto cleanup;
> +    }
> +
> +  content =
> +    grub_asn1_allocate_and_read (content_info, "content",
> +				 "PKCS#7 message content", &content_size);
> +  if (!content)
> +    {
> +      err = grub_errno;
> +      goto cleanup;
> +    }
> +
> +  err = process_content (content, content_size, msg);
> +  grub_free (content);
> +
> +cleanup:
> +  asn1_delete_structure (&content_info);
> +  return err;
> +}
> +
> +/*
> + * Release all the storage associated with the PKCS#7 message.
> + * If the caller dynamically allocated the message, it must free it.
> + */
> +void
> +pkcs7_signedData_release (struct pkcs7_signedData *msg)
> +{
> +  grub_ssize_t i;
> +  for (i = 0; i < msg->signerInfo_count; i++)


Nit: probably empty line after variable declaration


> +    {
> +      gcry_mpi_release (msg->signerInfos[i].sig_mpi);
> +    }
> +  grub_free (msg->signerInfos);
> +}
> diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
> new file mode 100644
> index 000000000000..a17a46102872
> --- /dev/null
> +++ b/grub-core/commands/appendedsig/x509.c
> @@ -0,0 +1,1079 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020  IBM Corporation.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/libtasn1.h>
> +#include <grub/types.h>
> +#include <grub/err.h>
> +#include <grub/mm.h>
> +#include <grub/crypto.h>
> +#include <grub/misc.h>
> +#include <grub/gcrypt/gcrypt.h>
> +
> +#include "appendedsig.h"
> +
> +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
> +
> +/*
> + * RFC 3279 2.3.1  RSA Keys
> + */
> +const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
> +
> +/*
> + * RFC 5280 Appendix A
> + */
> +const char *commonName_oid = "2.5.4.3";
> +
> +/*
> + * RFC 5280 4.2.1.3 Key Usage
> + */
> +const char *keyUsage_oid = "2.5.29.15";
> +
> +const grub_uint8_t digitalSignatureUsage = 0x80;
> +
> +/*
> + * RFC 5280 4.2.1.9 Basic Constraints
> + */
> +const char *basicConstraints_oid = "2.5.29.19";
> +
> +/*
> + * RFC 5280 4.2.1.12 Extended Key Usage
> + */
> +const char *extendedKeyUsage_oid = "2.5.29.37";
> +const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";


Should they be visible to other modules or only used here and can be 
'static'?


> +
> +/*
> + * RFC 3279 2.3.1
> + *
> + *  The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey:
> + *
> + *     RSAPublicKey ::= SEQUENCE {
> + *        modulus            INTEGER,    -- n
> + *        publicExponent     INTEGER  }  -- e
> + *
> + *  where modulus is the modulus n, and publicExponent is the public
> + *  exponent e.
> + */
> +static grub_err_t
> +grub_parse_rsa_pubkey (grub_uint8_t *der, int dersize,
> +		       struct x509_certificate *certificate)
> +{
> +  int result;
> +  asn1_node spk = ASN1_TYPE_EMPTY;
> +  grub_uint8_t *m_data, *e_data;
> +  int m_size, e_size;
> +  grub_err_t err = GRUB_ERR_NONE;
> +  gcry_error_t gcry_err;
> +
> +  result =
> +    asn1_create_element (_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Cannot create storage for public key ASN.1 data");
> +    }
> +
> +  result = asn1_der_decoding2 (&spk, der, &dersize,
> +			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Cannot decode certificate public key DER: %s",
> +		    asn1_error);
> +      goto cleanup;
> +    }
> +
> +  m_data =
> +    grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size);
> +  if (!m_data)
> +    {
> +      err = grub_errno;
> +      goto cleanup;
> +    }
> +
> +  e_data =
> +    grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent",
> +				 &e_size);
> +  if (!e_data)
> +    {
> +      err = grub_errno;
> +      goto cleanup_m_data;
> +    }
> +
> +  /*
> +   * convert m, e to mpi
> +   *
> +   * nscanned is not set for FMT_USG, it's only set for FMT_PGP,
> +   * so we can't verify it
> +   */
> +  gcry_err =
> +    gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size,
> +		   NULL);
> +  if (gcry_err != GPG_ERR_NO_ERROR)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error loading RSA modulus into MPI structure: %d",
> +		    gcry_err);
> +      goto cleanup_e_data;
> +    }
> +
> +  gcry_err =
> +    gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size,
> +		   NULL);
> +  if (gcry_err != GPG_ERR_NO_ERROR)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error loading RSA exponent into MPI structure: %d",
> +		    gcry_err);
> +      goto cleanup_m_mpi;
> +    }
> +
> +  grub_free (e_data);
> +  grub_free (m_data);
> +  asn1_delete_structure (&spk);
> +  return GRUB_ERR_NONE;
> +
> +cleanup_m_mpi:
> +  gcry_mpi_release (certificate->mpis[0]);
> +cleanup_e_data:
> +  grub_free (e_data);
> +cleanup_m_data:
> +  grub_free (m_data);
> +cleanup:
> +  asn1_delete_structure (&spk);
> +  return err;
> +}
> +
> +
> +/*
> + * RFC 5280:
> + *   SubjectPublicKeyInfo  ::=  SEQUENCE  {
> + *       algorithm            AlgorithmIdentifier,
> + *       subjectPublicKey     BIT STRING  }
> + *
> + * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we
> + * only support RSA Encryption.
> + */
> +
> +static grub_err_t
> +grub_x509_read_subject_public_key (asn1_node asn,
> +				   struct x509_certificate *results)
> +{
> +  int result;
> +  grub_err_t err;
> +  const char *algo_name =
> +    "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm";
> +  const char *params_name =
> +    "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters";
> +  const char *pk_name =
> +    "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey";
> +  char algo_oid[MAX_OID_LEN];
> +  int algo_size = sizeof (algo_oid);
> +  char params_value[2];
> +  int params_size = sizeof (params_value);
> +  grub_uint8_t *key_data = NULL;
> +  int key_size = 0;
> +  unsigned int key_type;
> +
> +  /* algorithm: see notes for rsaEncryption_oid */
> +  result = asn1_read_value (asn, algo_name, algo_oid, &algo_size);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Error reading x509 public key algorithm: %s",
> +			 asn1_strerror (result));
> +    }
> +
> +  if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid))
> +      != 0)
> +    {
> +      return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
> +			 "Unsupported x509 public key algorithm: %s",
> +			 algo_oid);
> +    }
> +
> +  /*
> +   * RFC 3279 2.3.1
> +   * The rsaEncryption OID is intended to be used in the algorithm field
> +   * of a value of type AlgorithmIdentifier.  The parameters field MUST
> +   * have ASN.1 type NULL for this algorithm identifier.
> +   */
> +  result = asn1_read_value (asn, params_name, params_value, &params_size);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Error reading x509 public key parameters: %s",
> +			 asn1_strerror (result));
> +    }
> +
> +  if (params_value[0] != ASN1_TAG_NULL)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Invalid x509 public key parameters: expected NULL");
> +    }
> +
> +  /*
> +   * RFC 3279 2.3.1:  The DER encoded RSAPublicKey is the value of the BIT
> +   * STRING subjectPublicKey.
> +   */
> +  result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type);
> +  if (result != ASN1_MEM_ERROR)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Error reading size of x509 public key: %s",
> +			 asn1_strerror (result));
> +    }
> +  if (key_type != ASN1_ETYPE_BIT_STRING)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Unexpected ASN.1 type when reading x509 public key: %x",
> +			 key_type);
> +    }
> +
> +  /* length is in bits */
> +  key_size = (key_size + 7) / 8;
> +
> +  key_data = grub_malloc (key_size);
> +  if (!key_data)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Out of memory for x509 public key");
> +    }
> +
> +  result = asn1_read_value (asn, pk_name, key_data, &key_size);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      grub_free (key_data);
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Error reading public key data");
> +    }
> +  key_size = (key_size + 7) / 8;
> +
> +  err = grub_parse_rsa_pubkey (key_data, key_size, results);
> +  grub_free (key_data);
> +
> +  return err;
> +}
> +
> +/* Decode a string as defined in Appendix A */
> +static grub_err_t
> +decode_string (char *der, int der_size, char **string,
> +	       grub_size_t *string_size)
> +{
> +  asn1_node strasn;
> +  int result;
> +  char *choice;
> +  int choice_size = 0;
> +  int tmp_size = 0;
> +  grub_err_t err = GRUB_ERR_NONE;
> +
> +  result =
> +    asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Could not create ASN.1 structure for certificate: %s",
> +			 asn1_strerror (result));
> +    }
> +
> +  result = asn1_der_decoding2 (&strasn, der, &der_size,
> +			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Could not parse DER for DirectoryString: %s",
> +		    asn1_error);
> +      goto cleanup;
> +    }
> +
> +  choice =
> +    grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice",
> +				 &choice_size);
> +  if (!choice)
> +    {
> +      err = grub_errno;
> +      goto cleanup;
> +    }
> +
> +  if (grub_strncmp ("utf8String", choice, choice_size) == 0)
> +    {
> +      result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size);
> +      if (result != ASN1_MEM_ERROR)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			"Error reading size of UTF-8 string: %s",
> +			asn1_strerror (result));
> +	  goto cleanup_choice;
> +	}
> +    }
> +  else if (grub_strncmp ("printableString", choice, choice_size) == 0)
> +    {
> +      result = asn1_read_value (strasn, "printableString", NULL, &tmp_size);
> +      if (result != ASN1_MEM_ERROR)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			"Error reading size of UTF-8 string: %s",
> +			asn1_strerror (result));
> +	  goto cleanup_choice;
> +	}
> +    }
> +  else
> +    {
> +      err =
> +	grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
> +		    "Only UTF-8 and printable DirectoryStrings are supported, got %s",
> +		    choice);
> +      goto cleanup_choice;
> +    }
> +
> +  /* read size does not include trailing null */
> +  tmp_size++;
> +
> +  *string = grub_malloc (tmp_size);
> +  if (!*string)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +		    "Cannot allocate memory for DirectoryString contents");
> +      goto cleanup_choice;
> +    }
> +
> +  result = asn1_read_value (strasn, choice, *string, &tmp_size);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error reading out %s in DirectoryString: %s",
> +		    choice, asn1_strerror (result));
> +      grub_free (*string);
> +      goto cleanup_choice;
> +    }
> +  *string_size = tmp_size + 1;
> +  (*string)[tmp_size] = '\0';
> +
> +cleanup_choice:
> +  grub_free (choice);
> +cleanup:
> +  asn1_delete_structure (&strasn);
> +  return err;
> +}
> +
> +/*
> + * TBSCertificate  ::=  SEQUENCE  {
> + *       version         [0]  EXPLICIT Version DEFAULT v1,
> + * ...
> + *
> + * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
> + */
> +static grub_err_t
> +check_version (asn1_node certificate)
> +{
> +  int rc;
> +  const char *name = "tbsCertificate.version";
> +  grub_uint8_t version;
> +  int len = sizeof (version);
> +
> +  rc = asn1_read_value (certificate, name, &version, &len);
> +
> +  /* require version 3 */
> +  if (rc != ASN1_SUCCESS || len != 1)
> +    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		       "Error reading certificate version");
> +
> +  if (version != 0x02)
> +    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		       "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x",
> +		       version);
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +/*
> + * This is an X.501 Name, which is complex.
> + *
> + * For simplicity, we extract only the CN.
> + */
> +static grub_err_t
> +read_name (asn1_node asn, const char *name_path, char **name,
> +	   grub_size_t *name_size)
> +{
> +  int seq_components, set_components;
> +  int result;
> +  int i, j;
> +  char *top_path, *set_path, *type_path, *val_path;
> +  char type[MAX_OID_LEN];
> +  int type_len = sizeof (type);
> +  int string_size = 0;
> +  char *string_der;
> +  grub_err_t err;
> +
> +  *name = NULL;
> +
> +  top_path = grub_xasprintf ("%s.rdnSequence", name_path);
> +  if (!top_path)
> +    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +		       "Could not allocate memory for %s name parsing path",
> +		       name_path);
> +
> +  result = asn1_number_of_elements (asn, top_path, &seq_components);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error counting name components: %s",
> +		    asn1_strerror (result));
> +      goto cleanup;
> +    }
> +
> +  for (i = 1; i <= seq_components; i++)
> +    {
> +      set_path = grub_xasprintf ("%s.?%d", top_path, i);
> +      if (!set_path)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			"Could not allocate memory for %s name set parsing path",
> +			name_path);
> +	  goto cleanup_set;
> +	}
> +      /* this brings us, hopefully, to a set */
> +      result = asn1_number_of_elements (asn, set_path, &set_components);
> +      if (result != ASN1_SUCCESS)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			"Error counting name sub-components components (element %d): %s",
> +			i, asn1_strerror (result));
> +	  goto cleanup_set;
> +	}
> +      for (j = 1; j <= set_components; j++)
> +	{
> +	  type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j);
> +	  if (!type_path)
> +	    {
> +	      err =
> +		grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			    "Could not allocate memory for %s name component type path",
> +			    name_path);
> +	      goto cleanup_set;
> +	    }
> +	  type_len = sizeof (type);
> +	  result = asn1_read_value (asn, type_path, type, &type_len);
> +	  if (result != ASN1_SUCCESS)
> +	    {
> +	      err =
> +		grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			    "Error reading %s name component type: %s",
> +			    name_path, asn1_strerror (result));
> +	      goto cleanup_type;
> +	    }
> +
> +	  if (grub_strncmp (type, commonName_oid, type_len) != 0)
> +	    {
> +	      grub_free (type_path);
> +	      continue;
> +	    }
> +
> +	  val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j);
> +	  if (!val_path)
> +	    {
> +	      err =
> +		grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			    "Could not allocate memory for %s name component value path",
> +			    name_path);
> +	      goto cleanup_set;
> +	    }
> +
> +	  string_der =
> +	    grub_asn1_allocate_and_read (asn, val_path, name_path,
> +					 &string_size);
> +	  if (!string_der)
> +	    {
> +	      err = grub_errno;
> +	      goto cleanup_val_path;
> +	    }
> +
> +	  err = decode_string (string_der, string_size, name, name_size);
> +	  if (err)
> +	    goto cleanup_string;
> +
> +	  grub_free (string_der);
> +	  grub_free (type_path);
> +	  grub_free (val_path);
> +	  break;
> +	}
> +      grub_free (set_path);
> +
> +      if (*name)
> +	break;
> +    }
> +
> +  grub_free (top_path);
> +
> +  return GRUB_ERR_NONE;
> +
> +cleanup_string:
> +  grub_free (string_der);
> +cleanup_val_path:
> +  grub_free (val_path);
> +cleanup_type:
> +  grub_free (type_path);
> +cleanup_set:
> +  grub_free (set_path);
> +cleanup:
> +  grub_free (top_path);
> +  return err;
> +}
> +
> +/*
> + * Verify the Key Usage extension.
> + * We only permit the Digital signature usage.
> + */
> +static grub_err_t
> +verify_key_usage (grub_uint8_t *value, int value_size)
> +{
> +  asn1_node usageasn;
> +  int result;
> +  grub_err_t err = GRUB_ERR_NONE;
> +  grub_uint8_t usage = 0xff;
> +  int usage_size = sizeof (usage_size);
> +
> +  result =
> +    asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Could not create ASN.1 structure for key usage");
> +    }
> +
> +  result = asn1_der_decoding2 (&usageasn, value, &value_size,
> +			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error parsing DER for Key Usage: %s", asn1_error);
> +      goto cleanup;
> +    }
> +
> +  result = asn1_read_value (usageasn, "", &usage, &usage_size);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error reading Key Usage value: %s",
> +		    asn1_strerror (result));
> +      goto cleanup;
> +    }
> +
> +  if (usage != digitalSignatureUsage)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x",
> +		    usage);
> +      goto cleanup;
> +    }
> +
> +cleanup:
> +  asn1_delete_structure (&usageasn);
> +  return err;
> +}
> +
> +/*
> + * BasicConstraints ::= SEQUENCE {
> + *       cA                      BOOLEAN DEFAULT FALSE,
> + *       pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
> + */
> +static grub_err_t
> +verify_basic_constraints (grub_uint8_t *value, int value_size)
> +{
> +  asn1_node basicasn;
> +  int result;
> +  grub_err_t err = GRUB_ERR_NONE;
> +  char cA[6];			/* FALSE or TRUE */
> +  int cA_size = sizeof (cA);
> +
> +  result =
> +    asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints",
> +			 &basicasn);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Could not create ASN.1 structure for Basic Constraints");
> +    }
> +
> +  result = asn1_der_decoding2 (&basicasn, value, &value_size,
> +			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error parsing DER for Basic Constraints: %s",
> +		    asn1_error);
> +      goto cleanup;
> +    }
> +
> +  result = asn1_read_value (basicasn, "cA", cA, &cA_size);
> +  if (result == ASN1_ELEMENT_NOT_FOUND)
> +    {
> +      /* Not present, default is False, so this is OK */
> +      err = GRUB_ERR_NONE;
> +      goto cleanup;
> +    }
> +  else if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error reading Basic Constraints cA value: %s",
> +		    asn1_strerror (result));
> +      goto cleanup;
> +    }
> +
> +  /* The certificate must not be a CA certificate */
> +  if (grub_strncmp ("FALSE", cA, cA_size) != 0)
> +    {
> +      err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected CA value: %s",
> +			cA);
> +      goto cleanup;
> +    }
> +
> +cleanup:
> +  asn1_delete_structure (&basicasn);
> +  return err;
> +}
> +
> +/*
> + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
> + *
> + * KeyPurposeId ::= OBJECT IDENTIFIER
> + */
> +static grub_err_t
> +verify_extended_key_usage (grub_uint8_t *value, int value_size)
> +{
> +  asn1_node extendedasn;
> +  int result, count;
> +  grub_err_t err = GRUB_ERR_NONE;
> +  char usage[MAX_OID_LEN];
> +  int usage_size = sizeof (usage);
> +
> +  result =
> +    asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax",
> +			 &extendedasn);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Could not create ASN.1 structure for Extended Key Usage");
> +    }
> +
> +  result = asn1_der_decoding2 (&extendedasn, value, &value_size,
> +			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error parsing DER for Extended Key Usage: %s",
> +		    asn1_error);
> +      goto cleanup;
> +    }
> +
> +  /*
> +   * If EKUs are present, there must be exactly 1 and it must be a
> +   * codeSigning usage.


Is this comment correct? It looks like your code requires one EKU.


> +   */
> +  result = asn1_number_of_elements (extendedasn, "", &count);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error counting number of Extended Key Usages: %s",
> +		    asn1_strerror (result));
> +      goto cleanup;
> +    }
> +
> +  if (count != 1)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Unexpected number of Extended Key Usages: %d, 1 expected",
> +		    count);
> +      goto cleanup;
> +    }
> +
> +  result = asn1_read_value (extendedasn, "?1", usage, &usage_size);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Error reading Extended Key Usage: %s",
> +		    asn1_strerror (result));
> +      goto cleanup;
> +    }
> +
> +  if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) != 0)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Unexpected Extended Key Usage OID, got: %s", usage);
> +      goto cleanup;
> +    }
> +
> +cleanup:
> +  asn1_delete_structure (&extendedasn);
> +  return err;
> +}
> +
> +/*
> + * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
> + *
> + * Extension  ::=  SEQUENCE  {
> + *      extnID      OBJECT IDENTIFIER,
> + *      critical    BOOLEAN DEFAULT FALSE,
> + *      extnValue   OCTET STRING
> + *                  -- contains the DER encoding of an ASN.1 value
> + *                  -- corresponding to the extension type identified
> + *                  -- by extnID
> + * }
> + *
> + * A certificate must:
> + *  - contain the Digital Signature usage only
> + *  - not be a CA
> + *  - contain no extended usages, or only a code signing extended usage
> + *  - not contain any other critical extensions (RFC 5280 s 4.2)
> + */
> +static grub_err_t
> +verify_extensions (asn1_node cert)
> +{
> +  int result;
> +  int ext, num_extensions = 0;
> +  int usage_present = 0, constraints_present = 0, extended_usage_present = 0;
> +  char *oid_path, *critical_path, *value_path;
> +  char extnID[MAX_OID_LEN];
> +  int extnID_size;
> +  grub_err_t err;
> +  char critical[6];		/* we get either "TRUE" or "FALSE" */
> +  int critical_size;
> +  grub_uint8_t *value;
> +  int value_size;
> +
> +  result =
> +    asn1_number_of_elements (cert, "tbsCertificate.extensions",
> +			     &num_extensions);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Error counting number of extensions: %s",
> +			 asn1_strerror (result));
> +    }
> +
> +  if (num_extensions < 2)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Insufficient number of extensions for certificate, need at least 2, got %d",
> +			 num_extensions);
> +    }
> +
> +  for (ext = 1; ext <= num_extensions; ext++)
> +    {
> +      oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext);
> +
> +      extnID_size = sizeof (extnID);
> +      result = asn1_read_value (cert, oid_path, extnID, &extnID_size);
> +      if (result != ASN1_SUCCESS)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			"Error reading extension OID: %s",
> +			asn1_strerror (result));
> +	  goto cleanup_oid_path;
> +	}
> +
> +      critical_path =
> +	grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext);
> +      critical_size = sizeof (critical);
> +      result =
> +	asn1_read_value (cert, critical_path, critical, &critical_size);
> +      if (result == ASN1_ELEMENT_NOT_FOUND)
> +	{
> +	  critical[0] = '\0';
> +	}
> +      else if (result != ASN1_SUCCESS)
> +	{
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			"Error reading extension criticality: %s",
> +			asn1_strerror (result));
> +	  goto cleanup_critical_path;
> +	}
> +
> +      value_path =
> +	grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext);
> +      value =
> +	grub_asn1_allocate_and_read (cert, value_path,
> +				     "certificate extension value",
> +				     &value_size);
> +      if (!value)
> +	{
> +	  err = grub_errno;
> +	  goto cleanup_value_path;
> +	}
> +
> +      /*
> +       * Now we must see if we recognise the OID.
> +       * If we have an unrecognised critical extension we MUST bail.
> +       */
> +      if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0)
> +	{
> +	  err = verify_key_usage (value, value_size);
> +	  if (err != GRUB_ERR_NONE)
> +	    {
> +	      goto cleanup_value;
> +	    }
> +	  usage_present++;
> +	}
> +      else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0)
> +	{
> +	  err = verify_basic_constraints (value, value_size);
> +	  if (err != GRUB_ERR_NONE)
> +	    {
> +	      goto cleanup_value;
> +	    }
> +	  constraints_present++;
> +	}
> +      else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0)
> +	{
> +	  err = verify_extended_key_usage (value, value_size);
> +	  if (err != GRUB_ERR_NONE)
> +	    {
> +	      goto cleanup_value;
> +	    }
> +	  extended_usage_present++;
> +	}
> +      else if (grub_strncmp ("TRUE", critical, critical_size) == 0)
> +	{
> +	  /*
> +	   * per the RFC, we must not process a certificate with
> +	   * a critical extension we do not understand.
> +	   */
> +	  err =
> +	    grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			"Unhandled critical x509 extension with OID %s",
> +			extnID);
> +	  goto cleanup_value;
> +	}
> +
> +      grub_free (value);
> +      grub_free (value_path);
> +      grub_free (critical_path);
> +      grub_free (oid_path);
> +    }
> +
> +  if (usage_present != 1)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Unexpected number of Key Usage extensions - expected 1, got %d",
> +			 usage_present);
> +    }
> +  if (constraints_present != 1)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Unexpected number of basic constraints extensions - expected 1, got %d",
> +			 constraints_present);
> +    }
> +  if (extended_usage_present > 1)
> +    {
> +      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +			 "Unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d",
> +			 extended_usage_present);
> +    }
> +  return GRUB_ERR_NONE;
> +
> +cleanup_value:
> +  grub_free (value);
> +cleanup_value_path:
> +  grub_free (value_path);
> +cleanup_critical_path:
> +  grub_free (critical_path);
> +cleanup_oid_path:
> +  grub_free (oid_path);
> +  return err;
> +}
> +
> +/*
> + * Parse a certificate whose DER-encoded form is in @data, of size @data_size.
> + * Return the results in @results, which must point to an allocated x509 certificate.
> + */
> +grub_err_t
> +parse_x509_certificate (const void *data, grub_size_t data_size,
> +			struct x509_certificate *results)
> +{
> +  int result = 0;
> +  asn1_node cert;
> +  grub_err_t err;
> +  int size;
> +  int tmp_size;
> +
> +  if (data_size > GRUB_INT_MAX)
> +    return grub_error (GRUB_ERR_OUT_OF_RANGE,
> +		       "Cannot parse a certificate where data size > INT_MAX");
> +  size = (int) data_size;
> +
> +  result = asn1_create_element (_gnutls_pkix_asn, "PKIX1.Certificate", &cert);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +			 "Could not create ASN.1 structure for certificate: %s",
> +			 asn1_strerror (result));
> +    }
> +
> +  result = asn1_der_decoding2 (&cert, data, &size,
> +			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
> +  if (result != ASN1_SUCCESS)
> +    {
> +      err =
> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
> +		    "Could not parse DER for certificate: %s", asn1_error);
> +      goto cleanup;
> +    }
> +
> +  /*
> +   * TBSCertificate  ::=  SEQUENCE {
> +   *     version         [0]  EXPLICIT Version DEFAULT v1
> +   */
> +  err = check_version (cert);
> +  if (err != GRUB_ERR_NONE)
> +    {
> +      goto cleanup;
> +    }
> +
> +  /*
> +   * serialNumber         CertificateSerialNumber,
> +   *
> +   * CertificateSerialNumber  ::=  INTEGER
> +   */
> +  results->serial =
> +    grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber",
> +				 "certificate serial number", &tmp_size);
> +  if (!results->serial)
> +    {
> +      err = grub_errno;
> +      goto cleanup;
> +    }
> +  /*
> +   * It's safe to cast the signed int to an unsigned here, we know
> +   * length is non-negative
> +   */
> +  results->serial_len = tmp_size;
> +
> +  /*
> +   * signature            AlgorithmIdentifier,
> +   *
> +   * We don't load the signature or issuer at the moment,
> +   * as we don't attempt x509 verification.
> +   */
> +
> +  /*
> +   * issuer               Name,
> +   *
> +   * The RFC only requires the serial number to be unique within
> +   * issuers, so to avoid ambiguity we _technically_ ought to make
> +   * this available.
> +   */
> +
> +  /*
> +   * validity             Validity,
> +   *
> +   * Validity ::= SEQUENCE {
> +   *     notBefore      Time,
> +   *     notAfter       Time }
> +   *
> +   * We can't validate this reasonably, we have no true time source on several
> +   * platforms. For now we do not parse them.
> +   */
> +
> +  /*
> +   * subject              Name,
> +   *
> +   * This is an X501 name, we parse out just the CN.
> +   */
> +  err =
> +    read_name (cert, "tbsCertificate.subject", &results->subject,
> +	       &results->subject_len);
> +  if (err != GRUB_ERR_NONE)
> +    goto cleanup_serial;
> +
> +  /*
> +   * TBSCertificate  ::=  SEQUENCE  {
> +   *    ...
> +   *    subjectPublicKeyInfo SubjectPublicKeyInfo,
> +   *    ...
> +   */
> +  err = grub_x509_read_subject_public_key (cert, results);
> +  if (err != GRUB_ERR_NONE)
> +    goto cleanup_name;
> +
> +  /*
> +   * TBSCertificate  ::=  SEQUENCE  {
> +   *    ...
> +   *    extensions      [3]  EXPLICIT Extensions OPTIONAL
> +   *                         -- If present, version MUST be v3
> +   * }
> +   */
> +
> +  err = verify_extensions (cert);
> +  if (err != GRUB_ERR_NONE)
> +    goto cleanup_mpis;
> +
> +  /*
> +   * We do not read or check the signature on the certificate:
> +   * as discussed we do not try to validate the certificate but trust
> +   * it implictly.
> +   */
> +
> +  asn1_delete_structure (&cert);
> +  return GRUB_ERR_NONE;
> +
> +cleanup_mpis:
> +  gcry_mpi_release (results->mpis[0]);
> +  gcry_mpi_release (results->mpis[1]);
> +cleanup_name:
> +  grub_free (results->subject);
> +cleanup_serial:
> +  grub_free (results->serial);
> +cleanup:
> +  asn1_delete_structure (&cert);
> +  return err;
> +}
> +
> +/*
> + * Release all the storage associated with the x509 certificate.
> + * If the caller dynamically allocated the certificate, it must free it.
> + * The caller is also responsible for maintenance of the linked list.
> + */
> +void
> +certificate_release (struct x509_certificate *cert)
> +{
> +  grub_free (cert->subject);
> +  grub_free (cert->serial);
> +  gcry_mpi_release (cert->mpis[0]);
> +  gcry_mpi_release (cert->mpis[1]);
> +}


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 22/22] ieee1275: enter lockdown based on /ibm,secure-boot
  2021-06-30  8:40 ` [PATCH v2 22/22] ieee1275: enter lockdown based on /ibm,secure-boot Daniel Axtens
@ 2021-07-19 22:08   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-19 22:08 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> If the 'ibm,secure-boot' property of the root node is 2 or greater,
> enter lockdown.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


> ---
>   docs/grub.texi                 |  4 ++--
>   grub-core/Makefile.core.def    |  1 +
>   grub-core/kern/ieee1275/init.c | 27 +++++++++++++++++++++++++++
>   include/grub/lockdown.h        |  3 ++-
>   4 files changed, 32 insertions(+), 3 deletions(-)
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index 02fcda11e3bd..b13316cdb491 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -6189,8 +6189,8 @@ Measured boot is currently only supported on EFI platforms.
>   @section Lockdown when booting on a secure setup
>   
>   The GRUB can be locked down when booted on a secure boot environment, for example
> -if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will
> -be restricted and some operations/commands cannot be executed.
> +if UEFI or Power secure boot is enabled. On a locked down configuration, the
> +GRUB will be restricted and some operations/commands cannot be executed.
>   
>   The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down.
>   Otherwise it does not exit.
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index 4aa4cf263d94..775a031f1843 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -317,6 +317,7 @@ kernel = {
>     powerpc_ieee1275 = kern/powerpc/cache.S;
>     powerpc_ieee1275 = kern/powerpc/dl.c;
>     powerpc_ieee1275 = kern/powerpc/compiler-rt.S;
> +  powerpc_ieee1275 = kern/lockdown.c;
>   
>     sparc64_ieee1275 = kern/sparc64/cache.S;
>     sparc64_ieee1275 = kern/sparc64/dl.c;
> diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
> index 4586bec939b2..5faf4e736074 100644
> --- a/grub-core/kern/ieee1275/init.c
> +++ b/grub-core/kern/ieee1275/init.c
> @@ -44,6 +44,7 @@
>   #ifdef __sparc__
>   #include <grub/machine/kernel.h>
>   #endif
> +#include <grub/lockdown.h>
>   
>   /* The maximum heap size we're going to claim. Not used by sparc.
>      We allocate 1/4 of the available memory under 4G, up to this limit. */
> @@ -442,6 +443,30 @@ grub_parse_cmdline (void)
>       }
>   }
>   
> +static void
> +grub_get_ieee1275_secure_boot (void)
> +{
> +  grub_ieee1275_phandle_t root;
> +  int rc;
> +  grub_uint32_t is_sb;
> +
> +  grub_ieee1275_finddevice ("/", &root);
> +
> +  rc = grub_ieee1275_get_integer_property (root, "ibm,secure-boot", &is_sb,
> +                                           sizeof (is_sb), 0);
> +
> +  /* ibm,secure-boot:
> +   * 0 - disabled
> +   * 1 - audit
> +   * 2 - enforce
> +   * 3 - enforce + OS-specific behaviour
> +   *
> +   * We only support enforce.
> +   */
> +  if (rc >= 0 && is_sb >= 2)
> +    grub_lockdown ();
> +}
> +
>   grub_addr_t grub_modbase;
>   
>   void
> @@ -467,6 +492,8 @@ grub_machine_init (void)
>   #else
>     grub_install_get_time_ms (grub_rtc_get_time_ms);
>   #endif
> +
> +  grub_get_ieee1275_secure_boot ();
>   }
>   
>   void
> diff --git a/include/grub/lockdown.h b/include/grub/lockdown.h
> index 40531fa823bf..ebfee4bf06e7 100644
> --- a/include/grub/lockdown.h
> +++ b/include/grub/lockdown.h
> @@ -24,7 +24,8 @@
>   #define GRUB_LOCKDOWN_DISABLED       0
>   #define GRUB_LOCKDOWN_ENABLED        1
>   
> -#ifdef GRUB_MACHINE_EFI
> +#if defined(GRUB_MACHINE_EFI) || \
> +    (defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275))
>   extern void
>   EXPORT_FUNC (grub_lockdown) (void);
>   extern int


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 21/22] appended signatures: documentation
  2021-06-30  8:40 ` [PATCH v2 21/22] appended signatures: documentation Daniel Axtens
@ 2021-07-19 22:24   ` Stefan Berger
  2022-04-21  7:15     ` Daniel Axtens
  0 siblings, 1 reply; 56+ messages in thread
From: Stefan Berger @ 2021-07-19 22:24 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> This explains how appended signatures can be used to form part of
> a secure boot chain, and documents the commands and variables
> introduced.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

One small thing below.


>
> ---
>
> v2: fix a grammar issue, thanks Stefan Berger.
> ---
>   docs/grub.texi | 193 ++++++++++++++++++++++++++++++++++++++++++++-----
>   1 file changed, 176 insertions(+), 17 deletions(-)
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index bed565371460..02fcda11e3bd 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -3209,6 +3209,7 @@ These variables have special meaning to GRUB.
>   
>   @menu
>   * biosnum::
> +* check_appended_signatures::
>   * check_signatures::
>   * chosen::
>   * cmdpath::
> @@ -3268,11 +3269,18 @@ For an alternative approach which also changes BIOS drive mappings for the
>   chain-loaded system, @pxref{drivemap}.
>   
>   
> +@node check_appended_signatures
> +@subsection check_appended_signatures
> +
> +This variable controls whether GRUB enforces appended signature validation on
> +certain loaded files. @xref{Using appended signatures}.
> +
> +
>   @node check_signatures
>   @subsection check_signatures
>   
> -This variable controls whether GRUB enforces digital signature
> -validation on loaded files. @xref{Using digital signatures}.
> +This variable controls whether GRUB enforces GPG-style digital signature
> +validation on loaded files. @xref{Using GPG-style digital signatures}.
>   
>   @node chosen
>   @subsection chosen
> @@ -3989,6 +3997,7 @@ you forget a command, you can run the command @command{help}
>   * date::                        Display or set current date and time
>   * devicetree::                  Load a device tree blob
>   * distrust::                    Remove a pubkey from trusted keys
> +* distrust_certificate::        Remove a certificate from the list of trusted certificates
>   * drivemap::                    Map a drive to another
>   * echo::                        Display a line of text
>   * eval::                        Evaluate agruments as GRUB commands
> @@ -4005,6 +4014,7 @@ you forget a command, you can run the command @command{help}
>   * keystatus::                   Check key modifier status
>   * linux::                       Load a Linux kernel
>   * linux16::                     Load a Linux kernel (16-bit mode)
> +* list_certificates::           List trusted certificates
>   * list_env::                    List variables in environment block
>   * list_trusted::                List trusted public keys
>   * load_env::                    Load variables from environment block
> @@ -4042,8 +4052,10 @@ you forget a command, you can run the command @command{help}
>   * test::                        Check file types and compare values
>   * true::                        Do nothing, successfully
>   * trust::                       Add public key to list of trusted keys
> +* trust_certificate::           Add an x509 certificate to the list of trusted certificates
>   * unset::                       Unset an environment variable
>   @comment * vbeinfo::                     List available video modes
> +* verify_appended::             Verify appended digital signature
>   * verify_detached::             Verify detached digital signature
>   * videoinfo::                   List available video modes
>   @comment * xen_*::              Xen boot commands for AArch64
> @@ -4371,9 +4383,28 @@ These keys are used to validate signatures when environment variable
>   @code{check_signatures} is set to @code{enforce}
>   (@pxref{check_signatures}), and by some invocations of
>   @command{verify_detached} (@pxref{verify_detached}).  @xref{Using
> -digital signatures}, for more information.
> +GPG-style digital signatures}, for more information.
> +@end deffn
> +
> +
> +@node distrust_certificate
> +@subsection distrust_certificate
> +
> +@deffn Command distrust_certificate cert_number
> +Remove the x509 certificate numbered @var{cert_number} from GRUB's keyring of
> +trusted x509 certificates for verifying appended signatures.
> +
> +@var{cert_number} is the certificate number as listed by
> +@command{list_certificates} (@pxref{list_certificates}).
> +
> +These certificates are used to validate appended signatures when environment
> +variable @code{check_appended_signatures} is set to @code{enforce}
> +(@pxref{check_appended_signatures}), and by @command{verify_appended}
> +(@pxref{verify_appended}). See @xref{Using appended signatures} for more
> +information.
>   @end deffn
>   
> +
>   @node drivemap
>   @subsection drivemap
>   
> @@ -4631,6 +4662,21 @@ This command is only available on x86 systems.
>   @end deffn
>   
>   
> +@node list_certificates
> +@subsection list_certificates
> +
> +@deffn Command list_certificates
> +List all x509 certificates trusted by GRUB for validating appended signatures.
> +The output is a numbered list of certificates, showing the certificate's serial
> +number and Common Name.
> +
> +The certificate number can be used as an argument to
> +@command{distrust_certificate} (@pxref{distrust_certificate}).
> +
> +See @xref{Using appended signatures} for more information.
> +@end deffn
> +
> +
>   @node list_env
>   @subsection list_env
>   
> @@ -4650,7 +4696,7 @@ The output is in GPG's v4 key fingerprint format (i.e., the output of
>   @code{gpg --fingerprint}).  The least significant four bytes (last
>   eight hexadecimal digits) can be used as an argument to
>   @command{distrust} (@pxref{distrust}).
> -@xref{Using digital signatures}, for more information about uses for
> +@xref{Using GPG-style digital signatures}, for more information about uses for
>   these keys.
>   @end deffn
>   
> @@ -4685,8 +4731,12 @@ When used with care, @option{--skip-sig} and the whitelist enable an
>   administrator to configure a system to boot only signed
>   configurations, but to allow the user to select from among multiple
>   configurations, and to enable ``one-shot'' boot attempts and
> -``savedefault'' behavior.  @xref{Using digital signatures}, for more
> +``savedefault'' behavior.  @xref{Using GPG-style digital signatures}, for more
>   information.
> +
> +Extra care should be taken when combining this command with appended signatures
> +(@pxref{Using appended signatures}), as this file is not validated by an
> +appended signature and could set @code{check_appended_signatures=no}.
>   @end deffn
>   
>   
> @@ -4982,7 +5032,7 @@ read.  It is possible to modify a digitally signed environment block
>   file from within GRUB using this command, such that its signature will
>   no longer be valid on subsequent boots.  Care should be taken in such
>   advanced configurations to avoid rendering the system
> -unbootable. @xref{Using digital signatures}, for more information.
> +unbootable. @xref{Using GPG-style digital signatures}, for more information.
>   @end deffn
>   
>   
> @@ -5382,11 +5432,31 @@ signatures when environment variable @code{check_signatures} is set to
>   must itself be properly signed.  The @option{--skip-sig} option can be
>   used to disable signature-checking when reading @var{pubkey_file}
>   itself. It is expected that @option{--skip-sig} is useful for testing
> -and manual booting. @xref{Using digital signatures}, for more
> +and manual booting. @xref{Using GPG-style digital signatures}, for more
>   information.
>   @end deffn
>   
>   
> +@node trust_certificate
> +@subsection trust_certificate
> +
> +@deffn Command trust_certificate x509_certificate
> +Read a DER-formatted x509 certificate from the file @var{x509_certificate}
> +and add it to GRUB's internal list of trusted x509 certificates. These
> +certificates are used to validate appended signatures when the environment
> +variable @code{check_appended_signatures} is set to @code{enforce}.
> +
> +Note that if @code{check_appended_signatures} is set to @code{enforce}
> +when @command{trust_certificate} is executed, then @var{x509_certificate}
> +must itself bear an appended signature. (It is not sufficient that
> +@var{x509_certificate} be signed by a trusted certificate according to the
> +x509 rules: grub does not include support for validating signatures within x509
> +certificates themselves.)
> +
> +See @xref{Using appended signatures} for more information.
> +@end deffn
> +
> +
>   @node unset
>   @subsection unset
>   
> @@ -5405,6 +5475,18 @@ only on PC BIOS platforms.
>   @end deffn
>   @end ignore
>   
> +@node verify_appended
> +@subsection verify_appended
> +
> +@deffn Command verify_appended file
> +Verifies an appended signature on @var{file} against the trusted certificates
> +known to GRUB (See @pxref{list_certificates}, @pxref{trust_certificate}, and
> +@pxref{distrust_certificate}).
> +
> +Exit code @code{$?} is set to 0 if the signature validates
> +successfully.  If validation fails, it is set to a non-zero value.
> +See @xref{Using appended signatures}, for more information.
> +@end deffn
>   
>   @node verify_detached
>   @subsection verify_detached
> @@ -5423,7 +5505,7 @@ tried.
>   
>   Exit code @code{$?} is set to 0 if the signature validates
>   successfully.  If validation fails, it is set to a non-zero value.
> -@xref{Using digital signatures}, for more information.
> +@xref{Using GPG-style digital signatures}, for more information.
>   @end deffn
>   
>   @node videoinfo
> @@ -5789,13 +5871,14 @@ environment variables and commands are listed in the same order.
>   @chapter Security
>   
>   @menu
> -* Authentication and authorisation:: Users and access control
> -* Using digital signatures::         Booting digitally signed code
> -* UEFI secure boot and shim::        Booting digitally signed PE files
> -* Secure Boot Advanced Targeting::   Embedded information for generation number based revocation
> -* Measured Boot::                    Measuring boot components
> -* Lockdown::                         Lockdown when booting on a secure setup
> -* Signing GRUB itself::              Ensuring the integrity of the GRUB core image
> +* Authentication and authorisation::   Users and access control
> +* Using GPG-style digital signatures:: Booting digitally signed code
> +* Using appended signatures::          An alternative approach to booting digitally signed code
> +* UEFI secure boot and shim::          Booting digitally signed PE files
> +* Secure Boot Advanced Targeting::     Embedded information for generation number based revocation
> +* Measured Boot::                      Measuring boot components
> +* Lockdown::                           Lockdown when booting on a secure setup
> +* Signing GRUB itself::                Ensuring the integrity of the GRUB core image
>   @end menu
>   
>   @node Authentication and authorisation
> @@ -5869,8 +5952,8 @@ generating configuration files with authentication.  You can use
>   adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2}
>   commands.
>   
> -@node Using digital signatures
> -@section Using digital signatures in GRUB
> +@node Using GPG-style digital signatures
> +@section Using GPG-style digital signatures in GRUB
>   
>   GRUB's @file{core.img} can optionally provide enforcement that all files
>   subsequently read from disk are covered by a valid digital signature.
> @@ -5953,6 +6036,82 @@ or BIOS) configuration to cause the machine to boot from a different
>   (attacker-controlled) device.  GRUB is at best only one link in a
>   secure boot chain.
>   
> +@node Using appended signatures
> +@section Using appended signatures in GRUB
> +
> +GRUB supports verifying Linux-style 'appended signatures' for secure boot.
> +Appended signatures are PKCS#7 messages containing a signature over the
> +contents of a file, plus some metadata, appended to the end of a file. A file
> +with an appended signature ends with the magic string:
> +
> +@example
> +~Module signature appended~\n
> +@end example
> +
> +where @code{\n} represents the carriage-return character, @code{0x0a}.


\n is 0xa but it's called line-feed.


> +
> +To enable appended signature verification, load the appendedsig module and an
> +x509 certificate for verification. Building the appendedsig module into the
> +core grub image is recommended.
> +
> +Certificates can be managed at boot time using the @pxref{trust_certificate},
> +@pxref{distrust_certificate} and @pxref{list_certificates} commands.
> +Certificates can also be built in to the core image using the @code{--x509}
> +parameter to @command{grub-install} or @command{grub-mkimage}.
> +
> +A file can be explictly verified using the @pxref{verify_appended} command.
> +
> +Only signatures made with the SHA-256 or SHA-512 hash algorithm are supported,
> +and only RSA signatures are supported.
> +
> +A file can be signed with the @command{sign-file} utility supplied with the
> +Linux kernel source. For example, if you have @code{signing.key} as the private
> +key and @code{certificate.der} as the x509 certificate containing the public key:
> +
> +@example
> +sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed
> +@end example
> +
> +Enforcement of signature verification is controlled by the
> +@code{check_appended_signatures} variable. Verification will only take place
> +when files are loaded if the variable is set to @code{enforce}. If a
> +certificate is built into the grub core image with the @code{--x509} parameter,
> +the variable will be automatically set to @code{enforce} when the appendedsig
> +module is loaded.
> +
> +Unlike GPG-style signatures, not all files loaded by GRUB are required to be
> +signed. Once verification is turned on, the following file types must carry
> +appended signatures:
> +
> +@enumerate
> +@item Linux, Multiboot, BSD, XNU and Plan9 kernels
> +@item Grub modules, except those built in to the core image
> +@item Any new certificate files to be trusted
> +@end enumerate
> +
> +ACPI tables and Device Tree images will not be checked for appended signatures
> +but must be verified by another mechanism such as GPG-style signatures before
> +they will be loaded.
> +
> +No attempt is made to validate any other file type. In particular,
> +chain-loaded binaries are not verified - if your platform supports
> +chain-loading and this cannot be disabled, consider an alternative secure
> +boot mechanism.
> +
> +As with GPG-style appended signatures, signature checking does @strong{not}
> +stop an attacker with console access from dropping manually to the GRUB
> +console and executing:
> +
> +@example
> +set check_appended_signatures=no
> +@end example
> +
> +Refer to the section on password-protecting GRUB (@pxref{Authentication
> +and authorisation}) for more information on preventing this.
> +
> +Additionally, special care must be taken around the @command{loadenv} command,
> +which can be used to turn off @code{check_appended_signature}.
> +
>   @node UEFI secure boot and shim
>   @section UEFI secure boot and shim support
>   


With this nit fixed: Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>




^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 19/22] appended signatures: support verifying appended signatures
  2021-06-30  8:40 ` [PATCH v2 19/22] appended signatures: support verifying appended signatures Daniel Axtens
@ 2021-07-20  1:31   ` Stefan Berger
  2022-04-21  7:10     ` Daniel Axtens
  0 siblings, 1 reply; 56+ messages in thread
From: Stefan Berger @ 2021-07-20  1:31 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> Building on the parsers and the ability to embed x509 certificates, as
> well as the existing gcrypt functionality, add a module for verifying
> appended signatures.
>
> This includes a verifier that requires that Linux kernels and grub modules
> have appended signatures, and commands to manage the list of trusted
> certificates for verification.
>
> Verification must be enabled by setting check_appended_signatures. If
> GRUB is locked down when the module is loaded, verification will be
> enabled and locked automatically.
>
> As with the PGP verifier, it is not a complete secure-boot solution:
> other mechanisms, such as a password or lockdown, must be used to ensure
> that a user cannot drop to the grub shell and disable verification.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>
>
> ---
>
> v2 changes:
>
>   - Improve x509 parser function name
>   - Constify data parameters in function signatures
>   - Support multiple signers
>   - Use an enum rather than 0, 1 and 2 for various signature
>      enforcement states.
>   - Spin out a file reading function that was duplicated.
>   - Fix some code style and clarity issues.
>
> Thanks to Nayna Jain and Stefan Berger for their reviews.
>
> Revert "fixups so that you can build pkcs7 without posixly"
>
> This reverts commit 676a19fa8a7f9cca7a58ce2180110f609185b2bd.
> ---
>   grub-core/Makefile.core.def                  |  14 +
>   grub-core/commands/appendedsig/appendedsig.c | 669 +++++++++++++++++++
>   include/grub/file.h                          |   2 +
>   3 files changed, 685 insertions(+)
>   create mode 100644 grub-core/commands/appendedsig/appendedsig.c
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index d63f216111ca..beeedd8ca356 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -946,6 +946,20 @@ module = {
>     cppflags = '-I$(srcdir)/lib/posix_wrap';
>   };
>   
> +module = {
> +  name = appendedsig;
> +  common = commands/appendedsig/appendedsig.c;
> +  common = commands/appendedsig/x509.c;
> +  common = commands/appendedsig/pkcs7.c;
> +  common = commands/appendedsig/asn1util.c;
> +  common = commands/appendedsig/gnutls_asn1_tab.c;
> +  common = commands/appendedsig/pkix_asn1_tab.c;
> +
> +  // posix wrapper required for gcry to get sys/types.h
> +  cflags = '$(CFLAGS_POSIX)';
> +  cppflags = '-I$(srcdir)/lib/posix_wrap';
> +};
> +
>   module = {
>     name = hdparm;
>     common = commands/hdparm.c;
> diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c
> new file mode 100644
> index 000000000000..e63ad1ac645a
> --- /dev/null
> +++ b/grub-core/commands/appendedsig/appendedsig.c
> @@ -0,0 +1,669 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020-2021  IBM Corporation.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/types.h>
> +#include <grub/misc.h>
> +#include <grub/mm.h>
> +#include <grub/err.h>
> +#include <grub/dl.h>
> +#include <grub/file.h>
> +#include <grub/command.h>
> +#include <grub/crypto.h>
> +#include <grub/pkcs1_v15.h>
> +#include <grub/i18n.h>
> +#include <grub/gcrypt/gcrypt.h>
> +#include <grub/kernel.h>
> +#include <grub/extcmd.h>
> +#include <grub/verify.h>
> +#include <grub/libtasn1.h>
> +#include <grub/env.h>
> +#include <grub/lockdown.h>
> +
> +#include "appendedsig.h"
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +const char magic[] = "~Module signature appended~\n";
> +
> +/*
> + * This structure is extracted from scripts/sign-file.c in the linux kernel
> + * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible.
> + */
> +struct module_signature
> +{
> +  grub_uint8_t algo;		/* Public-key crypto algorithm [0] */
> +  grub_uint8_t hash;		/* Digest algorithm [0] */
> +  grub_uint8_t id_type;		/* Key identifier type [PKEY_ID_PKCS7] */
> +  grub_uint8_t signer_len;	/* Length of signer's name [0] */
> +  grub_uint8_t key_id_len;	/* Length of key identifier [0] */
> +  grub_uint8_t __pad[3];
> +  grub_uint32_t sig_len;	/* Length of signature data */
> +} GRUB_PACKED;
> +
> +
> +/* This represents an entire, parsed, appended signature */
> +struct grub_appended_signature
> +{
> +  grub_size_t signature_len;		/* Length of PKCS#7 data +
> +                                         * metadata + magic */
> +
> +  struct module_signature sig_metadata;	/* Module signature metadata */
> +  struct pkcs7_signedData pkcs7;	/* Parsed PKCS#7 data */
> +};
> +
> +/* Trusted certificates for verifying appended signatures */
> +struct x509_certificate *grub_trusted_key;
> +
> +/*
> + * Force gcry_rsa to be a module dependency.
> + *
> + * If we use grub_crypto_pk_rsa, then then the gcry_rsa module won't be built
> + * in if you add 'appendedsig' to grub-install --modules. You would need to
> + * add 'gcry_rsa' too. That's confusing and seems suboptimal, especially when
> + * we only support RSA.
> + *
> + * Dynamic loading also causes some concerns. We can't load gcry_rsa from the
> + * the filesystem after we install the verifier - we won't be able to verify
> + * it without having it already present. We also shouldn't load it before we
> + * install the verifier, because that would mean it wouldn't be verified - an
> + * attacker could insert any code they wanted into the module.
> + *
> + * So instead, reference the internal symbol from gcry_rsa. That creates a
> + * direct dependency on gcry_rsa, so it will be built in when this module
> + * is built in. Being built in (assuming the core image is itself signed!)
> + * also resolves our concerns about loading from the filesystem.
> + */
> +extern gcry_pk_spec_t _gcry_pubkey_spec_rsa;
> +
> +static enum
> +{ check_sigs_no = 0,


nit: newline after '{'


> +  check_sigs_enforce = 1,
> +  check_sigs_forced = 2
> +} check_sigs = check_sigs_no;


What does 'forced' mean?


> +
> +static const char *
> +grub_env_read_sec (struct grub_env_var *var __attribute__((unused)),
> +		   const char *val __attribute__((unused)))
> +{
> +  if (check_sigs == check_sigs_forced)
> +    return "forced";
> +  else if (check_sigs == check_sigs_enforce)
> +    return "enforce";
> +  else
> +    return "no";
> +}
> +
> +static char *
> +grub_env_write_sec (struct grub_env_var *var __attribute__((unused)),
> +		    const char *val)
> +{
> +  /* Do not allow the value to be changed if set to forced */
> +  if (check_sigs == check_sigs_forced)
> +    return grub_strdup ("forced");
> +
> +  if ((*val == '2') || (*val == 'f'))
> +    check_sigs = check_sigs_forced;
> +  else if ((*val == '1') || (*val == 'e'))
> +    check_sigs = check_sigs_enforce;
> +  else if ((*val == '0') || (*val == 'n'))
> +    check_sigs = check_sigs_no;
> +
> +  return grub_strdup (grub_env_read_sec (NULL, NULL));
> +}
> +
> +static grub_err_t
> +file_read_all (grub_file_t file, grub_uint8_t **buf, grub_size_t *len)
> +{
> +  grub_off_t full_file_size;
> +  grub_size_t file_size, total_read_size = 0;
> +  grub_ssize_t read_size;
> +
> +  full_file_size = grub_file_size (file);
> +  if (full_file_size == GRUB_FILE_SIZE_UNKNOWN)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT,
> +		       N_("Cannot read a file of unknown size into a buffer"));
> +
> +  if (full_file_size > GRUB_SIZE_MAX)
> +    return grub_error (GRUB_ERR_OUT_OF_RANGE,
> +		       N_("File is too large to read: %" PRIuGRUB_UINT64_T
> +			  " bytes"), full_file_size);
> +
> +  file_size = (grub_size_t) full_file_size;
> +
> +  *buf = grub_malloc (file_size);
> +  if (!*buf)
> +    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +		       N_("Could not allocate file data buffer size %"
> +			  PRIuGRUB_SIZE), file_size);
> +
> +  while (total_read_size < file_size)
> +    {
> +      read_size =
> +	grub_file_read (file, *buf + total_read_size,
> +			file_size - total_read_size);
> +
> +      if (read_size < 0)
> +	{
> +	  grub_free (*buf);
> +	  return grub_errno;
> +	}
> +      else if (read_size == 0)
> +	{
> +	  grub_free (*buf);
> +	  return grub_error (GRUB_ERR_IO,
> +			     N_("Could not read full file size (%"
> +				PRIuGRUB_SIZE "), only %" PRIuGRUB_SIZE
> +				" bytes read"), file_size, total_read_size);
> +	}
> +
> +      total_read_size += read_size;
> +    }
> +  *len = file_size;
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +read_cert_from_file (grub_file_t f, struct x509_certificate *certificate)
> +{
> +  grub_err_t err;
> +  grub_uint8_t *buf;
> +  grub_size_t file_size;
> +
> +  err = file_read_all (f, &buf, &file_size);
> +  if (err != GRUB_ERR_NONE)
> +    return err;
> +
> +  err = parse_x509_certificate (buf, file_size, certificate);
> +  if (err != GRUB_ERR_NONE)
> +    {
> +      grub_free (buf);
> +      return err;
> +    }

forgot grub-free(buf) ?


> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize,
> +			    struct grub_appended_signature *sig)
> +{
> +  grub_err_t err;
> +  grub_size_t pkcs7_size;
> +  grub_size_t remaining_len;
> +  const grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic);
> +
> +  if (bufsize < grub_strlen (magic))
> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		       N_("File too short for signature magic"));
> +
> +  if (grub_memcmp (appsigdata, (grub_uint8_t *) magic, grub_strlen (magic)))
> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		       N_("Missing or invalid signature magic"));
> +
> +  remaining_len = bufsize - grub_strlen (magic);
> +
> +  if (remaining_len < sizeof (struct module_signature))
> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		       N_("File too short for signature metadata"));
> +
> +  appsigdata -= sizeof (struct module_signature);
> +
> +  /* extract the metadata */
> +  grub_memcpy (&(sig->sig_metadata), appsigdata,
> +	       sizeof (struct module_signature));
> +
> +  remaining_len -= sizeof (struct module_signature);
> +
> +  if (sig->sig_metadata.id_type != 2)
> +    return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Wrong signature type"));
> +
> +  pkcs7_size = grub_be_to_cpu32 (sig->sig_metadata.sig_len);
> +
> +  if (pkcs7_size > remaining_len)
> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		       N_("File too short for PKCS#7 message"));
> +
> +  grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", pkcs7_size);
> +
> +  sig->signature_len =
> +    grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size;
> +
> +  /* rewind pointer and parse pkcs7 data */
> +  appsigdata -= pkcs7_size;
> +
> +  err = parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7);
> +  if (err != GRUB_ERR_NONE)
> +    return err;
> +
> +  return GRUB_ERR_NONE;
just 'return parse_pkcs7_signedData (...)' ?
> +}
> +
> +static grub_err_t
> +grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
> +{
> +  grub_err_t err = GRUB_ERR_NONE;
> +  grub_size_t datasize;
> +  void *context;
> +  unsigned char *hash;
> +  gcry_mpi_t hashmpi;
> +  gcry_err_code_t rc;
> +  struct x509_certificate *pk;
> +  struct grub_appended_signature sig;
> +  struct pkcs7_signerInfo *si;
> +  int i;
> +
> +  if (!grub_trusted_key)
> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		       N_("No trusted keys to verify against"));
> +
> +  err = extract_appended_signature (buf, bufsize, &sig);
> +  if (err != GRUB_ERR_NONE)
> +    return err;
> +
> +  datasize = bufsize - sig.signature_len;
> +
> +  for (i = 0; i < sig.pkcs7.signerInfo_count; i++)
> +    {
> +      /* This could be optimised in a couple of ways:
> +	  - we could only compute hashes once per hash type
> +	  - we could track signer information and only verify where IDs match
> +	 For now we do the naive O(trusted keys * pkcs7 signers) approach.
> +	*/
> +      si = &sig.pkcs7.signerInfos[i];
> +      context = grub_zalloc (si->hash->contextsize);
> +      if (!context)
> +	return grub_errno;
> +
> +      si->hash->init (context);
> +      si->hash->write (context, buf, datasize);
> +      si->hash->final (context);
> +      hash = si->hash->read (context);
> +
> +      grub_dprintf ("appendedsig",
> +		    "data size %" PRIxGRUB_SIZE ", signer %d hash %02x%02x%02x%02x...\n",
> +		    datasize, i, hash[0], hash[1], hash[2], hash[3]);
> +
> +      err = GRUB_ERR_BAD_SIGNATURE;
> +      for (pk = grub_trusted_key; pk; pk = pk->next)
> +	{
> +	  rc = grub_crypto_rsa_pad (&hashmpi, hash, si->hash, pk->mpis[0]);
> +	  if (rc)
> +	    {
> +	      err = grub_error (GRUB_ERR_BAD_SIGNATURE,
> +    				N_("Error padding hash for RSA verification: %d"),
> +    				rc);
> +	      grub_free (context);
> +	      goto cleanup;
> +	    }
> +
> +	  rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &si->sig_mpi,
> +					     pk->mpis, NULL, NULL);
> +	  gcry_mpi_release (hashmpi);
> +
> +	  if (rc == 0)
> +	    {
> +	      grub_dprintf ("appendedsig",
> +			    "verify signer %d with key '%s' succeeded\n", i,
> +			    pk->subject);
> +	      err = GRUB_ERR_NONE;
> +	      break;
> +	    }
> +
> +	  grub_dprintf ("appendedsig",
> +			"verify signer %d with key '%s' failed with %d\n", i,
> +			pk->subject, rc);
> +	}
> +
> +      grub_free (context);
> +
> +      if (err == GRUB_ERR_NONE)
> +	break;
> +    }
> +
> +  /* If we didn't verify, provide a neat message */
> +  if (err != GRUB_ERR_NONE)
> +    err = grub_error (GRUB_ERR_BAD_SIGNATURE,
> +		      N_("Failed to verify signature against a trusted key"));
> +
> +cleanup:
> +  pkcs7_signedData_release (&sig.pkcs7);
> +
> +  return err;
> +}
> +
> +static grub_err_t
> +grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)),
> +			   int argc, char **args)
> +{
> +  grub_file_t f;
> +  grub_err_t err = GRUB_ERR_NONE;
> +  grub_uint8_t *data;
> +  grub_size_t file_size;
> +
> +  if (argc < 1)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
> +
> +  grub_dprintf ("appendedsig", "verifying %s\n", args[0]);
> +
> +  f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE);
> +  if (!f)
> +    {
> +      err = grub_errno;
> +      goto cleanup;
> +    }
> +
> +  err = file_read_all (f, &data, &file_size);
> +  if (err != GRUB_ERR_NONE)
> +    goto cleanup;
> +
> +  err = grub_verify_appended_signature (data, file_size);
> +
> +  grub_free (data);
> +
> +cleanup:
> +  if (f)
> +    grub_file_close (f);
> +  return err;
> +}
> +
> +static grub_err_t
> +grub_cmd_distrust (grub_command_t cmd __attribute__((unused)),
> +		   int argc, char **args)
> +{
> +  unsigned long cert_num, i;
> +  struct x509_certificate *cert, *prev;
> +
> +  if (argc != 1)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("One argument expected"));
> +
> +  grub_errno = GRUB_ERR_NONE;
> +  cert_num = grub_strtoul (args[0], NULL, 10);
> +  if (grub_errno != GRUB_ERR_NONE)
> +    return grub_errno;
> +
> +  if (cert_num < 1)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT,
> +		       N_("Certificate number too small - numbers start at 1"));
> +
> +  if (cert_num == 1)
> +    {
> +      cert = grub_trusted_key;
> +      grub_trusted_key = cert->next;
> +
> +      certificate_release (cert);
> +      grub_free (cert);
> +      return GRUB_ERR_NONE;
> +    }
> +  i = 2;
> +  prev = grub_trusted_key;
> +  cert = grub_trusted_key->next;
> +  while (cert)
> +    {
> +      if (i == cert_num)
> +	{
> +	  prev->next = cert->next;
> +	  certificate_release (cert);
> +	  grub_free (cert);
> +	  return GRUB_ERR_NONE;
> +	}
> +      i++;
> +      prev = cert;
> +      cert = cert->next;
> +    }
> +
> +  return grub_error (GRUB_ERR_BAD_ARGUMENT,
> +		     N_("No certificate number %lu found - only %lu certificates in the store"),
> +		     cert_num, i - 1);
> +}
> +
> +static grub_err_t
> +grub_cmd_trust (grub_command_t cmd __attribute__((unused)),
> +		int argc, char **args)
> +{
> +  grub_file_t certf;
> +  struct x509_certificate *cert = NULL;
> +  grub_err_t err;
> +
> +  if (argc != 1)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
> +
> +  certf = grub_file_open (args[0],
> +			  GRUB_FILE_TYPE_CERTIFICATE_TRUST
> +			  | GRUB_FILE_TYPE_NO_DECOMPRESS);
> +  if (!certf)
> +    return grub_errno;
> +
> +
> +  cert = grub_zalloc (sizeof (struct x509_certificate));
> +  if (!cert)
> +    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
> +		       N_("Could not allocate memory for certificate"));
> +
> +  err = read_cert_from_file (certf, cert);
> +  grub_file_close (certf);
> +  if (err != GRUB_ERR_NONE)
> +    {
> +      grub_free (cert);
> +      return err;
> +    }
> +  grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n",
> +		cert->subject);
> +
> +  cert->next = grub_trusted_key;
> +  grub_trusted_key = cert;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_cmd_list (grub_command_t cmd __attribute__((unused)),
> +	       int argc __attribute__((unused)),
> +	       char **args __attribute__((unused)))
> +{
> +  struct x509_certificate *cert;
> +  int cert_num = 1;
> +  grub_size_t i;
> +
> +  for (cert = grub_trusted_key; cert; cert = cert->next)
> +    {
> +      grub_printf (N_("Certificate %d:\n"), cert_num);
> +
> +      grub_printf (N_("\tSerial: "));
> +      for (i = 0; i < cert->serial_len - 1; i++)
> +	{
> +	  grub_printf ("%02x:", cert->serial[i]);
> +	}
> +      grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]);
> +
> +      grub_printf ("\tCN: %s\n\n", cert->subject);
> +      cert_num++;
> +
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +appendedsig_init (grub_file_t io __attribute__((unused)),
> +		  enum grub_file_type type,
> +		  void **context __attribute__((unused)),
> +		  enum grub_verify_flags *flags)
> +{
> +  if (check_sigs == check_sigs_no)
> +    {
> +      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
> +      return GRUB_ERR_NONE;
> +    }
> +
> +  switch (type & GRUB_FILE_TYPE_MASK)
> +    {
> +    case GRUB_FILE_TYPE_CERTIFICATE_TRUST:
> +      /*
> +       * This is a certificate to add to trusted keychain.
> +       *
> +       * This needs to be verified or blocked. Ideally we'd write an x509
> +       * verifier, but we lack the hubris required to take this on. Instead,
> +       * require that it have an appended signature.
> +       */
> +
> +      /* Fall through */
> +
> +    case GRUB_FILE_TYPE_LINUX_KERNEL:
> +    case GRUB_FILE_TYPE_GRUB_MODULE:
> +      /*
> +       * Appended signatures are only defined for ELF binaries.
> +       * Out of an abundance of caution, we only verify Linux kernels and
> +       * GRUB modules at this point.
> +       */
> +      *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
> +      return GRUB_ERR_NONE;
> +
> +    case GRUB_FILE_TYPE_ACPI_TABLE:
> +    case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE:
> +      /*
> +       * It is possible to use appended signature verification without
> +       * lockdown - like the PGP verifier. When combined with an embedded
> +       * config file in a signed grub binary, this could still be a meaningful
> +       * secure-boot chain - so long as it isn't subverted by something like a
> +       * rouge ACPI table or DT image. Defer them explicitly.
> +       */
> +      *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;
> +      return GRUB_ERR_NONE;
> +
> +    default:
> +      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
> +      return GRUB_ERR_NONE;
> +    }
> +}
> +
> +static grub_err_t
> +appendedsig_write (void *ctxt __attribute__((unused)),
> +		   void *buf, grub_size_t size)
> +{
> +  return grub_verify_appended_signature (buf, size);
> +}
> +
> +struct grub_file_verifier grub_appendedsig_verifier = {
> +  .name = "appendedsig",
> +  .init = appendedsig_init,
> +  .write = appendedsig_write,
> +};
> +
> +static grub_ssize_t
> +pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
> +{
> +  grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len);
> +  return len;
> +}
> +
> +/* Filesystem descriptor.  */
> +static struct grub_fs pseudo_fs = {
> +  .name = "pseudo",
> +  .fs_read = pseudo_read
> +};
> +
> +static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust;
> +
> +GRUB_MOD_INIT (appendedsig)
> +{
> +  int rc;
> +  struct grub_module_header *header;
> +
> +  /* If in lockdown, immediately enter forced mode */
> +  if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
> +    check_sigs = check_sigs_forced;
> +
> +  grub_trusted_key = NULL;
> +
> +  grub_register_variable_hook ("check_appended_signatures",
> +			       grub_env_read_sec, grub_env_write_sec);
> +  grub_env_export ("check_appended_signatures");
> +
> +  rc = asn1_init ();
> +  if (rc)
> +    grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc,
> +		asn1_strerror (rc));
> +
> +  FOR_MODULES (header)
> +  {
> +    struct grub_file pseudo_file;
> +    struct x509_certificate *pk = NULL;
> +    grub_err_t err;
> +
> +    /* Not an ELF module, skip.  */
> +    if (header->type != OBJ_TYPE_X509_PUBKEY)
> +      continue;

Is that comment correct? What does an ELF module have to do with type 
OBJ_TYPE_X509_PUBKEY?


> +
> +    grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
> +    pseudo_file.fs = &pseudo_fs;
> +    pseudo_file.size = header->size - sizeof (struct grub_module_header);
> +    pseudo_file.data = (char *) header + sizeof (struct grub_module_header);
> +
> +    grub_dprintf ("appendedsig",
> +		  "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n",
> +		  pseudo_file.size);
> +
> +    pk = grub_zalloc (sizeof (struct x509_certificate));
> +    if (!pk)
> +      {
> +	grub_fatal ("Out of memory loading initial certificates");
> +      }
> +
> +    err = read_cert_from_file (&pseudo_file, pk);
> +    if (err != GRUB_ERR_NONE)
> +      grub_fatal ("Error loading initial key: %s", grub_errmsg);
> +
> +    grub_dprintf ("appendedsig", "loaded certificate CN='%s'\n", pk->subject);
> +
> +    pk->next = grub_trusted_key;
> +    grub_trusted_key = pk;
> +  }
> +
> +  cmd_trust =
> +    grub_register_command ("trust_certificate", grub_cmd_trust,
> +			   N_("X509_CERTIFICATE"),
> +			   N_("Add X509_CERTIFICATE to trusted certificates."));
> +  cmd_list =
> +    grub_register_command ("list_certificates", grub_cmd_list, 0,
> +			   N_("Show the list of trusted x509 certificates."));
> +  cmd_verify =
> +    grub_register_command ("verify_appended", grub_cmd_verify_signature,
> +			   N_("FILE"),
> +			   N_("Verify FILE against the trusted x509 certificates."));
> +  cmd_distrust =
> +    grub_register_command ("distrust_certificate", grub_cmd_distrust,
> +			   N_("CERT_NUMBER"),
> +			   N_("Remove CERT_NUMBER (as listed by list_certificates) from trusted certificates."));
> +
> +  grub_verifier_register (&grub_appendedsig_verifier);
> +  grub_dl_set_persistent (mod);
> +}
> +
> +GRUB_MOD_FINI (appendedsig)
> +{
> +  /*
> +   * grub_dl_set_persistent should prevent this from actually running, but
> +   * it does still run under emu.
> +   */
> +
> +  grub_verifier_unregister (&grub_appendedsig_verifier);
> +  grub_unregister_command (cmd_verify);
> +  grub_unregister_command (cmd_list);
> +  grub_unregister_command (cmd_trust);
> +  grub_unregister_command (cmd_distrust);
> +}
> diff --git a/include/grub/file.h b/include/grub/file.h
> index 31567483ccfc..96827a4f8961 100644
> --- a/include/grub/file.h
> +++ b/include/grub/file.h
> @@ -80,6 +80,8 @@ enum grub_file_type
>       GRUB_FILE_TYPE_PUBLIC_KEY,
>       /* File holding public key to add to trused keys.  */
>       GRUB_FILE_TYPE_PUBLIC_KEY_TRUST,
> +    /* File holding x509 certificiate to add to trusted keys.  */
> +    GRUB_FILE_TYPE_CERTIFICATE_TRUST,
>       /* File of which we intend to print a blocklist to the user.  */
>       GRUB_FILE_TYPE_PRINT_BLOCKLIST,
>       /* File we intend to use for test loading or testing speed.  */


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 20/22] appended signatures: verification tests
  2021-06-30  8:40 ` [PATCH v2 20/22] appended signatures: verification tests Daniel Axtens
@ 2021-07-20 12:49   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-20 12:49 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> These tests are run through all_functional_test and test a range
> of commands and behaviours.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>


Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>

>
> ---
>
> v2 changes:
>
>    - add a test for EKU
>    - add tests for files signed with multiple signers
>    - add a test of padded PKCS#7 messages
>    - use macros to reduce duplication for exposing certificate files
>       to the test via procfs
>    - more useful comments
> ---
>   grub-core/Makefile.core.def               |   6 +
>   grub-core/tests/appended_signature_test.c | 273 ++++++
>   grub-core/tests/appended_signatures.h     | 975 ++++++++++++++++++++++
>   grub-core/tests/lib/functional_test.c     |   1 +
>   4 files changed, 1255 insertions(+)
>   create mode 100644 grub-core/tests/appended_signature_test.c
>   create mode 100644 grub-core/tests/appended_signatures.h
>
> diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
> index beeedd8ca356..4aa4cf263d94 100644
> --- a/grub-core/Makefile.core.def
> +++ b/grub-core/Makefile.core.def
> @@ -2131,6 +2131,12 @@ module = {
>     common = tests/setjmp_test.c;
>   };
>   
> +module = {
> +  name = appended_signature_test;
> +  common = tests/appended_signature_test.c;
> +  common = tests/appended_signatures.h;
> +};
> +
>   module = {
>     name = signature_test;
>     common = tests/signature_test.c;
> diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c
> new file mode 100644
> index 000000000000..5365185cb5cc
> --- /dev/null
> +++ b/grub-core/tests/appended_signature_test.c
> @@ -0,0 +1,273 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2020  IBM Corporation.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB 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 General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <grub/time.h>
> +#include <grub/misc.h>
> +#include <grub/dl.h>
> +#include <grub/command.h>
> +#include <grub/env.h>
> +#include <grub/test.h>
> +#include <grub/mm.h>
> +#include <grub/procfs.h>
> +#include <grub/file.h>
> +
> +#include "appended_signatures.h"
> +
> +GRUB_MOD_LICENSE ("GPLv3+");
> +
> +#define PROC_FILE(identifier, file_name) \
> +static char * \
> +get_ ## identifier (grub_size_t *sz) \
> +{ \
> +  char *ret; \
> +  \
> +  *sz = identifier ## _len; \
> +  ret = grub_malloc (*sz); \
> +  if (ret) \
> +    grub_memcpy (ret, identifier, *sz); \
> +  return ret; \
> +} \
> +\
> +static struct grub_procfs_entry identifier ## _entry = \
> +{ \
> +  .name = file_name, \
> +  .get_contents = get_ ## identifier \
> +};
> +
> +#define DEFINE_TEST_CASE(case_name) PROC_FILE(case_name, #case_name)
> +
> +#define DO_TEST(case_name, is_valid) \
> +{ \
> +  grub_procfs_register (#case_name, &case_name ## _entry); \
> +  do_verify ("(proc)/" #case_name, is_valid); \
> +  grub_procfs_unregister (&case_name ## _entry); \
> +}
> +
> +
> +DEFINE_TEST_CASE (hi_signed);
> +DEFINE_TEST_CASE (hi_signed_sha256);
> +DEFINE_TEST_CASE (hj_signed);
> +DEFINE_TEST_CASE (short_msg);
> +DEFINE_TEST_CASE (unsigned_msg);
> +DEFINE_TEST_CASE (hi_signed_2nd);
> +DEFINE_TEST_CASE (hi_double);
> +DEFINE_TEST_CASE (hi_double_extended);
> +
> +PROC_FILE (certificate_der, "certificate.der")
> +PROC_FILE (certificate2_der, "certificate2.der")
> +PROC_FILE (certificate_printable_der, "certificate_printable.der")
> +PROC_FILE (certificate_eku_der, "certificate_eku.der")
> +
> +static void
> +do_verify (const char *f, int is_valid)
> +{
> +  grub_command_t cmd;
> +  char *args[] = { (char *) f, NULL };
> +  grub_err_t err;
> +
> +  cmd = grub_command_find ("verify_appended");
> +  if (!cmd)
> +    {
> +      grub_test_assert (0, "can't find command `%s'", "verify_appended");
> +      return;
> +    }
> +  err = (cmd->func) (cmd, 1, args);
> +  if (is_valid)
> +    {
> +      grub_test_assert (err == GRUB_ERR_NONE,
> +			"verification of %s failed: %d: %s", f, grub_errno,
> +			grub_errmsg);
> +    }
> +  else
> +    {
> +      grub_test_assert (err == GRUB_ERR_BAD_SIGNATURE,
> +			"verification of %s unexpectedly succeeded", f);
> +    }
> +  grub_errno = GRUB_ERR_NONE;
> +
> +}
> +
> +static void
> +appended_signature_test (void)
> +{
> +  grub_command_t cmd_trust, cmd_distrust;
> +  char *trust_args[] = { (char *) "(proc)/certificate.der", NULL };
> +  char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL };
> +  char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der",
> +				   NULL };
> +  char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL };
> +  char *distrust_args[] = { (char *) "1", NULL };
> +  char *distrust2_args[] = { (char *) "2", NULL };
> +  grub_err_t err;
> +
> +  grub_procfs_register ("certificate.der", &certificate_der_entry);
> +  grub_procfs_register ("certificate2.der", &certificate2_der_entry);
> +  grub_procfs_register ("certificate_printable.der",
> +			&certificate_printable_der_entry);
> +  grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry);
> +
> +  cmd_trust = grub_command_find ("trust_certificate");
> +  if (!cmd_trust)
> +    {
> +      grub_test_assert (0, "can't find command `%s'", "trust_certificate");
> +      return;
> +    }
> +  err = (cmd_trust->func) (cmd_trust, 1, trust_args);
> +
> +  grub_test_assert (err == GRUB_ERR_NONE,
> +		    "loading certificate failed: %d: %s", grub_errno,
> +		    grub_errmsg);
> +
> +  /* If we have no certificate the remainder of the tests are meaningless */
> +  if (err != GRUB_ERR_NONE)
> +    return;
> +
> +  /*
> +   * Reload the command: this works around some 'interesting' behaviour in the
> +   * dynamic command dispatcher. The first time you call cmd->func you get a
> +   * dispatcher that loads the module, finds the real cmd, calls it, and then
> +   * releases some internal storage. This means it's not safe to call a second
> +   * time and we need to reload it.
> +   */
> +  cmd_trust = grub_command_find ("trust_certificate");
> +
> +  /* hi, signed with key 1, SHA-512 */
> +  DO_TEST (hi_signed, 1);
> +
> +  /* hi, signed with key 1, SHA-256 */
> +  DO_TEST (hi_signed_sha256, 1);
> +
> +  /* hi, key 1, SHA-512, second byte corrupted */
> +  DO_TEST (hj_signed, 0);
> +
> +  /* message too short for a signature */
> +  DO_TEST (short_msg, 0);
> +
> +  /* lorem ipsum */
> +  DO_TEST (unsigned_msg, 0);
> +
> +  /* hi, signed with both keys, SHA-512 */
> +  DO_TEST (hi_double, 1);
> +
> +  /*
> +   * hi, signed with both keys and with empty space to test we haven't broken
> +   * support for adding more signatures after the fact
> +   */
> +  DO_TEST (hi_double_extended, 1);
> +
> +  /*
> +   * in enforcing mode, we shouldn't be able to load a certificate that isn't
> +   * signed by an existing trusted key.
> +   *
> +   * However, procfs files automatically skip the verification test, so we can't
> +   * easily test this.
> +   */
> +
> +  /*
> +   * verify that testing with 2 trusted certs works
> +   */
> +  DO_TEST (hi_signed_2nd, 0);
> +
> +  err = (cmd_trust->func) (cmd_trust, 1, trust_args2);
> +
> +  grub_test_assert (err == GRUB_ERR_NONE,
> +		    "loading certificate 2 failed: %d: %s", grub_errno,
> +		    grub_errmsg);
> +
> +  if (err != GRUB_ERR_NONE)
> +    return;
> +
> +  DO_TEST (hi_signed_2nd, 1);
> +  DO_TEST (hi_signed, 1);
> +  DO_TEST (hi_double, 1);
> +  DO_TEST (hi_double_extended, 1);
> +
> +  /*
> +   * Check certificate removal. They're added to the _top_ of the list and
> +   * removed by position in the list. Current the list looks like [#2, #1].
> +   *
> +   * First test removing the second certificate in the list, which is
> +   * certificate #1, giving us just [#2].
> +   */
> +  cmd_distrust = grub_command_find ("distrust_certificate");
> +  if (!cmd_distrust)
> +    {
> +      grub_test_assert (0, "can't find command `%s'", "distrust_certificate");
> +      return;
> +    }
> +
> +  err = (cmd_distrust->func) (cmd_distrust, 1, distrust2_args);
> +  grub_test_assert (err == GRUB_ERR_NONE,
> +		    "distrusting certificate 1 failed: %d: %s", grub_errno,
> +		    grub_errmsg);
> +  DO_TEST (hi_signed_2nd, 1);
> +  DO_TEST (hi_signed, 0);
> +  DO_TEST (hi_double, 1);
> +
> +  /*
> +   * Now reload certificate #1. This will make the list look like [#1, #2]
> +   */
> +  err = (cmd_trust->func) (cmd_trust, 1, trust_args);
> +
> +  grub_test_assert (err == GRUB_ERR_NONE,
> +		    "reloading certificate 1 failed: %d: %s", grub_errno,
> +		    grub_errmsg);
> +  DO_TEST (hi_signed, 1);
> +
> +  /* Remove the first certificate in the list, giving us just [#2] */
> +  err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args);
> +  grub_test_assert (err == GRUB_ERR_NONE,
> +		    "distrusting certificate 1 (first time) failed: %d: %s",
> +		    grub_errno, grub_errmsg);
> +  DO_TEST (hi_signed_2nd, 1);
> +  DO_TEST (hi_signed, 0);
> +
> +  /*
> +   * Remove the first certificate again, giving an empty list.
> +   *
> +   * verify_appended should fail if there are no certificates to verify against.
> +   */
> +  err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args);
> +  grub_test_assert (err == GRUB_ERR_NONE,
> +		    "distrusting certificate 1 (second time) failed: %d: %s",
> +		    grub_errno, grub_errmsg);
> +  DO_TEST (hi_signed_2nd, 0);
> +  DO_TEST (hi_double, 0);
> +
> +  /*
> +   * Lastly, check a certificate that uses printableString rather than
> +   * utf8String loads properly, and that a certificate with an appropriate
> +   * extended key usage loads.
> +   */
> +  err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable);
> +  grub_test_assert (err == GRUB_ERR_NONE,
> +		    "trusting printable certificate failed: %d: %s",
> +		    grub_errno, grub_errmsg);
> +
> +  err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku);
> +  grub_test_assert (err == GRUB_ERR_NONE,
> +		    "trusting certificate with extended key usage failed: %d: %s",
> +		    grub_errno, grub_errmsg);
> +
> +  grub_procfs_unregister (&certificate_der_entry);
> +  grub_procfs_unregister (&certificate2_der_entry);
> +  grub_procfs_unregister (&certificate_printable_der_entry);
> +  grub_procfs_unregister (&certificate_eku_der_entry);
> +}
> +
> +GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test);
> diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h
> new file mode 100644
> index 000000000000..c6aa12d86ac7
> --- /dev/null
> +++ b/grub-core/tests/appended_signatures.h
> @@ -0,0 +1,975 @@
> +unsigned char certificate_der[] = {
> +  0x30, 0x82, 0x05, 0x5d, 0x30, 0x82, 0x03, 0x45, 0xa0, 0x03, 0x02, 0x01,
> +  0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
> +  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
> +  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
> +  0x05, 0x00, 0x30, 0x3d, 0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04,
> +  0x03, 0x0c, 0x32, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65,
> +  0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75,
> +  0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74,
> +  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68,
> +  0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x31, 0x30,
> +  0x36, 0x32, 0x39, 0x30, 0x38, 0x33, 0x36, 0x31, 0x33, 0x5a, 0x18, 0x0f,
> +  0x32, 0x31, 0x32, 0x31, 0x30, 0x36, 0x30, 0x35, 0x30, 0x38, 0x33, 0x36,
> +  0x31, 0x33, 0x5a, 0x30, 0x33, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55,
> +  0x04, 0x03, 0x0c, 0x28, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70,
> +  0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74,
> +  0x75, 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x69, 0x67,
> +  0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, 0x22,
> +  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
> +  0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, 0x0a,
> +  0x02, 0x82, 0x02, 0x01, 0x00, 0xb9, 0x09, 0xb2, 0xf6, 0x24, 0x34, 0xdc,
> +  0x62, 0xe6, 0x4e, 0xee, 0x04, 0xdb, 0x29, 0xdc, 0x94, 0xcc, 0xee, 0x8a,
> +  0x5b, 0xc3, 0x9e, 0x06, 0xba, 0xa7, 0x9b, 0xa4, 0x5f, 0x15, 0x59, 0x8e,
> +  0xb8, 0x6e, 0x3c, 0xeb, 0x2e, 0xf2, 0xac, 0x21, 0x42, 0xbd, 0x30, 0xa1,
> +  0x39, 0xe5, 0xb9, 0x4f, 0xa0, 0x53, 0xd5, 0x42, 0xdc, 0x8a, 0x87, 0x30,
> +  0x38, 0x93, 0x44, 0x80, 0x3b, 0x1a, 0x7e, 0x9e, 0x8e, 0x3e, 0xea, 0x45,
> +  0xa0, 0x11, 0x8b, 0xfb, 0x78, 0xe4, 0xbc, 0x65, 0x6b, 0x73, 0xea, 0x6e,
> +  0xdf, 0x7c, 0x5b, 0x63, 0x7e, 0x5b, 0x0a, 0x1c, 0xe6, 0x76, 0x19, 0xb5,
> +  0x01, 0xde, 0xf6, 0x65, 0x51, 0x30, 0x0a, 0x56, 0x69, 0x69, 0xe8, 0x20,
> +  0xf9, 0x13, 0xf1, 0xbf, 0x6f, 0xdd, 0xce, 0x94, 0x96, 0x6e, 0x63, 0xd6,
> +  0xfa, 0xa4, 0x91, 0x5f, 0xb3, 0x9c, 0xc7, 0xfa, 0xa9, 0xff, 0x66, 0x5f,
> +  0xf3, 0xab, 0x5e, 0xdf, 0x4e, 0xca, 0x11, 0xcf, 0xbf, 0xf8, 0xad, 0x65,
> +  0xb1, 0x49, 0x8b, 0xe9, 0x2a, 0xad, 0x7d, 0xf3, 0x0b, 0xfa, 0x5b, 0x6a,
> +  0x6a, 0x20, 0x12, 0x77, 0xef, 0x4b, 0xb6, 0xbe, 0x92, 0xba, 0x14, 0x9c,
> +  0x5e, 0xea, 0xdc, 0x56, 0x6d, 0x92, 0xd3, 0x64, 0x22, 0xf6, 0x12, 0xe8,
> +  0x7d, 0x5e, 0x9c, 0xd6, 0xf9, 0x75, 0x68, 0x7f, 0x8f, 0xd3, 0x6e, 0x05,
> +  0x94, 0x91, 0x4f, 0xa1, 0xd6, 0x50, 0x72, 0x3b, 0x11, 0x1f, 0x28, 0x13,
> +  0xe8, 0x25, 0x6b, 0xdf, 0xff, 0x72, 0x46, 0x25, 0xe9, 0x05, 0x6f, 0x02,
> +  0xc7, 0x1e, 0xc9, 0xcf, 0x99, 0xe9, 0xa7, 0xe2, 0xae, 0xbc, 0xc1, 0x22,
> +  0x32, 0x73, 0x2d, 0xa3, 0x70, 0x8f, 0xa7, 0x8d, 0xbf, 0x5f, 0x74, 0x05,
> +  0x1b, 0x5e, 0xfe, 0x97, 0x3c, 0xe7, 0x3b, 0x86, 0x0d, 0xf6, 0x38, 0xdb,
> +  0xd2, 0x39, 0x47, 0x82, 0x00, 0x44, 0x6c, 0x7b, 0x40, 0x24, 0x0b, 0x3a,
> +  0xd4, 0x19, 0x31, 0xba, 0x4e, 0x8e, 0xa3, 0x33, 0xa6, 0x78, 0xef, 0x72,
> +  0x9f, 0x06, 0x37, 0x01, 0x9b, 0x79, 0x0d, 0x04, 0xbf, 0xba, 0xd5, 0x1f,
> +  0x27, 0xdc, 0x85, 0xbb, 0xef, 0xd2, 0x60, 0xda, 0xa0, 0x3f, 0x66, 0xce,
> +  0x9f, 0xa2, 0x7e, 0xa8, 0x8d, 0xee, 0x14, 0x4b, 0xcb, 0x93, 0xf1, 0x38,
> +  0xac, 0x4f, 0xd8, 0x29, 0xf3, 0x6f, 0xd4, 0xfd, 0x4d, 0x34, 0x77, 0x58,
> +  0x99, 0xdb, 0x16, 0xc1, 0xd0, 0xc7, 0x43, 0x41, 0x70, 0xc4, 0xad, 0x01,
> +  0x29, 0x65, 0x22, 0x43, 0x00, 0x6f, 0xb3, 0x00, 0x27, 0x38, 0xc1, 0x4f,
> +  0xda, 0x28, 0x96, 0x42, 0xdc, 0xbc, 0x3e, 0x34, 0x8e, 0x14, 0xb8, 0xf3,
> +  0x86, 0x4a, 0xea, 0x16, 0x90, 0xf9, 0x0e, 0x9e, 0x8f, 0x66, 0x0c, 0xbf,
> +  0x29, 0xd3, 0x8f, 0xfc, 0x4d, 0x38, 0x68, 0xe2, 0xe7, 0x64, 0x32, 0x47,
> +  0xdd, 0x56, 0xc9, 0xe4, 0x47, 0x9f, 0x18, 0x89, 0xfc, 0x30, 0x7a, 0xae,
> +  0x63, 0xe4, 0xec, 0x93, 0x04, 0xd4, 0x61, 0xe7, 0xbf, 0x0a, 0x06, 0x29,
> +  0xc2, 0xa6, 0xd5, 0x53, 0x5d, 0x65, 0x6d, 0x4a, 0xd0, 0xb7, 0x68, 0x4d,
> +  0x46, 0x0a, 0xb5, 0xff, 0x52, 0x5e, 0x92, 0x7e, 0x75, 0x08, 0xa4, 0x63,
> +  0x0a, 0x6c, 0x31, 0x7a, 0xaa, 0x0c, 0x52, 0xf4, 0x2e, 0xcd, 0x08, 0xeb,
> +  0xb3, 0xbd, 0xad, 0x8b, 0x8b, 0x9b, 0x8d, 0x71, 0x42, 0x30, 0x8e, 0xc7,
> +  0xfd, 0xec, 0xb7, 0xe6, 0x26, 0x96, 0xf2, 0x74, 0x1b, 0x78, 0x95, 0x22,
> +  0x14, 0xf3, 0xc9, 0xd3, 0x79, 0x11, 0xd9, 0xb7, 0x4d, 0x0d, 0x61, 0x60,
> +  0x5c, 0x47, 0x50, 0xf3, 0xca, 0x84, 0x4c, 0x5c, 0x30, 0x2c, 0x6a, 0x18,
> +  0x26, 0xb0, 0xf3, 0xd1, 0x15, 0x19, 0x39, 0xc3, 0x23, 0x13, 0x0f, 0x9c,
> +  0x97, 0x2b, 0x97, 0x93, 0xf9, 0xf8, 0x18, 0x9b, 0x4a, 0x4d, 0xd6, 0xd3,
> +  0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c,
> +  0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00,
> +  0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07,
> +  0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14,
> +  0x8f, 0xba, 0x8b, 0xf5, 0xf4, 0x77, 0xb2, 0xa4, 0x19, 0xef, 0x43, 0xb1,
> +  0x8b, 0x03, 0x4b, 0x45, 0x47, 0xb5, 0x2a, 0x48, 0x30, 0x1f, 0x06, 0x03,
> +  0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x59, 0x1c, 0xb5,
> +  0x52, 0x62, 0x83, 0x05, 0x3b, 0x41, 0x4c, 0x63, 0x4d, 0x5b, 0xf4, 0x8c,
> +  0xe6, 0xd7, 0xda, 0x87, 0x54, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
> +  0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01,
> +  0x00, 0x36, 0x2d, 0x0a, 0xcb, 0x49, 0x54, 0x75, 0xd7, 0xca, 0x21, 0x86,
> +  0xae, 0x40, 0x0f, 0x63, 0x10, 0x35, 0xfd, 0xbc, 0xba, 0x28, 0x31, 0x33,
> +  0x07, 0x08, 0x64, 0x03, 0x6c, 0xd3, 0xd5, 0xf7, 0xb7, 0x79, 0x11, 0x0c,
> +  0xa8, 0x9e, 0xfd, 0x34, 0xa2, 0xba, 0x77, 0x15, 0x15, 0x2d, 0x2c, 0x96,
> +  0xae, 0x47, 0xbb, 0x82, 0x89, 0x09, 0x7f, 0xd1, 0x95, 0x69, 0x9b, 0xfe,
> +  0xd7, 0x6f, 0x4e, 0x68, 0xf6, 0xe7, 0x5f, 0x54, 0xa1, 0x3a, 0xeb, 0xa4,
> +  0xbf, 0x7a, 0xb6, 0x7f, 0xaa, 0xd8, 0xd7, 0x99, 0xcb, 0xae, 0x88, 0x6d,
> +  0x7a, 0xf3, 0xfa, 0x9e, 0x44, 0x2f, 0x30, 0xa8, 0xe6, 0xb9, 0x75, 0xa0,
> +  0x82, 0xd6, 0xb0, 0xe3, 0x03, 0xb3, 0x12, 0xa3, 0xdc, 0xb9, 0x4d, 0x93,
> +  0xd4, 0x30, 0xea, 0xce, 0x96, 0x92, 0x07, 0xf8, 0xba, 0xe4, 0x0f, 0x41,
> +  0xe3, 0x04, 0xaa, 0x8c, 0x07, 0x1a, 0x34, 0x60, 0xfc, 0xc0, 0x05, 0xd2,
> +  0x5a, 0xa8, 0x66, 0xef, 0xe0, 0x94, 0xc5, 0x2f, 0x0f, 0xff, 0xdc, 0x70,
> +  0xfb, 0xe2, 0x9d, 0x61, 0x51, 0x25, 0x02, 0xff, 0x4b, 0x69, 0xfd, 0x66,
> +  0xb9, 0xeb, 0x0c, 0xc8, 0x50, 0xd3, 0xb1, 0x08, 0x1e, 0x09, 0x54, 0x87,
> +  0xe8, 0xa3, 0x4b, 0xef, 0x0c, 0x32, 0x0a, 0x6c, 0xec, 0x27, 0x22, 0xba,
> +  0x7f, 0xdc, 0x52, 0x27, 0x31, 0x14, 0x9a, 0xa8, 0xf7, 0xf9, 0xeb, 0xc8,
> +  0xb5, 0x8d, 0x12, 0xed, 0x94, 0xab, 0x3d, 0x9a, 0xfb, 0x4e, 0x04, 0x05,
> +  0xd2, 0x3c, 0x7c, 0x8a, 0xed, 0x46, 0x1b, 0x7c, 0xb5, 0x6c, 0x40, 0xb8,
> +  0xc1, 0xbf, 0xb0, 0xd2, 0x93, 0x8e, 0xa8, 0x0f, 0xde, 0x78, 0xf3, 0x8c,
> +  0xd8, 0x9f, 0xf8, 0xdc, 0xa1, 0x23, 0x20, 0x40, 0x17, 0xb4, 0xdb, 0xb7,
> +  0x09, 0x74, 0xa7, 0x80, 0xc2, 0x12, 0xd9, 0x76, 0x79, 0x5b, 0x71, 0xa9,
> +  0x6c, 0xd4, 0x57, 0x48, 0xe8, 0xfe, 0xc5, 0xc2, 0x6e, 0xe7, 0x83, 0x5a,
> +  0x07, 0xf0, 0x33, 0xc1, 0xc1, 0x1d, 0x34, 0xd4, 0xc8, 0xb0, 0xb7, 0xdb,
> +  0xeb, 0xe9, 0xe3, 0x59, 0xdc, 0x7f, 0x36, 0x58, 0xa9, 0xb8, 0x52, 0xdd,
> +  0xf9, 0xfd, 0x1c, 0x22, 0x2f, 0x93, 0x3d, 0x53, 0x89, 0x80, 0xde, 0xa2,
> +  0xb5, 0xa5, 0x36, 0xbd, 0xc3, 0x92, 0x03, 0xf3, 0x93, 0xc8, 0xc7, 0x4a,
> +  0x0b, 0x8b, 0x62, 0xfe, 0xd0, 0xf8, 0x0d, 0x7a, 0x32, 0xb4, 0x39, 0x1a,
> +  0xb7, 0x4e, 0xaa, 0xc4, 0x33, 0x32, 0x90, 0x8c, 0xab, 0xd4, 0xae, 0xa5,
> +  0xa4, 0x85, 0xcf, 0xba, 0xe1, 0x1b, 0x26, 0x7f, 0x74, 0x02, 0x12, 0x09,
> +  0x89, 0x56, 0xe4, 0xe7, 0x9d, 0x91, 0xde, 0x88, 0xe7, 0x1c, 0xed, 0x80,
> +  0x05, 0xa8, 0x58, 0x9a, 0x3e, 0x16, 0x97, 0xd5, 0xbc, 0x54, 0xcc, 0xf0,
> +  0x32, 0xf2, 0x93, 0x09, 0x94, 0x9f, 0x3c, 0xd9, 0x58, 0xca, 0x68, 0x0b,
> +  0xde, 0x3f, 0x73, 0x64, 0xb7, 0xf4, 0xd7, 0x5f, 0x2b, 0xe7, 0x7b, 0x06,
> +  0xca, 0xb1, 0x3e, 0xed, 0xd2, 0xb9, 0x29, 0xc1, 0x95, 0x87, 0xad, 0xd6,
> +  0x63, 0x69, 0xb8, 0x1f, 0x70, 0xdb, 0xeb, 0xc7, 0x11, 0x7d, 0xe2, 0x99,
> +  0x64, 0x6a, 0xf5, 0x3f, 0x30, 0x74, 0x5f, 0x2a, 0x21, 0xda, 0xef, 0x44,
> +  0x1d, 0xad, 0x97, 0xa1, 0xfe, 0x14, 0xa7, 0x88, 0x99, 0xd0, 0x1e, 0xb0,
> +  0x61, 0x88, 0x09, 0xc9, 0xfa, 0xd1, 0xb3, 0xcb, 0x1d, 0x76, 0x04, 0xbe,
> +  0x06, 0x44, 0xd2, 0x30, 0x5e, 0x95, 0x4b, 0x96, 0xc0, 0xd6, 0xbe, 0xd0,
> +  0x4d, 0xf2, 0xf4, 0x71, 0x72, 0xa9, 0xbd, 0x07, 0x4f, 0xbc, 0xb3, 0x78,
> +  0xb4, 0x8a, 0x44, 0xbd, 0x58, 0xd5, 0x21, 0xb6, 0x47, 0x9c, 0x88, 0x1f,
> +  0xbc, 0xbd, 0x54, 0xfa, 0x1d, 0x49, 0xec, 0x51, 0xd9, 0x43, 0x49, 0x9c,
> +  0x0c, 0xfa, 0x18, 0xdb, 0xeb, 0x05, 0x77, 0xa2, 0x9a
> +};
> +unsigned int certificate_der_len = 1377;
> +
> +unsigned char hi_signed[] = {
> +  0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
> +  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
> +  0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
> +  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
> +  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
> +  0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
> +  0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
> +  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
> +  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
> +  0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
> +  0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
> +  0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
> +  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
> +  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
> +  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
> +  0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77,
> +  0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03,
> +  0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e,
> +  0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52,
> +  0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01,
> +  0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75,
> +  0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1,
> +  0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d,
> +  0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e,
> +  0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f,
> +  0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae,
> +  0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5,
> +  0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a,
> +  0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33,
> +  0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2,
> +  0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9,
> +  0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70,
> +  0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67,
> +  0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a,
> +  0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3,
> +  0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33,
> +  0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4,
> +  0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1,
> +  0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26,
> +  0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41,
> +  0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda,
> +  0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3,
> +  0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98,
> +  0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5,
> +  0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e,
> +  0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe,
> +  0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c,
> +  0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6,
> +  0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5,
> +  0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f,
> +  0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb,
> +  0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0,
> +  0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4,
> +  0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b,
> +  0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52,
> +  0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f,
> +  0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c,
> +  0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17,
> +  0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
> +  0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
> +  0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
> +};
> +unsigned int hi_signed_len = 739;
> +
> +unsigned char hj_signed[] = {
> +  0x68, 0x6a, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
> +  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
> +  0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
> +  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
> +  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
> +  0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
> +  0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
> +  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
> +  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
> +  0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
> +  0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
> +  0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
> +  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
> +  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
> +  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
> +  0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77,
> +  0x97, 0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03,
> +  0xdf, 0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e,
> +  0x77, 0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52,
> +  0x55, 0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01,
> +  0xc2, 0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75,
> +  0x88, 0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1,
> +  0xd1, 0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d,
> +  0xe3, 0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e,
> +  0x45, 0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f,
> +  0x86, 0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae,
> +  0xcd, 0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5,
> +  0x27, 0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a,
> +  0xf1, 0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33,
> +  0x1a, 0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2,
> +  0x8d, 0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9,
> +  0xc5, 0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70,
> +  0xa1, 0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67,
> +  0x04, 0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a,
> +  0x9a, 0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3,
> +  0x1c, 0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33,
> +  0xd6, 0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4,
> +  0xe9, 0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1,
> +  0x60, 0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26,
> +  0x83, 0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41,
> +  0xd7, 0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda,
> +  0x35, 0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3,
> +  0x0f, 0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98,
> +  0xa0, 0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5,
> +  0xaa, 0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e,
> +  0xe2, 0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe,
> +  0xc3, 0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c,
> +  0x24, 0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6,
> +  0xe6, 0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5,
> +  0x5e, 0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f,
> +  0x74, 0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb,
> +  0x3b, 0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0,
> +  0x8a, 0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4,
> +  0xdc, 0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b,
> +  0x83, 0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52,
> +  0x21, 0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f,
> +  0x51, 0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c,
> +  0x19, 0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17,
> +  0x35, 0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
> +  0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
> +  0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
> +};
> +unsigned int hj_signed_len = 739;
> +
> +unsigned char hi_signed_sha256[] = {
> +  0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb4, 0x06, 0x09, 0x2a, 0x86, 0x48,
> +  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa5, 0x30, 0x82,
> +  0x02, 0xa1, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
> +  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09,
> +  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
> +  0x7e, 0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d,
> +  0x31, 0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47,
> +  0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64,
> +  0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54,
> +  0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
> +  0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74,
> +  0x79, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
> +  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30,
> +  0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
> +  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
> +  0x01, 0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0x96, 0x02, 0x7b, 0xa4, 0x07,
> +  0xa7, 0x39, 0x8d, 0xa6, 0x0b, 0xde, 0x33, 0xdd, 0xf8, 0xec, 0x24, 0x5d,
> +  0x06, 0x81, 0xe7, 0x3c, 0x2d, 0x0c, 0x53, 0xfb, 0x7e, 0x5a, 0xf3, 0xee,
> +  0xe5, 0x4c, 0x7c, 0xf7, 0xe7, 0x8f, 0x36, 0x62, 0x35, 0xb8, 0x99, 0xc3,
> +  0xeb, 0x85, 0x1d, 0x2e, 0x40, 0x6e, 0x2a, 0xb4, 0x3a, 0x76, 0x48, 0x4f,
> +  0x8b, 0x29, 0xd4, 0x9e, 0x5c, 0xd2, 0x41, 0x4d, 0xc1, 0x72, 0x0f, 0x97,
> +  0xe0, 0x7d, 0x88, 0xed, 0x1a, 0xb0, 0xde, 0x1b, 0x21, 0xa6, 0x0c, 0x19,
> +  0xd8, 0xb0, 0x12, 0x54, 0x7b, 0xd8, 0x19, 0x03, 0xbd, 0x77, 0x83, 0x23,
> +  0xeb, 0xeb, 0x68, 0x0a, 0x7b, 0x3a, 0x4d, 0x25, 0x44, 0xe1, 0x64, 0x8d,
> +  0x43, 0x5a, 0x1c, 0x9f, 0x74, 0x79, 0x31, 0x3f, 0xc7, 0x8e, 0xae, 0xe1,
> +  0xf9, 0x1e, 0x54, 0x12, 0x36, 0x85, 0xf2, 0x55, 0xba, 0x42, 0x60, 0x64,
> +  0x25, 0x9f, 0x73, 0x62, 0x42, 0xd2, 0x1c, 0x5e, 0x39, 0x4f, 0x7d, 0x91,
> +  0xb8, 0xf9, 0x59, 0x3c, 0x13, 0x6b, 0x84, 0x76, 0x6d, 0x8a, 0xc3, 0xcb,
> +  0x2d, 0x14, 0x27, 0x16, 0xdc, 0x20, 0x2c, 0xbc, 0x6b, 0xc9, 0xda, 0x9f,
> +  0xef, 0xe2, 0x2d, 0xc3, 0x83, 0xd8, 0xf9, 0x94, 0x18, 0xbc, 0xfe, 0x8f,
> +  0xa9, 0x44, 0xad, 0xff, 0x1b, 0xcb, 0x86, 0x30, 0x96, 0xa8, 0x3c, 0x7a,
> +  0x4b, 0x73, 0x1b, 0xa9, 0xc3, 0x3b, 0xaa, 0xd7, 0x44, 0xa8, 0x4d, 0xd6,
> +  0x92, 0xb6, 0x00, 0x04, 0x09, 0x05, 0x4a, 0x95, 0x02, 0x90, 0x19, 0x8c,
> +  0x9a, 0xa5, 0xee, 0x58, 0x24, 0xb0, 0xca, 0x5e, 0x6f, 0x73, 0xdb, 0xf5,
> +  0xa1, 0xf4, 0xf0, 0xa9, 0xeb, 0xe4, 0xdc, 0x55, 0x9f, 0x8f, 0x7a, 0xd0,
> +  0xf7, 0xb6, 0xaa, 0xa6, 0xb5, 0xb4, 0xab, 0xb8, 0x65, 0xad, 0x12, 0x32,
> +  0x1c, 0xe6, 0x99, 0x71, 0x93, 0xe8, 0xb4, 0x1e, 0x21, 0x27, 0x52, 0xea,
> +  0x8c, 0xc8, 0x79, 0x96, 0x2e, 0x48, 0x60, 0x57, 0x1c, 0x7d, 0x8c, 0x0d,
> +  0x07, 0xa7, 0x12, 0x83, 0x0a, 0x76, 0x6a, 0x64, 0xed, 0xbe, 0x8d, 0xaf,
> +  0xdf, 0x51, 0x05, 0xdd, 0xf2, 0xd3, 0xb8, 0x93, 0xa9, 0x13, 0xa5, 0x96,
> +  0xe8, 0xfa, 0x82, 0x02, 0x18, 0x71, 0x7a, 0x71, 0xbb, 0x39, 0x6f, 0x85,
> +  0xee, 0x16, 0x82, 0x27, 0x42, 0x9f, 0x83, 0xc8, 0xab, 0x6a, 0x3b, 0x99,
> +  0xba, 0x38, 0x92, 0x38, 0xae, 0x59, 0xfa, 0xaa, 0x40, 0x2b, 0x52, 0x95,
> +  0xca, 0x5e, 0xe1, 0x9b, 0x00, 0xbd, 0xb9, 0x63, 0x25, 0x8d, 0xc7, 0x22,
> +  0xaf, 0xe5, 0x67, 0x76, 0x91, 0xf4, 0xda, 0xc9, 0x7e, 0x9e, 0xec, 0x9b,
> +  0x1f, 0x7d, 0x3b, 0xfe, 0xa1, 0x20, 0x52, 0xac, 0xd0, 0xe5, 0xa6, 0xf1,
> +  0xfd, 0x4c, 0x08, 0x59, 0x7d, 0x50, 0xbb, 0x0c, 0xcf, 0xd8, 0xb6, 0x0f,
> +  0xc7, 0x19, 0xcb, 0x7a, 0x96, 0x6f, 0x0f, 0x6c, 0x71, 0x56, 0x72, 0xd1,
> +  0x06, 0x29, 0x0f, 0x08, 0xa2, 0x46, 0x3e, 0x58, 0x42, 0xc4, 0x8c, 0xe0,
> +  0x6e, 0xe9, 0x37, 0xd5, 0x2f, 0x74, 0x36, 0x1d, 0x14, 0xcb, 0x10, 0x0e,
> +  0x7d, 0x67, 0xbd, 0x38, 0x0e, 0xa4, 0x27, 0x1d, 0x3c, 0x78, 0x4d, 0x0d,
> +  0x15, 0x42, 0x70, 0x20, 0xe0, 0x1d, 0x83, 0x6c, 0x4d, 0xf1, 0x02, 0xa1,
> +  0x51, 0xc4, 0xc5, 0x5d, 0x69, 0x90, 0x58, 0x82, 0x94, 0x50, 0x36, 0x22,
> +  0xb3, 0xa4, 0x15, 0x77, 0xdc, 0x44, 0xb0, 0x50, 0xa2, 0x3f, 0xd0, 0x0e,
> +  0x1b, 0xfc, 0xf4, 0x5b, 0x3b, 0x7d, 0x63, 0x94, 0x22, 0xf3, 0x87, 0x0a,
> +  0x41, 0x8a, 0x27, 0x48, 0xcb, 0x6c, 0xfd, 0x70, 0x66, 0x5f, 0x11, 0x6f,
> +  0x74, 0x2c, 0x42, 0xaf, 0x74, 0x45, 0x3f, 0x0c, 0x03, 0xc8, 0x80, 0xe2,
> +  0x71, 0x08, 0x93, 0xbd, 0x4d, 0x18, 0x78, 0x1e, 0x8e, 0xb9, 0x3a, 0xd6,
> +  0x1a, 0xde, 0xf9, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x02, 0xb8, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
> +  0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
> +  0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
> +};
> +unsigned int hi_signed_sha256_len = 739;
> +
> +unsigned char short_msg[] = {
> +  0x68, 0x69, 0x0a
> +};
> +unsigned int short_msg_len = 3;
> +
> +unsigned char unsigned_msg[] = {
> +  0x53, 0x65, 0x64, 0x20, 0x75, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x70,
> +  0x69, 0x63, 0x69, 0x61, 0x74, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65,
> +  0x20, 0x6f, 0x6d, 0x6e, 0x69, 0x73, 0x20, 0x69, 0x73, 0x74, 0x65, 0x20,
> +  0x6e, 0x61, 0x74, 0x75, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20,
> +  0x73, 0x69, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
> +  0x65, 0x6d, 0x20, 0x61, 0x63, 0x63, 0x75, 0x73, 0x61, 0x6e, 0x74, 0x69,
> +  0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x71, 0x75,
> +  0x65, 0x20, 0x6c, 0x61, 0x75, 0x64, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d,
> +  0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6d, 0x20, 0x72, 0x65, 0x6d, 0x20,
> +  0x61, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x65, 0x61, 0x71,
> +  0x75, 0x65, 0x20, 0x69, 0x70, 0x73, 0x61, 0x20, 0x71, 0x75, 0x61, 0x65,
> +  0x20, 0x61, 0x62, 0x20, 0x69, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6e, 0x76,
> +  0x65, 0x6e, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x74,
> +  0x61, 0x74, 0x69, 0x73, 0x20, 0x65, 0x74, 0x20, 0x71, 0x75, 0x61, 0x73,
> +  0x69, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x6f,
> +  0x20, 0x62, 0x65, 0x61, 0x74, 0x61, 0x65, 0x20, 0x76, 0x69, 0x74, 0x61,
> +  0x65, 0x20, 0x64, 0x69, 0x63, 0x74, 0x61, 0x20, 0x73, 0x75, 0x6e, 0x74,
> +  0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6f, 0x2e, 0x20,
> +  0x4e, 0x65, 0x6d, 0x6f, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x70,
> +  0x73, 0x61, 0x6d, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
> +  0x65, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75,
> +  0x70, 0x74, 0x61, 0x73, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x73, 0x70,
> +  0x65, 0x72, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x20, 0x61, 0x75, 0x74, 0x20,
> +  0x6f, 0x64, 0x69, 0x74, 0x20, 0x61, 0x75, 0x74, 0x20, 0x66, 0x75, 0x67,
> +  0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x71, 0x75, 0x69, 0x61,
> +  0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x75, 0x6e, 0x74, 0x75,
> +  0x72, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f,
> +  0x72, 0x65, 0x73, 0x20, 0x65, 0x6f, 0x73, 0x20, 0x71, 0x75, 0x69, 0x20,
> +  0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x20, 0x76, 0x6f, 0x6c, 0x75,
> +  0x70, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x65, 0x71, 0x75, 0x69,
> +  0x20, 0x6e, 0x65, 0x73, 0x63, 0x69, 0x75, 0x6e, 0x74, 0x2e, 0x20, 0x4e,
> +  0x65, 0x71, 0x75, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x72, 0x6f, 0x20, 0x71,
> +  0x75, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x73, 0x74, 0x2c,
> +  0x20, 0x71, 0x75, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d,
> +  0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20,
> +  0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d,
> +  0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65,
> +  0x74, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63,
> +  0x69, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64,
> +  0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75,
> +  0x6d, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x69, 0x75, 0x73, 0x20, 0x6d,
> +  0x6f, 0x64, 0x69, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x20,
> +  0x69, 0x6e, 0x63, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20,
> +  0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x74, 0x20, 0x64, 0x6f,
> +  0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x6d, 0x20,
> +  0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x65,
> +  0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74,
> +  0x65, 0x6d, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20,
> +  0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x20, 0x76, 0x65,
> +  0x6e, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e,
> +  0x6f, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63,
> +  0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x6d, 0x20, 0x75, 0x6c,
> +  0x6c, 0x61, 0x6d, 0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x69, 0x73,
> +  0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x6c, 0x61,
> +  0x62, 0x6f, 0x72, 0x69, 0x6f, 0x73, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x69,
> +  0x73, 0x69, 0x20, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69,
> +  0x64, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d,
> +  0x6f, 0x64, 0x69, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61,
> +  0x74, 0x75, 0x72, 0x3f, 0x20, 0x51, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75,
> +  0x74, 0x65, 0x6d, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x65, 0x75, 0x6d, 0x20,
> +  0x69, 0x75, 0x72, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65,
> +  0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69, 0x20, 0x69,
> +  0x6e, 0x20, 0x65, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61,
> +  0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x20, 0x65, 0x73, 0x73,
> +  0x65, 0x20, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x6e, 0x69, 0x68, 0x69, 0x6c,
> +  0x20, 0x6d, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x61, 0x65, 0x20, 0x63,
> +  0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x75, 0x72, 0x2c, 0x20,
> +  0x76, 0x65, 0x6c, 0x20, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x71, 0x75,
> +  0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x65, 0x75,
> +  0x6d, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x71, 0x75, 0x6f,
> +  0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x73, 0x20, 0x6e, 0x75,
> +  0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72,
> +  0x3f, 0x0a
> +};
> +unsigned int unsigned_msg_len = 866;
> +
> +unsigned char certificate2_der[] = {
> +  0x30, 0x82, 0x05, 0x52, 0x30, 0x82, 0x03, 0x3a, 0xa0, 0x03, 0x02, 0x01,
> +  0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5,
> +  0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30,
> +  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
> +  0x05, 0x00, 0x30, 0x3a, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04,
> +  0x03, 0x0c, 0x2f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20,
> +  0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20,
> +  0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
> +  0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
> +  0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38,
> +  0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32,
> +  0x30, 0x30, 0x37, 0x30, 0x34, 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a,
> +  0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
> +  0x20, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65,
> +  0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69,
> +  0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02,
> +  0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
> +  0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02,
> +  0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xb0, 0x2f, 0x50, 0x01, 0x9c, 0x0e,
> +  0xd6, 0x8c, 0x07, 0xca, 0xc1, 0xcf, 0xbc, 0x03, 0xdd, 0xd3, 0xfa, 0xe3,
> +  0x4f, 0x71, 0xc1, 0x30, 0xaa, 0x09, 0x96, 0xe4, 0xd0, 0x6c, 0x42, 0x93,
> +  0xdb, 0x35, 0xf6, 0x7e, 0x1b, 0x67, 0xc0, 0xc2, 0x2d, 0x5b, 0xec, 0xca,
> +  0x35, 0x06, 0x32, 0x6c, 0x7b, 0x2c, 0xd3, 0x71, 0x2b, 0xe9, 0x7a, 0x19,
> +  0xd1, 0xf2, 0xa0, 0x7f, 0xd7, 0x4d, 0x6e, 0x28, 0xbb, 0xae, 0x49, 0x4a,
> +  0xbc, 0xea, 0x47, 0x67, 0xb8, 0x36, 0xa6, 0xf5, 0x0d, 0x0e, 0x20, 0x14,
> +  0x0c, 0x66, 0x67, 0x28, 0xb5, 0x97, 0x8b, 0x1f, 0x5e, 0x32, 0x06, 0x29,
> +  0x9c, 0x99, 0x92, 0x0f, 0x73, 0xac, 0xfd, 0xd2, 0x1d, 0xf2, 0xa8, 0x55,
> +  0x9d, 0x1b, 0xd8, 0x3d, 0xb0, 0x76, 0x9a, 0xb6, 0x6c, 0x9f, 0x62, 0x37,
> +  0x2f, 0xc0, 0xef, 0x44, 0xb3, 0x0d, 0x4a, 0x3e, 0x4f, 0x7d, 0xbd, 0xdb,
> +  0xd8, 0x75, 0x5f, 0x68, 0xe3, 0xf0, 0xec, 0x82, 0x66, 0x7c, 0x31, 0x70,
> +  0xa9, 0xa1, 0x6f, 0x38, 0x9f, 0xdf, 0xf5, 0xf0, 0x7d, 0x23, 0x9d, 0x34,
> +  0xa5, 0x85, 0xd3, 0xdf, 0x68, 0x41, 0xfc, 0x4f, 0x89, 0x45, 0x3c, 0x24,
> +  0x81, 0xa6, 0xf2, 0x3c, 0x02, 0x26, 0x09, 0x48, 0xdd, 0xfe, 0x4b, 0xb6,
> +  0x66, 0xbf, 0x8f, 0xe5, 0x5f, 0xf0, 0x5d, 0x8a, 0x61, 0x2e, 0x5f, 0x9f,
> +  0x80, 0xd9, 0xd5, 0xe6, 0x41, 0xd8, 0x10, 0x5e, 0x7a, 0xc6, 0xdb, 0x89,
> +  0xc7, 0xca, 0x6c, 0x5b, 0xb1, 0x4e, 0x7d, 0x0c, 0x03, 0xfd, 0x50, 0xca,
> +  0xbf, 0xbb, 0xe2, 0x69, 0x4b, 0x4e, 0xc2, 0x3d, 0x75, 0xfa, 0xd1, 0xcc,
> +  0xd6, 0xf9, 0x39, 0xb9, 0xdc, 0x53, 0xad, 0x62, 0xfb, 0x1b, 0x94, 0x26,
> +  0x7f, 0x21, 0x54, 0x5c, 0xb7, 0xdc, 0xe7, 0x96, 0x8c, 0xce, 0x75, 0xe0,
> +  0x17, 0x01, 0x3a, 0x3c, 0x77, 0x6e, 0xa4, 0x8b, 0x7a, 0x83, 0x28, 0x7a,
> +  0xf7, 0xb0, 0x5f, 0xfc, 0x7f, 0x2d, 0x2e, 0xec, 0xf5, 0xeb, 0x9c, 0x63,
> +  0x74, 0xd0, 0xe5, 0xdc, 0x19, 0xe4, 0x71, 0xc5, 0x4a, 0x8a, 0x54, 0xa4,
> +  0xe0, 0x7d, 0x4e, 0xbf, 0x53, 0x30, 0xaf, 0xd0, 0xeb, 0x96, 0xc3, 0xbb,
> +  0x65, 0xf7, 0x67, 0xf5, 0xae, 0xd3, 0x96, 0xf2, 0x63, 0xc8, 0x69, 0xf7,
> +  0x47, 0xcb, 0x27, 0x79, 0xe1, 0xff, 0x2f, 0x68, 0xdf, 0x1e, 0xb3, 0xb8,
> +  0x0c, 0xc5, 0x58, 0x73, 0xcc, 0xfe, 0x8c, 0xda, 0x4e, 0x3b, 0x01, 0x04,
> +  0xcd, 0xcb, 0xb8, 0x3e, 0x06, 0xfd, 0x4c, 0x0a, 0x9f, 0x5e, 0x76, 0x8c,
> +  0x0c, 0x83, 0x75, 0x09, 0x08, 0xb2, 0xdb, 0xf4, 0x49, 0x4e, 0xa0, 0xf2,
> +  0x0c, 0x7b, 0x87, 0x38, 0x9e, 0x22, 0x67, 0xbd, 0xd1, 0x97, 0x57, 0x24,
> +  0xf1, 0x46, 0x07, 0xf9, 0xd2, 0x1b, 0xec, 0x25, 0x5e, 0x67, 0xd9, 0x66,
> +  0x23, 0x1b, 0xd3, 0xe4, 0xaa, 0xec, 0x88, 0xf0, 0x7e, 0x15, 0x83, 0x51,
> +  0x31, 0x67, 0x51, 0x76, 0x5f, 0x55, 0xd7, 0x36, 0xdf, 0x4a, 0x84, 0x0b,
> +  0x6f, 0x5c, 0xbb, 0x5b, 0x8f, 0x37, 0x23, 0x7f, 0xf8, 0x17, 0x84, 0xa2,
> +  0x70, 0x20, 0x07, 0x0c, 0x90, 0x3a, 0x04, 0xfd, 0xf0, 0x08, 0x4a, 0xb1,
> +  0x16, 0x0f, 0xe6, 0xf6, 0x40, 0x51, 0x83, 0xd2, 0x87, 0x40, 0x9c, 0x1c,
> +  0x9f, 0x13, 0x38, 0x17, 0xd3, 0x34, 0x58, 0xad, 0x05, 0x71, 0xa0, 0x73,
> +  0xca, 0x40, 0xa6, 0xa4, 0x81, 0x02, 0xee, 0xa8, 0x72, 0x41, 0xa1, 0x41,
> +  0x18, 0x64, 0x8a, 0x86, 0x8a, 0x5d, 0xe6, 0x4f, 0x0a, 0xc5, 0x95, 0x98,
> +  0xf9, 0x78, 0xfe, 0x19, 0x0d, 0xc9, 0xb3, 0x89, 0xc1, 0x2b, 0x09, 0xbe,
> +  0xf1, 0xd2, 0x04, 0x5d, 0xcc, 0x28, 0xf5, 0x4b, 0xd2, 0x20, 0x4f, 0xc5,
> +  0x41, 0x9d, 0x8c, 0x85, 0xd8, 0xb0, 0x68, 0x5e, 0xc1, 0x0c, 0xb7, 0x24,
> +  0x4d, 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30,
> +  0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30,
> +  0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02,
> +  0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
> +  0x14, 0xac, 0xf5, 0x47, 0x17, 0xd9, 0x7d, 0xc1, 0xb1, 0xc4, 0x41, 0xe1,
> +  0x41, 0x60, 0xcb, 0x37, 0x11, 0x60, 0x28, 0x78, 0x5f, 0x30, 0x1f, 0x06,
> +  0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x94,
> +  0xfb, 0xf9, 0xb2, 0x43, 0xe9, 0x33, 0xd7, 0x50, 0x7d, 0xc7, 0x37, 0xdb,
> +  0xd5, 0x82, 0x5a, 0x4e, 0xbe, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86,
> +  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02,
> +  0x01, 0x00, 0x96, 0x70, 0x65, 0x26, 0x42, 0xf8, 0xdc, 0x69, 0xde, 0xcf,
> +  0x41, 0x3a, 0x2e, 0x7f, 0x5b, 0xf1, 0xf9, 0x3b, 0x9b, 0xd2, 0x4e, 0x64,
> +  0x48, 0x81, 0xe4, 0x5d, 0x1e, 0x22, 0xce, 0x68, 0x63, 0x62, 0xe5, 0x1b,
> +  0x9b, 0xf2, 0xc7, 0x12, 0xda, 0x1e, 0x9b, 0x90, 0x84, 0x79, 0x48, 0x12,
> +  0xe6, 0x21, 0x6f, 0x2f, 0x7e, 0x18, 0x77, 0xdb, 0x8c, 0xc4, 0xd1, 0x0d,
> +  0x91, 0xbf, 0x39, 0x22, 0x0f, 0x64, 0xcf, 0x25, 0x2e, 0x8c, 0x1f, 0x91,
> +  0x81, 0xb5, 0xe9, 0x6c, 0x02, 0x3a, 0xf8, 0x07, 0xa2, 0x6f, 0x46, 0x5d,
> +  0x7b, 0xfd, 0x43, 0xff, 0x41, 0x0f, 0xe2, 0x57, 0x1c, 0xbd, 0x48, 0x60,
> +  0x53, 0x11, 0x48, 0x87, 0x88, 0x9d, 0x13, 0x82, 0x40, 0x68, 0x44, 0x2c,
> +  0xc6, 0xc8, 0x95, 0x27, 0x4f, 0xb6, 0xb9, 0x4a, 0x22, 0x0a, 0xfd, 0xe4,
> +  0x46, 0x8f, 0x35, 0x12, 0x98, 0x5a, 0x34, 0x6f, 0x2b, 0x57, 0x62, 0xa1,
> +  0x4d, 0x8d, 0x79, 0x37, 0xe4, 0x6b, 0x8a, 0x32, 0x5b, 0xcb, 0xef, 0x79,
> +  0x11, 0xed, 0xa7, 0xf8, 0x7a, 0x1c, 0xbd, 0x86, 0xdc, 0x0e, 0x2e, 0xfd,
> +  0xd3, 0x51, 0xbb, 0x73, 0xad, 0x00, 0xa0, 0x1b, 0xf9, 0x1d, 0xd1, 0x4a,
> +  0xe4, 0xd4, 0x02, 0x63, 0x2b, 0x39, 0x5f, 0x18, 0x08, 0x2f, 0x42, 0xb7,
> +  0x23, 0x4b, 0x48, 0x46, 0x1f, 0x63, 0x87, 0xae, 0x6d, 0xd5, 0xdb, 0x60,
> +  0xf8, 0x5f, 0xd3, 0x13, 0xec, 0xca, 0xdd, 0x60, 0x60, 0x79, 0x52, 0x70,
> +  0x47, 0xae, 0x1d, 0x38, 0x78, 0x71, 0xcf, 0xb3, 0x04, 0x03, 0xbe, 0xba,
> +  0x81, 0xba, 0x74, 0xb1, 0x30, 0x35, 0xdc, 0xea, 0x21, 0x4a, 0x9b, 0x70,
> +  0xfb, 0xd6, 0x60, 0x59, 0x78, 0x0c, 0x4d, 0x39, 0x19, 0x1d, 0xe5, 0x75,
> +  0xba, 0x07, 0xf4, 0x22, 0x37, 0x64, 0xb7, 0xf2, 0x9a, 0xc9, 0x11, 0x2d,
> +  0x8e, 0x58, 0xa6, 0xcf, 0x83, 0xf1, 0xcb, 0x6c, 0x7f, 0x02, 0xbd, 0xda,
> +  0x03, 0x92, 0xa9, 0x45, 0x24, 0x56, 0xc5, 0xbd, 0x41, 0xd1, 0x20, 0x86,
> +  0xc0, 0xb6, 0xb7, 0xe8, 0xa7, 0xb2, 0x46, 0xf7, 0x8e, 0xa9, 0x38, 0x0e,
> +  0x23, 0x77, 0x3c, 0x0d, 0x66, 0x83, 0x6a, 0x1a, 0x6b, 0x7f, 0x54, 0x11,
> +  0x58, 0x0d, 0x4a, 0xb5, 0x74, 0x60, 0xca, 0xed, 0xff, 0x91, 0x47, 0xd9,
> +  0x29, 0xe0, 0xaa, 0x8c, 0xa8, 0x8f, 0x10, 0x4c, 0x15, 0x7d, 0xce, 0x95,
> +  0xf9, 0x87, 0x1e, 0x18, 0x38, 0x18, 0xfc, 0xcc, 0xaf, 0x91, 0x17, 0x3f,
> +  0xfa, 0xf0, 0x8a, 0x09, 0x6f, 0xba, 0x4e, 0x53, 0xf7, 0xfa, 0x4f, 0x20,
> +  0xa3, 0xf4, 0x4a, 0x5a, 0xde, 0x17, 0x1c, 0x29, 0x6a, 0x6f, 0x03, 0x48,
> +  0xdf, 0xad, 0x4f, 0xe4, 0xbc, 0x71, 0xc4, 0x72, 0x32, 0x11, 0x84, 0xac,
> +  0x09, 0xd2, 0x18, 0x44, 0x35, 0xf1, 0xcd, 0xaf, 0xa8, 0x98, 0xe0, 0x8b,
> +  0xec, 0xa0, 0x83, 0x37, 0xc3, 0x35, 0x85, 0xd6, 0xd8, 0x1b, 0xe0, 0x75,
> +  0xdc, 0xfd, 0xde, 0xc9, 0xeb, 0xd5, 0x18, 0x0f, 0xd3, 0x4c, 0x2f, 0x71,
> +  0xdc, 0x48, 0xe3, 0x14, 0xeb, 0xda, 0x00, 0x24, 0x24, 0x9e, 0xa3, 0x8e,
> +  0x3e, 0x08, 0x6f, 0x22, 0x24, 0xd6, 0xc4, 0x85, 0x8f, 0x68, 0x00, 0x4a,
> +  0x82, 0x4c, 0x33, 0x6e, 0xa5, 0x35, 0x7b, 0xeb, 0x4b, 0xdc, 0xa0, 0xa6,
> +  0x65, 0x6f, 0x5a, 0x7a, 0xdf, 0x8a, 0x01, 0x52, 0xa1, 0x6c, 0xff, 0x59,
> +  0x22, 0x7f, 0xe1, 0x96, 0x1b, 0x19, 0xb8, 0xf9, 0x5d, 0x44, 0x9f, 0x91,
> +  0x03, 0x3c, 0x3d, 0xa1, 0x2a, 0xb6, 0x5a, 0x51, 0xa0, 0xce, 0x4a, 0x88,
> +  0x22, 0x72, 0x9c, 0xdc, 0xc0, 0x47, 0x76, 0x35, 0x84, 0x75, 0x9b, 0x87,
> +  0x5c, 0xd3, 0xcf, 0xe7, 0xdd, 0xa3, 0x57, 0x14, 0xdf, 0x00, 0xfd, 0x19,
> +  0x2a, 0x7d, 0x89, 0x27, 0x1c, 0x78, 0x97, 0x04, 0x58, 0x48
> +};
> +unsigned int certificate2_der_len = 1366;
> +
> +unsigned char hi_signed_2nd[] = {
> +  0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb1, 0x06, 0x09, 0x2a, 0x86, 0x48,
> +  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa2, 0x30, 0x82,
> +  0x02, 0x9e, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
> +  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
> +  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02,
> +  0x7b, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
> +  0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
> +  0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
> +  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
> +  0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
> +  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
> +  0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
> +  0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
> +  0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
> +  0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
> +  0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
> +  0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
> +  0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
> +  0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
> +  0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
> +  0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
> +  0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
> +  0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
> +  0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
> +  0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
> +  0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
> +  0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
> +  0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
> +  0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
> +  0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
> +  0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
> +  0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
> +  0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
> +  0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
> +  0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
> +  0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
> +  0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
> +  0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
> +  0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
> +  0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
> +  0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
> +  0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
> +  0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
> +  0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
> +  0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
> +  0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
> +  0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
> +  0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
> +  0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
> +  0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
> +  0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
> +  0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
> +  0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
> +  0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
> +  0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
> +  0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
> +  0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
> +  0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
> +  0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb5,
> +  0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e,
> +  0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64,
> +  0x65, 0x64, 0x7e, 0x0a
> +};
> +unsigned int hi_signed_2nd_len = 736;
> +
> +unsigned char hi_double[] = {
> +  0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48,
> +  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82,
> +  0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
> +  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
> +  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04,
> +  0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
> +  0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
> +  0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
> +  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
> +  0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
> +  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
> +  0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
> +  0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
> +  0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
> +  0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
> +  0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
> +  0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
> +  0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
> +  0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
> +  0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
> +  0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
> +  0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
> +  0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
> +  0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
> +  0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
> +  0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
> +  0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
> +  0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
> +  0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
> +  0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
> +  0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
> +  0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
> +  0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
> +  0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
> +  0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
> +  0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
> +  0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
> +  0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
> +  0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
> +  0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
> +  0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
> +  0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
> +  0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
> +  0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
> +  0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
> +  0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
> +  0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
> +  0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
> +  0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
> +  0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
> +  0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
> +  0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
> +  0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
> +  0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
> +  0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
> +  0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
> +  0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
> +  0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
> +  0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31,
> +  0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72,
> +  0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20,
> +  0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65,
> +  0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
> +  0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
> +  0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a,
> +  0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b,
> +  0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30,
> +  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
> +  0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97,
> +  0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf,
> +  0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77,
> +  0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55,
> +  0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2,
> +  0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88,
> +  0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1,
> +  0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3,
> +  0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45,
> +  0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86,
> +  0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd,
> +  0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27,
> +  0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1,
> +  0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a,
> +  0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d,
> +  0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5,
> +  0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1,
> +  0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04,
> +  0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a,
> +  0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c,
> +  0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6,
> +  0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9,
> +  0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60,
> +  0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83,
> +  0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7,
> +  0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35,
> +  0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f,
> +  0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0,
> +  0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa,
> +  0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2,
> +  0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3,
> +  0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24,
> +  0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6,
> +  0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e,
> +  0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74,
> +  0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b,
> +  0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a,
> +  0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc,
> +  0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83,
> +  0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21,
> +  0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51,
> +  0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19,
> +  0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35,
> +  0x23, 0x2d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x05, 0x33, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69,
> +  0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65,
> +  0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
> +};
> +unsigned int hi_double_len = 1374;
> +
> +unsigned char hi_double_extended[] = {
> +  0x68, 0x69, 0x0a, 0x30, 0x82, 0x05, 0x2f, 0x06, 0x09, 0x2a, 0x86, 0x48,
> +  0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x05, 0x20, 0x30, 0x82,
> +  0x05, 0x1c, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60,
> +  0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09,
> +  0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x04,
> +  0xf9, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a,
> +  0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47,
> +  0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74,
> +  0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74,
> +  0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
> +  0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14,
> +  0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07,
> +  0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09,
> +  0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06,
> +  0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
> +  0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90,
> +  0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99,
> +  0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07,
> +  0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe,
> +  0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f,
> +  0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f,
> +  0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f,
> +  0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53,
> +  0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2,
> +  0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0,
> +  0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf,
> +  0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05,
> +  0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d,
> +  0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d,
> +  0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81,
> +  0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36,
> +  0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5,
> +  0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc,
> +  0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70,
> +  0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94,
> +  0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3,
> +  0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20,
> +  0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2,
> +  0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac,
> +  0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb,
> +  0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49,
> +  0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0,
> +  0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb,
> +  0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76,
> +  0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5,
> +  0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49,
> +  0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b,
> +  0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25,
> +  0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e,
> +  0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0,
> +  0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98,
> +  0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b,
> +  0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83,
> +  0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e,
> +  0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a,
> +  0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7,
> +  0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26,
> +  0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac,
> +  0x30, 0x82, 0x02, 0x7a, 0x02, 0x01, 0x01, 0x30, 0x55, 0x30, 0x3d, 0x31,
> +  0x3b, 0x30, 0x39, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x32, 0x47, 0x72,
> +  0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20,
> +  0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x65,
> +  0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
> +  0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79,
> +  0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a,
> +  0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x92, 0x03, 0x30, 0x0b,
> +  0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30,
> +  0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
> +  0x05, 0x00, 0x04, 0x82, 0x02, 0x00, 0xa0, 0x83, 0x3f, 0xac, 0x77, 0x97,
> +  0x74, 0x9b, 0x4b, 0x25, 0xa1, 0x64, 0x09, 0x8d, 0x53, 0xa6, 0x03, 0xdf,
> +  0x9a, 0x10, 0x52, 0xe5, 0x3f, 0xd4, 0x72, 0x75, 0x30, 0xd4, 0x6e, 0x77,
> +  0x32, 0x49, 0x84, 0xe2, 0xbe, 0xef, 0xe4, 0xf3, 0xac, 0xb0, 0x52, 0x55,
> +  0xbf, 0xa9, 0x57, 0x12, 0x08, 0x7d, 0xb0, 0x86, 0xc0, 0x9d, 0x01, 0xc2,
> +  0x1a, 0x4a, 0x2e, 0x3d, 0xd5, 0xc8, 0x56, 0xac, 0xd1, 0x83, 0x75, 0x88,
> +  0xd4, 0xcc, 0x9f, 0x0d, 0xcf, 0xd3, 0xa6, 0x91, 0xb6, 0xb6, 0xb1, 0xd1,
> +  0x24, 0x9c, 0xd0, 0x13, 0xe8, 0x6b, 0x15, 0x9c, 0x62, 0x33, 0x8d, 0xe3,
> +  0x67, 0x9b, 0xb1, 0x8a, 0x72, 0x38, 0xf7, 0x48, 0x32, 0x2f, 0x1e, 0x45,
> +  0xa8, 0xc4, 0xa5, 0xae, 0xb1, 0xfc, 0x35, 0x25, 0xb5, 0xf9, 0x7f, 0x86,
> +  0xef, 0xa2, 0x6d, 0x78, 0xcc, 0xfd, 0x0c, 0xca, 0x8a, 0xe1, 0xae, 0xcd,
> +  0x0f, 0x58, 0x69, 0xf1, 0x8b, 0xcc, 0x22, 0x35, 0x6d, 0x1f, 0xd5, 0x27,
> +  0x87, 0x39, 0x62, 0x3f, 0xb2, 0x17, 0x3d, 0x5e, 0xb1, 0x32, 0x7a, 0xf1,
> +  0x70, 0xce, 0xfa, 0xab, 0x1c, 0x92, 0xa8, 0xe1, 0xc4, 0xb2, 0x33, 0x1a,
> +  0x16, 0xf3, 0x60, 0x39, 0xdf, 0xb8, 0x85, 0xe7, 0x5d, 0x4d, 0xc2, 0x8d,
> +  0x55, 0x00, 0x49, 0x94, 0x04, 0x17, 0x88, 0x7c, 0xf4, 0xac, 0xa9, 0xc5,
> +  0x3a, 0x09, 0xc4, 0xc2, 0xcd, 0x3d, 0xc3, 0xfc, 0x4e, 0xf3, 0x70, 0xa1,
> +  0xc1, 0x54, 0x36, 0x1f, 0x38, 0x6d, 0x7a, 0x6b, 0x6a, 0xd3, 0x67, 0x04,
> +  0xd5, 0x53, 0xd4, 0xa5, 0xad, 0x63, 0x55, 0x0e, 0x06, 0x06, 0x3a, 0x9a,
> +  0xc5, 0xfe, 0x38, 0xc9, 0xb0, 0x69, 0x42, 0x90, 0x35, 0x1f, 0xe3, 0x1c,
> +  0x57, 0xea, 0xdb, 0x51, 0x35, 0x53, 0xd3, 0x94, 0xfe, 0x72, 0x33, 0xd6,
> +  0x8a, 0x46, 0x74, 0xf9, 0x6e, 0x94, 0x40, 0x2f, 0xba, 0xa2, 0xc4, 0xe9,
> +  0xc9, 0x8a, 0xf4, 0xda, 0xe2, 0xca, 0x3e, 0x98, 0x85, 0xa5, 0xd1, 0x60,
> +  0x94, 0xc8, 0xdf, 0x82, 0xee, 0x5c, 0x0d, 0x2a, 0xa9, 0x8e, 0x26, 0x83,
> +  0x3f, 0x02, 0xa2, 0xaf, 0xb8, 0x3b, 0x83, 0xf2, 0x44, 0x46, 0x41, 0xd7,
> +  0x5c, 0xa1, 0x42, 0x17, 0xa2, 0xd0, 0x50, 0x42, 0xef, 0x66, 0xda, 0x35,
> +  0x03, 0xd1, 0x8e, 0x77, 0x22, 0x7d, 0x4e, 0xf7, 0x4e, 0x04, 0xe3, 0x0f,
> +  0x98, 0x7d, 0xaa, 0x58, 0xba, 0xef, 0x9a, 0xd0, 0x88, 0x7c, 0x98, 0xa0,
> +  0xc2, 0xff, 0xa6, 0xb1, 0xec, 0xbe, 0x6e, 0xb0, 0x7e, 0xc6, 0xe5, 0xaa,
> +  0xcf, 0x10, 0x73, 0xc9, 0x13, 0x1a, 0x20, 0x12, 0x5c, 0xd2, 0x0e, 0xe2,
> +  0x60, 0x17, 0xdf, 0x4a, 0x44, 0x08, 0x22, 0xbc, 0xcd, 0x75, 0xbe, 0xc3,
> +  0x7a, 0x12, 0x90, 0x90, 0xc7, 0x94, 0x4c, 0x98, 0x45, 0x02, 0x5c, 0x24,
> +  0xae, 0x82, 0x2f, 0xcd, 0x30, 0xa6, 0xf5, 0x3f, 0xd3, 0xa7, 0xa6, 0xe6,
> +  0xea, 0x11, 0x4e, 0x45, 0xb7, 0xc0, 0xe6, 0x24, 0x8b, 0x76, 0xc5, 0x5e,
> +  0xc1, 0xd8, 0x07, 0x1e, 0x26, 0x94, 0x7a, 0x80, 0xc6, 0x3b, 0x1f, 0x74,
> +  0xe6, 0xae, 0x43, 0x2d, 0x11, 0xee, 0x96, 0x56, 0x6c, 0xff, 0xcb, 0x3b,
> +  0xde, 0xcc, 0xb3, 0x7b, 0x08, 0xf5, 0x3e, 0x6e, 0x51, 0x71, 0xe0, 0x8a,
> +  0xfa, 0xdd, 0x19, 0x39, 0xcf, 0x3f, 0x29, 0x4f, 0x2d, 0xd2, 0xd4, 0xdc,
> +  0x5c, 0xc4, 0xd1, 0xa7, 0xf5, 0xbf, 0x4a, 0xc0, 0x9b, 0xb4, 0x2b, 0x83,
> +  0x7a, 0x63, 0x4d, 0x20, 0x40, 0x8b, 0x11, 0x5c, 0x53, 0xd4, 0x52, 0x21,
> +  0xe7, 0xe4, 0x1f, 0x01, 0xf6, 0xd1, 0x25, 0x28, 0xba, 0x51, 0x6f, 0x51,
> +  0x69, 0xf4, 0x41, 0x45, 0x75, 0x23, 0x25, 0x77, 0xef, 0xa8, 0x1c, 0x19,
> +  0x8a, 0x66, 0x8c, 0x61, 0x13, 0x37, 0x4f, 0xa3, 0xa1, 0x83, 0x17, 0x35,
> +  0x23, 0x2d, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +  0x00, 0x05, 0x34, 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73,
> +  0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70,
> +  0x65, 0x6e, 0x64, 0x65, 0x64, 0x7e, 0x0a
> +};
> +unsigned int hi_double_extended_len = 1375;
> +
> +unsigned char certificate_printable_der[] = {
> +  0x30, 0x82, 0x03, 0x39, 0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01,
> +  0x02, 0x02, 0x09, 0x00, 0xde, 0xf6, 0x22, 0xc4, 0xf2, 0xf1, 0x86, 0x02,
> +  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
> +  0x0b, 0x05, 0x00, 0x30, 0x2a, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55,
> +  0x04, 0x03, 0x13, 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
> +  0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
> +  0x43, 0x41, 0x20, 0x32, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30,
> +  0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33, 0x31, 0x31, 0x34, 0x31,
> +  0x39, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x30, 0x32, 0x35,
> +  0x31, 0x34, 0x31, 0x39, 0x32, 0x33, 0x5a, 0x30, 0x2f, 0x31, 0x2d, 0x30,
> +  0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x52, 0x65, 0x64, 0x20,
> +  0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42,
> +  0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20,
> +  0x33, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, 0x82, 0x01, 0x22,
> +  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
> +  0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
> +  0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0xda, 0xa1, 0xed, 0x8d, 0x8e, 0x15,
> +  0x5c, 0xf8, 0x01, 0x77, 0x48, 0x4a, 0x60, 0x96, 0xf9, 0x27, 0xfa, 0xe2,
> +  0xb1, 0x69, 0x0f, 0x51, 0x19, 0x52, 0x7e, 0xc4, 0x34, 0x8e, 0xe1, 0x9b,
> +  0x9c, 0xa4, 0xb1, 0x5c, 0xd6, 0x81, 0x98, 0x78, 0xfe, 0xa9, 0xe5, 0x0b,
> +  0x00, 0xba, 0x9c, 0x64, 0x7e, 0xc7, 0xcc, 0x72, 0xb1, 0x73, 0x4b, 0x11,
> +  0x07, 0x52, 0xf0, 0x20, 0x96, 0x8b, 0x99, 0x39, 0xde, 0xdb, 0xfa, 0x3d,
> +  0x45, 0xe2, 0x98, 0x7b, 0x0c, 0x41, 0xe4, 0x0c, 0xb5, 0x5d, 0x92, 0x74,
> +  0x39, 0x96, 0xe1, 0x97, 0x97, 0xa1, 0xad, 0x2e, 0xcc, 0xd0, 0x1b, 0x4d,
> +  0x9d, 0xbd, 0x3e, 0xa9, 0x36, 0x8e, 0xcc, 0xc7, 0x5f, 0x6a, 0x7d, 0x39,
> +  0x5e, 0x0b, 0x8d, 0xca, 0xe4, 0x83, 0xe9, 0x3b, 0x5c, 0x86, 0x47, 0xd4,
> +  0xba, 0x7d, 0x98, 0x26, 0xa1, 0xf4, 0xe8, 0x90, 0x6b, 0x0f, 0xf1, 0x6b,
> +  0x8c, 0xe3, 0xa2, 0x80, 0x3c, 0x96, 0xf1, 0x0a, 0xb6, 0x66, 0xc0, 0x4b,
> +  0x61, 0xf7, 0x74, 0xcd, 0xd3, 0x7b, 0x8e, 0x5e, 0x39, 0xda, 0x99, 0x20,
> +  0x33, 0x93, 0xd3, 0xf0, 0x7f, 0xad, 0x35, 0xe9, 0x88, 0x8d, 0x9c, 0xbf,
> +  0x65, 0xf1, 0x47, 0x02, 0xf9, 0x7c, 0xed, 0x27, 0x5f, 0x4a, 0x65, 0x3c,
> +  0xcf, 0x5f, 0x0e, 0x88, 0x95, 0x74, 0xde, 0xfb, 0x9e, 0x2e, 0x91, 0x9b,
> +  0x45, 0x37, 0xc8, 0x85, 0xff, 0xe3, 0x41, 0x70, 0xfe, 0xd5, 0xef, 0x0e,
> +  0x82, 0x22, 0x08, 0xb7, 0x3b, 0x44, 0x3e, 0xdc, 0x5b, 0x7f, 0xba, 0xbf,
> +  0xe6, 0x58, 0x9d, 0x02, 0x6e, 0x75, 0xbf, 0x50, 0xec, 0xcf, 0x3f, 0xa5,
> +  0x91, 0x0a, 0xe2, 0x59, 0x2c, 0xc3, 0xe7, 0x05, 0x03, 0xe8, 0xf2, 0x6f,
> +  0x2a, 0x04, 0x68, 0x9a, 0x31, 0x32, 0x8f, 0x04, 0x35, 0xcd, 0x1f, 0x34,
> +  0xcc, 0x4f, 0x79, 0x5a, 0x99, 0x8d, 0x9d, 0x5c, 0xf5, 0x02, 0x03, 0x01,
> +  0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
> +  0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03,
> +  0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06,
> +  0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x65, 0xc5, 0xbe, 0xca,
> +  0xe6, 0x59, 0x6a, 0xfd, 0x6c, 0x71, 0xc4, 0xa7, 0x98, 0xc6, 0x25, 0x8d,
> +  0x7b, 0x67, 0x05, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
> +  0x18, 0x30, 0x16, 0x80, 0x14, 0x81, 0xf8, 0xee, 0x47, 0x5c, 0x3e, 0xed,
> +  0xfb, 0xce, 0xa5, 0x84, 0xbe, 0xd7, 0xae, 0xdb, 0xd3, 0x7d, 0x64, 0xb3,
> +  0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
> +  0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x1e, 0x3d,
> +  0x1d, 0x53, 0x33, 0xde, 0x4e, 0xc7, 0xc4, 0xf4, 0xdf, 0xda, 0x18, 0x19,
> +  0x8a, 0xa9, 0xff, 0xe2, 0x63, 0x2b, 0xbe, 0xf2, 0x61, 0x63, 0xe2, 0xf6,
> +  0xed, 0x47, 0x1a, 0x71, 0x02, 0xec, 0x2a, 0xef, 0x89, 0x77, 0xe3, 0xfd,
> +  0x86, 0x69, 0xf1, 0x3f, 0x0d, 0xf9, 0x6e, 0xf9, 0x3b, 0xad, 0x26, 0x47,
> +  0xb7, 0xf2, 0x0d, 0xad, 0x23, 0xa3, 0x67, 0x3b, 0xcb, 0x6d, 0x9e, 0x03,
> +  0x0f, 0xbc, 0x69, 0x73, 0x9f, 0xd4, 0xa5, 0x0f, 0x6f, 0xf8, 0xab, 0x4d,
> +  0x36, 0xd1, 0xe0, 0xe0, 0x5d, 0x20, 0x43, 0x90, 0xc4, 0x65, 0x61, 0x93,
> +  0xe2, 0x0f, 0x51, 0x59, 0x0a, 0xf7, 0x88, 0x70, 0x57, 0xb9, 0x04, 0xa9,
> +  0x32, 0x57, 0x9c, 0xb3, 0x57, 0x38, 0x8b, 0x8e, 0x46, 0xc8, 0x32, 0x6c,
> +  0xb4, 0xf3, 0x96, 0x7f, 0x4b, 0xf0, 0x88, 0xf9, 0x7f, 0xe2, 0x71, 0xe1,
> +  0x8b, 0xe2, 0x14, 0xf1, 0x4b, 0x25, 0x00, 0x48, 0x1c, 0x7e, 0xe5, 0x8d,
> +  0x65, 0x2d, 0xeb, 0x72, 0x4f, 0x92, 0x44, 0xf3, 0xe6, 0xe0, 0xd0, 0xdf,
> +  0x85, 0xa8, 0x13, 0x4a, 0xfb, 0x99, 0xca, 0x14, 0x2c, 0x97, 0x80, 0x93,
> +  0x27, 0xd3, 0x20, 0xf8, 0x6d, 0x29, 0x28, 0x2c, 0xb9, 0x77, 0xea, 0xb1,
> +  0x63, 0xbd, 0x7d, 0x53, 0xfd, 0x4a, 0x62, 0x64, 0x0b, 0x98, 0xa8, 0xae,
> +  0x11, 0xfc, 0x6e, 0x8d, 0x63, 0xd4, 0x15, 0x55, 0xc6, 0x4c, 0x74, 0xf5,
> +  0x5f, 0xa0, 0xb9, 0x2c, 0x2d, 0x9a, 0x7a, 0x87, 0x6e, 0xf0, 0x5e, 0x25,
> +  0xed, 0xfc, 0xd8, 0xc4, 0x34, 0x33, 0x32, 0xad, 0x01, 0xd4, 0x4b, 0x49,
> +  0x51, 0xc2, 0x07, 0x7f, 0x90, 0x6d, 0xea, 0xf5, 0x4c, 0x41, 0x71, 0x64,
> +  0xeb, 0x1f, 0x29, 0xa3, 0x1f, 0x64, 0xa2, 0x1e, 0x0e, 0x6f, 0xa1, 0x67,
> +  0x99, 0x8d, 0x98, 0x1c, 0xb8, 0x53, 0x9d, 0x30, 0x1d, 0xae, 0x32, 0x56,
> +  0xd2
> +};
> +unsigned int certificate_printable_der_len = 829;
> +
> +unsigned char certificate_eku_der[] = {
> +  0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01,
> +  0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45,
> +  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
> +  0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
> +  0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20,
> +  0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20,
> +  0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86,
> +  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63,
> +  0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74,
> +  0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32,
> +  0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33,
> +  0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a,
> +  0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c,
> +  0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63,
> +  0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67,
> +  0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20,
> +  0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16,
> +  0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65,
> +  0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22,
> +  0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
> +  0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
> +  0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15,
> +  0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85,
> +  0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06,
> +  0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5,
> +  0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76,
> +  0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd,
> +  0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c,
> +  0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9,
> +  0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78,
> +  0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29,
> +  0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb,
> +  0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09,
> +  0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6,
> +  0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27,
> +  0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7,
> +  0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55,
> +  0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68,
> +  0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90,
> +  0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62,
> +  0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16,
> +  0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b,
> +  0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01,
> +  0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
> +  0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03,
> +  0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80,
> +  0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c,
> +  0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03,
> +  0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c,
> +  0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f,
> +  0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55,
> +  0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab,
> +  0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4,
> +  0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
> +  0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
> +  0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9,
> +  0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef,
> +  0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29,
> +  0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b,
> +  0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf,
> +  0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32,
> +  0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9,
> +  0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74,
> +  0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb,
> +  0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0,
> +  0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24,
> +  0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06,
> +  0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57,
> +  0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61,
> +  0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21,
> +  0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1,
> +  0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81,
> +  0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3,
> +  0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e,
> +  0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd,
> +  0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21,
> +  0x89, 0xa0, 0x55, 0xf7
> +};
> +unsigned int certificate_eku_der_len = 916;
> diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c
> index 96781fb39b5f..403fa5c789ab 100644
> --- a/grub-core/tests/lib/functional_test.c
> +++ b/grub-core/tests/lib/functional_test.c
> @@ -73,6 +73,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)),
>     grub_dl_load ("xnu_uuid_test");
>     grub_dl_load ("pbkdf2_test");
>     grub_dl_load ("signature_test");
> +  grub_dl_load ("appended_signature_test");
>     grub_dl_load ("sleep_test");
>     grub_dl_load ("bswap_test");
>     grub_dl_load ("ctz_test");


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 11/22] libtasn1: import libtasn1-4.16.0
  2021-06-30  8:40 ` [PATCH v2 11/22] libtasn1: import libtasn1-4.16.0 Daniel Axtens
@ 2021-07-20 21:46   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-20 21:46 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> Import a very trimmed-down set of libtasn1 files:
>
> pushd /tmp
> wget https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.16.0.tar.gz
> popd
> pushd grub-core/lib
> mkdir libtasn1
> cp /tmp/libtasn1-4.16.0/{README.md,LICENSE} libtasn1/
> mkdir libtasn1/lib
> cp /tmp/libtasn1-4.16.0/lib/{coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h}  libtasn1/lib
> cp /tmp/libtasn1-4.16.0/lib/includes/libtasn1.h ../../include/grub/
> git add libtasn1/ ../../include/grub/libtasn1.h
> popd
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Acked-by: Stefan Berger <stefanb@linux.ibm.com>




^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 12/22] libtasn1: disable code not needed in grub
  2021-06-30  8:40 ` [PATCH v2 12/22] libtasn1: disable code not needed in grub Daniel Axtens
@ 2021-07-20 21:47   ` Stefan Berger
  0 siblings, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-20 21:47 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Axtens; +Cc: rashmica.g, alastair, nayna


On 6/30/21 4:40 AM, Daniel Axtens wrote:
> We don't expect to be able to write ASN.1, only read it,
> so we can disable some code.
>
> Do that with #if 0/#endif, rather than deletion. This means
> that the difference between upstream and grub is smaller,
> which should make updating libtasn1 easier in the future.
>
> With these exclusions we also avoid the need for minmax.h,
> which is convenient because it means we don't have to
> import it from gnulib.
>
> Signed-off-by: Daniel Axtens <dja@axtens.net>

Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>




^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 02/22] ieee1275: claim more memory
  2021-07-16  3:59     ` Patrick Steinhardt
@ 2021-07-21 14:45       ` Daniel Kiper
  2021-07-21 15:24         ` Stefan Berger
  2021-07-22 17:11         ` Stefan Berger
  2021-07-28 11:17       ` Daniel Kiper
  1 sibling, 2 replies; 56+ messages in thread
From: Daniel Kiper @ 2021-07-21 14:45 UTC (permalink / raw)
  To: Patrick Steinhardt
  Cc: Daniel Axtens, grub-devel, rashmica.g, alastair, nayna,
	chris.coulson, hanson.char, hchar, javierm, leif, phcoder,
	pjones, pmenzel, stefanb, xnox

On Fri, Jul 16, 2021 at 05:59:20AM +0200, Patrick Steinhardt wrote:
> On Thu, Jul 15, 2021 at 11:51:04PM +0200, Daniel Kiper wrote:
> > CC-in a few people who can be interested in this...
> >
> > On Wed, Jun 30, 2021 at 06:40:11PM +1000, Daniel Axtens wrote:
> > > On powerpc-ieee1275, we are running out of memory trying to verify
> > > anything. This is because:
> > >
> > >  - we have to load an entire file into memory to verify it. This is
> > >    extremely difficult to change with appended signatures.
> > >  - We only have 32MB of heap.
> > >  - Distro kernels are now often around 30MB.
> > >
> > > So we want to claim more memory from OpenFirmware for our heap.
> >
> > AFAICT it is common problem in the GRUB right now. Please take a look at
> > [1], [2]. It would be nice to find general solution for all. Of course
> > if possible. So, if somebody could take a look at memory management in
> > the GRUB and propose a solution for the problem that would be perfect.
> > Any volunteers?
> >
> > Daniel
> >
> > [1] https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00009.html
> > [2] https://lists.gnu.org/archive/html/grub-devel/2021-01/msg00031.html
>
> I think that my [1] should solve the issue generically. Instead of
> bumping any of the static limits we have in place, we just drop them
> altogether in favor of dynamically requesting additional EFI regions
> whenever we realize that the currently mapped regions cannot satisfy our
> needs. Like this, we can lower the initially requested regions, but
> scale them to the specific needs if need be.
>
> I had planned to revisit this patch series much earlier, but somehow I
> didn't yet find the time. Any comments on my approach would be welcome
> though, and if we agree that this may be a viable route to go down then
> I'd be happy to further pursue it.

Patrick, thanks a lot! I will take a look at the [1] patchset next week.

Daniel A., Stefan, may I ask you to take a look at the [1] too and tell
us how it will work in your cases?

Daniel

[1] https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00009.html


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 02/22] ieee1275: claim more memory
  2021-07-21 14:45       ` Daniel Kiper
@ 2021-07-21 15:24         ` Stefan Berger
  2021-07-22 17:11         ` Stefan Berger
  1 sibling, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-21 15:24 UTC (permalink / raw)
  To: The development of GNU GRUB, Daniel Kiper, Patrick Steinhardt
  Cc: Daniel Axtens, rashmica.g, alastair, nayna, chris.coulson,
	hanson.char, hchar, javierm, leif, phcoder, pjones, pmenzel,
	xnox


On 7/21/21 10:45 AM, Daniel Kiper wrote:
> On Fri, Jul 16, 2021 at 05:59:20AM +0200, Patrick Steinhardt wrote:
>> On Thu, Jul 15, 2021 at 11:51:04PM +0200, Daniel Kiper wrote:
>>> CC-in a few people who can be interested in this...
>>>
>>> On Wed, Jun 30, 2021 at 06:40:11PM +1000, Daniel Axtens wrote:
>>>> On powerpc-ieee1275, we are running out of memory trying to verify
>>>> anything. This is because:
>>>>
>>>>   - we have to load an entire file into memory to verify it. This is
>>>>     extremely difficult to change with appended signatures.
>>>>   - We only have 32MB of heap.
>>>>   - Distro kernels are now often around 30MB.
>>>>
>>>> So we want to claim more memory from OpenFirmware for our heap.
>>> AFAICT it is common problem in the GRUB right now. Please take a look at
>>> [1], [2]. It would be nice to find general solution for all. Of course
>>> if possible. So, if somebody could take a look at memory management in
>>> the GRUB and propose a solution for the problem that would be perfect.
>>> Any volunteers?
>>>
>>> Daniel
>>>
>>> [1] https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00009.html
>>> [2] https://lists.gnu.org/archive/html/grub-devel/2021-01/msg00031.html
>> I think that my [1] should solve the issue generically. Instead of
>> bumping any of the static limits we have in place, we just drop them
>> altogether in favor of dynamically requesting additional EFI regions
>> whenever we realize that the currently mapped regions cannot satisfy our
>> needs. Like this, we can lower the initially requested regions, but
>> scale them to the specific needs if need be.
>>
>> I had planned to revisit this patch series much earlier, but somehow I
>> didn't yet find the time. Any comments on my approach would be welcome
>> though, and if we agree that this may be a viable route to go down then
>> I'd be happy to further pursue it.
> Patrick, thanks a lot! I will take a look at the [1] patchset next week.
>
> Daniel A., Stefan, may I ask you to take a look at the [1] too and tell
> us how it will work in your cases?


I will try this later today by replacing Daniel A.'s 3 patches with 
those in [1] and see what happens. I see a lot of _efi_ in the functions 
in the 3 patches in [1] ...


>
> Daniel
>
> [1] https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00009.html
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 02/22] ieee1275: claim more memory
  2021-07-21 14:45       ` Daniel Kiper
  2021-07-21 15:24         ` Stefan Berger
@ 2021-07-22 17:11         ` Stefan Berger
  1 sibling, 0 replies; 56+ messages in thread
From: Stefan Berger @ 2021-07-22 17:11 UTC (permalink / raw)
  To: Daniel Kiper, Patrick Steinhardt
  Cc: Daniel Axtens, grub-devel, rashmica.g, alastair, nayna,
	chris.coulson, hanson.char, hchar, javierm, leif, phcoder,
	pjones, pmenzel, xnox


On 7/21/21 10:45 AM, Daniel Kiper wrote:
> On Fri, Jul 16, 2021 at 05:59:20AM +0200, Patrick Steinhardt wrote:
>>
>> I think that my [1] should solve the issue generically. Instead of
>> bumping any of the static limits we have in place, we just drop them
>> altogether in favor of dynamically requesting additional EFI regions
>> whenever we realize that the currently mapped regions cannot satisfy our
>> needs. Like this, we can lower the initially requested regions, but
>> scale them to the specific needs if need be.
>>
>> I had planned to revisit this patch series much earlier, but somehow I
>> didn't yet find the time. Any comments on my approach would be welcome
>> though, and if we agree that this may be a viable route to go down then
>> I'd be happy to further pursue it.
> Patrick, thanks a lot! I will take a look at the [1] patchset next week.
>
> Daniel A., Stefan, may I ask you to take a look at the [1] too and tell
> us how it will work in your cases?
>
> Daniel
>
> [1] https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00009.html


I tried to apply these patches now. 2/3 and 3/3 need forward porting. 
The file grub-core/kern/efi/mm.c that they touch is EFI-specific and not 
even compiled when building for/on [IBM IEEE1275] PowerPC, so I don't 
see how they would help on that platform.

   Stefan



^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 02/22] ieee1275: claim more memory
  2021-07-16  3:59     ` Patrick Steinhardt
  2021-07-21 14:45       ` Daniel Kiper
@ 2021-07-28 11:17       ` Daniel Kiper
  1 sibling, 0 replies; 56+ messages in thread
From: Daniel Kiper @ 2021-07-28 11:17 UTC (permalink / raw)
  To: Patrick Steinhardt
  Cc: Daniel Axtens, grub-devel, rashmica.g, alastair, nayna,
	chris.coulson, hanson.char, hchar, javierm, leif, phcoder,
	pjones, pmenzel, stefanb, xnox

On Fri, Jul 16, 2021 at 05:59:20AM +0200, Patrick Steinhardt wrote:
> On Thu, Jul 15, 2021 at 11:51:04PM +0200, Daniel Kiper wrote:
> > CC-in a few people who can be interested in this...
> >
> > On Wed, Jun 30, 2021 at 06:40:11PM +1000, Daniel Axtens wrote:
> > > On powerpc-ieee1275, we are running out of memory trying to verify
> > > anything. This is because:
> > >
> > >  - we have to load an entire file into memory to verify it. This is
> > >    extremely difficult to change with appended signatures.
> > >  - We only have 32MB of heap.
> > >  - Distro kernels are now often around 30MB.
> > >
> > > So we want to claim more memory from OpenFirmware for our heap.
> >
> > AFAICT it is common problem in the GRUB right now. Please take a look at
> > [1], [2]. It would be nice to find general solution for all. Of course
> > if possible. So, if somebody could take a look at memory management in
> > the GRUB and propose a solution for the problem that would be perfect.
> > Any volunteers?
> >
> > Daniel
> >
> > [1] https://lists.gnu.org/archive/html/grub-devel/2020-06/msg00009.html
> > [2] https://lists.gnu.org/archive/html/grub-devel/2021-01/msg00031.html
>
> I think that my [1] should solve the issue generically. Instead of
> bumping any of the static limits we have in place, we just drop them
> altogether in favor of dynamically requesting additional EFI regions
> whenever we realize that the currently mapped regions cannot satisfy our
> needs. Like this, we can lower the initially requested regions, but
> scale them to the specific needs if need be.
>
> I had planned to revisit this patch series much earlier, but somehow I
> didn't yet find the time. Any comments on my approach would be welcome
> though, and if we agree that this may be a viable route to go down then
> I'd be happy to further pursue it.

Patrick, I went quickly through the patches and I like the idea. I found
some minor issues there but they are not important at this point. If you
could rebase your patch set on current master that would be nice. Though
please remember that the interface should be sufficiently generic to
make use of it on the other architectures and platforms.

When you send new patch set please CC all people who are CC-ed in this email.

Daniel


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 13/22] libtasn1: changes for grub compatibility
  2021-07-12 13:04   ` Stefan Berger
@ 2022-04-21  6:16     ` Daniel Axtens
  0 siblings, 0 replies; 56+ messages in thread
From: Daniel Axtens @ 2022-04-21  6:16 UTC (permalink / raw)
  To: Stefan Berger, The development of GNU GRUB; +Cc: rashmica.g, alastair, nayna

Stefan Berger <stefanb@linux.ibm.com> writes:

> On 6/30/21 4:40 AM, Daniel Axtens wrote:
>> Do a few things to make libtasn1 compile as part of grub:
>>
>>   - redefine _asn1_strcat. grub removed strcat so replace it with the
>>     appropriate calls to memcpy and strlen. Use this internally where
>>     strcat was used.
>>
>>   - replace c_isdigit with grub_isdigit (and don't import c-ctype from
>>     gnulib) grub_isdigit provides the same functionality as c_isdigit: it
>>     determines if the input is an ASCII digit without regard for locale.
>>
>>   - replace GL_ATTRIBUTE_PURE with __attribute__((pure)) which been
>>     supported since gcc-2.96. This avoids messing around with gnulib.
>>
>>   - adjust libtasn1.h: drop the ASN1_API logic, it's not needed for our
>>     modules. Unconditionally support const and pure attributes and adjust
>>     header paths.
>>
>>   - adjust header paths to "grub/libtasn1.h".
>>
>>   - replace a 64 bit division with a call to grub_divmod64, preventing
>>     creation of __udivdi3 calls on 32 bit platforms.
>>
>> Signed-off-by: Daniel Axtens <dja@axtens.net>
>>
>> ---
>>
>> v2: Clean up strcat handling, thanks Stefan Berger.
>> ---
>>   grub-core/lib/libtasn1/lib/decoding.c   | 11 ++++++-----
>>   grub-core/lib/libtasn1/lib/element.c    |  3 ++-
>>   grub-core/lib/libtasn1/lib/gstr.c       |  4 ++--
>>   grub-core/lib/libtasn1/lib/int.h        |  4 ++--
>>   grub-core/lib/libtasn1/lib/parser_aux.c |  7 ++++---
>>   include/grub/libtasn1.h                 | 26 ++++++-------------------
>>   6 files changed, 22 insertions(+), 33 deletions(-)
>>
>> diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c
>> index 42f9a92b5d44..3406e1832746 100644
>> --- a/grub-core/lib/libtasn1/lib/decoding.c
>> +++ b/grub-core/lib/libtasn1/lib/decoding.c
>> @@ -32,7 +32,8 @@
>>   #include <element.h>
>>   #include <limits.h>
>>   #include <intprops.h>
>> -#include <c-ctype.h>
>> +
>> +#define c_isdigit grub_isdigit
>>   
>>   #ifdef DEBUG
>>   # define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__)
>> @@ -2008,8 +2009,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
>>   	  (p2->type & CONST_ASSIGN))
>>   	{
>>   	  strcpy (name, definitions->name);
>> -	  strcat (name, ".");
>> -	  strcat (name, p2->name);
>> +	  _asn1_strcat (name, ".");
>> +	  _asn1_strcat (name, p2->name);
>>   
>>   	  len = sizeof (value);
>>   	  result = asn1_read_value (definitions, name, value, &len);
>> @@ -2026,8 +2027,8 @@ asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element,
>>   	      if (p2)
>>   		{
>>   		  strcpy (name, definitions->name);
>> -		  strcat (name, ".");
>> -		  strcat (name, p2->name);
>> +		  _asn1_strcat (name, ".");
>> +		  _asn1_strcat (name, p2->name);
>>   
>>   		  result = asn1_create_element (definitions, name, &aux);
>>   		  if (result == ASN1_SUCCESS)
>> diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c
>> index 539008d8e949..ed761ff56bd9 100644
>> --- a/grub-core/lib/libtasn1/lib/element.c
>> +++ b/grub-core/lib/libtasn1/lib/element.c
>> @@ -30,9 +30,10 @@
>>   #include "parser_aux.h"
>>   #include <gstr.h>
>>   #include "structure.h"
>> -#include "c-ctype.h"
>>   #include "element.h"
>>   
>> +#define c_isdigit grub_isdigit
>> +
>>   void
>>   _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size)
>>   {
>> diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c
>> index e91a3a151c0d..a092c9a5a24b 100644
>> --- a/grub-core/lib/libtasn1/lib/gstr.c
>> +++ b/grub-core/lib/libtasn1/lib/gstr.c
>> @@ -36,13 +36,13 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src)
>>   
>>     if (dest_tot_size - dest_size > str_size)
>>       {
>> -      strcat (dest, src);
>> +      _asn1_strcat (dest, src);
>>       }
>>     else
>>       {
>>         if (dest_tot_size - dest_size > 0)
>>   	{
>> -	  strncat (dest, src, (dest_tot_size - dest_size) - 1);
>> +	  memcpy (dest + dest_size, src, (dest_tot_size - dest_size) - 1);
>
>
> With dest_size = strlen(dest) this is following the 'pattern' of the 
> #define below.

Hmm, is it?

#define _asn1_strcat(a,b) memcpy((char *)a + strlen((const char *)a), (const char *)b, strlen((const char *)b) + 1)

so _asn1_strcat(dest, src) =>
     memcpy(dest + dest_size, src, src_size + 1)

I'm not sure that src_size + 1 = dest_tot_size - 1 ?

I've left it as is for now but I'm happy to be convinced (or more
accurately for my successor to be convinced) that I'm wrong...

Kind regards,
Daniel

>
>
>>   	  dest[dest_tot_size - 1] = 0;
>>   	}
>>       }
>> diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
>> index ea1625786c1b..4a568efee9c1 100644
>> --- a/grub-core/lib/libtasn1/lib/int.h
>> +++ b/grub-core/lib/libtasn1/lib/int.h
>> @@ -35,7 +35,7 @@
>>   #include <sys/types.h>
>>   #endif
>>   
>> -#include <libtasn1.h>
>> +#include "grub/libtasn1.h"
>>   
>>   #define ASN1_SMALL_VALUE_SIZE 16
>>   
>> @@ -115,7 +115,7 @@ extern const tag_and_class_st _asn1_tags[];
>>   #define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
>>   #define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
>>   #define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
>> -#define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
>> +#define _asn1_strcat(a,b) memcpy((char *)a + strlen((const char *)a), (const char *)b, strlen((const char *)b) + 1)
>>   
>>   #if SIZEOF_UNSIGNED_LONG_INT == 8
>>   # define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
>> diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c
>> index d5dbbf8765da..89c9be69dc2a 100644
>> --- a/grub-core/lib/libtasn1/lib/parser_aux.c
>> +++ b/grub-core/lib/libtasn1/lib/parser_aux.c
>> @@ -26,7 +26,8 @@
>>   #include "gstr.h"
>>   #include "structure.h"
>>   #include "element.h"
>> -#include "c-ctype.h"
>> +
>> +#define c_isdigit grub_isdigit
>>   
>>   char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not found */
>>   
>> @@ -40,7 +41,7 @@ char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1];	/* identifier name not fou
>>   #ifdef __clang__
>>   __attribute__((no_sanitize("integer")))
>>   #endif
>> -_GL_ATTRIBUTE_PURE
>> +__attribute__((__pure__))
>>   static unsigned int
>>   _asn1_hash_name (const char *x)
>>   {
>> @@ -634,7 +635,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
>>     count = 0;
>>     do
>>       {
>> -      d = val / 10;
>> +      d = grub_divmod64(val, 10, NULL);
>>         r = val - d * 10;
>>         temp[start + count] = '0' + (char) r;
>>         count++;
>> diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
>> index 785eda2ae3f8..28dbf16c4e0c 100644
>> --- a/include/grub/libtasn1.h
>> +++ b/include/grub/libtasn1.h
>> @@ -38,29 +38,15 @@
>>   #ifndef LIBTASN1_H
>>   #define LIBTASN1_H
>>   
>> -#ifndef ASN1_API
>> -#if defined ASN1_BUILDING && defined HAVE_VISIBILITY && HAVE_VISIBILITY
>> -#define ASN1_API __attribute__((__visibility__("default")))
>> -#elif defined ASN1_BUILDING && defined _MSC_VER && ! defined ASN1_STATIC
>> -#define ASN1_API __declspec(dllexport)
>> -#elif defined _MSC_VER && ! defined ASN1_STATIC
>> -#define ASN1_API __declspec(dllimport)
>> -#else
>> +/* grub: ASN1_API is not used */
>>   #define ASN1_API
>> -#endif
>> -#endif
>>   
>> -#ifdef __GNUC__
>> -# define __LIBTASN1_CONST__  __attribute__((const))
>> -# define __LIBTASN1_PURE__  __attribute__((pure))
>> -#else
>> -# define __LIBTASN1_CONST__
>> -# define __LIBTASN1_PURE__
>> -#endif
>> +/* grub: all our supported compilers support these attributes */
>> +#define __LIBTASN1_CONST__  __attribute__((const))
>> +#define __LIBTASN1_PURE__  __attribute__((pure))
>>   
>> -#include <sys/types.h>
>> -#include <time.h>
>> -#include <stdio.h>		/* for FILE* */
>> +#include <grub/types.h>
>> +#include <grub/time.h>
>>   
>>   #ifdef __cplusplus
>>   extern "C"
>
>
> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 18/22] appended signatures: parse PKCS#7 signedData and X.509 certificates
  2021-07-19 22:02   ` Stefan Berger
@ 2022-04-21  6:36     ` Daniel Axtens
  0 siblings, 0 replies; 56+ messages in thread
From: Daniel Axtens @ 2022-04-21  6:36 UTC (permalink / raw)
  To: Stefan Berger, The development of GNU GRUB
  Cc: rashmica.g, alastair, nayna, Javier Martinez Canillas

Stefan Berger <stefanb@linux.ibm.com> writes:

> On 6/30/21 4:40 AM, Daniel Axtens wrote:
>
>> This code allows us to parse:
>>
>>   - PKCS#7 signedData messages. Only a single signerInfo is supported,
>>     which is all that the Linux sign-file utility supports creating
>>     out-of-the-box. Only RSA, SHA-256 and SHA-512 are supported.
>>     Any certificate embedded in the PKCS#7 message will be ignored.
>>
>>   - X.509 certificates: at least enough to verify the signatures on the
>>     PKCS#7 messages. We expect that the certificates embedded in grub will
>>     be leaf certificates, not CA certificates. The parser enforces this.
>>
>>   - X.509 certificates support the Extended Key Usage extension and handle
>>     it by verifying that the certificate has a single purpose, that is code
>>     signing. This is required because Red Hat certificates have both Key
>>     Usage and Extended Key Usage extensions present.
>>
>> Signed-off-by: Javier Martinez Canillas <javierm@redhat.com> # EKU support
>> Signed-off-by: Daniel Axtens <dja@axtens.net>
>
> A few comments below.
>
>
>>
>> ---
>>
>> v2 changes:
>>
>>   - Handle the Extended Key Usage extension
>>   - Fix 2 leaks in x509 cert parsing
>>   - Improve x509 parser function name
>>   - Constify the data parameter in parser signatures
>>   - Support multiple signers in a pkcs7 message. Accept any passing sig.
>>   - Allow padding after a pkcs7 message in an appended signature, required
>>      to support my model for signers separated in time.
>>   - Fix a test that used GRUB_ERR_NONE rather than ASN1_SUCCESS. They're
>>      both 0 so no harm was done, but better to be correct.
>>   - Various code and comment cleanups.
>>
>> Thanks to Nayna Jain and Stefan Berger for their reviews.
>>
>> revert
>>
>> Signed-off-by: Daniel Axtens <dja@axtens.net>
>> ---
>>   grub-core/commands/appendedsig/appendedsig.h |  118 ++
>>   grub-core/commands/appendedsig/asn1util.c    |  103 ++
>>   grub-core/commands/appendedsig/pkcs7.c       |  509 +++++++++
>>   grub-core/commands/appendedsig/x509.c        | 1079 ++++++++++++++++++
>>   4 files changed, 1809 insertions(+)
>>   create mode 100644 grub-core/commands/appendedsig/appendedsig.h
>>   create mode 100644 grub-core/commands/appendedsig/asn1util.c
>>   create mode 100644 grub-core/commands/appendedsig/pkcs7.c
>>   create mode 100644 grub-core/commands/appendedsig/x509.c
>>
>> diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h
>> new file mode 100644
>> index 000000000000..327d68ddb1b7
>> --- /dev/null
>> +++ b/grub-core/commands/appendedsig/appendedsig.h
>> @@ -0,0 +1,118 @@
>> +/*
>> + *  GRUB  --  GRand Unified Bootloader
>> + *  Copyright (C) 2020  IBM Corporation.
>> + *
>> + *  GRUB is free software: you can redistribute it and/or modify
>> + *  it under the terms of the GNU General Public License as published by
>> + *  the Free Software Foundation, either version 3 of the License, or
>> + *  (at your option) any later version.
>> + *
>> + *  GRUB 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 General Public License for more details.
>> + *
>> + *  You should have received a copy of the GNU General Public License
>> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <grub/crypto.h>
>> +#include <grub/libtasn1.h>
>> +
>> +extern asn1_node _gnutls_gnutls_asn;
>> +extern asn1_node _gnutls_pkix_asn;
>> +
>> +#define MAX_OID_LEN 32
>> +
>> +/*
>> + * One or more x509 certificates.
>> + *
>> + * We do limited parsing: extracting only the serial, CN and RSA public key.
>> + */
>> +struct x509_certificate
>> +{
>> +  struct x509_certificate *next;
>> +
>> +  grub_uint8_t *serial;
>> +  grub_size_t serial_len;
>> +
>> +  char *subject;
>> +  grub_size_t subject_len;
>> +
>> +  /* We only support RSA public keys. This encodes [modulus, publicExponent] */
>> +  gcry_mpi_t mpis[2];
>> +};
>> +
>> +/*
>> + * A PKCS#7 signedData signerInfo.
>> + */
>> +struct pkcs7_signerInfo
>> +{
>> +  const gcry_md_spec_t *hash;
>> +  gcry_mpi_t sig_mpi;
>> +};
>> +
>> +/*
>> + * A PKCS#7 signedData message.
>> + *
>> + * We make no attempt to match intelligently, so we don't save any info about
>> + * the signer.
>> + */
>> +struct pkcs7_signedData
>> +{
>> +  int signerInfo_count;
>> +  struct pkcs7_signerInfo *signerInfos;
>> +};
>> +
>> +
>> +/* Do libtasn1 init */
>> +int asn1_init (void);
>> +
>> +/*
>> + * Import a DER-encoded certificate at 'data', of size 'size'.
>> + *
>> + * Place the results into 'results', which must be already allocated.
>> + */
>> +grub_err_t
>> +parse_x509_certificate (const void *data, grub_size_t size,
>> +			struct x509_certificate *results);
>> +
>> +/*
>> + * Release all the storage associated with the x509 certificate.
>> + * If the caller dynamically allocated the certificate, it must free it.
>> + * The caller is also responsible for maintenance of the linked list.
>> + */
>> +void certificate_release (struct x509_certificate *cert);
>> +
>> +/*
>> + * Parse a PKCS#7 message, which must be a signedData message.
>> + *
>> + * The message must be in 'sigbuf' and of size 'data_size'. The result is
>> + * placed in 'msg', which must already be allocated.
>> + */
>> +grub_err_t
>> +parse_pkcs7_signedData (const void *sigbuf, grub_size_t data_size,
>> +			struct pkcs7_signedData *msg);
>> +
>> +/*
>> + * Release all the storage associated with the PKCS#7 message.
>> + * If the caller dynamically allocated the message, it must free it.
>> + */
>> +void pkcs7_signedData_release (struct pkcs7_signedData *msg);
>> +
>> +/*
>> + * Read a value from an ASN1 node, allocating memory to store it.
>> + *
>> + * It will work for anything where the size libtasn1 returns is right:
>> + *  - Integers
>> + *  - Octet strings
>> + *  - DER encoding of other structures
>> + * It will _not_ work for things where libtasn1 size requires adjustment:
>> + *  - Strings that require an extra NULL byte at the end
>> + *  - Bit strings because libtasn1 returns the length in bits, not bytes.
>> + *
>> + * If the function returns a non-NULL value, the caller must free it.
>> + */
>> +void *grub_asn1_allocate_and_read (asn1_node node, const char *name,
>> +				   const char *friendly_name,
>> +				   int *content_size);
>> diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c
>> new file mode 100644
>> index 000000000000..6b508222a081
>> --- /dev/null
>> +++ b/grub-core/commands/appendedsig/asn1util.c
>> @@ -0,0 +1,103 @@
>> +/*
>> + *  GRUB  --  GRand Unified Bootloader
>> + *  Copyright (C) 2020  IBM Corporation.
>> + *
>> + *  GRUB is free software: you can redistribute it and/or modify
>> + *  it under the terms of the GNU General Public License as published by
>> + *  the Free Software Foundation, either version 3 of the License, or
>> + *  (at your option) any later version.
>> + *
>> + *  GRUB 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 General Public License for more details.
>> + *
>> + *  You should have received a copy of the GNU General Public License
>> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <grub/libtasn1.h>
>> +#include <grub/types.h>
>> +#include <grub/err.h>
>> +#include <grub/mm.h>
>> +#include <grub/crypto.h>
>> +#include <grub/misc.h>
>> +#include <grub/gcrypt/gcrypt.h>
>> +
>> +#include "appendedsig.h"
>> +
>> +asn1_node _gnutls_gnutls_asn = ASN1_TYPE_EMPTY;
>> +asn1_node _gnutls_pkix_asn = ASN1_TYPE_EMPTY;
>> +
>> +extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[];
>> +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[];
>> +
>> +/*
>> + * Read a value from an ASN1 node, allocating memory to store it.
>> + *
>> + * It will work for anything where the size libtasn1 returns is right:
>> + *  - Integers
>> + *  - Octet strings
>> + *  - DER encoding of other structures
>> + * It will _not_ work for things where libtasn1 size requires adjustment:
>> + *  - Strings that require an extra NULL byte at the end
>> + *  - Bit strings because libtasn1 returns the length in bits, not bytes.
>> + *
>> + * If the function returns a non-NULL value, the caller must free it.
>> + */
>> +void *
>> +grub_asn1_allocate_and_read (asn1_node node, const char *name,
>> +			     const char *friendly_name, int *content_size)
>> +{
>> +  int result;
>> +  grub_uint8_t *tmpstr = NULL;
>> +  int tmpstr_size = 0;
>> +
>> +  result = asn1_read_value (node, name, NULL, &tmpstr_size);
>> +  if (result != ASN1_MEM_ERROR)
>> +    {
>> +      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
>> +		     _
>> +		     ("Reading size of %s did not return expected status: %s"),
>> +		     friendly_name, asn1_strerror (result));
>> +      grub_errno = GRUB_ERR_BAD_FILE_TYPE;
>> +      return NULL;
>> +    }
>> +
>> +  tmpstr = grub_malloc (tmpstr_size);
>> +  if (tmpstr == NULL)
>> +    {
>> +      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
>> +		     "Could not allocate memory to store %s", friendly_name);
>> +      grub_errno = GRUB_ERR_OUT_OF_MEMORY;
>> +      return NULL;
>> +    }
>> +
>> +  result = asn1_read_value (node, name, tmpstr, &tmpstr_size);
>> +  if (result != ASN1_SUCCESS)
>> +    {
>> +      grub_free (tmpstr);
>> +      grub_snprintf (grub_errmsg, sizeof (grub_errmsg),
>> +		     "Error reading %s: %s",
>> +		     friendly_name, asn1_strerror (result));
>> +      grub_errno = GRUB_ERR_BAD_FILE_TYPE;
>> +      return NULL;
>> +    }
>> +
>> +  *content_size = tmpstr_size;
>> +
>> +  return tmpstr;
>> +}
>> +
>> +int
>> +asn1_init (void)
>> +{
>> +  int res;
>> +  res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL);
>> +  if (res != ASN1_SUCCESS)
>> +    {
>> +      return res;
>> +    }
>> +  res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL);
>> +  return res;
>> +}
>> diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c
>> new file mode 100644
>> index 000000000000..845f58a53e83
>> --- /dev/null
>> +++ b/grub-core/commands/appendedsig/pkcs7.c
>> @@ -0,0 +1,509 @@
>> +/*
>> + *  GRUB  --  GRand Unified Bootloader
>> + *  Copyright (C) 2020  IBM Corporation.
>> + *
>> + *  GRUB is free software: you can redistribute it and/or modify
>> + *  it under the terms of the GNU General Public License as published by
>> + *  the Free Software Foundation, either version 3 of the License, or
>> + *  (at your option) any later version.
>> + *
>> + *  GRUB 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 General Public License for more details.
>> + *
>> + *  You should have received a copy of the GNU General Public License
>> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include "appendedsig.h"
>> +#include <grub/misc.h>
>> +#include <grub/crypto.h>
>> +#include <grub/gcrypt/gcrypt.h>
>> +#include <sys/types.h>
>> +
>> +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
>> +
>> +/*
>> + * RFC 5652 s 5.1
>> + */
>> +const char *signedData_oid = "1.2.840.113549.1.7.2";
>> +
>> +/*
>> + * RFC 4055 s 2.1
>> + */
>> +const char *sha256_oid = "2.16.840.1.101.3.4.2.1";
>> +const char *sha512_oid = "2.16.840.1.101.3.4.2.3";

Made these static too.

>> +	  goto cleanup_signerInfos;
>> +	}
>> +
>> +      result_buf =
>> +	grub_asn1_allocate_and_read (signed_part, si_sig_path,
>> +				     "signature data", &result_size);
>> +      if (!result_buf)
>> +	{
>> +	  err = grub_errno;
>> +	  grub_free (si_sig_path);
>> +	  goto cleanup_signerInfos;
>> +	}
>> +      grub_free (si_sig_path);
>
> Nit: You could probably move this before the if statement so you only 
> have to write this once.
>

Yep, fixed.
>
>> +
>> +      gcry_err =
>> +	gcry_mpi_scan (&(msg->signerInfos[i].sig_mpi), GCRYMPI_FMT_USG,
>> +		       result_buf, result_size, NULL);
>> +      if (gcry_err != GPG_ERR_NO_ERROR)
>> +	{
>> +	  err =
>> +	    grub_error (GRUB_ERR_BAD_SIGNATURE,
>> +			"Error loading signature %d into MPI structure: %d",
>> +			i, gcry_err);
>> +	  grub_free (result_buf);
>> +	  goto cleanup_signerInfos;
>> +	}
>> +
>> +      grub_free (result_buf);
>
> Same with this one.
>
>
Fixed.

>> +
>> +      /* use msg->signerInfo_count to track fully populated signerInfos so we
>> +         know how many we need to clean up */
>> +      msg->signerInfo_count++;
>> +    }

>> +/*
>> + * Release all the storage associated with the PKCS#7 message.
>> + * If the caller dynamically allocated the message, it must free it.
>> + */
>> +void
>> +pkcs7_signedData_release (struct pkcs7_signedData *msg)
>> +{
>> +  grub_ssize_t i;
>> +  for (i = 0; i < msg->signerInfo_count; i++)
>
>
> Nit: probably empty line after variable declaration

Done
>
>
>> +    {
>> +      gcry_mpi_release (msg->signerInfos[i].sig_mpi);
>> +    }
>> +  grub_free (msg->signerInfos);
>> +}
>> diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c
>> new file mode 100644
>> index 000000000000..a17a46102872
>> --- /dev/null
>> +++ b/grub-core/commands/appendedsig/x509.c
>> @@ -0,0 +1,1079 @@
>> +/*
>> + *  GRUB  --  GRand Unified Bootloader
>> + *  Copyright (C) 2020  IBM Corporation.
>> + *
>> + *  GRUB is free software: you can redistribute it and/or modify
>> + *  it under the terms of the GNU General Public License as published by
>> + *  the Free Software Foundation, either version 3 of the License, or
>> + *  (at your option) any later version.
>> + *
>> + *  GRUB 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 General Public License for more details.
>> + *
>> + *  You should have received a copy of the GNU General Public License
>> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <grub/libtasn1.h>
>> +#include <grub/types.h>
>> +#include <grub/err.h>
>> +#include <grub/mm.h>
>> +#include <grub/crypto.h>
>> +#include <grub/misc.h>
>> +#include <grub/gcrypt/gcrypt.h>
>> +
>> +#include "appendedsig.h"
>> +
>> +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE];
>> +
>> +/*
>> + * RFC 3279 2.3.1  RSA Keys
>> + */
>> +const char *rsaEncryption_oid = "1.2.840.113549.1.1.1";
>> +
>> +/*
>> + * RFC 5280 Appendix A
>> + */
>> +const char *commonName_oid = "2.5.4.3";
>> +
>> +/*
>> + * RFC 5280 4.2.1.3 Key Usage
>> + */
>> +const char *keyUsage_oid = "2.5.29.15";
>> +
>> +const grub_uint8_t digitalSignatureUsage = 0x80;
>> +
>> +/*
>> + * RFC 5280 4.2.1.9 Basic Constraints
>> + */
>> +const char *basicConstraints_oid = "2.5.29.19";
>> +
>> +/*
>> + * RFC 5280 4.2.1.12 Extended Key Usage
>> + */
>> +const char *extendedKeyUsage_oid = "2.5.29.37";
>> +const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3";
>
>
> Should they be visible to other modules or only used here and can be 
> 'static'?
>
Indeed you are right. Marked as static.


>> +  result = asn1_der_decoding2 (&extendedasn, value, &value_size,
>> +			       ASN1_DECODE_FLAG_STRICT_DER, asn1_error);
>> +  if (result != ASN1_SUCCESS)
>> +    {
>> +      err =
>> +	grub_error (GRUB_ERR_BAD_FILE_TYPE,
>> +		    "Error parsing DER for Extended Key Usage: %s",
>> +		    asn1_error);
>> +      goto cleanup;
>> +    }
>> +
>> +  /*
>> +   * If EKUs are present, there must be exactly 1 and it must be a
>> +   * codeSigning usage.
>
>
> Is this comment correct? It looks like your code requires one EKU.
>

That function will only be called if we have an EKU extension in
verify_extensions. I will clarify the comment: what it should be saying
is if we have an EKU extension, there must be at least 1 usage.

Thank you for the detailed review, I'm sorry it's taken me so long to respond!

Kind regards,
Daniel


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 19/22] appended signatures: support verifying appended signatures
  2021-07-20  1:31   ` Stefan Berger
@ 2022-04-21  7:10     ` Daniel Axtens
  0 siblings, 0 replies; 56+ messages in thread
From: Daniel Axtens @ 2022-04-21  7:10 UTC (permalink / raw)
  To: Stefan Berger, The development of GNU GRUB; +Cc: rashmica.g, alastair, nayna

>> +static enum
>> +{ check_sigs_no = 0,
>
>
> nit: newline after '{'
>
fixed

>
>> +  check_sigs_enforce = 1,
>> +  check_sigs_forced = 2
>> +} check_sigs = check_sigs_no;
>
>
> What does 'forced' mean?

It means that it cannot be turned of with `set check_appended_signatures=0`
at the grub prompt. I'm open to better names.

>
>> +
>> +static const char *
>> +grub_env_read_sec (struct grub_env_var *var __attribute__((unused)),
>> +		   const char *val __attribute__((unused)))
>> +{
>> +  if (check_sigs == check_sigs_forced)
>> +    return "forced";
>> +  else if (check_sigs == check_sigs_enforce)
>> +    return "enforce";
>> +  else
>> +    return "no";
>> +}
>> +
>> +static char *
>> +grub_env_write_sec (struct grub_env_var *var __attribute__((unused)),
>> +		    const char *val)
>> +{
>> +  /* Do not allow the value to be changed if set to forced */
>> +  if (check_sigs == check_sigs_forced)
>> +    return grub_strdup ("forced");
>> +
>> +  if ((*val == '2') || (*val == 'f'))
>> +    check_sigs = check_sigs_forced;
>> +  else if ((*val == '1') || (*val == 'e'))
>> +    check_sigs = check_sigs_enforce;
>> +  else if ((*val == '0') || (*val == 'n'))
>> +    check_sigs = check_sigs_no;
>> +
>> +  return grub_strdup (grub_env_read_sec (NULL, NULL));
>> +}
>> +
>> +static grub_err_t
>> +file_read_all (grub_file_t file, grub_uint8_t **buf, grub_size_t *len)
>> +{
>> +  grub_off_t full_file_size;
>> +  grub_size_t file_size, total_read_size = 0;
>> +  grub_ssize_t read_size;
>> +
>> +  full_file_size = grub_file_size (file);
>> +  if (full_file_size == GRUB_FILE_SIZE_UNKNOWN)
>> +    return grub_error (GRUB_ERR_BAD_ARGUMENT,
>> +		       N_("Cannot read a file of unknown size into a buffer"));
>> +
>> +  if (full_file_size > GRUB_SIZE_MAX)
>> +    return grub_error (GRUB_ERR_OUT_OF_RANGE,
>> +		       N_("File is too large to read: %" PRIuGRUB_UINT64_T
>> +			  " bytes"), full_file_size);
>> +
>> +  file_size = (grub_size_t) full_file_size;
>> +
>> +  *buf = grub_malloc (file_size);
>> +  if (!*buf)
>> +    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
>> +		       N_("Could not allocate file data buffer size %"
>> +			  PRIuGRUB_SIZE), file_size);
>> +
>> +  while (total_read_size < file_size)
>> +    {
>> +      read_size =
>> +	grub_file_read (file, *buf + total_read_size,
>> +			file_size - total_read_size);
>> +
>> +      if (read_size < 0)
>> +	{
>> +	  grub_free (*buf);
>> +	  return grub_errno;
>> +	}
>> +      else if (read_size == 0)
>> +	{
>> +	  grub_free (*buf);
>> +	  return grub_error (GRUB_ERR_IO,
>> +			     N_("Could not read full file size (%"
>> +				PRIuGRUB_SIZE "), only %" PRIuGRUB_SIZE
>> +				" bytes read"), file_size, total_read_size);
>> +	}
>> +
>> +      total_read_size += read_size;
>> +    }
>> +  *len = file_size;
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +read_cert_from_file (grub_file_t f, struct x509_certificate *certificate)
>> +{
>> +  grub_err_t err;
>> +  grub_uint8_t *buf;
>> +  grub_size_t file_size;
>> +
>> +  err = file_read_all (f, &buf, &file_size);
>> +  if (err != GRUB_ERR_NONE)
>> +    return err;
>> +
>> +  err = parse_x509_certificate (buf, file_size, certificate);
>> +  if (err != GRUB_ERR_NONE)
>> +    {
>> +      grub_free (buf);
>> +      return err;
>> +    }
>
> forgot grub-free(buf) ?

Huh, yeah, seems so. Fixed, thank you.

>
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +extract_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize,
>> +			    struct grub_appended_signature *sig)
>> +{
>> +  grub_err_t err;
>> +  grub_size_t pkcs7_size;
>> +  grub_size_t remaining_len;
>> +  const grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic);
>> +
>> +  if (bufsize < grub_strlen (magic))
>> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
>> +		       N_("File too short for signature magic"));
>> +
>> +  if (grub_memcmp (appsigdata, (grub_uint8_t *) magic, grub_strlen (magic)))
>> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
>> +		       N_("Missing or invalid signature magic"));
>> +
>> +  remaining_len = bufsize - grub_strlen (magic);
>> +
>> +  if (remaining_len < sizeof (struct module_signature))
>> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
>> +		       N_("File too short for signature metadata"));
>> +
>> +  appsigdata -= sizeof (struct module_signature);
>> +
>> +  /* extract the metadata */
>> +  grub_memcpy (&(sig->sig_metadata), appsigdata,
>> +	       sizeof (struct module_signature));
>> +
>> +  remaining_len -= sizeof (struct module_signature);
>> +
>> +  if (sig->sig_metadata.id_type != 2)
>> +    return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Wrong signature type"));
>> +
>> +  pkcs7_size = grub_be_to_cpu32 (sig->sig_metadata.sig_len);
>> +
>> +  if (pkcs7_size > remaining_len)
>> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
>> +		       N_("File too short for PKCS#7 message"));
>> +
>> +  grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", pkcs7_size);
>> +
>> +  sig->signature_len =
>> +    grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size;
>> +
>> +  /* rewind pointer and parse pkcs7 data */
>> +  appsigdata -= pkcs7_size;
>> +
>> +  err = parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7);
>> +  if (err != GRUB_ERR_NONE)
>> +    return err;
>> +
>> +  return GRUB_ERR_NONE;
> just 'return parse_pkcs7_signedData (...)' ?

Good call.

>> +}
>> +
>> +static grub_err_t
>> +grub_verify_appended_signature (const grub_uint8_t *buf, grub_size_t bufsize)
>> +{
>> +  grub_err_t err = GRUB_ERR_NONE;
>> +  grub_size_t datasize;
>> +  void *context;
>> +  unsigned char *hash;
>> +  gcry_mpi_t hashmpi;
>> +  gcry_err_code_t rc;
>> +  struct x509_certificate *pk;
>> +  struct grub_appended_signature sig;
>> +  struct pkcs7_signerInfo *si;
>> +  int i;
>> +
>> +  if (!grub_trusted_key)
>> +    return grub_error (GRUB_ERR_BAD_SIGNATURE,
>> +		       N_("No trusted keys to verify against"));
>> +
>> +  err = extract_appended_signature (buf, bufsize, &sig);
>> +  if (err != GRUB_ERR_NONE)
>> +    return err;
>> +
>> +  datasize = bufsize - sig.signature_len;
>> +
>> +  for (i = 0; i < sig.pkcs7.signerInfo_count; i++)
>> +    {
>> +      /* This could be optimised in a couple of ways:
>> +	  - we could only compute hashes once per hash type
>> +	  - we could track signer information and only verify where IDs match
>> +	 For now we do the naive O(trusted keys * pkcs7 signers) approach.
>> +	*/
>> +      si = &sig.pkcs7.signerInfos[i];
>> +      context = grub_zalloc (si->hash->contextsize);
>> +      if (!context)
>> +	return grub_errno;
>> +
>> +      si->hash->init (context);
>> +      si->hash->write (context, buf, datasize);
>> +      si->hash->final (context);
>> +      hash = si->hash->read (context);
>> +
>> +      grub_dprintf ("appendedsig",
>> +		    "data size %" PRIxGRUB_SIZE ", signer %d hash %02x%02x%02x%02x...\n",
>> +		    datasize, i, hash[0], hash[1], hash[2], hash[3]);
>> +
>> +      err = GRUB_ERR_BAD_SIGNATURE;
>> +      for (pk = grub_trusted_key; pk; pk = pk->next)
>> +	{
>> +	  rc = grub_crypto_rsa_pad (&hashmpi, hash, si->hash, pk->mpis[0]);
>> +	  if (rc)
>> +	    {
>> +	      err = grub_error (GRUB_ERR_BAD_SIGNATURE,
>> +    				N_("Error padding hash for RSA verification: %d"),
>> +    				rc);
>> +	      grub_free (context);
>> +	      goto cleanup;
>> +	    }
>> +
>> +	  rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &si->sig_mpi,
>> +					     pk->mpis, NULL, NULL);
>> +	  gcry_mpi_release (hashmpi);
>> +
>> +	  if (rc == 0)
>> +	    {
>> +	      grub_dprintf ("appendedsig",
>> +			    "verify signer %d with key '%s' succeeded\n", i,
>> +			    pk->subject);
>> +	      err = GRUB_ERR_NONE;
>> +	      break;
>> +	    }
>> +
>> +	  grub_dprintf ("appendedsig",
>> +			"verify signer %d with key '%s' failed with %d\n", i,
>> +			pk->subject, rc);
>> +	}
>> +
>> +      grub_free (context);
>> +
>> +      if (err == GRUB_ERR_NONE)
>> +	break;
>> +    }
>> +
>> +  /* If we didn't verify, provide a neat message */
>> +  if (err != GRUB_ERR_NONE)
>> +    err = grub_error (GRUB_ERR_BAD_SIGNATURE,
>> +		      N_("Failed to verify signature against a trusted key"));
>> +
>> +cleanup:
>> +  pkcs7_signedData_release (&sig.pkcs7);
>> +
>> +  return err;
>> +}
>> +
>> +static grub_err_t
>> +grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)),
>> +			   int argc, char **args)
>> +{
>> +  grub_file_t f;
>> +  grub_err_t err = GRUB_ERR_NONE;
>> +  grub_uint8_t *data;
>> +  grub_size_t file_size;
>> +
>> +  if (argc < 1)
>> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
>> +
>> +  grub_dprintf ("appendedsig", "verifying %s\n", args[0]);
>> +
>> +  f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE);
>> +  if (!f)
>> +    {
>> +      err = grub_errno;
>> +      goto cleanup;
>> +    }
>> +
>> +  err = file_read_all (f, &data, &file_size);
>> +  if (err != GRUB_ERR_NONE)
>> +    goto cleanup;
>> +
>> +  err = grub_verify_appended_signature (data, file_size);
>> +
>> +  grub_free (data);
>> +
>> +cleanup:
>> +  if (f)
>> +    grub_file_close (f);
>> +  return err;
>> +}
>> +
>> +static grub_err_t
>> +grub_cmd_distrust (grub_command_t cmd __attribute__((unused)),
>> +		   int argc, char **args)
>> +{
>> +  unsigned long cert_num, i;
>> +  struct x509_certificate *cert, *prev;
>> +
>> +  if (argc != 1)
>> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("One argument expected"));
>> +
>> +  grub_errno = GRUB_ERR_NONE;
>> +  cert_num = grub_strtoul (args[0], NULL, 10);
>> +  if (grub_errno != GRUB_ERR_NONE)
>> +    return grub_errno;
>> +
>> +  if (cert_num < 1)
>> +    return grub_error (GRUB_ERR_BAD_ARGUMENT,
>> +		       N_("Certificate number too small - numbers start at 1"));
>> +
>> +  if (cert_num == 1)
>> +    {
>> +      cert = grub_trusted_key;
>> +      grub_trusted_key = cert->next;
>> +
>> +      certificate_release (cert);
>> +      grub_free (cert);
>> +      return GRUB_ERR_NONE;
>> +    }
>> +  i = 2;
>> +  prev = grub_trusted_key;
>> +  cert = grub_trusted_key->next;
>> +  while (cert)
>> +    {
>> +      if (i == cert_num)
>> +	{
>> +	  prev->next = cert->next;
>> +	  certificate_release (cert);
>> +	  grub_free (cert);
>> +	  return GRUB_ERR_NONE;
>> +	}
>> +      i++;
>> +      prev = cert;
>> +      cert = cert->next;
>> +    }
>> +
>> +  return grub_error (GRUB_ERR_BAD_ARGUMENT,
>> +		     N_("No certificate number %lu found - only %lu certificates in the store"),
>> +		     cert_num, i - 1);
>> +}
>> +
>> +static grub_err_t
>> +grub_cmd_trust (grub_command_t cmd __attribute__((unused)),
>> +		int argc, char **args)
>> +{
>> +  grub_file_t certf;
>> +  struct x509_certificate *cert = NULL;
>> +  grub_err_t err;
>> +
>> +  if (argc != 1)
>> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
>> +
>> +  certf = grub_file_open (args[0],
>> +			  GRUB_FILE_TYPE_CERTIFICATE_TRUST
>> +			  | GRUB_FILE_TYPE_NO_DECOMPRESS);
>> +  if (!certf)
>> +    return grub_errno;
>> +
>> +
>> +  cert = grub_zalloc (sizeof (struct x509_certificate));
>> +  if (!cert)
>> +    return grub_error (GRUB_ERR_OUT_OF_MEMORY,
>> +		       N_("Could not allocate memory for certificate"));
>> +
>> +  err = read_cert_from_file (certf, cert);
>> +  grub_file_close (certf);
>> +  if (err != GRUB_ERR_NONE)
>> +    {
>> +      grub_free (cert);
>> +      return err;
>> +    }
>> +  grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n",
>> +		cert->subject);
>> +
>> +  cert->next = grub_trusted_key;
>> +  grub_trusted_key = cert;
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +grub_cmd_list (grub_command_t cmd __attribute__((unused)),
>> +	       int argc __attribute__((unused)),
>> +	       char **args __attribute__((unused)))
>> +{
>> +  struct x509_certificate *cert;
>> +  int cert_num = 1;
>> +  grub_size_t i;
>> +
>> +  for (cert = grub_trusted_key; cert; cert = cert->next)
>> +    {
>> +      grub_printf (N_("Certificate %d:\n"), cert_num);
>> +
>> +      grub_printf (N_("\tSerial: "));
>> +      for (i = 0; i < cert->serial_len - 1; i++)
>> +	{
>> +	  grub_printf ("%02x:", cert->serial[i]);
>> +	}
>> +      grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]);
>> +
>> +      grub_printf ("\tCN: %s\n\n", cert->subject);
>> +      cert_num++;
>> +
>> +    }
>> +
>> +  return GRUB_ERR_NONE;
>> +}
>> +
>> +static grub_err_t
>> +appendedsig_init (grub_file_t io __attribute__((unused)),
>> +		  enum grub_file_type type,
>> +		  void **context __attribute__((unused)),
>> +		  enum grub_verify_flags *flags)
>> +{
>> +  if (check_sigs == check_sigs_no)
>> +    {
>> +      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
>> +      return GRUB_ERR_NONE;
>> +    }
>> +
>> +  switch (type & GRUB_FILE_TYPE_MASK)
>> +    {
>> +    case GRUB_FILE_TYPE_CERTIFICATE_TRUST:
>> +      /*
>> +       * This is a certificate to add to trusted keychain.
>> +       *
>> +       * This needs to be verified or blocked. Ideally we'd write an x509
>> +       * verifier, but we lack the hubris required to take this on. Instead,
>> +       * require that it have an appended signature.
>> +       */
>> +
>> +      /* Fall through */
>> +
>> +    case GRUB_FILE_TYPE_LINUX_KERNEL:
>> +    case GRUB_FILE_TYPE_GRUB_MODULE:
>> +      /*
>> +       * Appended signatures are only defined for ELF binaries.
>> +       * Out of an abundance of caution, we only verify Linux kernels and
>> +       * GRUB modules at this point.
>> +       */
>> +      *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
>> +      return GRUB_ERR_NONE;
>> +
>> +    case GRUB_FILE_TYPE_ACPI_TABLE:
>> +    case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE:
>> +      /*
>> +       * It is possible to use appended signature verification without
>> +       * lockdown - like the PGP verifier. When combined with an embedded
>> +       * config file in a signed grub binary, this could still be a meaningful
>> +       * secure-boot chain - so long as it isn't subverted by something like a
>> +       * rouge ACPI table or DT image. Defer them explicitly.
>> +       */
>> +      *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH;
>> +      return GRUB_ERR_NONE;
>> +
>> +    default:
>> +      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
>> +      return GRUB_ERR_NONE;
>> +    }
>> +}
>> +
>> +static grub_err_t
>> +appendedsig_write (void *ctxt __attribute__((unused)),
>> +		   void *buf, grub_size_t size)
>> +{
>> +  return grub_verify_appended_signature (buf, size);
>> +}
>> +
>> +struct grub_file_verifier grub_appendedsig_verifier = {
>> +  .name = "appendedsig",
>> +  .init = appendedsig_init,
>> +  .write = appendedsig_write,
>> +};
>> +
>> +static grub_ssize_t
>> +pseudo_read (struct grub_file *file, char *buf, grub_size_t len)
>> +{
>> +  grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len);
>> +  return len;
>> +}
>> +
>> +/* Filesystem descriptor.  */
>> +static struct grub_fs pseudo_fs = {
>> +  .name = "pseudo",
>> +  .fs_read = pseudo_read
>> +};
>> +
>> +static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust;
>> +
>> +GRUB_MOD_INIT (appendedsig)
>> +{
>> +  int rc;
>> +  struct grub_module_header *header;
>> +
>> +  /* If in lockdown, immediately enter forced mode */
>> +  if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
>> +    check_sigs = check_sigs_forced;
>> +
>> +  grub_trusted_key = NULL;
>> +
>> +  grub_register_variable_hook ("check_appended_signatures",
>> +			       grub_env_read_sec, grub_env_write_sec);
>> +  grub_env_export ("check_appended_signatures");
>> +
>> +  rc = asn1_init ();
>> +  if (rc)
>> +    grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc,
>> +		asn1_strerror (rc));
>> +
>> +  FOR_MODULES (header)
>> +  {
>> +    struct grub_file pseudo_file;
>> +    struct x509_certificate *pk = NULL;
>> +    grub_err_t err;
>> +
>> +    /* Not an ELF module, skip.  */
>> +    if (header->type != OBJ_TYPE_X509_PUBKEY)
>> +      continue;
>
> Is that comment correct? What does an ELF module have to do with type 
> OBJ_TYPE_X509_PUBKEY?

lolol sorry. I probably copied from pgp.c, which looks like it copied
from kern/main.c. The comment would only have been correct in
kern/main.c. Fixed.

Thanks again!

Kind regards,
Daniel
>> +
>> +    grub_memset (&pseudo_file, 0, sizeof (pseudo_file));
>> +    pseudo_file.fs = &pseudo_fs;
>> +    pseudo_file.size = header->size - sizeof (struct grub_module_header);
>> +    pseudo_file.data = (char *) header + sizeof (struct grub_module_header);
>> +
>> +    grub_dprintf ("appendedsig",
>> +		  "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n",
>> +		  pseudo_file.size);
>> +
>> +    pk = grub_zalloc (sizeof (struct x509_certificate));
>> +    if (!pk)
>> +      {
>> +	grub_fatal ("Out of memory loading initial certificates");
>> +      }
>> +
>> +    err = read_cert_from_file (&pseudo_file, pk);
>> +    if (err != GRUB_ERR_NONE)
>> +      grub_fatal ("Error loading initial key: %s", grub_errmsg);
>> +
>> +    grub_dprintf ("appendedsig", "loaded certificate CN='%s'\n", pk->subject);
>> +
>> +    pk->next = grub_trusted_key;
>> +    grub_trusted_key = pk;
>> +  }
>> +
>> +  cmd_trust =
>> +    grub_register_command ("trust_certificate", grub_cmd_trust,
>> +			   N_("X509_CERTIFICATE"),
>> +			   N_("Add X509_CERTIFICATE to trusted certificates."));
>> +  cmd_list =
>> +    grub_register_command ("list_certificates", grub_cmd_list, 0,
>> +			   N_("Show the list of trusted x509 certificates."));
>> +  cmd_verify =
>> +    grub_register_command ("verify_appended", grub_cmd_verify_signature,
>> +			   N_("FILE"),
>> +			   N_("Verify FILE against the trusted x509 certificates."));
>> +  cmd_distrust =
>> +    grub_register_command ("distrust_certificate", grub_cmd_distrust,
>> +			   N_("CERT_NUMBER"),
>> +			   N_("Remove CERT_NUMBER (as listed by list_certificates) from trusted certificates."));
>> +
>> +  grub_verifier_register (&grub_appendedsig_verifier);
>> +  grub_dl_set_persistent (mod);
>> +}
>> +
>> +GRUB_MOD_FINI (appendedsig)
>> +{
>> +  /*
>> +   * grub_dl_set_persistent should prevent this from actually running, but
>> +   * it does still run under emu.
>> +   */
>> +
>> +  grub_verifier_unregister (&grub_appendedsig_verifier);
>> +  grub_unregister_command (cmd_verify);
>> +  grub_unregister_command (cmd_list);
>> +  grub_unregister_command (cmd_trust);
>> +  grub_unregister_command (cmd_distrust);
>> +}
>> diff --git a/include/grub/file.h b/include/grub/file.h
>> index 31567483ccfc..96827a4f8961 100644
>> --- a/include/grub/file.h
>> +++ b/include/grub/file.h
>> @@ -80,6 +80,8 @@ enum grub_file_type
>>       GRUB_FILE_TYPE_PUBLIC_KEY,
>>       /* File holding public key to add to trused keys.  */
>>       GRUB_FILE_TYPE_PUBLIC_KEY_TRUST,
>> +    /* File holding x509 certificiate to add to trusted keys.  */
>> +    GRUB_FILE_TYPE_CERTIFICATE_TRUST,
>>       /* File of which we intend to print a blocklist to the user.  */
>>       GRUB_FILE_TYPE_PRINT_BLOCKLIST,
>>       /* File we intend to use for test loading or testing speed.  */


^ permalink raw reply	[flat|nested] 56+ messages in thread

* Re: [PATCH v2 21/22] appended signatures: documentation
  2021-07-19 22:24   ` Stefan Berger
@ 2022-04-21  7:15     ` Daniel Axtens
  0 siblings, 0 replies; 56+ messages in thread
From: Daniel Axtens @ 2022-04-21  7:15 UTC (permalink / raw)
  To: Stefan Berger, The development of GNU GRUB; +Cc: rashmica.g, alastair, nayna


>> +@example
>> +~Module signature appended~\n
>> +@end example
>> +
>> +where @code{\n} represents the carriage-return character, @code{0x0a}.
>
>
> \n is 0xa but it's called line-feed.

D'oh, you're completely right, of course. Fixed.

>> +
>> +To enable appended signature verification, load the appendedsig module and an
>> +x509 certificate for verification. Building the appendedsig module into the
>> +core grub image is recommended.
>> +
>> +Certificates can be managed at boot time using the @pxref{trust_certificate},
>> +@pxref{distrust_certificate} and @pxref{list_certificates} commands.
>> +Certificates can also be built in to the core image using the @code{--x509}
>> +parameter to @command{grub-install} or @command{grub-mkimage}.
>> +
>> +A file can be explictly verified using the @pxref{verify_appended} command.
>> +
>> +Only signatures made with the SHA-256 or SHA-512 hash algorithm are supported,
>> +and only RSA signatures are supported.
>> +
>> +A file can be signed with the @command{sign-file} utility supplied with the
>> +Linux kernel source. For example, if you have @code{signing.key} as the private
>> +key and @code{certificate.der} as the x509 certificate containing the public key:
>> +
>> +@example
>> +sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed
>> +@end example
>> +
>> +Enforcement of signature verification is controlled by the
>> +@code{check_appended_signatures} variable. Verification will only take place
>> +when files are loaded if the variable is set to @code{enforce}. If a
>> +certificate is built into the grub core image with the @code{--x509} parameter,
>> +the variable will be automatically set to @code{enforce} when the appendedsig
>> +module is loaded.
>> +
>> +Unlike GPG-style signatures, not all files loaded by GRUB are required to be
>> +signed. Once verification is turned on, the following file types must carry
>> +appended signatures:
>> +
>> +@enumerate
>> +@item Linux, Multiboot, BSD, XNU and Plan9 kernels
>> +@item Grub modules, except those built in to the core image
>> +@item Any new certificate files to be trusted
>> +@end enumerate
>> +
>> +ACPI tables and Device Tree images will not be checked for appended signatures
>> +but must be verified by another mechanism such as GPG-style signatures before
>> +they will be loaded.
>> +
>> +No attempt is made to validate any other file type. In particular,
>> +chain-loaded binaries are not verified - if your platform supports
>> +chain-loading and this cannot be disabled, consider an alternative secure
>> +boot mechanism.
>> +
>> +As with GPG-style appended signatures, signature checking does @strong{not}
>> +stop an attacker with console access from dropping manually to the GRUB
>> +console and executing:
>> +
>> +@example
>> +set check_appended_signatures=no
>> +@end example
>> +
>> +Refer to the section on password-protecting GRUB (@pxref{Authentication
>> +and authorisation}) for more information on preventing this.
>> +
>> +Additionally, special care must be taken around the @command{loadenv} command,
>> +which can be used to turn off @code{check_appended_signature}.
>> +
>>   @node UEFI secure boot and shim
>>   @section UEFI secure boot and shim support
>>   
>
>
> With this nit fixed: Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>

Thanks!

Kind regards,
Daniel Axtens


^ permalink raw reply	[flat|nested] 56+ messages in thread

end of thread, other threads:[~2022-04-21  7:15 UTC | newest]

Thread overview: 56+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-30  8:40 [PATCH v2 00/22] appended signature secure boot support Daniel Axtens
2021-06-30  8:40 ` [PATCH v2 01/22] ieee1275: drop HEAP_MAX_ADDR, HEAP_MIN_SIZE Daniel Axtens
2021-07-12 12:33   ` Stefan Berger
2021-07-14 16:21   ` Daniel Kiper
2021-06-30  8:40 ` [PATCH v2 02/22] ieee1275: claim more memory Daniel Axtens
2021-07-12 12:35   ` Stefan Berger
2021-07-15 21:51   ` Daniel Kiper
2021-07-16  3:59     ` Patrick Steinhardt
2021-07-21 14:45       ` Daniel Kiper
2021-07-21 15:24         ` Stefan Berger
2021-07-22 17:11         ` Stefan Berger
2021-07-28 11:17       ` Daniel Kiper
2021-06-30  8:40 ` [PATCH v2 03/22] ieee1275: request memory with ibm, client-architecture-support Daniel Axtens
2021-07-12 12:40   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 04/22] Add suport for signing grub with an appended signature Daniel Axtens
2021-07-12 12:43   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 05/22] docs/grub: Document signing grub under UEFI Daniel Axtens
2021-07-12 12:44   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 06/22] docs/grub: Document signing grub with an appended signature Daniel Axtens
2021-07-12 12:46   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 07/22] dl: provide a fake grub_dl_set_persistent for the emu target Daniel Axtens
2021-07-12 12:48   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 08/22] pgp: factor out rsa_pad Daniel Axtens
2021-07-12 12:52   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 09/22] crypto: move storage for grub_crypto_pk_* to crypto.c Daniel Axtens
2021-07-12 12:54   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 10/22] posix_wrap: tweaks in preparation for libtasn1 Daniel Axtens
2021-07-12 12:56   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 11/22] libtasn1: import libtasn1-4.16.0 Daniel Axtens
2021-07-20 21:46   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 12/22] libtasn1: disable code not needed in grub Daniel Axtens
2021-07-20 21:47   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 13/22] libtasn1: changes for grub compatibility Daniel Axtens
2021-07-12 13:04   ` Stefan Berger
2022-04-21  6:16     ` Daniel Axtens
2021-06-30  8:40 ` [PATCH v2 14/22] libtasn1: compile into asn1 module Daniel Axtens
2021-07-12 13:05   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 15/22] test_asn1: test module for libtasn1 Daniel Axtens
2021-07-12 19:35   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 16/22] grub-install: support embedding x509 certificates Daniel Axtens
2021-07-12 20:24   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 17/22] appended signatures: import GNUTLS's ASN.1 description files Daniel Axtens
2021-07-19 21:09   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 18/22] appended signatures: parse PKCS#7 signedData and X.509 certificates Daniel Axtens
2021-07-19 22:02   ` Stefan Berger
2022-04-21  6:36     ` Daniel Axtens
2021-06-30  8:40 ` [PATCH v2 19/22] appended signatures: support verifying appended signatures Daniel Axtens
2021-07-20  1:31   ` Stefan Berger
2022-04-21  7:10     ` Daniel Axtens
2021-06-30  8:40 ` [PATCH v2 20/22] appended signatures: verification tests Daniel Axtens
2021-07-20 12:49   ` Stefan Berger
2021-06-30  8:40 ` [PATCH v2 21/22] appended signatures: documentation Daniel Axtens
2021-07-19 22:24   ` Stefan Berger
2022-04-21  7:15     ` Daniel Axtens
2021-06-30  8:40 ` [PATCH v2 22/22] ieee1275: enter lockdown based on /ibm,secure-boot Daniel Axtens
2021-07-19 22:08   ` Stefan Berger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.