All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
@ 2019-10-11  7:41 AKASHI Takahiro
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 01/11] linux_compat: add kmemdup() AKASHI Takahiro
                   ` (11 more replies)
  0 siblings, 12 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Asn1 parsers of x509 certificates and pkcs7 messages are required
to implement image authentication and variable authentication as
part of UEFI secure boot feature.

As we discussed before in the thread[1], most people insisted that
we should re-use corresponding source code from Linux repository
for this purpose.

Here is my attempt to import all the necessary files from Linux; Those
will eventually be part of UEFI secure boot implementation, but I'd like
to get early feedback from other peoples before submitting the whole
patchset so that they will be better formatted for merging.

My approach here is
* files from the latest Linux
* modify files as little as possible
* mark/protect unavoidable changes with "#if(n)def __UBOOT__"
so that future fixes/differences in Linux repository will easily
be applied to U-Boot.

Please note that checkpatch.pl will complain with a bunch of
warnings/errors but I intentionally left them unchanged for the sake
of better maintainability I said above.

Any comments will be appreciated.
-Takahiro Akashi

[1] https://lists.denx.de/pipermail/u-boot/2019-April/366423.html

Changes in v1 (Oct 11, 2019) from RFC
* change the kernel code base from v5.0 to v5.3
* comment off x509_check_for_self_signed() which is not useful
  for UEFI secure boot (patch#9)
* improve usages of "#if(n)def __UBOOT__* to minimize differences
  between U-Boot and linux kernel

AKASHI Takahiro (11):
  linux_compat: add kmemdup()
  include: time.h: define time64_t
  include: kernel.h: include printk.h
  cmd: add asn1_compiler
  Makefile: add build script for asn1 parsers
  lib: add asn1 decoder
  lib: add oid registry utility
  lib: crypto: add public key utility
  lib: crypto: add x509 parser
  lib: crypto: add pkcs7 message parser
  lib: crypto: add rsa public key parser

 cmd/Kconfig                       |    3 +
 include/crypto/internal/rsa.h     |   57 +
 include/crypto/pkcs7.h            |   47 +
 include/crypto/public_key.h       |   90 ++
 include/keys/asymmetric-type.h    |   88 ++
 include/linux/asn1.h              |   65 ++
 include/linux/asn1_ber_bytecode.h |   89 ++
 include/linux/asn1_decoder.h      |   20 +
 include/linux/compat.h            |    4 +-
 include/linux/kernel.h            |    2 +
 include/linux/oid_registry.h      |  117 +++
 include/linux/time.h              |   24 +
 lib/Kconfig                       |   12 +
 lib/Makefile                      |   18 +
 lib/asn1_decoder.c                |  527 ++++++++++
 lib/build_OID_registry            |  203 ++++
 lib/crypto/Kconfig                |   38 +
 lib/crypto/Makefile               |   46 +
 lib/crypto/asymmetric_type.c      |  668 ++++++++++++
 lib/crypto/pkcs7.asn1             |  135 +++
 lib/crypto/pkcs7_parser.c         |  693 +++++++++++++
 lib/crypto/pkcs7_parser.h         |   65 ++
 lib/crypto/public_key.c           |  376 +++++++
 lib/crypto/rsa_helper.c           |  198 ++++
 lib/crypto/rsapubkey.asn1         |    4 +
 lib/crypto/x509.asn1              |   60 ++
 lib/crypto/x509_akid.asn1         |   35 +
 lib/crypto/x509_cert_parser.c     |  697 +++++++++++++
 lib/crypto/x509_parser.h          |   57 +
 lib/crypto/x509_public_key.c      |  292 ++++++
 lib/linux_compat.c                |   11 +
 lib/oid_registry.c                |  179 ++++
 scripts/Makefile                  |    3 +
 scripts/Makefile.build            |    2 +-
 scripts/asn1_compiler.c           | 1611 +++++++++++++++++++++++++++++
 35 files changed, 6533 insertions(+), 3 deletions(-)
 create mode 100644 include/crypto/internal/rsa.h
 create mode 100644 include/crypto/pkcs7.h
 create mode 100644 include/crypto/public_key.h
 create mode 100644 include/keys/asymmetric-type.h
 create mode 100644 include/linux/asn1.h
 create mode 100644 include/linux/asn1_ber_bytecode.h
 create mode 100644 include/linux/asn1_decoder.h
 create mode 100644 include/linux/oid_registry.h
 create mode 100644 lib/asn1_decoder.c
 create mode 100755 lib/build_OID_registry
 create mode 100644 lib/crypto/Kconfig
 create mode 100644 lib/crypto/Makefile
 create mode 100644 lib/crypto/asymmetric_type.c
 create mode 100644 lib/crypto/pkcs7.asn1
 create mode 100644 lib/crypto/pkcs7_parser.c
 create mode 100644 lib/crypto/pkcs7_parser.h
 create mode 100644 lib/crypto/public_key.c
 create mode 100644 lib/crypto/rsa_helper.c
 create mode 100644 lib/crypto/rsapubkey.asn1
 create mode 100644 lib/crypto/x509.asn1
 create mode 100644 lib/crypto/x509_akid.asn1
 create mode 100644 lib/crypto/x509_cert_parser.c
 create mode 100644 lib/crypto/x509_parser.h
 create mode 100644 lib/crypto/x509_public_key.c
 create mode 100644 lib/oid_registry.c
 create mode 100644 scripts/asn1_compiler.c

-- 
2.21.0

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

* [U-Boot] [PATCH v1 01/11] linux_compat: add kmemdup()
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-12 11:22   ` Heinrich Schuchardt
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 02/11] include: time.h: define time64_t AKASHI Takahiro
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Adding kmemdup() will help improve portability from linux kernel
code (in my case, lib/crypto/x509_cert_parser.c and pkcs7_parser.c).

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/linux/compat.h |  4 ++--
 lib/linux_compat.c     | 11 +++++++++++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/include/linux/compat.h b/include/linux/compat.h
index d0f51baab407..d23ef50454ce 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -117,6 +117,8 @@ static inline void kmem_cache_destroy(struct kmem_cache *cachep)
 	free(cachep);
 }
 
+void *kmemdup(const void *src, size_t size, int flags);
+
 #define DECLARE_WAITQUEUE(...)	do { } while (0)
 #define add_wait_queue(...)	do { } while (0)
 #define remove_wait_queue(...)	do { } while (0)
@@ -346,8 +348,6 @@ struct writeback_control {
 	unsigned for_sync:1;		/* sync(2) WB_SYNC_ALL writeback */
 };
 
-void *kmemdup(const void *src, size_t len, gfp_t gfp);
-
 typedef int irqreturn_t;
 
 struct timer_list {};
diff --git a/lib/linux_compat.c b/lib/linux_compat.c
index 6373b4451eb3..dd1e5b3c2087 100644
--- a/lib/linux_compat.c
+++ b/lib/linux_compat.c
@@ -40,3 +40,14 @@ void *kmem_cache_alloc(struct kmem_cache *obj, int flag)
 {
 	return malloc_cache_aligned(obj->sz);
 }
+
+void *kmemdup(const void *src, size_t size, int flags)
+{
+	void *p;
+
+	p = kmalloc(size, flags);
+	if (p)
+		memcpy(p, src, size);
+
+	return p;
+}
-- 
2.21.0

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

* [U-Boot] [PATCH v1 02/11] include: time.h: define time64_t
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 01/11] linux_compat: add kmemdup() AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-12 11:40   ` Heinrich Schuchardt
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 03/11] include: kernel.h: include printk.h AKASHI Takahiro
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Adding time64_t definition will help improve portability of linux kernel
code (in my case, lib/crypto/x509_cert_parser.c).

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/linux/time.h | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/include/linux/time.h b/include/linux/time.h
index b8d298eb4d68..6186985856d7 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -150,4 +150,28 @@ _DEFUN (ctime_r, (tim_p, result),
     return asctime_r (localtime_r (tim_p, &tm), result);
 }
 
+/* from <linux>/kernel/time/time.c */
+typedef __s64 time64_t;
+
+inline time64_t mktime64(const unsigned int year0, const unsigned int mon0,
+			 const unsigned int day, const unsigned int hour,
+			 const unsigned int min, const unsigned int sec)
+{
+	unsigned int mon = mon0, year = year0;
+
+	/* 1..12 -> 11,12,1..10 */
+	mon -= 2;
+	if (0 >= (int)mon) {
+		mon += 12;	/* Puts Feb last since it has leap day */
+		year -= 1;
+	}
+
+	return ((((time64_t)
+		  (year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) +
+		  year * 365 - 719499
+	  ) * 24 + hour /* now have hours - midnight tomorrow handled here */
+	  ) * 60 + min /* now have minutes */
+	) * 60 + sec; /* finally seconds */
+}
+
 #endif
-- 
2.21.0

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

* [U-Boot] [PATCH v1 03/11] include: kernel.h: include printk.h
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 01/11] linux_compat: add kmemdup() AKASHI Takahiro
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 02/11] include: time.h: define time64_t AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-12 11:47   ` Heinrich Schuchardt
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 04/11] cmd: add asn1_compiler AKASHI Takahiro
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Adding "printk.h" will help improve portability from linux kernel
code (in my case, lib/asn1_decoder.c).

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/linux/kernel.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index a85c15d8dc28..919d12bdf89c 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -2,7 +2,9 @@
 #define _LINUX_KERNEL_H
 
 
+#include <vsprintf.h> /* for printf utilities */
 #include <linux/types.h>
+#include <linux/printk.h>
 
 #define USHRT_MAX	((u16)(~0U))
 #define SHRT_MAX	((s16)(USHRT_MAX>>1))
-- 
2.21.0

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

* [U-Boot] [PATCH v1 04/11] cmd: add asn1_compiler
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (2 preceding siblings ...)
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 03/11] include: kernel.h: include printk.h AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-12 12:22   ` Heinrich Schuchardt
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 05/11] Makefile: add build script for asn1 parsers AKASHI Takahiro
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Imported from linux kernel v5.3.

This host command will be used to create a ASN1 parser, for example,
for pkcs7 or x509.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 cmd/Kconfig                       |    3 +
 include/linux/asn1.h              |   65 ++
 include/linux/asn1_ber_bytecode.h |   89 ++
 include/linux/asn1_decoder.h      |   20 +
 scripts/Makefile                  |    3 +
 scripts/asn1_compiler.c           | 1611 +++++++++++++++++++++++++++++
 6 files changed, 1791 insertions(+)
 create mode 100644 include/linux/asn1.h
 create mode 100644 include/linux/asn1_ber_bytecode.h
 create mode 100644 include/linux/asn1_decoder.h
 create mode 100644 scripts/asn1_compiler.c

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 4e61565aab16..605dcfdf4282 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -184,6 +184,9 @@ endmenu
 config BUILD_BIN2C
 	bool
 
+config BUILD_ASN1
+	bool
+
 comment "Commands"
 
 menu "Info commands"
diff --git a/include/linux/asn1.h b/include/linux/asn1.h
new file mode 100644
index 000000000000..a4d0bdd10711
--- /dev/null
+++ b/include/linux/asn1.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ASN.1 BER/DER/CER encoding definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifndef _LINUX_ASN1_H
+#define _LINUX_ASN1_H
+
+/* Class */
+enum asn1_class {
+	ASN1_UNIV	= 0,	/* Universal */
+	ASN1_APPL	= 1,	/* Application */
+	ASN1_CONT	= 2,	/* Context */
+	ASN1_PRIV	= 3	/* Private */
+};
+#define ASN1_CLASS_BITS	0xc0
+
+
+enum asn1_method {
+	ASN1_PRIM	= 0,	/* Primitive */
+	ASN1_CONS	= 1	/* Constructed */
+};
+#define ASN1_CONS_BIT	0x20
+
+/* Tag */
+enum asn1_tag {
+	ASN1_EOC	= 0,	/* End Of Contents or N/A */
+	ASN1_BOOL	= 1,	/* Boolean */
+	ASN1_INT	= 2,	/* Integer */
+	ASN1_BTS	= 3,	/* Bit String */
+	ASN1_OTS	= 4,	/* Octet String */
+	ASN1_NULL	= 5,	/* Null */
+	ASN1_OID	= 6,	/* Object Identifier  */
+	ASN1_ODE	= 7,	/* Object Description */
+	ASN1_EXT	= 8,	/* External */
+	ASN1_REAL	= 9,	/* Real float */
+	ASN1_ENUM	= 10,	/* Enumerated */
+	ASN1_EPDV	= 11,	/* Embedded PDV */
+	ASN1_UTF8STR	= 12,	/* UTF8 String */
+	ASN1_RELOID	= 13,	/* Relative OID */
+	/* 14 - Reserved */
+	/* 15 - Reserved */
+	ASN1_SEQ	= 16,	/* Sequence and Sequence of */
+	ASN1_SET	= 17,	/* Set and Set of */
+	ASN1_NUMSTR	= 18,	/* Numerical String */
+	ASN1_PRNSTR	= 19,	/* Printable String */
+	ASN1_TEXSTR	= 20,	/* T61 String / Teletext String */
+	ASN1_VIDSTR	= 21,	/* Videotex String */
+	ASN1_IA5STR	= 22,	/* IA5 String */
+	ASN1_UNITIM	= 23,	/* Universal Time */
+	ASN1_GENTIM	= 24,	/* General Time */
+	ASN1_GRASTR	= 25,	/* Graphic String */
+	ASN1_VISSTR	= 26,	/* Visible String */
+	ASN1_GENSTR	= 27,	/* General String */
+	ASN1_UNISTR	= 28,	/* Universal String */
+	ASN1_CHRSTR	= 29,	/* Character String */
+	ASN1_BMPSTR	= 30,	/* BMP String */
+	ASN1_LONG_TAG	= 31	/* Long form tag */
+};
+
+#define ASN1_INDEFINITE_LENGTH 0x80
+
+#endif /* _LINUX_ASN1_H */
diff --git a/include/linux/asn1_ber_bytecode.h b/include/linux/asn1_ber_bytecode.h
new file mode 100644
index 000000000000..b38361953a48
--- /dev/null
+++ b/include/linux/asn1_ber_bytecode.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ASN.1 BER/DER/CER parsing state machine internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifndef _LINUX_ASN1_BER_BYTECODE_H
+#define _LINUX_ASN1_BER_BYTECODE_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#endif
+#include <linux/asn1.h>
+
+typedef int (*asn1_action_t)(void *context,
+			     size_t hdrlen, /* In case of ANY type */
+			     unsigned char tag, /* In case of ANY type */
+			     const void *value, size_t vlen);
+
+struct asn1_decoder {
+	const unsigned char *machine;
+	size_t machlen;
+	const asn1_action_t *actions;
+};
+
+enum asn1_opcode {
+	/* The tag-matching ops come first and the odd-numbered slots
+	 * are for OR_SKIP ops.
+	 */
+#define ASN1_OP_MATCH__SKIP		  0x01
+#define ASN1_OP_MATCH__ACT		  0x02
+#define ASN1_OP_MATCH__JUMP		  0x04
+#define ASN1_OP_MATCH__ANY		  0x08
+#define ASN1_OP_MATCH__COND		  0x10
+
+	ASN1_OP_MATCH			= 0x00,
+	ASN1_OP_MATCH_OR_SKIP		= 0x01,
+	ASN1_OP_MATCH_ACT		= 0x02,
+	ASN1_OP_MATCH_ACT_OR_SKIP	= 0x03,
+	ASN1_OP_MATCH_JUMP		= 0x04,
+	ASN1_OP_MATCH_JUMP_OR_SKIP	= 0x05,
+	ASN1_OP_MATCH_ANY		= 0x08,
+	ASN1_OP_MATCH_ANY_OR_SKIP	= 0x09,
+	ASN1_OP_MATCH_ANY_ACT		= 0x0a,
+	ASN1_OP_MATCH_ANY_ACT_OR_SKIP	= 0x0b,
+	/* Everything before here matches unconditionally */
+
+	ASN1_OP_COND_MATCH_OR_SKIP	= 0x11,
+	ASN1_OP_COND_MATCH_ACT_OR_SKIP	= 0x13,
+	ASN1_OP_COND_MATCH_JUMP_OR_SKIP	= 0x15,
+	ASN1_OP_COND_MATCH_ANY		= 0x18,
+	ASN1_OP_COND_MATCH_ANY_OR_SKIP	= 0x19,
+	ASN1_OP_COND_MATCH_ANY_ACT	= 0x1a,
+	ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP = 0x1b,
+
+	/* Everything before here will want a tag from the data */
+#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP
+
+	/* These are here to help fill up space */
+	ASN1_OP_COND_FAIL		= 0x1c,
+	ASN1_OP_COMPLETE		= 0x1d,
+	ASN1_OP_ACT			= 0x1e,
+	ASN1_OP_MAYBE_ACT		= 0x1f,
+
+	/* The following eight have bit 0 -> SET, 1 -> OF, 2 -> ACT */
+	ASN1_OP_END_SEQ			= 0x20,
+	ASN1_OP_END_SET			= 0x21,
+	ASN1_OP_END_SEQ_OF		= 0x22,
+	ASN1_OP_END_SET_OF		= 0x23,
+	ASN1_OP_END_SEQ_ACT		= 0x24,
+	ASN1_OP_END_SET_ACT		= 0x25,
+	ASN1_OP_END_SEQ_OF_ACT		= 0x26,
+	ASN1_OP_END_SET_OF_ACT		= 0x27,
+#define ASN1_OP_END__SET		  0x01
+#define ASN1_OP_END__OF			  0x02
+#define ASN1_OP_END__ACT		  0x04
+
+	ASN1_OP_RETURN			= 0x28,
+
+	ASN1_OP__NR
+};
+
+#define _tag(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | ASN1_##TAG)
+#define _tagn(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | TAG)
+#define _jump_target(N) (N)
+#define _action(N) (N)
+
+#endif /* _LINUX_ASN1_BER_BYTECODE_H */
diff --git a/include/linux/asn1_decoder.h b/include/linux/asn1_decoder.h
new file mode 100644
index 000000000000..83f9c6e1e5e9
--- /dev/null
+++ b/include/linux/asn1_decoder.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ASN.1 decoder
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _LINUX_ASN1_DECODER_H
+#define _LINUX_ASN1_DECODER_H
+
+#include <linux/asn1.h>
+
+struct asn1_decoder;
+
+extern int asn1_ber_decoder(const struct asn1_decoder *decoder,
+			    void *context,
+			    const unsigned char *data,
+			    size_t datalen);
+
+#endif /* _LINUX_ASN1_DECODER_H */
diff --git a/scripts/Makefile b/scripts/Makefile
index e7b353f77f43..c4e2ae5db2ea 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -5,6 +5,9 @@
 # ---------------------------------------------------------------------------
 
 hostprogs-$(CONFIG_BUILD_BIN2C)		+= bin2c
+hostprogs-$(CONFIG_BUILD_ASN1)		+= asn1_compiler
+
+HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
 
 always		:= $(hostprogs-y)
 
diff --git a/scripts/asn1_compiler.c b/scripts/asn1_compiler.c
new file mode 100644
index 000000000000..adabd4145264
--- /dev/null
+++ b/scripts/asn1_compiler.c
@@ -0,0 +1,1611 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Simplified ASN.1 notation parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <linux/asn1_ber_bytecode.h>
+
+enum token_type {
+	DIRECTIVE_ABSENT,
+	DIRECTIVE_ALL,
+	DIRECTIVE_ANY,
+	DIRECTIVE_APPLICATION,
+	DIRECTIVE_AUTOMATIC,
+	DIRECTIVE_BEGIN,
+	DIRECTIVE_BIT,
+	DIRECTIVE_BMPString,
+	DIRECTIVE_BOOLEAN,
+	DIRECTIVE_BY,
+	DIRECTIVE_CHARACTER,
+	DIRECTIVE_CHOICE,
+	DIRECTIVE_CLASS,
+	DIRECTIVE_COMPONENT,
+	DIRECTIVE_COMPONENTS,
+	DIRECTIVE_CONSTRAINED,
+	DIRECTIVE_CONTAINING,
+	DIRECTIVE_DEFAULT,
+	DIRECTIVE_DEFINED,
+	DIRECTIVE_DEFINITIONS,
+	DIRECTIVE_EMBEDDED,
+	DIRECTIVE_ENCODED,
+	DIRECTIVE_ENCODING_CONTROL,
+	DIRECTIVE_END,
+	DIRECTIVE_ENUMERATED,
+	DIRECTIVE_EXCEPT,
+	DIRECTIVE_EXPLICIT,
+	DIRECTIVE_EXPORTS,
+	DIRECTIVE_EXTENSIBILITY,
+	DIRECTIVE_EXTERNAL,
+	DIRECTIVE_FALSE,
+	DIRECTIVE_FROM,
+	DIRECTIVE_GeneralString,
+	DIRECTIVE_GeneralizedTime,
+	DIRECTIVE_GraphicString,
+	DIRECTIVE_IA5String,
+	DIRECTIVE_IDENTIFIER,
+	DIRECTIVE_IMPLICIT,
+	DIRECTIVE_IMPLIED,
+	DIRECTIVE_IMPORTS,
+	DIRECTIVE_INCLUDES,
+	DIRECTIVE_INSTANCE,
+	DIRECTIVE_INSTRUCTIONS,
+	DIRECTIVE_INTEGER,
+	DIRECTIVE_INTERSECTION,
+	DIRECTIVE_ISO646String,
+	DIRECTIVE_MAX,
+	DIRECTIVE_MIN,
+	DIRECTIVE_MINUS_INFINITY,
+	DIRECTIVE_NULL,
+	DIRECTIVE_NumericString,
+	DIRECTIVE_OBJECT,
+	DIRECTIVE_OCTET,
+	DIRECTIVE_OF,
+	DIRECTIVE_OPTIONAL,
+	DIRECTIVE_ObjectDescriptor,
+	DIRECTIVE_PATTERN,
+	DIRECTIVE_PDV,
+	DIRECTIVE_PLUS_INFINITY,
+	DIRECTIVE_PRESENT,
+	DIRECTIVE_PRIVATE,
+	DIRECTIVE_PrintableString,
+	DIRECTIVE_REAL,
+	DIRECTIVE_RELATIVE_OID,
+	DIRECTIVE_SEQUENCE,
+	DIRECTIVE_SET,
+	DIRECTIVE_SIZE,
+	DIRECTIVE_STRING,
+	DIRECTIVE_SYNTAX,
+	DIRECTIVE_T61String,
+	DIRECTIVE_TAGS,
+	DIRECTIVE_TRUE,
+	DIRECTIVE_TeletexString,
+	DIRECTIVE_UNION,
+	DIRECTIVE_UNIQUE,
+	DIRECTIVE_UNIVERSAL,
+	DIRECTIVE_UTCTime,
+	DIRECTIVE_UTF8String,
+	DIRECTIVE_UniversalString,
+	DIRECTIVE_VideotexString,
+	DIRECTIVE_VisibleString,
+	DIRECTIVE_WITH,
+	NR__DIRECTIVES,
+	TOKEN_ASSIGNMENT = NR__DIRECTIVES,
+	TOKEN_OPEN_CURLY,
+	TOKEN_CLOSE_CURLY,
+	TOKEN_OPEN_SQUARE,
+	TOKEN_CLOSE_SQUARE,
+	TOKEN_OPEN_ACTION,
+	TOKEN_CLOSE_ACTION,
+	TOKEN_COMMA,
+	TOKEN_NUMBER,
+	TOKEN_TYPE_NAME,
+	TOKEN_ELEMENT_NAME,
+	NR__TOKENS
+};
+
+static const unsigned char token_to_tag[NR__TOKENS] = {
+	/* EOC goes first */
+	[DIRECTIVE_BOOLEAN]		= ASN1_BOOL,
+	[DIRECTIVE_INTEGER]		= ASN1_INT,
+	[DIRECTIVE_BIT]			= ASN1_BTS,
+	[DIRECTIVE_OCTET]		= ASN1_OTS,
+	[DIRECTIVE_NULL]		= ASN1_NULL,
+	[DIRECTIVE_OBJECT]		= ASN1_OID,
+	[DIRECTIVE_ObjectDescriptor]	= ASN1_ODE,
+	[DIRECTIVE_EXTERNAL]		= ASN1_EXT,
+	[DIRECTIVE_REAL]		= ASN1_REAL,
+	[DIRECTIVE_ENUMERATED]		= ASN1_ENUM,
+	[DIRECTIVE_EMBEDDED]		= 0,
+	[DIRECTIVE_UTF8String]		= ASN1_UTF8STR,
+	[DIRECTIVE_RELATIVE_OID]	= ASN1_RELOID,
+	/* 14 */
+	/* 15 */
+	[DIRECTIVE_SEQUENCE]		= ASN1_SEQ,
+	[DIRECTIVE_SET]			= ASN1_SET,
+	[DIRECTIVE_NumericString]	= ASN1_NUMSTR,
+	[DIRECTIVE_PrintableString]	= ASN1_PRNSTR,
+	[DIRECTIVE_T61String]		= ASN1_TEXSTR,
+	[DIRECTIVE_TeletexString]	= ASN1_TEXSTR,
+	[DIRECTIVE_VideotexString]	= ASN1_VIDSTR,
+	[DIRECTIVE_IA5String]		= ASN1_IA5STR,
+	[DIRECTIVE_UTCTime]		= ASN1_UNITIM,
+	[DIRECTIVE_GeneralizedTime]	= ASN1_GENTIM,
+	[DIRECTIVE_GraphicString]	= ASN1_GRASTR,
+	[DIRECTIVE_VisibleString]	= ASN1_VISSTR,
+	[DIRECTIVE_GeneralString]	= ASN1_GENSTR,
+	[DIRECTIVE_UniversalString]	= ASN1_UNITIM,
+	[DIRECTIVE_CHARACTER]		= ASN1_CHRSTR,
+	[DIRECTIVE_BMPString]		= ASN1_BMPSTR,
+};
+
+static const char asn1_classes[4][5] = {
+	[ASN1_UNIV]	= "UNIV",
+	[ASN1_APPL]	= "APPL",
+	[ASN1_CONT]	= "CONT",
+	[ASN1_PRIV]	= "PRIV"
+};
+
+static const char asn1_methods[2][5] = {
+	[ASN1_UNIV]	= "PRIM",
+	[ASN1_APPL]	= "CONS"
+};
+
+static const char *const asn1_universal_tags[32] = {
+	"EOC",
+	"BOOL",
+	"INT",
+	"BTS",
+	"OTS",
+	"NULL",
+	"OID",
+	"ODE",
+	"EXT",
+	"REAL",
+	"ENUM",
+	"EPDV",
+	"UTF8STR",
+	"RELOID",
+	NULL,		/* 14 */
+	NULL,		/* 15 */
+	"SEQ",
+	"SET",
+	"NUMSTR",
+	"PRNSTR",
+	"TEXSTR",
+	"VIDSTR",
+	"IA5STR",
+	"UNITIM",
+	"GENTIM",
+	"GRASTR",
+	"VISSTR",
+	"GENSTR",
+	"UNISTR",
+	"CHRSTR",
+	"BMPSTR",
+	NULL		/* 31 */
+};
+
+static const char *filename;
+static const char *grammar_name;
+static const char *outputname;
+static const char *headername;
+
+static const char *const directives[NR__DIRECTIVES] = {
+#define _(X) [DIRECTIVE_##X] = #X
+	_(ABSENT),
+	_(ALL),
+	_(ANY),
+	_(APPLICATION),
+	_(AUTOMATIC),
+	_(BEGIN),
+	_(BIT),
+	_(BMPString),
+	_(BOOLEAN),
+	_(BY),
+	_(CHARACTER),
+	_(CHOICE),
+	_(CLASS),
+	_(COMPONENT),
+	_(COMPONENTS),
+	_(CONSTRAINED),
+	_(CONTAINING),
+	_(DEFAULT),
+	_(DEFINED),
+	_(DEFINITIONS),
+	_(EMBEDDED),
+	_(ENCODED),
+	[DIRECTIVE_ENCODING_CONTROL] = "ENCODING-CONTROL",
+	_(END),
+	_(ENUMERATED),
+	_(EXCEPT),
+	_(EXPLICIT),
+	_(EXPORTS),
+	_(EXTENSIBILITY),
+	_(EXTERNAL),
+	_(FALSE),
+	_(FROM),
+	_(GeneralString),
+	_(GeneralizedTime),
+	_(GraphicString),
+	_(IA5String),
+	_(IDENTIFIER),
+	_(IMPLICIT),
+	_(IMPLIED),
+	_(IMPORTS),
+	_(INCLUDES),
+	_(INSTANCE),
+	_(INSTRUCTIONS),
+	_(INTEGER),
+	_(INTERSECTION),
+	_(ISO646String),
+	_(MAX),
+	_(MIN),
+	[DIRECTIVE_MINUS_INFINITY] = "MINUS-INFINITY",
+	[DIRECTIVE_NULL] = "NULL",
+	_(NumericString),
+	_(OBJECT),
+	_(OCTET),
+	_(OF),
+	_(OPTIONAL),
+	_(ObjectDescriptor),
+	_(PATTERN),
+	_(PDV),
+	[DIRECTIVE_PLUS_INFINITY] = "PLUS-INFINITY",
+	_(PRESENT),
+	_(PRIVATE),
+	_(PrintableString),
+	_(REAL),
+	[DIRECTIVE_RELATIVE_OID] = "RELATIVE-OID",
+	_(SEQUENCE),
+	_(SET),
+	_(SIZE),
+	_(STRING),
+	_(SYNTAX),
+	_(T61String),
+	_(TAGS),
+	_(TRUE),
+	_(TeletexString),
+	_(UNION),
+	_(UNIQUE),
+	_(UNIVERSAL),
+	_(UTCTime),
+	_(UTF8String),
+	_(UniversalString),
+	_(VideotexString),
+	_(VisibleString),
+	_(WITH)
+};
+
+struct action {
+	struct action	*next;
+	char		*name;
+	unsigned char	index;
+};
+
+static struct action *action_list;
+static unsigned nr_actions;
+
+struct token {
+	unsigned short	line;
+	enum token_type	token_type : 8;
+	unsigned char	size;
+	struct action	*action;
+	char		*content;
+	struct type	*type;
+};
+
+static struct token *token_list;
+static unsigned nr_tokens;
+static bool verbose_opt;
+static bool debug_opt;
+
+#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0)
+#define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0)
+
+static int directive_compare(const void *_key, const void *_pdir)
+{
+	const struct token *token = _key;
+	const char *const *pdir = _pdir, *dir = *pdir;
+	size_t dlen, clen;
+	int val;
+
+	dlen = strlen(dir);
+	clen = (dlen < token->size) ? dlen : token->size;
+
+	//debug("cmp(%s,%s) = ", token->content, dir);
+
+	val = memcmp(token->content, dir, clen);
+	if (val != 0) {
+		//debug("%d [cmp]\n", val);
+		return val;
+	}
+
+	if (dlen == token->size) {
+		//debug("0\n");
+		return 0;
+	}
+	//debug("%d\n", (int)dlen - (int)token->size);
+	return dlen - token->size; /* shorter -> negative */
+}
+
+/*
+ * Tokenise an ASN.1 grammar
+ */
+static void tokenise(char *buffer, char *end)
+{
+	struct token *tokens;
+	char *line, *nl, *start, *p, *q;
+	unsigned tix, lineno;
+
+	/* Assume we're going to have half as many tokens as we have
+	 * characters
+	 */
+	token_list = tokens = calloc((end - buffer) / 2, sizeof(struct token));
+	if (!tokens) {
+		perror(NULL);
+		exit(1);
+	}
+	tix = 0;
+
+	lineno = 0;
+	while (buffer < end) {
+		/* First of all, break out a line */
+		lineno++;
+		line = buffer;
+		nl = memchr(line, '\n', end - buffer);
+		if (!nl) {
+			buffer = nl = end;
+		} else {
+			buffer = nl + 1;
+			*nl = '\0';
+		}
+
+		/* Remove "--" comments */
+		p = line;
+	next_comment:
+		while ((p = memchr(p, '-', nl - p))) {
+			if (p[1] == '-') {
+				/* Found a comment; see if there's a terminator */
+				q = p + 2;
+				while ((q = memchr(q, '-', nl - q))) {
+					if (q[1] == '-') {
+						/* There is - excise the comment */
+						q += 2;
+						memmove(p, q, nl - q);
+						goto next_comment;
+					}
+					q++;
+				}
+				*p = '\0';
+				nl = p;
+				break;
+			} else {
+				p++;
+			}
+		}
+
+		p = line;
+		while (p < nl) {
+			/* Skip white space */
+			while (p < nl && isspace(*p))
+				*(p++) = 0;
+			if (p >= nl)
+				break;
+
+			tokens[tix].line = lineno;
+			start = p;
+
+			/* Handle string tokens */
+			if (isalpha(*p)) {
+				const char **dir;
+
+				/* Can be a directive, type name or element
+				 * name.  Find the end of the name.
+				 */
+				q = p + 1;
+				while (q < nl && (isalnum(*q) || *q == '-' || *q == '_'))
+					q++;
+				tokens[tix].size = q - p;
+				p = q;
+
+				tokens[tix].content = malloc(tokens[tix].size + 1);
+				if (!tokens[tix].content) {
+					perror(NULL);
+					exit(1);
+				}
+				memcpy(tokens[tix].content, start, tokens[tix].size);
+				tokens[tix].content[tokens[tix].size] = 0;
+				
+				/* If it begins with a lowercase letter then
+				 * it's an element name
+				 */
+				if (islower(tokens[tix].content[0])) {
+					tokens[tix++].token_type = TOKEN_ELEMENT_NAME;
+					continue;
+				}
+
+				/* Otherwise we need to search the directive
+				 * table
+				 */
+				dir = bsearch(&tokens[tix], directives,
+					      sizeof(directives) / sizeof(directives[1]),
+					      sizeof(directives[1]),
+					      directive_compare);
+				if (dir) {
+					tokens[tix++].token_type = dir - directives;
+					continue;
+				}
+
+				tokens[tix++].token_type = TOKEN_TYPE_NAME;
+				continue;
+			}
+
+			/* Handle numbers */
+			if (isdigit(*p)) {
+				/* Find the end of the number */
+				q = p + 1;
+				while (q < nl && (isdigit(*q)))
+					q++;
+				tokens[tix].size = q - p;
+				p = q;
+				tokens[tix].content = malloc(tokens[tix].size + 1);
+				if (!tokens[tix].content) {
+					perror(NULL);
+					exit(1);
+				}
+				memcpy(tokens[tix].content, start, tokens[tix].size);
+				tokens[tix].content[tokens[tix].size] = 0;
+				tokens[tix++].token_type = TOKEN_NUMBER;
+				continue;
+			}
+
+			if (nl - p >= 3) {
+				if (memcmp(p, "::=", 3) == 0) {
+					p += 3;
+					tokens[tix].size = 3;
+					tokens[tix].content = "::=";
+					tokens[tix++].token_type = TOKEN_ASSIGNMENT;
+					continue;
+				}
+			}
+
+			if (nl - p >= 2) {
+				if (memcmp(p, "({", 2) == 0) {
+					p += 2;
+					tokens[tix].size = 2;
+					tokens[tix].content = "({";
+					tokens[tix++].token_type = TOKEN_OPEN_ACTION;
+					continue;
+				}
+				if (memcmp(p, "})", 2) == 0) {
+					p += 2;
+					tokens[tix].size = 2;
+					tokens[tix].content = "})";
+					tokens[tix++].token_type = TOKEN_CLOSE_ACTION;
+					continue;
+				}
+			}
+
+			if (nl - p >= 1) {
+				tokens[tix].size = 1;
+				switch (*p) {
+				case '{':
+					p += 1;
+					tokens[tix].content = "{";
+					tokens[tix++].token_type = TOKEN_OPEN_CURLY;
+					continue;
+				case '}':
+					p += 1;
+					tokens[tix].content = "}";
+					tokens[tix++].token_type = TOKEN_CLOSE_CURLY;
+					continue;
+				case '[':
+					p += 1;
+					tokens[tix].content = "[";
+					tokens[tix++].token_type = TOKEN_OPEN_SQUARE;
+					continue;
+				case ']':
+					p += 1;
+					tokens[tix].content = "]";
+					tokens[tix++].token_type = TOKEN_CLOSE_SQUARE;
+					continue;
+				case ',':
+					p += 1;
+					tokens[tix].content = ",";
+					tokens[tix++].token_type = TOKEN_COMMA;
+					continue;
+				default:
+					break;
+				}
+			}
+
+			fprintf(stderr, "%s:%u: Unknown character in grammar: '%c'\n",
+				filename, lineno, *p);
+			exit(1);
+		}
+	}
+
+	nr_tokens = tix;
+	verbose("Extracted %u tokens\n", nr_tokens);
+
+#if 0
+	{
+		int n;
+		for (n = 0; n < nr_tokens; n++)
+			debug("Token %3u: '%s'\n", n, token_list[n].content);
+	}
+#endif
+}
+
+static void build_type_list(void);
+static void parse(void);
+static void dump_elements(void);
+static void render(FILE *out, FILE *hdr);
+
+/*
+ *
+ */
+int main(int argc, char **argv)
+{
+	struct stat st;
+	ssize_t readlen;
+	FILE *out, *hdr;
+	char *buffer, *p;
+	char *kbuild_verbose;
+	int fd;
+
+	kbuild_verbose = getenv("KBUILD_VERBOSE");
+	if (kbuild_verbose)
+		verbose_opt = atoi(kbuild_verbose);
+
+	while (argc > 4) {
+		if (strcmp(argv[1], "-v") == 0)
+			verbose_opt = true;
+		else if (strcmp(argv[1], "-d") == 0)
+			debug_opt = true;
+		else
+			break;
+		memmove(&argv[1], &argv[2], (argc - 2) * sizeof(char *));
+		argc--;
+	}
+
+	if (argc != 4) {
+		fprintf(stderr, "Format: %s [-v] [-d] <grammar-file> <c-file> <hdr-file>\n",
+			argv[0]);
+		exit(2);
+	}
+
+	filename = argv[1];
+	outputname = argv[2];
+	headername = argv[3];
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		perror(filename);
+		exit(1);
+	}
+
+	if (fstat(fd, &st) < 0) {
+		perror(filename);
+		exit(1);
+	}
+
+	if (!(buffer = malloc(st.st_size + 1))) {
+		perror(NULL);
+		exit(1);
+	}
+
+	if ((readlen = read(fd, buffer, st.st_size)) < 0) {
+		perror(filename);
+		exit(1);
+	}
+
+	if (close(fd) < 0) {
+		perror(filename);
+		exit(1);
+	}
+
+	if (readlen != st.st_size) {
+		fprintf(stderr, "%s: Short read\n", filename);
+		exit(1);
+	}
+
+	p = strrchr(argv[1], '/');
+	p = p ? p + 1 : argv[1];
+	grammar_name = strdup(p);
+	if (!p) {
+		perror(NULL);
+		exit(1);
+	}
+	p = strchr(grammar_name, '.');
+	if (p)
+		*p = '\0';
+
+	buffer[readlen] = 0;
+	tokenise(buffer, buffer + readlen);
+	build_type_list();
+	parse();
+	dump_elements();
+
+	out = fopen(outputname, "w");
+	if (!out) {
+		perror(outputname);
+		exit(1);
+	}
+
+	hdr = fopen(headername, "w");
+	if (!hdr) {
+		perror(headername);
+		exit(1);
+	}
+
+	render(out, hdr);
+
+	if (fclose(out) < 0) {
+		perror(outputname);
+		exit(1);
+	}
+
+	if (fclose(hdr) < 0) {
+		perror(headername);
+		exit(1);
+	}
+
+	return 0;
+}
+
+enum compound {
+	NOT_COMPOUND,
+	SET,
+	SET_OF,
+	SEQUENCE,
+	SEQUENCE_OF,
+	CHOICE,
+	ANY,
+	TYPE_REF,
+	TAG_OVERRIDE
+};
+
+struct element {
+	struct type	*type_def;
+	struct token	*name;
+	struct token	*type;
+	struct action	*action;
+	struct element	*children;
+	struct element	*next;
+	struct element	*render_next;
+	struct element	*list_next;
+	uint8_t		n_elements;
+	enum compound	compound : 8;
+	enum asn1_class	class : 8;
+	enum asn1_method method : 8;
+	uint8_t		tag;
+	unsigned	entry_index;
+	unsigned	flags;
+#define ELEMENT_IMPLICIT	0x0001
+#define ELEMENT_EXPLICIT	0x0002
+#define ELEMENT_TAG_SPECIFIED	0x0004
+#define ELEMENT_RENDERED	0x0008
+#define ELEMENT_SKIPPABLE	0x0010
+#define ELEMENT_CONDITIONAL	0x0020
+};
+
+struct type {
+	struct token	*name;
+	struct token	*def;
+	struct element	*element;
+	unsigned	ref_count;
+	unsigned	flags;
+#define TYPE_STOP_MARKER	0x0001
+#define TYPE_BEGIN		0x0002
+};
+
+static struct type *type_list;
+static struct type **type_index;
+static unsigned nr_types;
+
+static int type_index_compare(const void *_a, const void *_b)
+{
+	const struct type *const *a = _a, *const *b = _b;
+
+	if ((*a)->name->size != (*b)->name->size)
+		return (*a)->name->size - (*b)->name->size;
+	else
+		return memcmp((*a)->name->content, (*b)->name->content,
+			      (*a)->name->size);
+}
+
+static int type_finder(const void *_key, const void *_ti)
+{
+	const struct token *token = _key;
+	const struct type *const *ti = _ti;
+	const struct type *type = *ti;
+
+	if (token->size != type->name->size)
+		return token->size - type->name->size;
+	else
+		return memcmp(token->content, type->name->content,
+			      token->size);
+}
+
+/*
+ * Build up a list of types and a sorted index to that list.
+ */
+static void build_type_list(void)
+{
+	struct type *types;
+	unsigned nr, t, n;
+
+	nr = 0;
+	for (n = 0; n < nr_tokens - 1; n++)
+		if (token_list[n + 0].token_type == TOKEN_TYPE_NAME &&
+		    token_list[n + 1].token_type == TOKEN_ASSIGNMENT)
+			nr++;
+
+	if (nr == 0) {
+		fprintf(stderr, "%s: No defined types\n", filename);
+		exit(1);
+	}
+
+	nr_types = nr;
+	types = type_list = calloc(nr + 1, sizeof(type_list[0]));
+	if (!type_list) {
+		perror(NULL);
+		exit(1);
+	}
+	type_index = calloc(nr, sizeof(type_index[0]));
+	if (!type_index) {
+		perror(NULL);
+		exit(1);
+	}
+
+	t = 0;
+	types[t].flags |= TYPE_BEGIN;
+	for (n = 0; n < nr_tokens - 1; n++) {
+		if (token_list[n + 0].token_type == TOKEN_TYPE_NAME &&
+		    token_list[n + 1].token_type == TOKEN_ASSIGNMENT) {
+			types[t].name = &token_list[n];
+			type_index[t] = &types[t];
+			t++;
+		}
+	}
+	types[t].name = &token_list[n + 1];
+	types[t].flags |= TYPE_STOP_MARKER;
+
+	qsort(type_index, nr, sizeof(type_index[0]), type_index_compare);
+
+	verbose("Extracted %u types\n", nr_types);
+#if 0
+	for (n = 0; n < nr_types; n++) {
+		struct type *type = type_index[n];
+		debug("- %*.*s\n", type->name->content);
+	}
+#endif
+}
+
+static struct element *parse_type(struct token **_cursor, struct token *stop,
+				  struct token *name);
+
+/*
+ * Parse the token stream
+ */
+static void parse(void)
+{
+	struct token *cursor;
+	struct type *type;
+
+	/* Parse one type definition statement at a time */
+	type = type_list;
+	do {
+		cursor = type->name;
+
+		if (cursor[0].token_type != TOKEN_TYPE_NAME ||
+		    cursor[1].token_type != TOKEN_ASSIGNMENT)
+			abort();
+		cursor += 2;
+
+		type->element = parse_type(&cursor, type[1].name, NULL);
+		type->element->type_def = type;
+
+		if (cursor != type[1].name) {
+			fprintf(stderr, "%s:%d: Parse error at token '%s'\n",
+				filename, cursor->line, cursor->content);
+			exit(1);
+		}
+
+	} while (type++, !(type->flags & TYPE_STOP_MARKER));
+
+	verbose("Extracted %u actions\n", nr_actions);
+}
+
+static struct element *element_list;
+
+static struct element *alloc_elem(struct token *type)
+{
+	struct element *e = calloc(1, sizeof(*e));
+	if (!e) {
+		perror(NULL);
+		exit(1);
+	}
+	e->list_next = element_list;
+	element_list = e;
+	return e;
+}
+
+static struct element *parse_compound(struct token **_cursor, struct token *end,
+				      int alternates);
+
+/*
+ * Parse one type definition statement
+ */
+static struct element *parse_type(struct token **_cursor, struct token *end,
+				  struct token *name)
+{
+	struct element *top, *element;
+	struct action *action, **ppaction;
+	struct token *cursor = *_cursor;
+	struct type **ref;
+	char *p;
+	int labelled = 0, implicit = 0;
+
+	top = element = alloc_elem(cursor);
+	element->class = ASN1_UNIV;
+	element->method = ASN1_PRIM;
+	element->tag = token_to_tag[cursor->token_type];
+	element->name = name;
+
+	/* Extract the tag value if one given */
+	if (cursor->token_type == TOKEN_OPEN_SQUARE) {
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+		switch (cursor->token_type) {
+		case DIRECTIVE_UNIVERSAL:
+			element->class = ASN1_UNIV;
+			cursor++;
+			break;
+		case DIRECTIVE_APPLICATION:
+			element->class = ASN1_APPL;
+			cursor++;
+			break;
+		case TOKEN_NUMBER:
+			element->class = ASN1_CONT;
+			break;
+		case DIRECTIVE_PRIVATE:
+			element->class = ASN1_PRIV;
+			cursor++;
+			break;
+		default:
+			fprintf(stderr, "%s:%d: Unrecognised tag class token '%s'\n",
+				filename, cursor->line, cursor->content);
+			exit(1);
+		}
+
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type != TOKEN_NUMBER) {
+			fprintf(stderr, "%s:%d: Missing tag number '%s'\n",
+				filename, cursor->line, cursor->content);
+			exit(1);
+		}
+
+		element->tag &= ~0x1f;
+		element->tag |= strtoul(cursor->content, &p, 10);
+		element->flags |= ELEMENT_TAG_SPECIFIED;
+		if (p - cursor->content != cursor->size)
+			abort();
+		cursor++;
+
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type != TOKEN_CLOSE_SQUARE) {
+			fprintf(stderr, "%s:%d: Missing closing square bracket '%s'\n",
+				filename, cursor->line, cursor->content);
+			exit(1);
+		}
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+		labelled = 1;
+	}
+
+	/* Handle implicit and explicit markers */
+	if (cursor->token_type == DIRECTIVE_IMPLICIT) {
+		element->flags |= ELEMENT_IMPLICIT;
+		implicit = 1;
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+	} else if (cursor->token_type == DIRECTIVE_EXPLICIT) {
+		element->flags |= ELEMENT_EXPLICIT;
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+	}
+
+	if (labelled) {
+		if (!implicit)
+			element->method |= ASN1_CONS;
+		element->compound = implicit ? TAG_OVERRIDE : SEQUENCE;
+		element->children = alloc_elem(cursor);
+		element = element->children;
+		element->class = ASN1_UNIV;
+		element->method = ASN1_PRIM;
+		element->tag = token_to_tag[cursor->token_type];
+		element->name = name;
+	}
+
+	/* Extract the type we're expecting here */
+	element->type = cursor;
+	switch (cursor->token_type) {
+	case DIRECTIVE_ANY:
+		element->compound = ANY;
+		cursor++;
+		break;
+
+	case DIRECTIVE_NULL:
+	case DIRECTIVE_BOOLEAN:
+	case DIRECTIVE_ENUMERATED:
+	case DIRECTIVE_INTEGER:
+		element->compound = NOT_COMPOUND;
+		cursor++;
+		break;
+
+	case DIRECTIVE_EXTERNAL:
+		element->method = ASN1_CONS;
+
+	case DIRECTIVE_BMPString:
+	case DIRECTIVE_GeneralString:
+	case DIRECTIVE_GraphicString:
+	case DIRECTIVE_IA5String:
+	case DIRECTIVE_ISO646String:
+	case DIRECTIVE_NumericString:
+	case DIRECTIVE_PrintableString:
+	case DIRECTIVE_T61String:
+	case DIRECTIVE_TeletexString:
+	case DIRECTIVE_UniversalString:
+	case DIRECTIVE_UTF8String:
+	case DIRECTIVE_VideotexString:
+	case DIRECTIVE_VisibleString:
+	case DIRECTIVE_ObjectDescriptor:
+	case DIRECTIVE_GeneralizedTime:
+	case DIRECTIVE_UTCTime:
+		element->compound = NOT_COMPOUND;
+		cursor++;
+		break;
+
+	case DIRECTIVE_BIT:
+	case DIRECTIVE_OCTET:
+		element->compound = NOT_COMPOUND;
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type != DIRECTIVE_STRING)
+			goto parse_error;
+		cursor++;
+		break;
+
+	case DIRECTIVE_OBJECT:
+		element->compound = NOT_COMPOUND;
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type != DIRECTIVE_IDENTIFIER)
+			goto parse_error;
+		cursor++;
+		break;
+
+	case TOKEN_TYPE_NAME:
+		element->compound = TYPE_REF;
+		ref = bsearch(cursor, type_index, nr_types, sizeof(type_index[0]),
+			      type_finder);
+		if (!ref) {
+			fprintf(stderr, "%s:%d: Type '%s' undefined\n",
+				filename, cursor->line, cursor->content);
+			exit(1);
+		}
+		cursor->type = *ref;
+		(*ref)->ref_count++;
+		cursor++;
+		break;
+
+	case DIRECTIVE_CHOICE:
+		element->compound = CHOICE;
+		cursor++;
+		element->children = parse_compound(&cursor, end, 1);
+		break;
+
+	case DIRECTIVE_SEQUENCE:
+		element->compound = SEQUENCE;
+		element->method = ASN1_CONS;
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type == DIRECTIVE_OF) {
+			element->compound = SEQUENCE_OF;
+			cursor++;
+			if (cursor >= end)
+				goto overrun_error;
+			element->children = parse_type(&cursor, end, NULL);
+		} else {
+			element->children = parse_compound(&cursor, end, 0);
+		}
+		break;
+
+	case DIRECTIVE_SET:
+		element->compound = SET;
+		element->method = ASN1_CONS;
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type == DIRECTIVE_OF) {
+			element->compound = SET_OF;
+			cursor++;
+			if (cursor >= end)
+				goto parse_error;
+			element->children = parse_type(&cursor, end, NULL);
+		} else {
+			element->children = parse_compound(&cursor, end, 1);
+		}
+		break;
+
+	default:
+		fprintf(stderr, "%s:%d: Token '%s' does not introduce a type\n",
+			filename, cursor->line, cursor->content);
+		exit(1);
+	}
+
+	/* Handle elements that are optional */
+	if (cursor < end && (cursor->token_type == DIRECTIVE_OPTIONAL ||
+			     cursor->token_type == DIRECTIVE_DEFAULT)
+	    ) {
+		cursor++;
+		top->flags |= ELEMENT_SKIPPABLE;
+	}
+
+	if (cursor < end && cursor->token_type == TOKEN_OPEN_ACTION) {
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type != TOKEN_ELEMENT_NAME) {
+			fprintf(stderr, "%s:%d: Token '%s' is not an action function name\n",
+				filename, cursor->line, cursor->content);
+			exit(1);
+		}
+
+		action = malloc(sizeof(struct action));
+		if (!action) {
+			perror(NULL);
+			exit(1);
+		}
+		action->index = 0;
+		action->name = cursor->content;
+
+		for (ppaction = &action_list;
+		     *ppaction;
+		     ppaction = &(*ppaction)->next
+		     ) {
+			int cmp = strcmp(action->name, (*ppaction)->name);
+			if (cmp == 0) {
+				free(action);
+				action = *ppaction;
+				goto found;
+			}
+			if (cmp < 0) {
+				action->next = *ppaction;
+				*ppaction = action;
+				nr_actions++;
+				goto found;
+			}
+		}
+		action->next = NULL;
+		*ppaction = action;
+		nr_actions++;
+	found:
+
+		element->action = action;
+		cursor->action = action;
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type != TOKEN_CLOSE_ACTION) {
+			fprintf(stderr, "%s:%d: Missing close action, got '%s'\n",
+				filename, cursor->line, cursor->content);
+			exit(1);
+		}
+		cursor++;
+	}
+
+	*_cursor = cursor;
+	return top;
+
+parse_error:
+	fprintf(stderr, "%s:%d: Unexpected token '%s'\n",
+		filename, cursor->line, cursor->content);
+	exit(1);
+
+overrun_error:
+	fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
+	exit(1);
+}
+
+/*
+ * Parse a compound type list
+ */
+static struct element *parse_compound(struct token **_cursor, struct token *end,
+				      int alternates)
+{
+	struct element *children, **child_p = &children, *element;
+	struct token *cursor = *_cursor, *name;
+
+	if (cursor->token_type != TOKEN_OPEN_CURLY) {
+		fprintf(stderr, "%s:%d: Expected compound to start with brace not '%s'\n",
+			filename, cursor->line, cursor->content);
+		exit(1);
+	}
+	cursor++;
+	if (cursor >= end)
+		goto overrun_error;
+
+	if (cursor->token_type == TOKEN_OPEN_CURLY) {
+		fprintf(stderr, "%s:%d: Empty compound\n",
+			filename, cursor->line);
+		exit(1);
+	}
+
+	for (;;) {
+		name = NULL;
+		if (cursor->token_type == TOKEN_ELEMENT_NAME) {
+			name = cursor;
+			cursor++;
+			if (cursor >= end)
+				goto overrun_error;
+		}
+
+		element = parse_type(&cursor, end, name);
+		if (alternates)
+			element->flags |= ELEMENT_SKIPPABLE | ELEMENT_CONDITIONAL;
+
+		*child_p = element;
+		child_p = &element->next;
+
+		if (cursor >= end)
+			goto overrun_error;
+		if (cursor->token_type != TOKEN_COMMA)
+			break;
+		cursor++;
+		if (cursor >= end)
+			goto overrun_error;
+	}
+
+	children->flags &= ~ELEMENT_CONDITIONAL;
+
+	if (cursor->token_type != TOKEN_CLOSE_CURLY) {
+		fprintf(stderr, "%s:%d: Expected compound closure, got '%s'\n",
+			filename, cursor->line, cursor->content);
+		exit(1);
+	}
+	cursor++;
+
+	*_cursor = cursor;
+	return children;
+
+overrun_error:
+	fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
+	exit(1);
+}
+
+static void dump_element(const struct element *e, int level)
+{
+	const struct element *c;
+	const struct type *t = e->type_def;
+	const char *name = e->name ? e->name->content : ".";
+	const char *tname = t && t->name ? t->name->content : ".";
+	char tag[32];
+
+	if (e->class == 0 && e->method == 0 && e->tag == 0)
+		strcpy(tag, "<...>");
+	else if (e->class == ASN1_UNIV)
+		sprintf(tag, "%s %s %s",
+			asn1_classes[e->class],
+			asn1_methods[e->method],
+			asn1_universal_tags[e->tag]);
+	else
+		sprintf(tag, "%s %s %u",
+			asn1_classes[e->class],
+			asn1_methods[e->method],
+			e->tag);
+
+	printf("%c%c%c%c%c %c %*s[*] \e[33m%s\e[m %s %s \e[35m%s\e[m\n",
+	       e->flags & ELEMENT_IMPLICIT ? 'I' : '-',
+	       e->flags & ELEMENT_EXPLICIT ? 'E' : '-',
+	       e->flags & ELEMENT_TAG_SPECIFIED ? 'T' : '-',
+	       e->flags & ELEMENT_SKIPPABLE ? 'S' : '-',
+	       e->flags & ELEMENT_CONDITIONAL ? 'C' : '-',
+	       "-tTqQcaro"[e->compound],
+	       level, "",
+	       tag,
+	       tname,
+	       name,
+	       e->action ? e->action->name : "");
+	if (e->compound == TYPE_REF)
+		dump_element(e->type->type->element, level + 3);
+	else
+		for (c = e->children; c; c = c->next)
+			dump_element(c, level + 3);
+}
+
+static void dump_elements(void)
+{
+	if (debug_opt)
+		dump_element(type_list[0].element, 0);
+}
+
+static void render_element(FILE *out, struct element *e, struct element *tag);
+static void render_out_of_line_list(FILE *out);
+
+static int nr_entries;
+static int render_depth = 1;
+static struct element *render_list, **render_list_p = &render_list;
+
+__attribute__((format(printf, 2, 3)))
+static void render_opcode(FILE *out, const char *fmt, ...)
+{
+	va_list va;
+
+	if (out) {
+		fprintf(out, "\t[%4d] =%*s", nr_entries, render_depth, "");
+		va_start(va, fmt);
+		vfprintf(out, fmt, va);
+		va_end(va);
+	}
+	nr_entries++;
+}
+
+__attribute__((format(printf, 2, 3)))
+static void render_more(FILE *out, const char *fmt, ...)
+{
+	va_list va;
+
+	if (out) {
+		va_start(va, fmt);
+		vfprintf(out, fmt, va);
+		va_end(va);
+	}
+}
+
+/*
+ * Render the grammar into a state machine definition.
+ */
+static void render(FILE *out, FILE *hdr)
+{
+	struct element *e;
+	struct action *action;
+	struct type *root;
+	int index;
+
+	fprintf(hdr, "/*\n");
+	fprintf(hdr, " * Automatically generated by asn1_compiler.  Do not edit\n");
+	fprintf(hdr, " *\n");
+	fprintf(hdr, " * ASN.1 parser for %s\n", grammar_name);
+	fprintf(hdr, " */\n");
+	fprintf(hdr, "#include <linux/asn1_decoder.h>\n");
+	fprintf(hdr, "\n");
+	fprintf(hdr, "extern const struct asn1_decoder %s_decoder;\n", grammar_name);
+	if (ferror(hdr)) {
+		perror(headername);
+		exit(1);
+	}
+
+	fprintf(out, "/*\n");
+	fprintf(out, " * Automatically generated by asn1_compiler.  Do not edit\n");
+	fprintf(out, " *\n");
+	fprintf(out, " * ASN.1 parser for %s\n", grammar_name);
+	fprintf(out, " */\n");
+	fprintf(out, "#include <linux/asn1_ber_bytecode.h>\n");
+	fprintf(out, "#include \"%s.asn1.h\"\n", grammar_name);
+	fprintf(out, "\n");
+	if (ferror(out)) {
+		perror(outputname);
+		exit(1);
+	}
+
+	/* Tabulate the action functions we might have to call */
+	fprintf(hdr, "\n");
+	index = 0;
+	for (action = action_list; action; action = action->next) {
+		action->index = index++;
+		fprintf(hdr,
+			"extern int %s(void *, size_t, unsigned char,"
+			" const void *, size_t);\n",
+			action->name);
+	}
+	fprintf(hdr, "\n");
+
+	fprintf(out, "enum %s_actions {\n", grammar_name);
+	for (action = action_list; action; action = action->next)
+		fprintf(out, "\tACT_%s = %u,\n",
+			action->name, action->index);
+	fprintf(out, "\tNR__%s_actions = %u\n", grammar_name, nr_actions);
+	fprintf(out, "};\n");
+
+	fprintf(out, "\n");
+	fprintf(out, "static const asn1_action_t %s_action_table[NR__%s_actions] = {\n",
+		grammar_name, grammar_name);
+	for (action = action_list; action; action = action->next)
+		fprintf(out, "\t[%4u] = %s,\n", action->index, action->name);
+	fprintf(out, "};\n");
+
+	if (ferror(out)) {
+		perror(outputname);
+		exit(1);
+	}
+
+	/* We do two passes - the first one calculates all the offsets */
+	verbose("Pass 1\n");
+	nr_entries = 0;
+	root = &type_list[0];
+	render_element(NULL, root->element, NULL);
+	render_opcode(NULL, "ASN1_OP_COMPLETE,\n");
+	render_out_of_line_list(NULL);
+
+	for (e = element_list; e; e = e->list_next)
+		e->flags &= ~ELEMENT_RENDERED;
+
+	/* And then we actually render */
+	verbose("Pass 2\n");
+	fprintf(out, "\n");
+	fprintf(out, "static const unsigned char %s_machine[] = {\n",
+		grammar_name);
+
+	nr_entries = 0;
+	root = &type_list[0];
+	render_element(out, root->element, NULL);
+	render_opcode(out, "ASN1_OP_COMPLETE,\n");
+	render_out_of_line_list(out);
+
+	fprintf(out, "};\n");
+
+	fprintf(out, "\n");
+	fprintf(out, "const struct asn1_decoder %s_decoder = {\n", grammar_name);
+	fprintf(out, "\t.machine = %s_machine,\n", grammar_name);
+	fprintf(out, "\t.machlen = sizeof(%s_machine),\n", grammar_name);
+	fprintf(out, "\t.actions = %s_action_table,\n", grammar_name);
+	fprintf(out, "};\n");
+}
+
+/*
+ * Render the out-of-line elements
+ */
+static void render_out_of_line_list(FILE *out)
+{
+	struct element *e, *ce;
+	const char *act;
+	int entry;
+
+	while ((e = render_list)) {
+		render_list = e->render_next;
+		if (!render_list)
+			render_list_p = &render_list;
+
+		render_more(out, "\n");
+		e->entry_index = entry = nr_entries;
+		render_depth++;
+		for (ce = e->children; ce; ce = ce->next)
+			render_element(out, ce, NULL);
+		render_depth--;
+
+		act = e->action ? "_ACT" : "";
+		switch (e->compound) {
+		case SEQUENCE:
+			render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
+			break;
+		case SEQUENCE_OF:
+			render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act);
+			render_opcode(out, "_jump_target(%u),\n", entry);
+			break;
+		case SET:
+			render_opcode(out, "ASN1_OP_END_SET%s,\n", act);
+			break;
+		case SET_OF:
+			render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
+			render_opcode(out, "_jump_target(%u),\n", entry);
+			break;
+		default:
+			break;
+		}
+		if (e->action)
+			render_opcode(out, "_action(ACT_%s),\n",
+				      e->action->name);
+		render_opcode(out, "ASN1_OP_RETURN,\n");
+	}
+}
+
+/*
+ * Render an element.
+ */
+static void render_element(FILE *out, struct element *e, struct element *tag)
+{
+	struct element *ec, *x;
+	const char *cond, *act;
+	int entry, skippable = 0, outofline = 0;
+
+	if (e->flags & ELEMENT_SKIPPABLE ||
+	    (tag && tag->flags & ELEMENT_SKIPPABLE))
+		skippable = 1;
+
+	if ((e->type_def && e->type_def->ref_count > 1) ||
+	    skippable)
+		outofline = 1;
+
+	if (e->type_def && out) {
+		render_more(out, "\t// %s\n", e->type_def->name->content);
+	}
+
+	/* Render the operation */
+	cond = (e->flags & ELEMENT_CONDITIONAL ||
+		(tag && tag->flags & ELEMENT_CONDITIONAL)) ? "COND_" : "";
+	act = e->action ? "_ACT" : "";
+	switch (e->compound) {
+	case ANY:
+		render_opcode(out, "ASN1_OP_%sMATCH_ANY%s%s,",
+			      cond, act, skippable ? "_OR_SKIP" : "");
+		if (e->name)
+			render_more(out, "\t\t// %s", e->name->content);
+		render_more(out, "\n");
+		goto dont_render_tag;
+
+	case TAG_OVERRIDE:
+		render_element(out, e->children, e);
+		return;
+
+	case SEQUENCE:
+	case SEQUENCE_OF:
+	case SET:
+	case SET_OF:
+		render_opcode(out, "ASN1_OP_%sMATCH%s%s,",
+			      cond,
+			      outofline ? "_JUMP" : "",
+			      skippable ? "_OR_SKIP" : "");
+		break;
+
+	case CHOICE:
+		goto dont_render_tag;
+
+	case TYPE_REF:
+		if (e->class == ASN1_UNIV && e->method == ASN1_PRIM && e->tag == 0)
+			goto dont_render_tag;
+	default:
+		render_opcode(out, "ASN1_OP_%sMATCH%s%s,",
+			      cond, act,
+			      skippable ? "_OR_SKIP" : "");
+		break;
+	}
+
+	x = tag ?: e;
+	if (x->name)
+		render_more(out, "\t\t// %s", x->name->content);
+	render_more(out, "\n");
+
+	/* Render the tag */
+	if (!tag || !(tag->flags & ELEMENT_TAG_SPECIFIED))
+		tag = e;
+
+	if (tag->class == ASN1_UNIV &&
+	    tag->tag != 14 &&
+	    tag->tag != 15 &&
+	    tag->tag != 31)
+		render_opcode(out, "_tag(%s, %s, %s),\n",
+			      asn1_classes[tag->class],
+			      asn1_methods[tag->method | e->method],
+			      asn1_universal_tags[tag->tag]);
+	else
+		render_opcode(out, "_tagn(%s, %s, %2u),\n",
+			      asn1_classes[tag->class],
+			      asn1_methods[tag->method | e->method],
+			      tag->tag);
+	tag = NULL;
+dont_render_tag:
+
+	/* Deal with compound types */
+	switch (e->compound) {
+	case TYPE_REF:
+		render_element(out, e->type->type->element, tag);
+		if (e->action)
+			render_opcode(out, "ASN1_OP_%sACT,\n",
+				      skippable ? "MAYBE_" : "");
+		break;
+
+	case SEQUENCE:
+		if (outofline) {
+			/* Render out-of-line for multiple use or
+			 * skipability */
+			render_opcode(out, "_jump_target(%u),", e->entry_index);
+			if (e->type_def && e->type_def->name)
+				render_more(out, "\t\t// --> %s",
+					    e->type_def->name->content);
+			render_more(out, "\n");
+			if (!(e->flags & ELEMENT_RENDERED)) {
+				e->flags |= ELEMENT_RENDERED;
+				*render_list_p = e;
+				render_list_p = &e->render_next;
+			}
+			return;
+		} else {
+			/* Render inline for single use */
+			render_depth++;
+			for (ec = e->children; ec; ec = ec->next)
+				render_element(out, ec, NULL);
+			render_depth--;
+			render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
+		}
+		break;
+
+	case SEQUENCE_OF:
+	case SET_OF:
+		if (outofline) {
+			/* Render out-of-line for multiple use or
+			 * skipability */
+			render_opcode(out, "_jump_target(%u),", e->entry_index);
+			if (e->type_def && e->type_def->name)
+				render_more(out, "\t\t// --> %s",
+					    e->type_def->name->content);
+			render_more(out, "\n");
+			if (!(e->flags & ELEMENT_RENDERED)) {
+				e->flags |= ELEMENT_RENDERED;
+				*render_list_p = e;
+				render_list_p = &e->render_next;
+			}
+			return;
+		} else {
+			/* Render inline for single use */
+			entry = nr_entries;
+			render_depth++;
+			render_element(out, e->children, NULL);
+			render_depth--;
+			if (e->compound == SEQUENCE_OF)
+				render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act);
+			else
+				render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
+			render_opcode(out, "_jump_target(%u),\n", entry);
+		}
+		break;
+
+	case SET:
+		/* I can't think of a nice way to do SET support without having
+		 * a stack of bitmasks to make sure no element is repeated.
+		 * The bitmask has also to be checked that no non-optional
+		 * elements are left out whilst not preventing optional
+		 * elements from being left out.
+		 */
+		fprintf(stderr, "The ASN.1 SET type is not currently supported.\n");
+		exit(1);
+
+	case CHOICE:
+		for (ec = e->children; ec; ec = ec->next)
+			render_element(out, ec, ec);
+		if (!skippable)
+			render_opcode(out, "ASN1_OP_COND_FAIL,\n");
+		if (e->action)
+			render_opcode(out, "ASN1_OP_ACT,\n");
+		break;
+
+	default:
+		break;
+	}
+
+	if (e->action)
+		render_opcode(out, "_action(ACT_%s),\n", e->action->name);
+}
-- 
2.21.0

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

* [U-Boot] [PATCH v1 05/11] Makefile: add build script for asn1 parsers
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (3 preceding siblings ...)
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 04/11] cmd: add asn1_compiler AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-12 12:36   ` Heinrich Schuchardt
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 06/11] lib: add asn1 decoder AKASHI Takahiro
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

This rule will be used to build x509 and pkcs7 parsers.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 scripts/Makefile.build | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index f7a041296d3d..9357d310e50b 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -331,7 +331,7 @@ quiet_cmd_asn1_compiler = ASN.1   $@
       cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \
 				$(subst .h,.c,$@) $(subst .c,.h,$@)
 
-$(obj)/%-asn1.c $(obj)/%-asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
+$(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
 	$(call cmd,asn1_compiler)
 
 # Build the compiled-in targets
@@ -419,9 +419,11 @@ targets += $(multi-used-y) $(multi-used-m)
 intermediate_targets = $(foreach sfx, $(2), \
 				$(patsubst %$(strip $(1)),%$(sfx), \
 					$(filter %$(strip $(1)), $(targets))))
+# %.asn1.o <- %.asn1.[ch] <- %.asn1
 # %.lex.o <- %.lex.c <- %.l
 # %.tab.o <- %.tab.[ch] <- %.y
-targets += $(call intermediate_targets, .lex.o, .lex.c) \
+targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
+	   $(call intermediate_targets, .lex.o, .lex.c) \
 	   $(call intermediate_targets, .tab.o, .tab.c .tab.h)
 
 # Descending
-- 
2.21.0

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

* [U-Boot] [PATCH v1 06/11] lib: add asn1 decoder
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (4 preceding siblings ...)
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 05/11] Makefile: add build script for asn1 parsers AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-12 12:29   ` Heinrich Schuchardt
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 07/11] lib: add oid registry utility AKASHI Takahiro
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Imported from linux kernel v5.3.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/Kconfig        |   6 +
 lib/Makefile       |   1 +
 lib/asn1_decoder.c | 527 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 534 insertions(+)
 create mode 100644 lib/asn1_decoder.c

diff --git a/lib/Kconfig b/lib/Kconfig
index 3da45a5ec322..26c94f49ecd2 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -521,6 +521,12 @@ config SMBIOS_PRODUCT_NAME
 
 endmenu
 
+config ASN1
+	bool
+	select BUILD_ASN1
+	help
+	  Enable asn1 decoder library.
+
 source lib/efi/Kconfig
 source lib/efi_loader/Kconfig
 source lib/optee/Kconfig
diff --git a/lib/Makefile b/lib/Makefile
index 2fffd68f943c..eb3a675fb8c2 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_OF_LIVE) += of_live.o
 obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
 obj-$(CONFIG_ARCH_AT91) += at91/
 obj-$(CONFIG_OPTEE) += optee/
+obj-$(CONFIG_BUILD_ASN1) += asn1_decoder.o
 
 obj-$(CONFIG_AES) += aes.o
 
diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c
new file mode 100644
index 000000000000..db222625dd0f
--- /dev/null
+++ b/lib/asn1_decoder.c
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Decoder for ASN.1 BER/DER/CER encoded bytestream
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifdef __UBOOT__
+#include <linux/compat.h>
+#else
+#include <linux/export.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#ifndef __UBOOT__
+#include <linux/module.h>
+#endif
+#include <linux/asn1_decoder.h>
+#include <linux/asn1_ber_bytecode.h>
+
+static const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
+	/*					OPC TAG JMP ACT */
+	[ASN1_OP_MATCH]				= 1 + 1,
+	[ASN1_OP_MATCH_OR_SKIP]			= 1 + 1,
+	[ASN1_OP_MATCH_ACT]			= 1 + 1     + 1,
+	[ASN1_OP_MATCH_ACT_OR_SKIP]		= 1 + 1     + 1,
+	[ASN1_OP_MATCH_JUMP]			= 1 + 1 + 1,
+	[ASN1_OP_MATCH_JUMP_OR_SKIP]		= 1 + 1 + 1,
+	[ASN1_OP_MATCH_ANY]			= 1,
+	[ASN1_OP_MATCH_ANY_OR_SKIP]		= 1,
+	[ASN1_OP_MATCH_ANY_ACT]			= 1         + 1,
+	[ASN1_OP_MATCH_ANY_ACT_OR_SKIP]		= 1         + 1,
+	[ASN1_OP_COND_MATCH_OR_SKIP]		= 1 + 1,
+	[ASN1_OP_COND_MATCH_ACT_OR_SKIP]	= 1 + 1     + 1,
+	[ASN1_OP_COND_MATCH_JUMP_OR_SKIP]	= 1 + 1 + 1,
+	[ASN1_OP_COND_MATCH_ANY]		= 1,
+	[ASN1_OP_COND_MATCH_ANY_OR_SKIP]	= 1,
+	[ASN1_OP_COND_MATCH_ANY_ACT]		= 1         + 1,
+	[ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP]	= 1         + 1,
+	[ASN1_OP_COND_FAIL]			= 1,
+	[ASN1_OP_COMPLETE]			= 1,
+	[ASN1_OP_ACT]				= 1         + 1,
+	[ASN1_OP_MAYBE_ACT]			= 1         + 1,
+	[ASN1_OP_RETURN]			= 1,
+	[ASN1_OP_END_SEQ]			= 1,
+	[ASN1_OP_END_SEQ_OF]			= 1     + 1,
+	[ASN1_OP_END_SET]			= 1,
+	[ASN1_OP_END_SET_OF]			= 1     + 1,
+	[ASN1_OP_END_SEQ_ACT]			= 1         + 1,
+	[ASN1_OP_END_SEQ_OF_ACT]		= 1     + 1 + 1,
+	[ASN1_OP_END_SET_ACT]			= 1         + 1,
+	[ASN1_OP_END_SET_OF_ACT]		= 1     + 1 + 1,
+};
+
+/*
+ * Find the length of an indefinite length object
+ * @data: The data buffer
+ * @datalen: The end of the innermost containing element in the buffer
+ * @_dp: The data parse cursor (updated before returning)
+ * @_len: Where to return the size of the element.
+ * @_errmsg: Where to return a pointer to an error message on error
+ */
+static int asn1_find_indefinite_length(const unsigned char *data, size_t datalen,
+				       size_t *_dp, size_t *_len,
+				       const char **_errmsg)
+{
+	unsigned char tag, tmp;
+	size_t dp = *_dp, len, n;
+	int indef_level = 1;
+
+next_tag:
+	if (unlikely(datalen - dp < 2)) {
+		if (datalen == dp)
+			goto missing_eoc;
+		goto data_overrun_error;
+	}
+
+	/* Extract a tag from the data */
+	tag = data[dp++];
+	if (tag == ASN1_EOC) {
+		/* It appears to be an EOC. */
+		if (data[dp++] != 0)
+			goto invalid_eoc;
+		if (--indef_level <= 0) {
+			*_len = dp - *_dp;
+			*_dp = dp;
+			return 0;
+		}
+		goto next_tag;
+	}
+
+	if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) {
+		do {
+			if (unlikely(datalen - dp < 2))
+				goto data_overrun_error;
+			tmp = data[dp++];
+		} while (tmp & 0x80);
+	}
+
+	/* Extract the length */
+	len = data[dp++];
+	if (len <= 0x7f)
+		goto check_length;
+
+	if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
+		/* Indefinite length */
+		if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5))
+			goto indefinite_len_primitive;
+		indef_level++;
+		goto next_tag;
+	}
+
+	n = len - 0x80;
+	if (unlikely(n > sizeof(len) - 1))
+		goto length_too_long;
+	if (unlikely(n > datalen - dp))
+		goto data_overrun_error;
+	len = 0;
+	for (; n > 0; n--) {
+		len <<= 8;
+		len |= data[dp++];
+	}
+check_length:
+	if (len > datalen - dp)
+		goto data_overrun_error;
+	dp += len;
+	goto next_tag;
+
+length_too_long:
+	*_errmsg = "Unsupported length";
+	goto error;
+indefinite_len_primitive:
+	*_errmsg = "Indefinite len primitive not permitted";
+	goto error;
+invalid_eoc:
+	*_errmsg = "Invalid length EOC";
+	goto error;
+data_overrun_error:
+	*_errmsg = "Data overrun error";
+	goto error;
+missing_eoc:
+	*_errmsg = "Missing EOC in indefinite len cons";
+error:
+	*_dp = dp;
+	return -1;
+}
+
+/**
+ * asn1_ber_decoder - Decoder BER/DER/CER ASN.1 according to pattern
+ * @decoder: The decoder definition (produced by asn1_compiler)
+ * @context: The caller's context (to be passed to the action functions)
+ * @data: The encoded data
+ * @datalen: The size of the encoded data
+ *
+ * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern
+ * produced by asn1_compiler.  Action functions are called on marked tags to
+ * allow the caller to retrieve significant data.
+ *
+ * LIMITATIONS:
+ *
+ * To keep down the amount of stack used by this function, the following limits
+ * have been imposed:
+ *
+ *  (1) This won't handle datalen > 65535 without increasing the size of the
+ *	cons stack elements and length_too_long checking.
+ *
+ *  (2) The stack of constructed types is 10 deep.  If the depth of non-leaf
+ *	constructed types exceeds this, the decode will fail.
+ *
+ *  (3) The SET type (not the SET OF type) isn't really supported as tracking
+ *	what members of the set have been seen is a pain.
+ */
+int asn1_ber_decoder(const struct asn1_decoder *decoder,
+		     void *context,
+		     const unsigned char *data,
+		     size_t datalen)
+{
+	const unsigned char *machine = decoder->machine;
+	const asn1_action_t *actions = decoder->actions;
+	size_t machlen = decoder->machlen;
+	enum asn1_opcode op;
+	unsigned char tag = 0, csp = 0, jsp = 0, optag = 0, hdr = 0;
+	const char *errmsg;
+	size_t pc = 0, dp = 0, tdp = 0, len = 0;
+	int ret;
+
+	unsigned char flags = 0;
+#define FLAG_INDEFINITE_LENGTH	0x01
+#define FLAG_MATCHED		0x02
+#define FLAG_LAST_MATCHED	0x04 /* Last tag matched */
+#define FLAG_CONS		0x20 /* Corresponds to CONS bit in the opcode tag
+				      * - ie. whether or not we are going to parse
+				      *   a compound type.
+				      */
+
+#define NR_CONS_STACK 10
+	unsigned short cons_dp_stack[NR_CONS_STACK];
+	unsigned short cons_datalen_stack[NR_CONS_STACK];
+	unsigned char cons_hdrlen_stack[NR_CONS_STACK];
+#define NR_JUMP_STACK 10
+	unsigned char jump_stack[NR_JUMP_STACK];
+
+	if (datalen > 65535)
+		return -EMSGSIZE;
+
+next_op:
+	pr_debug("next_op: pc=\e[32m%zu\e[m/%zu dp=\e[33m%zu\e[m/%zu C=%d J=%d\n",
+		 pc, machlen, dp, datalen, csp, jsp);
+	if (unlikely(pc >= machlen))
+		goto machine_overrun_error;
+	op = machine[pc];
+	if (unlikely(pc + asn1_op_lengths[op] > machlen))
+		goto machine_overrun_error;
+
+	/* If this command is meant to match a tag, then do that before
+	 * evaluating the command.
+	 */
+	if (op <= ASN1_OP__MATCHES_TAG) {
+		unsigned char tmp;
+
+		/* Skip conditional matches if possible */
+		if ((op & ASN1_OP_MATCH__COND && flags & FLAG_MATCHED) ||
+		    (op & ASN1_OP_MATCH__SKIP && dp == datalen)) {
+			flags &= ~FLAG_LAST_MATCHED;
+			pc += asn1_op_lengths[op];
+			goto next_op;
+		}
+
+		flags = 0;
+		hdr = 2;
+
+		/* Extract a tag from the data */
+		if (unlikely(datalen - dp < 2))
+			goto data_overrun_error;
+		tag = data[dp++];
+		if (unlikely((tag & 0x1f) == ASN1_LONG_TAG))
+			goto long_tag_not_supported;
+
+		if (op & ASN1_OP_MATCH__ANY) {
+			pr_debug("- any %02x\n", tag);
+		} else {
+			/* Extract the tag from the machine
+			 * - Either CONS or PRIM are permitted in the data if
+			 *   CONS is not set in the op stream, otherwise CONS
+			 *   is mandatory.
+			 */
+			optag = machine[pc + 1];
+			flags |= optag & FLAG_CONS;
+
+			/* Determine whether the tag matched */
+			tmp = optag ^ tag;
+			tmp &= ~(optag & ASN1_CONS_BIT);
+			pr_debug("- match? %02x %02x %02x\n", tag, optag, tmp);
+			if (tmp != 0) {
+				/* All odd-numbered tags are MATCH_OR_SKIP. */
+				if (op & ASN1_OP_MATCH__SKIP) {
+					pc += asn1_op_lengths[op];
+					dp--;
+					goto next_op;
+				}
+				goto tag_mismatch;
+			}
+		}
+		flags |= FLAG_MATCHED;
+
+		len = data[dp++];
+		if (len > 0x7f) {
+			if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
+				/* Indefinite length */
+				if (unlikely(!(tag & ASN1_CONS_BIT)))
+					goto indefinite_len_primitive;
+				flags |= FLAG_INDEFINITE_LENGTH;
+				if (unlikely(2 > datalen - dp))
+					goto data_overrun_error;
+			} else {
+				int n = len - 0x80;
+				if (unlikely(n > 2))
+					goto length_too_long;
+				if (unlikely(n > datalen - dp))
+					goto data_overrun_error;
+				hdr += n;
+				for (len = 0; n > 0; n--) {
+					len <<= 8;
+					len |= data[dp++];
+				}
+				if (unlikely(len > datalen - dp))
+					goto data_overrun_error;
+			}
+		} else {
+			if (unlikely(len > datalen - dp))
+				goto data_overrun_error;
+		}
+
+		if (flags & FLAG_CONS) {
+			/* For expected compound forms, we stack the positions
+			 * of the start and end of the data.
+			 */
+			if (unlikely(csp >= NR_CONS_STACK))
+				goto cons_stack_overflow;
+			cons_dp_stack[csp] = dp;
+			cons_hdrlen_stack[csp] = hdr;
+			if (!(flags & FLAG_INDEFINITE_LENGTH)) {
+				cons_datalen_stack[csp] = datalen;
+				datalen = dp + len;
+			} else {
+				cons_datalen_stack[csp] = 0;
+			}
+			csp++;
+		}
+
+		pr_debug("- TAG: %02x %zu%s\n",
+			 tag, len, flags & FLAG_CONS ? " CONS" : "");
+		tdp = dp;
+	}
+
+	/* Decide how to handle the operation */
+	switch (op) {
+	case ASN1_OP_MATCH:
+	case ASN1_OP_MATCH_OR_SKIP:
+	case ASN1_OP_MATCH_ACT:
+	case ASN1_OP_MATCH_ACT_OR_SKIP:
+	case ASN1_OP_MATCH_ANY:
+	case ASN1_OP_MATCH_ANY_OR_SKIP:
+	case ASN1_OP_MATCH_ANY_ACT:
+	case ASN1_OP_MATCH_ANY_ACT_OR_SKIP:
+	case ASN1_OP_COND_MATCH_OR_SKIP:
+	case ASN1_OP_COND_MATCH_ACT_OR_SKIP:
+	case ASN1_OP_COND_MATCH_ANY:
+	case ASN1_OP_COND_MATCH_ANY_OR_SKIP:
+	case ASN1_OP_COND_MATCH_ANY_ACT:
+	case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP:
+
+		if (!(flags & FLAG_CONS)) {
+			if (flags & FLAG_INDEFINITE_LENGTH) {
+				size_t tmp = dp;
+
+				ret = asn1_find_indefinite_length(
+					data, datalen, &tmp, &len, &errmsg);
+				if (ret < 0)
+					goto error;
+			}
+			pr_debug("- LEAF: %zu\n", len);
+		}
+
+		if (op & ASN1_OP_MATCH__ACT) {
+			unsigned char act;
+
+			if (op & ASN1_OP_MATCH__ANY)
+				act = machine[pc + 1];
+			else
+				act = machine[pc + 2];
+			ret = actions[act](context, hdr, tag, data + dp, len);
+			if (ret < 0)
+				return ret;
+		}
+
+		if (!(flags & FLAG_CONS))
+			dp += len;
+		pc += asn1_op_lengths[op];
+		goto next_op;
+
+	case ASN1_OP_MATCH_JUMP:
+	case ASN1_OP_MATCH_JUMP_OR_SKIP:
+	case ASN1_OP_COND_MATCH_JUMP_OR_SKIP:
+		pr_debug("- MATCH_JUMP\n");
+		if (unlikely(jsp == NR_JUMP_STACK))
+			goto jump_stack_overflow;
+		jump_stack[jsp++] = pc + asn1_op_lengths[op];
+		pc = machine[pc + 2];
+		goto next_op;
+
+	case ASN1_OP_COND_FAIL:
+		if (unlikely(!(flags & FLAG_MATCHED)))
+			goto tag_mismatch;
+		pc += asn1_op_lengths[op];
+		goto next_op;
+
+	case ASN1_OP_COMPLETE:
+		if (unlikely(jsp != 0 || csp != 0)) {
+			pr_err("ASN.1 decoder error: Stacks not empty@completion (%u, %u)\n",
+			       jsp, csp);
+			return -EBADMSG;
+		}
+		return 0;
+
+	case ASN1_OP_END_SET:
+	case ASN1_OP_END_SET_ACT:
+		if (unlikely(!(flags & FLAG_MATCHED)))
+			goto tag_mismatch;
+		/* fall through */
+
+	case ASN1_OP_END_SEQ:
+	case ASN1_OP_END_SET_OF:
+	case ASN1_OP_END_SEQ_OF:
+	case ASN1_OP_END_SEQ_ACT:
+	case ASN1_OP_END_SET_OF_ACT:
+	case ASN1_OP_END_SEQ_OF_ACT:
+		if (unlikely(csp <= 0))
+			goto cons_stack_underflow;
+		csp--;
+		tdp = cons_dp_stack[csp];
+		hdr = cons_hdrlen_stack[csp];
+		len = datalen;
+		datalen = cons_datalen_stack[csp];
+		pr_debug("- end cons t=%zu dp=%zu l=%zu/%zu\n",
+			 tdp, dp, len, datalen);
+		if (datalen == 0) {
+			/* Indefinite length - check for the EOC. */
+			datalen = len;
+			if (unlikely(datalen - dp < 2))
+				goto data_overrun_error;
+			if (data[dp++] != 0) {
+				if (op & ASN1_OP_END__OF) {
+					dp--;
+					csp++;
+					pc = machine[pc + 1];
+					pr_debug("- continue\n");
+					goto next_op;
+				}
+				goto missing_eoc;
+			}
+			if (data[dp++] != 0)
+				goto invalid_eoc;
+			len = dp - tdp - 2;
+		} else {
+			if (dp < len && (op & ASN1_OP_END__OF)) {
+				datalen = len;
+				csp++;
+				pc = machine[pc + 1];
+				pr_debug("- continue\n");
+				goto next_op;
+			}
+			if (dp != len)
+				goto cons_length_error;
+			len -= tdp;
+			pr_debug("- cons len l=%zu d=%zu\n", len, dp - tdp);
+		}
+
+		if (op & ASN1_OP_END__ACT) {
+			unsigned char act;
+			if (op & ASN1_OP_END__OF)
+				act = machine[pc + 2];
+			else
+				act = machine[pc + 1];
+			ret = actions[act](context, hdr, 0, data + tdp, len);
+			if (ret < 0)
+				return ret;
+		}
+		pc += asn1_op_lengths[op];
+		goto next_op;
+
+	case ASN1_OP_MAYBE_ACT:
+		if (!(flags & FLAG_LAST_MATCHED)) {
+			pc += asn1_op_lengths[op];
+			goto next_op;
+		}
+		/* fall through */
+
+	case ASN1_OP_ACT:
+		ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len);
+		if (ret < 0)
+			return ret;
+		pc += asn1_op_lengths[op];
+		goto next_op;
+
+	case ASN1_OP_RETURN:
+		if (unlikely(jsp <= 0))
+			goto jump_stack_underflow;
+		pc = jump_stack[--jsp];
+		flags |= FLAG_MATCHED | FLAG_LAST_MATCHED;
+		goto next_op;
+
+	default:
+		break;
+	}
+
+	/* Shouldn't reach here */
+	pr_err("ASN.1 decoder error: Found reserved opcode (%u) pc=%zu\n",
+	       op, pc);
+	return -EBADMSG;
+
+data_overrun_error:
+	errmsg = "Data overrun error";
+	goto error;
+machine_overrun_error:
+	errmsg = "Machine overrun error";
+	goto error;
+jump_stack_underflow:
+	errmsg = "Jump stack underflow";
+	goto error;
+jump_stack_overflow:
+	errmsg = "Jump stack overflow";
+	goto error;
+cons_stack_underflow:
+	errmsg = "Cons stack underflow";
+	goto error;
+cons_stack_overflow:
+	errmsg = "Cons stack overflow";
+	goto error;
+cons_length_error:
+	errmsg = "Cons length error";
+	goto error;
+missing_eoc:
+	errmsg = "Missing EOC in indefinite len cons";
+	goto error;
+invalid_eoc:
+	errmsg = "Invalid length EOC";
+	goto error;
+length_too_long:
+	errmsg = "Unsupported length";
+	goto error;
+indefinite_len_primitive:
+	errmsg = "Indefinite len primitive not permitted";
+	goto error;
+tag_mismatch:
+	errmsg = "Unexpected tag";
+	goto error;
+long_tag_not_supported:
+	errmsg = "Long tag not supported";
+error:
+	pr_debug("\nASN1: %s [m=%zu d=%zu ot=%02x t=%02x l=%zu]\n",
+		 errmsg, pc, dp, optag, tag, len);
+	return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(asn1_ber_decoder);
+
+MODULE_LICENSE("GPL");
-- 
2.21.0

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

* [U-Boot] [PATCH v1 07/11] lib: add oid registry utility
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (5 preceding siblings ...)
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 06/11] lib: add asn1 decoder AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-12 12:58   ` Heinrich Schuchardt
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 08/11] lib: crypto: add public key utility AKASHI Takahiro
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Imported from linux kernel v5.3.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/linux/oid_registry.h | 117 ++++++++++++++++++++
 lib/Kconfig                  |   5 +
 lib/Makefile                 |  16 +++
 lib/build_OID_registry       | 203 +++++++++++++++++++++++++++++++++++
 lib/oid_registry.c           | 179 ++++++++++++++++++++++++++++++
 5 files changed, 520 insertions(+)
 create mode 100644 include/linux/oid_registry.h
 create mode 100755 lib/build_OID_registry
 create mode 100644 lib/oid_registry.c

diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
new file mode 100644
index 000000000000..657d6bf2c064
--- /dev/null
+++ b/include/linux/oid_registry.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ASN.1 Object identifier (OID) registry
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifndef _LINUX_OID_REGISTRY_H
+#define _LINUX_OID_REGISTRY_H
+
+#include <linux/types.h>
+
+/*
+ * OIDs are turned into these values if possible, or OID__NR if not held here.
+ *
+ * NOTE!  Do not mess with the format of each line as this is read by
+ *	  build_OID_registry.pl to generate the data for look_up_OID().
+ */
+enum OID {
+	OID_id_dsa_with_sha1,		/* 1.2.840.10030.4.3 */
+	OID_id_dsa,			/* 1.2.840.10040.4.1 */
+	OID_id_ecdsa_with_sha1,		/* 1.2.840.10045.4.1 */
+	OID_id_ecPublicKey,		/* 1.2.840.10045.2.1 */
+
+	/* PKCS#1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)} */
+	OID_rsaEncryption,		/* 1.2.840.113549.1.1.1 */
+	OID_md2WithRSAEncryption,	/* 1.2.840.113549.1.1.2 */
+	OID_md3WithRSAEncryption,	/* 1.2.840.113549.1.1.3 */
+	OID_md4WithRSAEncryption,	/* 1.2.840.113549.1.1.4 */
+	OID_sha1WithRSAEncryption,	/* 1.2.840.113549.1.1.5 */
+	OID_sha256WithRSAEncryption,	/* 1.2.840.113549.1.1.11 */
+	OID_sha384WithRSAEncryption,	/* 1.2.840.113549.1.1.12 */
+	OID_sha512WithRSAEncryption,	/* 1.2.840.113549.1.1.13 */
+	OID_sha224WithRSAEncryption,	/* 1.2.840.113549.1.1.14 */
+	/* PKCS#7 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-7(7)} */
+	OID_data,			/* 1.2.840.113549.1.7.1 */
+	OID_signed_data,		/* 1.2.840.113549.1.7.2 */
+	/* PKCS#9 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)} */
+	OID_email_address,		/* 1.2.840.113549.1.9.1 */
+	OID_contentType,		/* 1.2.840.113549.1.9.3 */
+	OID_messageDigest,		/* 1.2.840.113549.1.9.4 */
+	OID_signingTime,		/* 1.2.840.113549.1.9.5 */
+	OID_smimeCapabilites,		/* 1.2.840.113549.1.9.15 */
+	OID_smimeAuthenticatedAttrs,	/* 1.2.840.113549.1.9.16.2.11 */
+
+	/* {iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2)} */
+	OID_md2,			/* 1.2.840.113549.2.2 */
+	OID_md4,			/* 1.2.840.113549.2.4 */
+	OID_md5,			/* 1.2.840.113549.2.5 */
+
+	/* Microsoft Authenticode & Software Publishing */
+	OID_msIndirectData,		/* 1.3.6.1.4.1.311.2.1.4 */
+	OID_msStatementType,		/* 1.3.6.1.4.1.311.2.1.11 */
+	OID_msSpOpusInfo,		/* 1.3.6.1.4.1.311.2.1.12 */
+	OID_msPeImageDataObjId,		/* 1.3.6.1.4.1.311.2.1.15 */
+	OID_msIndividualSPKeyPurpose,	/* 1.3.6.1.4.1.311.2.1.21 */
+	OID_msOutlookExpress,		/* 1.3.6.1.4.1.311.16.4 */
+
+	OID_certAuthInfoAccess,		/* 1.3.6.1.5.5.7.1.1 */
+	OID_sha1,			/* 1.3.14.3.2.26 */
+	OID_sha256,			/* 2.16.840.1.101.3.4.2.1 */
+	OID_sha384,			/* 2.16.840.1.101.3.4.2.2 */
+	OID_sha512,			/* 2.16.840.1.101.3.4.2.3 */
+	OID_sha224,			/* 2.16.840.1.101.3.4.2.4 */
+
+	/* Distinguished Name attribute IDs [RFC 2256] */
+	OID_commonName,			/* 2.5.4.3 */
+	OID_surname,			/* 2.5.4.4 */
+	OID_countryName,		/* 2.5.4.6 */
+	OID_locality,			/* 2.5.4.7 */
+	OID_stateOrProvinceName,	/* 2.5.4.8 */
+	OID_organizationName,		/* 2.5.4.10 */
+	OID_organizationUnitName,	/* 2.5.4.11 */
+	OID_title,			/* 2.5.4.12 */
+	OID_description,		/* 2.5.4.13 */
+	OID_name,			/* 2.5.4.41 */
+	OID_givenName,			/* 2.5.4.42 */
+	OID_initials,			/* 2.5.4.43 */
+	OID_generationalQualifier,	/* 2.5.4.44 */
+
+	/* Certificate extension IDs */
+	OID_subjectKeyIdentifier,	/* 2.5.29.14 */
+	OID_keyUsage,			/* 2.5.29.15 */
+	OID_subjectAltName,		/* 2.5.29.17 */
+	OID_issuerAltName,		/* 2.5.29.18 */
+	OID_basicConstraints,		/* 2.5.29.19 */
+	OID_crlDistributionPoints,	/* 2.5.29.31 */
+	OID_certPolicies,		/* 2.5.29.32 */
+	OID_authorityKeyIdentifier,	/* 2.5.29.35 */
+	OID_extKeyUsage,		/* 2.5.29.37 */
+
+	/* EC-RDSA */
+	OID_gostCPSignA,		/* 1.2.643.2.2.35.1 */
+	OID_gostCPSignB,		/* 1.2.643.2.2.35.2 */
+	OID_gostCPSignC,		/* 1.2.643.2.2.35.3 */
+	OID_gost2012PKey256,		/* 1.2.643.7.1.1.1.1 */
+	OID_gost2012PKey512,		/* 1.2.643.7.1.1.1.2 */
+	OID_gost2012Digest256,		/* 1.2.643.7.1.1.2.2 */
+	OID_gost2012Digest512,		/* 1.2.643.7.1.1.2.3 */
+	OID_gost2012Signature256,	/* 1.2.643.7.1.1.3.2 */
+	OID_gost2012Signature512,	/* 1.2.643.7.1.1.3.3 */
+	OID_gostTC26Sign256A,		/* 1.2.643.7.1.2.1.1.1 */
+	OID_gostTC26Sign256B,		/* 1.2.643.7.1.2.1.1.2 */
+	OID_gostTC26Sign256C,		/* 1.2.643.7.1.2.1.1.3 */
+	OID_gostTC26Sign256D,		/* 1.2.643.7.1.2.1.1.4 */
+	OID_gostTC26Sign512A,		/* 1.2.643.7.1.2.1.2.1 */
+	OID_gostTC26Sign512B,		/* 1.2.643.7.1.2.1.2.2 */
+	OID_gostTC26Sign512C,		/* 1.2.643.7.1.2.1.2.3 */
+
+	OID__NR
+};
+
+extern enum OID look_up_OID(const void *data, size_t datasize);
+extern int sprint_oid(const void *, size_t, char *, size_t);
+extern int sprint_OID(enum OID, char *, size_t);
+
+#endif /* _LINUX_OID_REGISTRY_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 26c94f49ecd2..d2955c8feb0e 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -527,6 +527,11 @@ config ASN1
 	help
 	  Enable asn1 decoder library.
 
+config OID_REGISTRY
+	bool
+	help
+	  Enable fast lookup object identifier registry.
+
 source lib/efi/Kconfig
 source lib/efi_loader/Kconfig
 source lib/optee/Kconfig
diff --git a/lib/Makefile b/lib/Makefile
index eb3a675fb8c2..677ec33a1ce1 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -118,4 +118,20 @@ else
 obj-y += vsprintf.o strto.o strmhz.o
 endif
 
+#
+# Build a fast OID lookip registry from include/linux/oid_registry.h
+#
+obj-$(CONFIG_OID_REGISTRY) += oid_registry.o
+
+$(obj)/oid_registry.o: $(obj)/oid_registry_data.c
+
+$(obj)/oid_registry_data.c: $(srctree)/include/linux/oid_registry.h \
+			    $(src)/build_OID_registry
+	$(call cmd,build_OID_registry)
+
+quiet_cmd_build_OID_registry = GEN     $@
+      cmd_build_OID_registry = perl $(srctree)/$(src)/build_OID_registry $< $@
+
+clean-files     += oid_registry_data.c
+
 subdir-ccflags-$(CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED) += -O2
diff --git a/lib/build_OID_registry b/lib/build_OID_registry
new file mode 100755
index 000000000000..d7fc32ea8ac2
--- /dev/null
+++ b/lib/build_OID_registry
@@ -0,0 +1,203 @@
+#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Build a static ASN.1 Object Identified (OID) registry
+#
+# Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+
+use strict;
+
+my @names = ();
+my @oids = ();
+
+if ($#ARGV != 1) {
+    print STDERR "Format: ", $0, " <in-h-file> <out-c-file>\n";
+    exit(2);
+}
+
+#
+# Open the file to read from
+#
+open IN_FILE, "<$ARGV[0]" || die;
+while (<IN_FILE>) {
+    chomp;
+    if (m!\s+OID_([a-zA-z][a-zA-Z0-9_]+),\s+/[*]\s+([012][.0-9]*)\s+[*]/!) {
+	push @names, $1;
+	push @oids, $2;
+    }
+}
+close IN_FILE || die;
+
+#
+# Open the files to write into
+#
+open C_FILE, ">$ARGV[1]" or die;
+print C_FILE "/*\n";
+print C_FILE " * Automatically generated by ", $0, ".  Do not edit\n";
+print C_FILE " */\n";
+
+#
+# Split the data up into separate lists and also determine the lengths of the
+# encoded data arrays.
+#
+my @indices = ();
+my @lengths = ();
+my $total_length = 0;
+
+for (my $i = 0; $i <= $#names; $i++) {
+    my $name = $names[$i];
+    my $oid = $oids[$i];
+
+    my @components = split(/[.]/, $oid);
+
+    # Determine the encoded length of this OID
+    my $size = $#components;
+    for (my $loop = 2; $loop <= $#components; $loop++) {
+	my $c = $components[$loop];
+
+	# We will base128 encode the number
+	my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
+	$tmp = int($tmp / 7);
+	$size += $tmp;
+    }
+    push @lengths, $size;
+    push @indices, $total_length;
+    $total_length += $size;
+}
+
+#
+# Emit the look-up-by-OID index table
+#
+print C_FILE "\n";
+if ($total_length <= 255) {
+    print C_FILE "static const unsigned char oid_index[OID__NR + 1] = {\n";
+} else {
+    print C_FILE "static const unsigned short oid_index[OID__NR + 1] = {\n";
+}
+for (my $i = 0; $i <= $#names; $i++) {
+    print C_FILE "\t[OID_", $names[$i], "] = ", $indices[$i], ",\n"
+}
+print C_FILE "\t[OID__NR] = ", $total_length, "\n";
+print C_FILE "};\n";
+
+#
+# Encode the OIDs
+#
+my @encoded_oids = ();
+
+for (my $i = 0; $i <= $#names; $i++) {
+    my @octets = ();
+
+    my @components = split(/[.]/, $oids[$i]);
+
+    push @octets, $components[0] * 40 + $components[1];
+
+    for (my $loop = 2; $loop <= $#components; $loop++) {
+	my $c = $components[$loop];
+
+	# Base128 encode the number
+	my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
+	$tmp = int($tmp / 7);
+
+	for (; $tmp > 0; $tmp--) {
+	    push @octets, (($c >> $tmp * 7) & 0x7f) | 0x80;
+	}
+	push @octets, $c & 0x7f;
+    }
+
+    push @encoded_oids, \@octets;
+}
+
+#
+# Create a hash value for each OID
+#
+my @hash_values = ();
+for (my $i = 0; $i <= $#names; $i++) {
+    my @octets = @{$encoded_oids[$i]};
+
+    my $hash = $#octets;
+    foreach (@octets) {
+	$hash += $_ * 33;
+    }
+
+    $hash = ($hash >> 24) ^ ($hash >> 16) ^ ($hash >> 8) ^ ($hash);
+
+    push @hash_values, $hash & 0xff;
+}
+
+#
+# Emit the OID data
+#
+print C_FILE "\n";
+print C_FILE "static const unsigned char oid_data[", $total_length, "] = {\n";
+for (my $i = 0; $i <= $#names; $i++) {
+    my @octets = @{$encoded_oids[$i]};
+    print C_FILE "\t";
+    print C_FILE $_, ", " foreach (@octets);
+    print C_FILE "\t// ", $names[$i];
+    print C_FILE "\n";
+}
+print C_FILE "};\n";
+
+#
+# Build the search index table (ordered by length then hash then content)
+#
+my @index_table = ( 0 .. $#names );
+
+@index_table = sort {
+    my @octets_a = @{$encoded_oids[$a]};
+    my @octets_b = @{$encoded_oids[$b]};
+
+    return $hash_values[$a] <=> $hash_values[$b]
+	if ($hash_values[$a] != $hash_values[$b]);
+    return $#octets_a <=> $#octets_b
+	if ($#octets_a != $#octets_b);
+    for (my $i = $#octets_a; $i >= 0; $i--) {
+	return $octets_a[$i] <=> $octets_b[$i]
+	    if ($octets_a[$i] != $octets_b[$i]);
+    }
+    return 0;
+
+} @index_table;
+
+#
+# Emit the search index and hash value table
+#
+print C_FILE "\n";
+print C_FILE "static const struct {\n";
+print C_FILE "\tunsigned char hash;\n";
+if ($#names <= 255) {
+    print C_FILE "\tenum OID oid : 8;\n";
+} else {
+    print C_FILE "\tenum OID oid : 16;\n";
+}
+print C_FILE "} oid_search_table[OID__NR] = {\n";
+for (my $i = 0; $i <= $#names; $i++) {
+    my @octets = @{$encoded_oids[$index_table[$i]]};
+    printf(C_FILE "\t[%3u] = { %3u, OID_%-35s }, // ",
+	   $i,
+	   $hash_values[$index_table[$i]],
+	   $names[$index_table[$i]]);
+    printf C_FILE "%02x", $_ foreach (@octets);
+    print C_FILE "\n";
+}
+print C_FILE "};\n";
+
+#
+# Emit the OID debugging name table
+#
+#print C_FILE "\n";
+#print C_FILE "const char *const oid_name_table[OID__NR + 1] = {\n";
+#
+#for (my $i = 0; $i <= $#names; $i++) {
+#    print C_FILE "\t\"", $names[$i], "\",\n"
+#}
+#print C_FILE "\t\"Unknown-OID\"\n";
+#print C_FILE "};\n";
+
+#
+# Polish off
+#
+close C_FILE or die;
diff --git a/lib/oid_registry.c b/lib/oid_registry.c
new file mode 100644
index 000000000000..209edc73b99f
--- /dev/null
+++ b/lib/oid_registry.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ASN.1 Object identifier (OID) registry
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifdef __UBOOT__
+#include <linux/compat.h>
+#else
+#include <linux/module.h>
+#include <linux/export.h>
+#endif
+#include <linux/oid_registry.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include "oid_registry_data.c"
+
+MODULE_DESCRIPTION("OID Registry");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+/**
+ * look_up_OID - Find an OID registration for the specified data
+ * @data: Binary representation of the OID
+ * @datasize: Size of the binary representation
+ */
+enum OID look_up_OID(const void *data, size_t datasize)
+{
+	const unsigned char *octets = data;
+	enum OID oid;
+	unsigned char xhash;
+	unsigned i, j, k, hash;
+	size_t len;
+
+	/* Hash the OID data */
+	hash = datasize - 1;
+
+	for (i = 0; i < datasize; i++)
+		hash += octets[i] * 33;
+	hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash;
+	hash &= 0xff;
+
+	/* Binary search the OID registry.  OIDs are stored in ascending order
+	 * of hash value then ascending order of size and then in ascending
+	 * order of reverse value.
+	 */
+	i = 0;
+	k = OID__NR;
+	while (i < k) {
+		j = (i + k) / 2;
+
+		xhash = oid_search_table[j].hash;
+		if (xhash > hash) {
+			k = j;
+			continue;
+		}
+		if (xhash < hash) {
+			i = j + 1;
+			continue;
+		}
+
+		oid = oid_search_table[j].oid;
+		len = oid_index[oid + 1] - oid_index[oid];
+		if (len > datasize) {
+			k = j;
+			continue;
+		}
+		if (len < datasize) {
+			i = j + 1;
+			continue;
+		}
+
+		/* Variation is most likely to be@the tail end of the
+		 * OID, so do the comparison in reverse.
+		 */
+		while (len > 0) {
+			unsigned char a = oid_data[oid_index[oid] + --len];
+			unsigned char b = octets[len];
+			if (a > b) {
+				k = j;
+				goto next;
+			}
+			if (a < b) {
+				i = j + 1;
+				goto next;
+			}
+		}
+		return oid;
+	next:
+		;
+	}
+
+	return OID__NR;
+}
+EXPORT_SYMBOL_GPL(look_up_OID);
+
+/*
+ * sprint_OID - Print an Object Identifier into a buffer
+ * @data: The encoded OID to print
+ * @datasize: The size of the encoded OID
+ * @buffer: The buffer to render into
+ * @bufsize: The size of the buffer
+ *
+ * The OID is rendered into the buffer in "a.b.c.d" format and the number of
+ * bytes is returned.  -EBADMSG is returned if the data could not be intepreted
+ * and -ENOBUFS if the buffer was too small.
+ */
+int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize)
+{
+	const unsigned char *v = data, *end = v + datasize;
+	unsigned long num;
+	unsigned char n;
+	size_t ret;
+	int count;
+
+	if (v >= end)
+		goto bad;
+
+	n = *v++;
+	ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40);
+	if (count >= bufsize)
+		return -ENOBUFS;
+	buffer += count;
+	bufsize -= count;
+
+	while (v < end) {
+		num = 0;
+		n = *v++;
+		if (!(n & 0x80)) {
+			num = n;
+		} else {
+			num = n & 0x7f;
+			do {
+				if (v >= end)
+					goto bad;
+				n = *v++;
+				num <<= 7;
+				num |= n & 0x7f;
+			} while (n & 0x80);
+		}
+		ret += count = snprintf(buffer, bufsize, ".%lu", num);
+		if (count >= bufsize)
+			return -ENOBUFS;
+		buffer += count;
+		bufsize -= count;
+	}
+
+	return ret;
+
+bad:
+	snprintf(buffer, bufsize, "(bad)");
+	return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(sprint_oid);
+
+/**
+ * sprint_OID - Print an Object Identifier into a buffer
+ * @oid: The OID to print
+ * @buffer: The buffer to render into
+ * @bufsize: The size of the buffer
+ *
+ * The OID is rendered into the buffer in "a.b.c.d" format and the number of
+ * bytes is returned.
+ */
+int sprint_OID(enum OID oid, char *buffer, size_t bufsize)
+{
+	int ret;
+
+	BUG_ON(oid >= OID__NR);
+
+	ret = sprint_oid(oid_data + oid_index[oid],
+			 oid_index[oid + 1] - oid_index[oid],
+			 buffer, bufsize);
+	BUG_ON(ret == -EBADMSG);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sprint_OID);
-- 
2.21.0

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

* [U-Boot] [PATCH v1 08/11] lib: crypto: add public key utility
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (6 preceding siblings ...)
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 07/11] lib: add oid registry utility AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 09/11] lib: crypto: add x509 parser AKASHI Takahiro
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Imported from linux kernel v5.3.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/crypto/public_key.h    |  90 +++++
 include/keys/asymmetric-type.h |  88 +++++
 lib/crypto/Kconfig             |  19 +
 lib/crypto/Makefile            |  10 +
 lib/crypto/asymmetric_type.c   | 668 +++++++++++++++++++++++++++++++++
 lib/crypto/public_key.c        | 376 +++++++++++++++++++
 6 files changed, 1251 insertions(+)
 create mode 100644 include/crypto/public_key.h
 create mode 100644 include/keys/asymmetric-type.h
 create mode 100644 lib/crypto/Kconfig
 create mode 100644 lib/crypto/Makefile
 create mode 100644 lib/crypto/asymmetric_type.c
 create mode 100644 lib/crypto/public_key.c

diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
new file mode 100644
index 000000000000..436a1ee1ee64
--- /dev/null
+++ b/include/crypto/public_key.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Asymmetric public-key algorithm definitions
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifndef _LINUX_PUBLIC_KEY_H
+#define _LINUX_PUBLIC_KEY_H
+
+#ifdef __UBOOT__
+#include <linux/types.h>
+#else
+#include <linux/keyctl.h>
+#endif
+#include <linux/oid_registry.h>
+
+/*
+ * Cryptographic data for the public-key subtype of the asymmetric key type.
+ *
+ * Note that this may include private part of the key as well as the public
+ * part.
+ */
+struct public_key {
+	void *key;
+	u32 keylen;
+	enum OID algo;
+	void *params;
+	u32 paramlen;
+	bool key_is_private;
+	const char *id_type;
+	const char *pkey_algo;
+};
+
+extern void public_key_free(struct public_key *key);
+
+/*
+ * Public key cryptography signature data
+ */
+struct public_key_signature {
+	struct asymmetric_key_id *auth_ids[2];
+	u8 *s;			/* Signature */
+	u32 s_size;		/* Number of bytes in signature */
+	u8 *digest;
+	u8 digest_size;		/* Number of bytes in digest */
+	const char *pkey_algo;
+	const char *hash_algo;
+	const char *encoding;
+};
+
+extern void public_key_signature_free(struct public_key_signature *sig);
+
+#ifndef __UBOOT__
+extern struct asymmetric_key_subtype public_key_subtype;
+
+struct key;
+struct key_type;
+union key_payload;
+
+extern int restrict_link_by_signature(struct key *dest_keyring,
+				      const struct key_type *type,
+				      const union key_payload *payload,
+				      struct key *trust_keyring);
+
+extern int restrict_link_by_key_or_keyring(struct key *dest_keyring,
+					   const struct key_type *type,
+					   const union key_payload *payload,
+					   struct key *trusted);
+
+extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring,
+						 const struct key_type *type,
+						 const union key_payload *payload,
+						 struct key *trusted);
+
+extern int query_asymmetric_key(const struct kernel_pkey_params *,
+				struct kernel_pkey_query *);
+
+extern int encrypt_blob(struct kernel_pkey_params *, const void *, void *);
+extern int decrypt_blob(struct kernel_pkey_params *, const void *, void *);
+extern int create_signature(struct kernel_pkey_params *, const void *, void *);
+extern int verify_signature(const struct key *,
+			    const struct public_key_signature *);
+
+int public_key_verify_signature(const struct public_key *pkey,
+				const struct public_key_signature *sig);
+#endif /* !__UBOOT__ */
+
+#endif /* _LINUX_PUBLIC_KEY_H */
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
new file mode 100644
index 000000000000..47d83917df2f
--- /dev/null
+++ b/include/keys/asymmetric-type.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Asymmetric Public-key cryptography key type interface
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifndef _KEYS_ASYMMETRIC_TYPE_H
+#define _KEYS_ASYMMETRIC_TYPE_H
+
+#ifndef __UBOOT__
+#include <linux/key-type.h>
+#include <linux/verification.h>
+
+extern struct key_type key_type_asymmetric;
+
+/*
+ * The key payload is four words.  The asymmetric-type key uses them as
+ * follows:
+ */
+enum asymmetric_payload_bits {
+	asym_crypto,		/* The data representing the key */
+	asym_subtype,		/* Pointer to an asymmetric_key_subtype struct */
+	asym_key_ids,		/* Pointer to an asymmetric_key_ids struct */
+	asym_auth		/* The key's authorisation (signature, parent key ID) */
+};
+#endif /* !__UBOOT__ */
+
+/*
+ * Identifiers for an asymmetric key ID.  We have three ways of looking up a
+ * key derived from an X.509 certificate:
+ *
+ * (1) Serial Number & Issuer.  Non-optional.  This is the only valid way to
+ *     map a PKCS#7 signature to an X.509 certificate.
+ *
+ * (2) Issuer & Subject Unique IDs.  Optional.  These were the original way to
+ *     match X.509 certificates, but have fallen into disuse in favour of (3).
+ *
+ * (3) Auth & Subject Key Identifiers.  Optional.  SKIDs are only provided on
+ *     CA keys that are intended to sign other keys, so don't appear in end
+ *     user certificates unless forced.
+ *
+ * We could also support an PGP key identifier, which is just a SHA1 sum of the
+ * public key and certain parameters, but since we don't support PGP keys at
+ * the moment, we shall ignore those.
+ *
+ * What we actually do is provide a place where binary identifiers can be
+ * stashed and then compare against them when checking for an id match.
+ */
+struct asymmetric_key_id {
+	unsigned short	len;
+	unsigned char	data[];
+};
+
+struct asymmetric_key_ids {
+	void		*id[2];
+};
+
+extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
+				   const struct asymmetric_key_id *kid2);
+
+extern bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
+				      const struct asymmetric_key_id *kid2);
+
+extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
+							    size_t len_1,
+							    const void *val_2,
+							    size_t len_2);
+#ifndef __UBOOT__
+static inline
+const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
+{
+	return key->payload.data[asym_key_ids];
+}
+
+extern struct key *find_asymmetric_key(struct key *keyring,
+				       const struct asymmetric_key_id *id_0,
+				       const struct asymmetric_key_id *id_1,
+				       bool partial);
+#endif
+
+/*
+ * The payload is at the discretion of the subtype.
+ */
+
+#endif /* _KEYS_ASYMMETRIC_TYPE_H */
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
new file mode 100644
index 000000000000..b8e8288d2f80
--- /dev/null
+++ b/lib/crypto/Kconfig
@@ -0,0 +1,19 @@
+menuconfig ASYMMETRIC_KEY_TYPE
+	bool "Asymmetric (public-key cryptographic) key Support"
+	help
+	  This option provides support for a key type that holds the data for
+	  the asymmetric keys used for public key cryptographic operations such
+	  as encryption, decryption, signature generation and signature
+	  verification.
+
+if ASYMMETRIC_KEY_TYPE
+
+config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	bool "Asymmetric public-key crypto algorithm subtype"
+	help
+	  This option provides support for asymmetric public key type handling.
+	  If signature generation and/or verification are to be used,
+	  appropriate hash algorithms (such as SHA-1) must be available.
+	  ENOPKG will be reported if the requisite algorithm is unavailable.
+
+endif # ASYMMETRIC_KEY_TYPE
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
new file mode 100644
index 000000000000..a284de9e0411
--- /dev/null
+++ b/lib/crypto/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Makefile for asymmetric cryptographic keys
+#
+
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
+
+asymmetric_keys-y := asymmetric_type.o
+
+obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
diff --git a/lib/crypto/asymmetric_type.c b/lib/crypto/asymmetric_type.c
new file mode 100644
index 000000000000..e04666c0801d
--- /dev/null
+++ b/lib/crypto/asymmetric_type.c
@@ -0,0 +1,668 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Asymmetric public-key cryptography key type
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+#ifndef __UBOOT__
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#endif
+#include <crypto/public_key.h>
+#ifdef __UBOOT__
+#include <linux/compat.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#else
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#endif
+#ifdef __UBOOT__
+#include <keys/asymmetric-type.h>
+#else
+#include <keys/system_keyring.h>
+#include <keys/user-type.h>
+#include "asymmetric_keys.h"
+#endif
+
+MODULE_LICENSE("GPL");
+
+#ifndef __UBOOT__
+const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
+	[VERIFYING_MODULE_SIGNATURE]		= "mod sig",
+	[VERIFYING_FIRMWARE_SIGNATURE]		= "firmware sig",
+	[VERIFYING_KEXEC_PE_SIGNATURE]		= "kexec PE sig",
+	[VERIFYING_KEY_SIGNATURE]		= "key sig",
+	[VERIFYING_KEY_SELF_SIGNATURE]		= "key self sig",
+	[VERIFYING_UNSPECIFIED_SIGNATURE]	= "unspec sig",
+};
+EXPORT_SYMBOL_GPL(key_being_used_for);
+
+static LIST_HEAD(asymmetric_key_parsers);
+static DECLARE_RWSEM(asymmetric_key_parsers_sem);
+
+/**
+ * find_asymmetric_key - Find a key by ID.
+ * @keyring: The keys to search.
+ * @id_0: The first ID to look for or NULL.
+ * @id_1: The second ID to look for or NULL.
+ * @partial: Use partial match if true, exact if false.
+ *
+ * Find a key in the given keyring by identifier.  The preferred identifier is
+ * the id_0 and the fallback identifier is the id_1.  If both are given, the
+ * lookup is by the former, but the latter must also match.
+ */
+struct key *find_asymmetric_key(struct key *keyring,
+				const struct asymmetric_key_id *id_0,
+				const struct asymmetric_key_id *id_1,
+				bool partial)
+{
+	struct key *key;
+	key_ref_t ref;
+	const char *lookup;
+	char *req, *p;
+	int len;
+
+	BUG_ON(!id_0 && !id_1);
+
+	if (id_0) {
+		lookup = id_0->data;
+		len = id_0->len;
+	} else {
+		lookup = id_1->data;
+		len = id_1->len;
+	}
+
+	/* Construct an identifier "id:<keyid>". */
+	p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
+	if (!req)
+		return ERR_PTR(-ENOMEM);
+
+	if (partial) {
+		*p++ = 'i';
+		*p++ = 'd';
+	} else {
+		*p++ = 'e';
+		*p++ = 'x';
+	}
+	*p++ = ':';
+	p = bin2hex(p, lookup, len);
+	*p = 0;
+
+	pr_debug("Look up: \"%s\"\n", req);
+
+	ref = keyring_search(make_key_ref(keyring, 1),
+			     &key_type_asymmetric, req, true);
+	if (IS_ERR(ref))
+		pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
+	kfree(req);
+
+	if (IS_ERR(ref)) {
+		switch (PTR_ERR(ref)) {
+			/* Hide some search errors */
+		case -EACCES:
+		case -ENOTDIR:
+		case -EAGAIN:
+			return ERR_PTR(-ENOKEY);
+		default:
+			return ERR_CAST(ref);
+		}
+	}
+
+	key = key_ref_to_ptr(ref);
+	if (id_0 && id_1) {
+		const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+
+		if (!kids->id[1]) {
+			pr_debug("First ID matches, but second is missing\n");
+			goto reject;
+		}
+		if (!asymmetric_key_id_same(id_1, kids->id[1])) {
+			pr_debug("First ID matches, but second does not\n");
+			goto reject;
+		}
+	}
+
+	pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
+	return key;
+
+reject:
+	key_put(key);
+	return ERR_PTR(-EKEYREJECTED);
+}
+EXPORT_SYMBOL_GPL(find_asymmetric_key);
+#endif /* !__UBOOT__ */
+
+/**
+ * asymmetric_key_generate_id: Construct an asymmetric key ID
+ * @val_1: First binary blob
+ * @len_1: Length of first binary blob
+ * @val_2: Second binary blob
+ * @len_2: Length of second binary blob
+ *
+ * Construct an asymmetric key ID from a pair of binary blobs.
+ */
+struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
+						     size_t len_1,
+						     const void *val_2,
+						     size_t len_2)
+{
+	struct asymmetric_key_id *kid;
+
+	kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
+		      GFP_KERNEL);
+	if (!kid)
+		return ERR_PTR(-ENOMEM);
+	kid->len = len_1 + len_2;
+	memcpy(kid->data, val_1, len_1);
+	memcpy(kid->data + len_1, val_2, len_2);
+	return kid;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_generate_id);
+
+/**
+ * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
+ * @kid_1, @kid_2: The key IDs to compare
+ */
+bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
+			    const struct asymmetric_key_id *kid2)
+{
+	if (!kid1 || !kid2)
+		return false;
+	if (kid1->len != kid2->len)
+		return false;
+	return memcmp(kid1->data, kid2->data, kid1->len) == 0;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_id_same);
+
+/**
+ * asymmetric_key_id_partial - Return true if two asymmetric keys IDs
+ * partially match
+ * @kid_1, @kid_2: The key IDs to compare
+ */
+bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
+			       const struct asymmetric_key_id *kid2)
+{
+	if (!kid1 || !kid2)
+		return false;
+	if (kid1->len < kid2->len)
+		return false;
+	return memcmp(kid1->data + (kid1->len - kid2->len),
+		      kid2->data, kid2->len) == 0;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
+
+#ifndef __UBOOT__
+/**
+ * asymmetric_match_key_ids - Search asymmetric key IDs
+ * @kids: The list of key IDs to check
+ * @match_id: The key ID we're looking for
+ * @match: The match function to use
+ */
+static bool asymmetric_match_key_ids(
+	const struct asymmetric_key_ids *kids,
+	const struct asymmetric_key_id *match_id,
+	bool (*match)(const struct asymmetric_key_id *kid1,
+		      const struct asymmetric_key_id *kid2))
+{
+	int i;
+
+	if (!kids || !match_id)
+		return false;
+	for (i = 0; i < ARRAY_SIZE(kids->id); i++)
+		if (match(kids->id[i], match_id))
+			return true;
+	return false;
+}
+
+/* helper function can be called directly with pre-allocated memory */
+inline int __asymmetric_key_hex_to_key_id(const char *id,
+				   struct asymmetric_key_id *match_id,
+				   size_t hexlen)
+{
+	match_id->len = hexlen;
+	return hex2bin(match_id->data, id, hexlen);
+}
+
+/**
+ * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID.
+ * @id: The ID as a hex string.
+ */
+struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
+{
+	struct asymmetric_key_id *match_id;
+	size_t asciihexlen;
+	int ret;
+
+	if (!*id)
+		return ERR_PTR(-EINVAL);
+	asciihexlen = strlen(id);
+	if (asciihexlen & 1)
+		return ERR_PTR(-EINVAL);
+
+	match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2,
+			   GFP_KERNEL);
+	if (!match_id)
+		return ERR_PTR(-ENOMEM);
+	ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2);
+	if (ret < 0) {
+		kfree(match_id);
+		return ERR_PTR(-EINVAL);
+	}
+	return match_id;
+}
+
+/*
+ * Match asymmetric keys by an exact match on an ID.
+ */
+static bool asymmetric_key_cmp(const struct key *key,
+			       const struct key_match_data *match_data)
+{
+	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+	const struct asymmetric_key_id *match_id = match_data->preparsed;
+
+	return asymmetric_match_key_ids(kids, match_id,
+					asymmetric_key_id_same);
+}
+
+/*
+ * Match asymmetric keys by a partial match on an IDs.
+ */
+static bool asymmetric_key_cmp_partial(const struct key *key,
+				       const struct key_match_data *match_data)
+{
+	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+	const struct asymmetric_key_id *match_id = match_data->preparsed;
+
+	return asymmetric_match_key_ids(kids, match_id,
+					asymmetric_key_id_partial);
+}
+
+/*
+ * Preparse the match criterion.  If we don't set lookup_type and cmp,
+ * the default will be an exact match on the key description.
+ *
+ * There are some specifiers for matching key IDs rather than by the key
+ * description:
+ *
+ *	"id:<id>" - find a key by partial match on any available ID
+ *	"ex:<id>" - find a key by exact match on any available ID
+ *
+ * These have to be searched by iteration rather than by direct lookup because
+ * the key is hashed according to its description.
+ */
+static int asymmetric_key_match_preparse(struct key_match_data *match_data)
+{
+	struct asymmetric_key_id *match_id;
+	const char *spec = match_data->raw_data;
+	const char *id;
+	bool (*cmp)(const struct key *, const struct key_match_data *) =
+		asymmetric_key_cmp;
+
+	if (!spec || !*spec)
+		return -EINVAL;
+	if (spec[0] == 'i' &&
+	    spec[1] == 'd' &&
+	    spec[2] == ':') {
+		id = spec + 3;
+		cmp = asymmetric_key_cmp_partial;
+	} else if (spec[0] == 'e' &&
+		   spec[1] == 'x' &&
+		   spec[2] == ':') {
+		id = spec + 3;
+	} else {
+		goto default_match;
+	}
+
+	match_id = asymmetric_key_hex_to_key_id(id);
+	if (IS_ERR(match_id))
+		return PTR_ERR(match_id);
+
+	match_data->preparsed = match_id;
+	match_data->cmp = cmp;
+	match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
+	return 0;
+
+default_match:
+	return 0;
+}
+
+/*
+ * Free the preparsed the match criterion.
+ */
+static void asymmetric_key_match_free(struct key_match_data *match_data)
+{
+	kfree(match_data->preparsed);
+}
+
+/*
+ * Describe the asymmetric key
+ */
+static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
+{
+	const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
+	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+	const struct asymmetric_key_id *kid;
+	const unsigned char *p;
+	int n;
+
+	seq_puts(m, key->description);
+
+	if (subtype) {
+		seq_puts(m, ": ");
+		subtype->describe(key, m);
+
+		if (kids && kids->id[1]) {
+			kid = kids->id[1];
+			seq_putc(m, ' ');
+			n = kid->len;
+			p = kid->data;
+			if (n > 4) {
+				p += n - 4;
+				n = 4;
+			}
+			seq_printf(m, "%*phN", n, p);
+		}
+
+		seq_puts(m, " [");
+		/* put something here to indicate the key's capabilities */
+		seq_putc(m, ']');
+	}
+}
+
+/*
+ * Preparse a asymmetric payload to get format the contents appropriately for the
+ * internal payload to cut down on the number of scans of the data performed.
+ *
+ * We also generate a proposed description from the contents of the key that
+ * can be used to name the key if the user doesn't want to provide one.
+ */
+static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
+{
+	struct asymmetric_key_parser *parser;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (prep->datalen == 0)
+		return -EINVAL;
+
+	down_read(&asymmetric_key_parsers_sem);
+
+	ret = -EBADMSG;
+	list_for_each_entry(parser, &asymmetric_key_parsers, link) {
+		pr_debug("Trying parser '%s'\n", parser->name);
+
+		ret = parser->parse(prep);
+		if (ret != -EBADMSG) {
+			pr_debug("Parser recognised the format (ret %d)\n",
+				 ret);
+			break;
+		}
+	}
+
+	up_read(&asymmetric_key_parsers_sem);
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+/*
+ * Clean up the key ID list
+ */
+static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
+{
+	int i;
+
+	if (kids) {
+		for (i = 0; i < ARRAY_SIZE(kids->id); i++)
+			kfree(kids->id[i]);
+		kfree(kids);
+	}
+}
+
+/*
+ * Clean up the preparse data
+ */
+static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
+{
+	struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype];
+	struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids];
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (subtype) {
+		subtype->destroy(prep->payload.data[asym_crypto],
+				 prep->payload.data[asym_auth]);
+		module_put(subtype->owner);
+	}
+	asymmetric_key_free_kids(kids);
+	kfree(prep->description);
+}
+
+/*
+ * dispose of the data dangling from the corpse of a asymmetric key
+ */
+static void asymmetric_key_destroy(struct key *key)
+{
+	struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
+	struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
+	void *data = key->payload.data[asym_crypto];
+	void *auth = key->payload.data[asym_auth];
+
+	key->payload.data[asym_crypto] = NULL;
+	key->payload.data[asym_subtype] = NULL;
+	key->payload.data[asym_key_ids] = NULL;
+	key->payload.data[asym_auth] = NULL;
+
+	if (subtype) {
+		subtype->destroy(data, auth);
+		module_put(subtype->owner);
+	}
+
+	asymmetric_key_free_kids(kids);
+}
+
+static struct key_restriction *asymmetric_restriction_alloc(
+	key_restrict_link_func_t check,
+	struct key *key)
+{
+	struct key_restriction *keyres =
+		kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
+
+	if (!keyres)
+		return ERR_PTR(-ENOMEM);
+
+	keyres->check = check;
+	keyres->key = key;
+	keyres->keytype = &key_type_asymmetric;
+
+	return keyres;
+}
+
+/*
+ * look up keyring restrict functions for asymmetric keys
+ */
+static struct key_restriction *asymmetric_lookup_restriction(
+	const char *restriction)
+{
+	char *restrict_method;
+	char *parse_buf;
+	char *next;
+	struct key_restriction *ret = ERR_PTR(-EINVAL);
+
+	if (strcmp("builtin_trusted", restriction) == 0)
+		return asymmetric_restriction_alloc(
+			restrict_link_by_builtin_trusted, NULL);
+
+	if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
+		return asymmetric_restriction_alloc(
+			restrict_link_by_builtin_and_secondary_trusted, NULL);
+
+	parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
+	if (!parse_buf)
+		return ERR_PTR(-ENOMEM);
+
+	next = parse_buf;
+	restrict_method = strsep(&next, ":");
+
+	if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
+		char *key_text;
+		key_serial_t serial;
+		struct key *key;
+		key_restrict_link_func_t link_fn =
+			restrict_link_by_key_or_keyring;
+		bool allow_null_key = false;
+
+		key_text = strsep(&next, ":");
+
+		if (next) {
+			if (strcmp(next, "chain") != 0)
+				goto out;
+
+			link_fn = restrict_link_by_key_or_keyring_chain;
+			allow_null_key = true;
+		}
+
+		if (kstrtos32(key_text, 0, &serial) < 0)
+			goto out;
+
+		if ((serial == 0) && allow_null_key) {
+			key = NULL;
+		} else {
+			key = key_lookup(serial);
+			if (IS_ERR(key)) {
+				ret = ERR_CAST(key);
+				goto out;
+			}
+		}
+
+		ret = asymmetric_restriction_alloc(link_fn, key);
+		if (IS_ERR(ret))
+			key_put(key);
+	}
+
+out:
+	kfree(parse_buf);
+	return ret;
+}
+
+int asymmetric_key_eds_op(struct kernel_pkey_params *params,
+			  const void *in, void *out)
+{
+	const struct asymmetric_key_subtype *subtype;
+	struct key *key = params->key;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (key->type != &key_type_asymmetric)
+		return -EINVAL;
+	subtype = asymmetric_key_subtype(key);
+	if (!subtype ||
+	    !key->payload.data[0])
+		return -EINVAL;
+	if (!subtype->eds_op)
+		return -ENOTSUPP;
+
+	ret = subtype->eds_op(params, in, out);
+
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
+					   const void *in, const void *in2)
+{
+	struct public_key_signature sig = {
+		.s_size		= params->in2_len,
+		.digest_size	= params->in_len,
+		.encoding	= params->encoding,
+		.hash_algo	= params->hash_algo,
+		.digest		= (void *)in,
+		.s		= (void *)in2,
+	};
+
+	return verify_signature(params->key, &sig);
+}
+
+struct key_type key_type_asymmetric = {
+	.name			= "asymmetric",
+	.preparse		= asymmetric_key_preparse,
+	.free_preparse		= asymmetric_key_free_preparse,
+	.instantiate		= generic_key_instantiate,
+	.match_preparse		= asymmetric_key_match_preparse,
+	.match_free		= asymmetric_key_match_free,
+	.destroy		= asymmetric_key_destroy,
+	.describe		= asymmetric_key_describe,
+	.lookup_restriction	= asymmetric_lookup_restriction,
+	.asym_query		= query_asymmetric_key,
+	.asym_eds_op		= asymmetric_key_eds_op,
+	.asym_verify_signature	= asymmetric_key_verify_signature,
+};
+EXPORT_SYMBOL_GPL(key_type_asymmetric);
+
+/**
+ * register_asymmetric_key_parser - Register a asymmetric key blob parser
+ * @parser: The parser to register
+ */
+int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
+{
+	struct asymmetric_key_parser *cursor;
+	int ret;
+
+	down_write(&asymmetric_key_parsers_sem);
+
+	list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
+		if (strcmp(cursor->name, parser->name) == 0) {
+			pr_err("Asymmetric key parser '%s' already registered\n",
+			       parser->name);
+			ret = -EEXIST;
+			goto out;
+		}
+	}
+
+	list_add_tail(&parser->link, &asymmetric_key_parsers);
+
+	pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
+	ret = 0;
+
+out:
+	up_write(&asymmetric_key_parsers_sem);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
+
+/**
+ * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
+ * @parser: The parser to unregister
+ */
+void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
+{
+	down_write(&asymmetric_key_parsers_sem);
+	list_del(&parser->link);
+	up_write(&asymmetric_key_parsers_sem);
+
+	pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
+}
+EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
+
+/*
+ * Module stuff
+ */
+static int __init asymmetric_key_init(void)
+{
+	return register_key_type(&key_type_asymmetric);
+}
+
+static void __exit asymmetric_key_cleanup(void)
+{
+	unregister_key_type(&key_type_asymmetric);
+}
+
+module_init(asymmetric_key_init);
+module_exit(asymmetric_key_cleanup);
+#endif /* !__UBOOT__ */
diff --git a/lib/crypto/public_key.c b/lib/crypto/public_key.c
new file mode 100644
index 000000000000..634377472f40
--- /dev/null
+++ b/lib/crypto/public_key.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* In-software asymmetric public-key crypto subtype
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) "PKEY: "fmt
+#ifdef __UBOOT__
+#include <linux/compat.h>
+#else
+#include <linux/module.h>
+#include <linux/export.h>
+#endif
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/scatterlist.h>
+#include <keys/asymmetric-subtype.h>
+#endif
+#include <crypto/public_key.h>
+#ifndef __UBOOT__
+#include <crypto/akcipher.h>
+#endif
+
+MODULE_DESCRIPTION("In-software asymmetric public-key subtype");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+#ifndef __UBOOT__
+/*
+ * Provide a part of a description of the key for /proc/keys.
+ */
+static void public_key_describe(const struct key *asymmetric_key,
+				struct seq_file *m)
+{
+	struct public_key *key = asymmetric_key->payload.data[asym_crypto];
+
+	if (key)
+		seq_printf(m, "%s.%s", key->id_type, key->pkey_algo);
+}
+#endif
+
+/*
+ * Destroy a public key algorithm key.
+ */
+void public_key_free(struct public_key *key)
+{
+	if (key) {
+		kfree(key->key);
+		kfree(key->params);
+		kfree(key);
+	}
+}
+EXPORT_SYMBOL_GPL(public_key_free);
+
+#ifdef __UBOOT__
+/*
+ * from <linux>/crypto/asymmetric_keys/signature.c
+ *
+ * Destroy a public key signature.
+ */
+void public_key_signature_free(struct public_key_signature *sig)
+{
+	int i;
+
+	if (sig) {
+		for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++)
+			free(sig->auth_ids[i]);
+		free(sig->s);
+		free(sig->digest);
+		free(sig);
+	}
+}
+EXPORT_SYMBOL_GPL(public_key_signature_free);
+
+#else
+/*
+ * Destroy a public key algorithm key.
+ */
+static void public_key_destroy(void *payload0, void *payload3)
+{
+	public_key_free(payload0);
+	public_key_signature_free(payload3);
+}
+
+/*
+ * Determine the crypto algorithm name.
+ */
+static
+int software_key_determine_akcipher(const char *encoding,
+				    const char *hash_algo,
+				    const struct public_key *pkey,
+				    char alg_name[CRYPTO_MAX_ALG_NAME])
+{
+	int n;
+
+	if (strcmp(encoding, "pkcs1") == 0) {
+		/* The data wangled by the RSA algorithm is typically padded
+		 * and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
+		 * sec 8.2].
+		 */
+		if (!hash_algo)
+			n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+				     "pkcs1pad(%s)",
+				     pkey->pkey_algo);
+		else
+			n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+				     "pkcs1pad(%s,%s)",
+				     pkey->pkey_algo, hash_algo);
+		return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
+	}
+
+	if (strcmp(encoding, "raw") == 0) {
+		strcpy(alg_name, pkey->pkey_algo);
+		return 0;
+	}
+
+	return -ENOPKG;
+}
+
+static u8 *pkey_pack_u32(u8 *dst, u32 val)
+{
+	memcpy(dst, &val, sizeof(val));
+	return dst + sizeof(val);
+}
+
+/*
+ * Query information about a key.
+ */
+static int software_key_query(const struct kernel_pkey_params *params,
+			      struct kernel_pkey_query *info)
+{
+	struct crypto_akcipher *tfm;
+	struct public_key *pkey = params->key->payload.data[asym_crypto];
+	char alg_name[CRYPTO_MAX_ALG_NAME];
+	u8 *key, *ptr;
+	int ret, len;
+
+	ret = software_key_determine_akcipher(params->encoding,
+					      params->hash_algo,
+					      pkey, alg_name);
+	if (ret < 0)
+		return ret;
+
+	tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
+		      GFP_KERNEL);
+	if (!key)
+		goto error_free_tfm;
+	memcpy(key, pkey->key, pkey->keylen);
+	ptr = key + pkey->keylen;
+	ptr = pkey_pack_u32(ptr, pkey->algo);
+	ptr = pkey_pack_u32(ptr, pkey->paramlen);
+	memcpy(ptr, pkey->params, pkey->paramlen);
+
+	if (pkey->key_is_private)
+		ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
+	else
+		ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
+	if (ret < 0)
+		goto error_free_key;
+
+	len = crypto_akcipher_maxsize(tfm);
+	info->key_size = len * 8;
+	info->max_data_size = len;
+	info->max_sig_size = len;
+	info->max_enc_size = len;
+	info->max_dec_size = len;
+	info->supported_ops = (KEYCTL_SUPPORTS_ENCRYPT |
+			       KEYCTL_SUPPORTS_VERIFY);
+	if (pkey->key_is_private)
+		info->supported_ops |= (KEYCTL_SUPPORTS_DECRYPT |
+					KEYCTL_SUPPORTS_SIGN);
+	ret = 0;
+
+error_free_key:
+	kfree(key);
+error_free_tfm:
+	crypto_free_akcipher(tfm);
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+/*
+ * Do encryption, decryption and signing ops.
+ */
+static int software_key_eds_op(struct kernel_pkey_params *params,
+			       const void *in, void *out)
+{
+	const struct public_key *pkey = params->key->payload.data[asym_crypto];
+	struct akcipher_request *req;
+	struct crypto_akcipher *tfm;
+	struct crypto_wait cwait;
+	struct scatterlist in_sg, out_sg;
+	char alg_name[CRYPTO_MAX_ALG_NAME];
+	char *key, *ptr;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	ret = software_key_determine_akcipher(params->encoding,
+					      params->hash_algo,
+					      pkey, alg_name);
+	if (ret < 0)
+		return ret;
+
+	tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	req = akcipher_request_alloc(tfm, GFP_KERNEL);
+	if (!req)
+		goto error_free_tfm;
+
+	key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
+		      GFP_KERNEL);
+	if (!key)
+		goto error_free_req;
+
+	memcpy(key, pkey->key, pkey->keylen);
+	ptr = key + pkey->keylen;
+	ptr = pkey_pack_u32(ptr, pkey->algo);
+	ptr = pkey_pack_u32(ptr, pkey->paramlen);
+	memcpy(ptr, pkey->params, pkey->paramlen);
+
+	if (pkey->key_is_private)
+		ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
+	else
+		ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
+	if (ret)
+		goto error_free_key;
+
+	sg_init_one(&in_sg, in, params->in_len);
+	sg_init_one(&out_sg, out, params->out_len);
+	akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len,
+				   params->out_len);
+	crypto_init_wait(&cwait);
+	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+				      CRYPTO_TFM_REQ_MAY_SLEEP,
+				      crypto_req_done, &cwait);
+
+	/* Perform the encryption calculation. */
+	switch (params->op) {
+	case kernel_pkey_encrypt:
+		ret = crypto_akcipher_encrypt(req);
+		break;
+	case kernel_pkey_decrypt:
+		ret = crypto_akcipher_decrypt(req);
+		break;
+	case kernel_pkey_sign:
+		ret = crypto_akcipher_sign(req);
+		break;
+	default:
+		BUG();
+	}
+
+	ret = crypto_wait_req(ret, &cwait);
+	if (ret == 0)
+		ret = req->dst_len;
+
+error_free_key:
+	kfree(key);
+error_free_req:
+	akcipher_request_free(req);
+error_free_tfm:
+	crypto_free_akcipher(tfm);
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+/*
+ * Verify a signature using a public key.
+ */
+int public_key_verify_signature(const struct public_key *pkey,
+				const struct public_key_signature *sig)
+{
+	struct crypto_wait cwait;
+	struct crypto_akcipher *tfm;
+	struct akcipher_request *req;
+	struct scatterlist src_sg[2];
+	char alg_name[CRYPTO_MAX_ALG_NAME];
+	char *key, *ptr;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	BUG_ON(!pkey);
+	BUG_ON(!sig);
+	BUG_ON(!sig->s);
+
+	ret = software_key_determine_akcipher(sig->encoding,
+					      sig->hash_algo,
+					      pkey, alg_name);
+	if (ret < 0)
+		return ret;
+
+	tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	ret = -ENOMEM;
+	req = akcipher_request_alloc(tfm, GFP_KERNEL);
+	if (!req)
+		goto error_free_tfm;
+
+	key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
+		      GFP_KERNEL);
+	if (!key)
+		goto error_free_req;
+
+	memcpy(key, pkey->key, pkey->keylen);
+	ptr = key + pkey->keylen;
+	ptr = pkey_pack_u32(ptr, pkey->algo);
+	ptr = pkey_pack_u32(ptr, pkey->paramlen);
+	memcpy(ptr, pkey->params, pkey->paramlen);
+
+	if (pkey->key_is_private)
+		ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
+	else
+		ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
+	if (ret)
+		goto error_free_key;
+
+	sg_init_table(src_sg, 2);
+	sg_set_buf(&src_sg[0], sig->s, sig->s_size);
+	sg_set_buf(&src_sg[1], sig->digest, sig->digest_size);
+	akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size,
+				   sig->digest_size);
+	crypto_init_wait(&cwait);
+	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+				      CRYPTO_TFM_REQ_MAY_SLEEP,
+				      crypto_req_done, &cwait);
+	ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait);
+
+error_free_key:
+	kfree(key);
+error_free_req:
+	akcipher_request_free(req);
+error_free_tfm:
+	crypto_free_akcipher(tfm);
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	if (WARN_ON_ONCE(ret > 0))
+		ret = -EINVAL;
+	return ret;
+}
+EXPORT_SYMBOL_GPL(public_key_verify_signature);
+
+static int public_key_verify_signature_2(const struct key *key,
+					 const struct public_key_signature *sig)
+{
+	const struct public_key *pk = key->payload.data[asym_crypto];
+	return public_key_verify_signature(pk, sig);
+}
+
+/*
+ * Public key algorithm asymmetric key subtype
+ */
+struct asymmetric_key_subtype public_key_subtype = {
+	.owner			= THIS_MODULE,
+	.name			= "public_key",
+	.name_len		= sizeof("public_key") - 1,
+	.describe		= public_key_describe,
+	.destroy		= public_key_destroy,
+	.query			= software_key_query,
+	.eds_op			= software_key_eds_op,
+	.verify_signature	= public_key_verify_signature_2,
+};
+EXPORT_SYMBOL_GPL(public_key_subtype);
+#endif /* !__UBOOT__ */
-- 
2.21.0

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

* [U-Boot] [PATCH v1 09/11] lib: crypto: add x509 parser
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (7 preceding siblings ...)
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 08/11] lib: crypto: add public key utility AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 10/11] lib: crypto: add pkcs7 message parser AKASHI Takahiro
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Imported from linux kernel v5.3.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 lib/Kconfig                   |   1 +
 lib/Makefile                  |   1 +
 lib/crypto/Kconfig            |  10 +
 lib/crypto/Makefile           |  17 +
 lib/crypto/x509.asn1          |  60 +++
 lib/crypto/x509_akid.asn1     |  35 ++
 lib/crypto/x509_cert_parser.c | 697 ++++++++++++++++++++++++++++++++++
 lib/crypto/x509_parser.h      |  57 +++
 lib/crypto/x509_public_key.c  | 292 ++++++++++++++
 9 files changed, 1170 insertions(+)
 create mode 100644 lib/crypto/x509.asn1
 create mode 100644 lib/crypto/x509_akid.asn1
 create mode 100644 lib/crypto/x509_cert_parser.c
 create mode 100644 lib/crypto/x509_parser.h
 create mode 100644 lib/crypto/x509_public_key.c

diff --git a/lib/Kconfig b/lib/Kconfig
index d2955c8feb0e..aa44f52d9181 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -253,6 +253,7 @@ config AES
 	  present.
 
 source lib/rsa/Kconfig
+source lib/crypto/Kconfig
 
 config TPM
 	bool "Trusted Platform Module (TPM) Support"
diff --git a/lib/Makefile b/lib/Makefile
index 677ec33a1ce1..64ff105945b3 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
 obj-$(CONFIG_ARCH_AT91) += at91/
 obj-$(CONFIG_OPTEE) += optee/
 obj-$(CONFIG_BUILD_ASN1) += asn1_decoder.o
+obj-y += crypto/
 
 obj-$(CONFIG_AES) += aes.o
 
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
index b8e8288d2f80..a87cf6107fc1 100644
--- a/lib/crypto/Kconfig
+++ b/lib/crypto/Kconfig
@@ -16,4 +16,14 @@ config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
 	  appropriate hash algorithms (such as SHA-1) must be available.
 	  ENOPKG will be reported if the requisite algorithm is unavailable.
 
+config X509_CERTIFICATE_PARSER
+	bool "X.509 certificate parser"
+	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select ASN1
+	select OID_REGISTRY
+	help
+	  This option provides support for parsing X.509 format blobs for key
+	  data and provides the ability to instantiate a crypto key from a
+	  public key packet found inside the certificate.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index a284de9e0411..2ec2d81a1f3c 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -8,3 +8,20 @@ obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
 asymmetric_keys-y := asymmetric_type.o
 
 obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
+
+#
+# X.509 Certificate handling
+#
+obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
+x509_key_parser-y := \
+	x509.asn1.o \
+	x509_akid.asn1.o \
+	x509_cert_parser.o \
+	x509_public_key.o
+
+$(obj)/x509_cert_parser.o: \
+	$(obj)/x509.asn1.h \
+	$(obj)/x509_akid.asn1.h
+
+$(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h
+$(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h
diff --git a/lib/crypto/x509.asn1 b/lib/crypto/x509.asn1
new file mode 100644
index 000000000000..5c9f4e4a5231
--- /dev/null
+++ b/lib/crypto/x509.asn1
@@ -0,0 +1,60 @@
+Certificate ::= SEQUENCE {
+	tbsCertificate		TBSCertificate ({ x509_note_tbs_certificate }),
+	signatureAlgorithm	AlgorithmIdentifier,
+	signature		BIT STRING ({ x509_note_signature })
+	}
+
+TBSCertificate ::= SEQUENCE {
+	version           [ 0 ]	Version DEFAULT,
+	serialNumber		CertificateSerialNumber ({ x509_note_serial }),
+	signature		AlgorithmIdentifier ({ x509_note_pkey_algo }),
+	issuer			Name ({ x509_note_issuer }),
+	validity		Validity,
+	subject			Name ({ x509_note_subject }),
+	subjectPublicKeyInfo	SubjectPublicKeyInfo,
+	issuerUniqueID    [ 1 ]	IMPLICIT UniqueIdentifier OPTIONAL,
+	subjectUniqueID   [ 2 ]	IMPLICIT UniqueIdentifier OPTIONAL,
+	extensions        [ 3 ]	Extensions OPTIONAL
+	}
+
+Version ::= INTEGER
+CertificateSerialNumber ::= INTEGER
+
+AlgorithmIdentifier ::= SEQUENCE {
+	algorithm		OBJECT IDENTIFIER ({ x509_note_OID }),
+	parameters		ANY OPTIONAL ({ x509_note_params })
+}
+
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+	attributeType		OBJECT IDENTIFIER ({ x509_note_OID }),
+	attributeValue		ANY ({ x509_extract_name_segment })
+	}
+
+Validity ::= SEQUENCE {
+	notBefore		Time ({ x509_note_not_before }),
+	notAfter		Time ({ x509_note_not_after })
+	}
+
+Time ::= CHOICE {
+	utcTime			UTCTime,
+	generalTime		GeneralizedTime
+	}
+
+SubjectPublicKeyInfo ::= SEQUENCE {
+	algorithm		AlgorithmIdentifier,
+	subjectPublicKey	BIT STRING ({ x509_extract_key_data })
+	}
+
+UniqueIdentifier ::= BIT STRING
+
+Extensions ::= SEQUENCE OF Extension
+
+Extension ::= SEQUENCE {
+	extnid			OBJECT IDENTIFIER ({ x509_note_OID }),
+	critical		BOOLEAN DEFAULT,
+	extnValue		OCTET STRING ({ x509_process_extension })
+	}
diff --git a/lib/crypto/x509_akid.asn1 b/lib/crypto/x509_akid.asn1
new file mode 100644
index 000000000000..1a33231a75a8
--- /dev/null
+++ b/lib/crypto/x509_akid.asn1
@@ -0,0 +1,35 @@
+-- X.509 AuthorityKeyIdentifier
+-- rfc5280 section 4.2.1.1
+
+AuthorityKeyIdentifier ::= SEQUENCE {
+	keyIdentifier			[0] IMPLICIT KeyIdentifier		OPTIONAL,
+	authorityCertIssuer		[1] IMPLICIT GeneralNames		OPTIONAL,
+	authorityCertSerialNumber	[2] IMPLICIT CertificateSerialNumber	OPTIONAL
+	}
+
+KeyIdentifier ::= OCTET STRING ({ x509_akid_note_kid })
+
+CertificateSerialNumber ::= INTEGER ({ x509_akid_note_serial })
+
+GeneralNames ::= SEQUENCE OF GeneralName
+
+GeneralName ::= CHOICE {
+	otherName			[0] ANY,
+	rfc822Name			[1] IA5String,
+	dNSName				[2] IA5String,
+	x400Address			[3] ANY,
+	directoryName			[4] Name ({ x509_akid_note_name }),
+	ediPartyName			[5] ANY,
+	uniformResourceIdentifier	[6] IA5String,
+	iPAddress			[7] OCTET STRING,
+	registeredID			[8] OBJECT IDENTIFIER
+	}
+
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+	attributeType		OBJECT IDENTIFIER ({ x509_note_OID }),
+	attributeValue		ANY ({ x509_extract_name_segment })
+	}
diff --git a/lib/crypto/x509_cert_parser.c b/lib/crypto/x509_cert_parser.c
new file mode 100644
index 000000000000..e6d2a426a0bc
--- /dev/null
+++ b/lib/crypto/x509_cert_parser.c
@@ -0,0 +1,697 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* X.509 certificate parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/export.h>
+#include <linux/slab.h>
+#endif
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#ifdef __UBOOT__
+#include <linux/string.h>
+#endif
+#include <crypto/public_key.h>
+#include "x509_parser.h"
+#include "x509.asn1.h"
+#include "x509_akid.asn1.h"
+
+struct x509_parse_context {
+	struct x509_certificate	*cert;		/* Certificate being constructed */
+	unsigned long	data;			/* Start of data */
+	const void	*cert_start;		/* Start of cert content */
+	const void	*key;			/* Key data */
+	size_t		key_size;		/* Size of key data */
+	const void	*params;		/* Key parameters */
+	size_t		params_size;		/* Size of key parameters */
+	enum OID	key_algo;		/* Public key algorithm */
+	enum OID	last_oid;		/* Last OID encountered */
+	enum OID	algo_oid;		/* Algorithm OID */
+	unsigned char	nr_mpi;			/* Number of MPIs stored */
+	u8		o_size;			/* Size of organizationName (O) */
+	u8		cn_size;		/* Size of commonName (CN) */
+	u8		email_size;		/* Size of emailAddress */
+	u16		o_offset;		/* Offset of organizationName (O) */
+	u16		cn_offset;		/* Offset of commonName (CN) */
+	u16		email_offset;		/* Offset of emailAddress */
+	unsigned	raw_akid_size;
+	const void	*raw_akid;		/* Raw authorityKeyId in ASN.1 */
+	const void	*akid_raw_issuer;	/* Raw directoryName in authorityKeyId */
+	unsigned	akid_raw_issuer_size;
+};
+
+/*
+ * Free an X.509 certificate
+ */
+void x509_free_certificate(struct x509_certificate *cert)
+{
+	if (cert) {
+		public_key_free(cert->pub);
+		public_key_signature_free(cert->sig);
+		kfree(cert->issuer);
+		kfree(cert->subject);
+		kfree(cert->id);
+		kfree(cert->skid);
+		kfree(cert);
+	}
+}
+EXPORT_SYMBOL_GPL(x509_free_certificate);
+
+/*
+ * Parse an X.509 certificate
+ */
+struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
+{
+	struct x509_certificate *cert;
+	struct x509_parse_context *ctx;
+	struct asymmetric_key_id *kid;
+	long ret;
+
+	ret = -ENOMEM;
+	cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
+	if (!cert)
+		goto error_no_cert;
+	cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+	if (!cert->pub)
+		goto error_no_ctx;
+	cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
+	if (!cert->sig)
+		goto error_no_ctx;
+	ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
+	if (!ctx)
+		goto error_no_ctx;
+
+	ctx->cert = cert;
+	ctx->data = (unsigned long)data;
+
+	/* Attempt to decode the certificate */
+	ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen);
+	if (ret < 0)
+		goto error_decode;
+
+	/* Decode the AuthorityKeyIdentifier */
+	if (ctx->raw_akid) {
+		pr_devel("AKID: %u %*phN\n",
+			 ctx->raw_akid_size, ctx->raw_akid_size, ctx->raw_akid);
+		ret = asn1_ber_decoder(&x509_akid_decoder, ctx,
+				       ctx->raw_akid, ctx->raw_akid_size);
+		if (ret < 0) {
+			pr_warn("Couldn't decode AuthKeyIdentifier\n");
+			goto error_decode;
+		}
+	}
+
+	ret = -ENOMEM;
+	cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL);
+	if (!cert->pub->key)
+		goto error_decode;
+
+	cert->pub->keylen = ctx->key_size;
+
+	cert->pub->params = kmemdup(ctx->params, ctx->params_size, GFP_KERNEL);
+	if (!cert->pub->params)
+		goto error_decode;
+
+	cert->pub->paramlen = ctx->params_size;
+	cert->pub->algo = ctx->key_algo;
+
+	/* Grab the signature bits */
+	ret = x509_get_sig_params(cert);
+	if (ret < 0)
+		goto error_decode;
+
+	/* Generate cert issuer + serial number key ID */
+	kid = asymmetric_key_generate_id(cert->raw_serial,
+					 cert->raw_serial_size,
+					 cert->raw_issuer,
+					 cert->raw_issuer_size);
+	if (IS_ERR(kid)) {
+		ret = PTR_ERR(kid);
+		goto error_decode;
+	}
+	cert->id = kid;
+
+#ifndef __UBOOT__
+	/* Detect self-signed certificates */
+	ret = x509_check_for_self_signed(cert);
+	if (ret < 0)
+		goto error_decode;
+#endif
+
+	kfree(ctx);
+	return cert;
+
+error_decode:
+	kfree(ctx);
+error_no_ctx:
+	x509_free_certificate(cert);
+error_no_cert:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(x509_cert_parse);
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int x509_note_OID(void *context, size_t hdrlen,
+	     unsigned char tag,
+	     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	ctx->last_oid = look_up_OID(value, vlen);
+	if (ctx->last_oid == OID__NR) {
+		char buffer[50];
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		pr_debug("Unknown OID: [%lu] %s\n",
+			 (unsigned long)value - ctx->data, buffer);
+	}
+	return 0;
+}
+
+/*
+ * Save the position of the TBS data so that we can check the signature over it
+ * later.
+ */
+int x509_note_tbs_certificate(void *context, size_t hdrlen,
+			      unsigned char tag,
+			      const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n",
+		 hdrlen, tag, (unsigned long)value - ctx->data, vlen);
+
+	ctx->cert->tbs = value - hdrlen;
+	ctx->cert->tbs_size = vlen + hdrlen;
+	return 0;
+}
+
+/*
+ * Record the public key algorithm
+ */
+int x509_note_pkey_algo(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	pr_debug("PubKey Algo: %u\n", ctx->last_oid);
+
+	switch (ctx->last_oid) {
+	case OID_md2WithRSAEncryption:
+	case OID_md3WithRSAEncryption:
+	default:
+		return -ENOPKG; /* Unsupported combination */
+
+	case OID_md4WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "md4";
+		goto rsa_pkcs1;
+
+	case OID_sha1WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha1";
+		goto rsa_pkcs1;
+
+	case OID_sha256WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha256";
+		goto rsa_pkcs1;
+
+	case OID_sha384WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha384";
+		goto rsa_pkcs1;
+
+	case OID_sha512WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha512";
+		goto rsa_pkcs1;
+
+	case OID_sha224WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha224";
+		goto rsa_pkcs1;
+
+	case OID_gost2012Signature256:
+		ctx->cert->sig->hash_algo = "streebog256";
+		goto ecrdsa;
+
+	case OID_gost2012Signature512:
+		ctx->cert->sig->hash_algo = "streebog512";
+		goto ecrdsa;
+	}
+
+rsa_pkcs1:
+	ctx->cert->sig->pkey_algo = "rsa";
+	ctx->cert->sig->encoding = "pkcs1";
+	ctx->algo_oid = ctx->last_oid;
+	return 0;
+ecrdsa:
+	ctx->cert->sig->pkey_algo = "ecrdsa";
+	ctx->cert->sig->encoding = "raw";
+	ctx->algo_oid = ctx->last_oid;
+	return 0;
+}
+
+/*
+ * Note the whereabouts and type of the signature.
+ */
+int x509_note_signature(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen);
+
+	if (ctx->last_oid != ctx->algo_oid) {
+		pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n",
+			ctx->algo_oid, ctx->last_oid);
+		return -EINVAL;
+	}
+
+	if (strcmp(ctx->cert->sig->pkey_algo, "rsa") == 0 ||
+	    strcmp(ctx->cert->sig->pkey_algo, "ecrdsa") == 0) {
+		/* Discard the BIT STRING metadata */
+		if (vlen < 1 || *(const u8 *)value != 0)
+			return -EBADMSG;
+
+		value++;
+		vlen--;
+	}
+
+	ctx->cert->raw_sig = value;
+	ctx->cert->raw_sig_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the certificate serial number
+ */
+int x509_note_serial(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	ctx->cert->raw_serial = value;
+	ctx->cert->raw_serial_size = vlen;
+	return 0;
+}
+
+/*
+ * Note some of the name segments from which we'll fabricate a name.
+ */
+int x509_extract_name_segment(void *context, size_t hdrlen,
+			      unsigned char tag,
+			      const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	switch (ctx->last_oid) {
+	case OID_commonName:
+		ctx->cn_size = vlen;
+		ctx->cn_offset = (unsigned long)value - ctx->data;
+		break;
+	case OID_organizationName:
+		ctx->o_size = vlen;
+		ctx->o_offset = (unsigned long)value - ctx->data;
+		break;
+	case OID_email_address:
+		ctx->email_size = vlen;
+		ctx->email_offset = (unsigned long)value - ctx->data;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Fabricate and save the issuer and subject names
+ */
+static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen,
+			       unsigned char tag,
+			       char **_name, size_t vlen)
+{
+	const void *name, *data = (const void *)ctx->data;
+	size_t namesize;
+	char *buffer;
+
+	if (*_name)
+		return -EINVAL;
+
+	/* Empty name string if no material */
+	if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) {
+		buffer = kmalloc(1, GFP_KERNEL);
+		if (!buffer)
+			return -ENOMEM;
+		buffer[0] = 0;
+		goto done;
+	}
+
+	if (ctx->cn_size && ctx->o_size) {
+		/* Consider combining O and CN, but use only the CN if it is
+		 * prefixed by the O, or a significant portion thereof.
+		 */
+		namesize = ctx->cn_size;
+		name = data + ctx->cn_offset;
+		if (ctx->cn_size >= ctx->o_size &&
+		    memcmp(data + ctx->cn_offset, data + ctx->o_offset,
+			   ctx->o_size) == 0)
+			goto single_component;
+		if (ctx->cn_size >= 7 &&
+		    ctx->o_size >= 7 &&
+		    memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0)
+			goto single_component;
+
+		buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1,
+				 GFP_KERNEL);
+		if (!buffer)
+			return -ENOMEM;
+
+		memcpy(buffer,
+		       data + ctx->o_offset, ctx->o_size);
+		buffer[ctx->o_size + 0] = ':';
+		buffer[ctx->o_size + 1] = ' ';
+		memcpy(buffer + ctx->o_size + 2,
+		       data + ctx->cn_offset, ctx->cn_size);
+		buffer[ctx->o_size + 2 + ctx->cn_size] = 0;
+		goto done;
+
+	} else if (ctx->cn_size) {
+		namesize = ctx->cn_size;
+		name = data + ctx->cn_offset;
+	} else if (ctx->o_size) {
+		namesize = ctx->o_size;
+		name = data + ctx->o_offset;
+	} else {
+		namesize = ctx->email_size;
+		name = data + ctx->email_offset;
+	}
+
+single_component:
+	buffer = kmalloc(namesize + 1, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+	memcpy(buffer, name, namesize);
+	buffer[namesize] = 0;
+
+done:
+	*_name = buffer;
+	ctx->cn_size = 0;
+	ctx->o_size = 0;
+	ctx->email_size = 0;
+	return 0;
+}
+
+int x509_note_issuer(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	ctx->cert->raw_issuer = value;
+	ctx->cert->raw_issuer_size = vlen;
+	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
+}
+
+int x509_note_subject(void *context, size_t hdrlen,
+		      unsigned char tag,
+		      const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	ctx->cert->raw_subject = value;
+	ctx->cert->raw_subject_size = vlen;
+	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
+}
+
+/*
+ * Extract the parameters for the public key
+ */
+int x509_note_params(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	/*
+	 * AlgorithmIdentifier is used three times in the x509, we should skip
+	 * first and ignore third, using second one which is after subject and
+	 * before subjectPublicKey.
+	 */
+	if (!ctx->cert->raw_subject || ctx->key)
+		return 0;
+	ctx->params = value - hdrlen;
+	ctx->params_size = vlen + hdrlen;
+	return 0;
+}
+
+/*
+ * Extract the data for the public key algorithm
+ */
+int x509_extract_key_data(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	ctx->key_algo = ctx->last_oid;
+	if (ctx->last_oid == OID_rsaEncryption)
+		ctx->cert->pub->pkey_algo = "rsa";
+	else if (ctx->last_oid == OID_gost2012PKey256 ||
+		 ctx->last_oid == OID_gost2012PKey512)
+		ctx->cert->pub->pkey_algo = "ecrdsa";
+	else
+		return -ENOPKG;
+
+	/* Discard the BIT STRING metadata */
+	if (vlen < 1 || *(const u8 *)value != 0)
+		return -EBADMSG;
+	ctx->key = value + 1;
+	ctx->key_size = vlen - 1;
+	return 0;
+}
+
+/* The keyIdentifier in AuthorityKeyIdentifier SEQUENCE is tag(CONT,PRIM,0) */
+#define SEQ_TAG_KEYID (ASN1_CONT << 6)
+
+/*
+ * Process certificate extensions that are used to qualify the certificate.
+ */
+int x509_process_extension(void *context, size_t hdrlen,
+			   unsigned char tag,
+			   const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	struct asymmetric_key_id *kid;
+	const unsigned char *v = value;
+
+	pr_debug("Extension: %u\n", ctx->last_oid);
+
+	if (ctx->last_oid == OID_subjectKeyIdentifier) {
+		/* Get hold of the key fingerprint */
+		if (ctx->cert->skid || vlen < 3)
+			return -EBADMSG;
+		if (v[0] != ASN1_OTS || v[1] != vlen - 2)
+			return -EBADMSG;
+		v += 2;
+		vlen -= 2;
+
+		ctx->cert->raw_skid_size = vlen;
+		ctx->cert->raw_skid = v;
+		kid = asymmetric_key_generate_id(v, vlen, "", 0);
+		if (IS_ERR(kid))
+			return PTR_ERR(kid);
+		ctx->cert->skid = kid;
+		pr_debug("subjkeyid %*phN\n", kid->len, kid->data);
+		return 0;
+	}
+
+	if (ctx->last_oid == OID_authorityKeyIdentifier) {
+		/* Get hold of the CA key fingerprint */
+		ctx->raw_akid = v;
+		ctx->raw_akid_size = vlen;
+		return 0;
+	}
+
+	return 0;
+}
+
+/**
+ * x509_decode_time - Decode an X.509 time ASN.1 object
+ * @_t: The time to fill in
+ * @hdrlen: The length of the object header
+ * @tag: The object tag
+ * @value: The object value
+ * @vlen: The size of the object value
+ *
+ * Decode an ASN.1 universal time or generalised time field into a struct the
+ * kernel can handle and check it for validity.  The time is decoded thus:
+ *
+ *	[RFC5280 ยง4.1.2.5]
+ *	CAs conforming to this profile MUST always encode certificate validity
+ *	dates through the year 2049 as UTCTime; certificate validity dates in
+ *	2050 or later MUST be encoded as GeneralizedTime.  Conforming
+ *	applications MUST be able to process validity dates that are encoded in
+ *	either UTCTime or GeneralizedTime.
+ */
+int x509_decode_time(time64_t *_t,  size_t hdrlen,
+		     unsigned char tag,
+		     const unsigned char *value, size_t vlen)
+{
+	static const unsigned char month_lengths[] = { 31, 28, 31, 30, 31, 30,
+						       31, 31, 30, 31, 30, 31 };
+	const unsigned char *p = value;
+	unsigned year, mon, day, hour, min, sec, mon_len;
+
+#define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; })
+#define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
+
+	if (tag == ASN1_UNITIM) {
+		/* UTCTime: YYMMDDHHMMSSZ */
+		if (vlen != 13)
+			goto unsupported_time;
+		year = DD2bin(p);
+		if (year >= 50)
+			year += 1900;
+		else
+			year += 2000;
+	} else if (tag == ASN1_GENTIM) {
+		/* GenTime: YYYYMMDDHHMMSSZ */
+		if (vlen != 15)
+			goto unsupported_time;
+		year = DD2bin(p) * 100 + DD2bin(p);
+		if (year >= 1950 && year <= 2049)
+			goto invalid_time;
+	} else {
+		goto unsupported_time;
+	}
+
+	mon  = DD2bin(p);
+	day = DD2bin(p);
+	hour = DD2bin(p);
+	min  = DD2bin(p);
+	sec  = DD2bin(p);
+
+	if (*p != 'Z')
+		goto unsupported_time;
+
+	if (year < 1970 ||
+	    mon < 1 || mon > 12)
+		goto invalid_time;
+
+	mon_len = month_lengths[mon - 1];
+	if (mon == 2) {
+		if (year % 4 == 0) {
+			mon_len = 29;
+			if (year % 100 == 0) {
+				mon_len = 28;
+				if (year % 400 == 0)
+					mon_len = 29;
+			}
+		}
+	}
+
+	if (day < 1 || day > mon_len ||
+	    hour > 24 || /* ISO 8601 permits 24:00:00 as midnight tomorrow */
+	    min > 59 ||
+	    sec > 60) /* ISO 8601 permits leap seconds [X.680 46.3] */
+		goto invalid_time;
+
+	*_t = mktime64(year, mon, day, hour, min, sec);
+	return 0;
+
+unsupported_time:
+	pr_debug("Got unsupported time [tag %02x]: '%*phN'\n",
+		 tag, (int)vlen, value);
+	return -EBADMSG;
+invalid_time:
+	pr_debug("Got invalid time [tag %02x]: '%*phN'\n",
+		 tag, (int)vlen, value);
+	return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(x509_decode_time);
+
+int x509_note_not_before(void *context, size_t hdrlen,
+			 unsigned char tag,
+			 const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
+}
+
+int x509_note_not_after(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
+}
+
+/*
+ * Note a key identifier-based AuthorityKeyIdentifier
+ */
+int x509_akid_note_kid(void *context, size_t hdrlen,
+		       unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	struct asymmetric_key_id *kid;
+
+	pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
+
+	if (ctx->cert->sig->auth_ids[1])
+		return 0;
+
+	kid = asymmetric_key_generate_id(value, vlen, "", 0);
+	if (IS_ERR(kid))
+		return PTR_ERR(kid);
+	pr_debug("authkeyid %*phN\n", kid->len, kid->data);
+	ctx->cert->sig->auth_ids[1] = kid;
+	return 0;
+}
+
+/*
+ * Note a directoryName in an AuthorityKeyIdentifier
+ */
+int x509_akid_note_name(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	pr_debug("AKID: name: %*phN\n", (int)vlen, value);
+
+	ctx->akid_raw_issuer = value;
+	ctx->akid_raw_issuer_size = vlen;
+	return 0;
+}
+
+/*
+ * Note a serial number in an AuthorityKeyIdentifier
+ */
+int x509_akid_note_serial(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	struct asymmetric_key_id *kid;
+
+	pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
+
+	if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0])
+		return 0;
+
+	kid = asymmetric_key_generate_id(value,
+					 vlen,
+					 ctx->akid_raw_issuer,
+					 ctx->akid_raw_issuer_size);
+	if (IS_ERR(kid))
+		return PTR_ERR(kid);
+
+	pr_debug("authkeyid %*phN\n", kid->len, kid->data);
+	ctx->cert->sig->auth_ids[0] = kid;
+	return 0;
+}
diff --git a/lib/crypto/x509_parser.h b/lib/crypto/x509_parser.h
new file mode 100644
index 000000000000..c233f136fb35
--- /dev/null
+++ b/lib/crypto/x509_parser.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* X.509 certificate parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#include <linux/time.h>
+#include <crypto/public_key.h>
+#include <keys/asymmetric-type.h>
+
+struct x509_certificate {
+	struct x509_certificate *next;
+	struct x509_certificate *signer;	/* Certificate that signed this one */
+	struct public_key *pub;			/* Public key details */
+	struct public_key_signature *sig;	/* Signature parameters */
+	char		*issuer;		/* Name of certificate issuer */
+	char		*subject;		/* Name of certificate subject */
+	struct asymmetric_key_id *id;		/* Issuer + Serial number */
+	struct asymmetric_key_id *skid;		/* Subject + subjectKeyId (optional) */
+	time64_t	valid_from;
+	time64_t	valid_to;
+	const void	*tbs;			/* Signed data */
+	unsigned	tbs_size;		/* Size of signed data */
+	unsigned	raw_sig_size;		/* Size of sigature */
+	const void	*raw_sig;		/* Signature data */
+	const void	*raw_serial;		/* Raw serial number in ASN.1 */
+	unsigned	raw_serial_size;
+	unsigned	raw_issuer_size;
+	const void	*raw_issuer;		/* Raw issuer name in ASN.1 */
+	const void	*raw_subject;		/* Raw subject name in ASN.1 */
+	unsigned	raw_subject_size;
+	unsigned	raw_skid_size;
+	const void	*raw_skid;		/* Raw subjectKeyId in ASN.1 */
+	unsigned	index;
+	bool		seen;			/* Infinite recursion prevention */
+	bool		verified;
+	bool		self_signed;		/* T if self-signed (check unsupported_sig too) */
+	bool		unsupported_key;	/* T if key uses unsupported crypto */
+	bool		unsupported_sig;	/* T if signature uses unsupported crypto */
+	bool		blacklisted;
+};
+
+/*
+ * x509_cert_parser.c
+ */
+extern void x509_free_certificate(struct x509_certificate *cert);
+extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
+extern int x509_decode_time(time64_t *_t,  size_t hdrlen,
+			    unsigned char tag,
+			    const unsigned char *value, size_t vlen);
+
+/*
+ * x509_public_key.c
+ */
+extern int x509_get_sig_params(struct x509_certificate *cert);
+extern int x509_check_for_self_signed(struct x509_certificate *cert);
diff --git a/lib/crypto/x509_public_key.c b/lib/crypto/x509_public_key.c
new file mode 100644
index 000000000000..04bdb672b496
--- /dev/null
+++ b/lib/crypto/x509_public_key.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Instantiate a public key crypto key from an X.509 Certificate
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#ifdef __UBOOT__
+#include <common.h>
+#include <linux/compat.h>
+#include <linux/errno.h>
+#else
+#include <linux/module.h>
+#endif
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/slab.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <keys/system_keyring.h>
+#include <crypto/hash.h>
+#include "asymmetric_keys.h"
+#endif
+#include "x509_parser.h"
+
+/*
+ * Set up the signature parameters in an X.509 certificate.  This involves
+ * digesting the signed data and extracting the signature.
+ */
+int x509_get_sig_params(struct x509_certificate *cert)
+{
+	struct public_key_signature *sig = cert->sig;
+#ifndef __UBOOT__
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	size_t desc_size;
+#endif
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (!cert->pub->pkey_algo)
+		cert->unsupported_key = true;
+
+	if (!sig->pkey_algo)
+		cert->unsupported_sig = true;
+
+	/* We check the hash if we can - even if we can't then verify it */
+	if (!sig->hash_algo) {
+		cert->unsupported_sig = true;
+		return 0;
+	}
+
+	sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL);
+	if (!sig->s)
+		return -ENOMEM;
+
+	sig->s_size = cert->raw_sig_size;
+
+#ifdef __UBOOT__
+	/*
+	 * Note:
+	 * This part (filling sig->digest) should be implemented if
+	 * x509_check_for_self_signed() is enabled x509_cert_parse().
+	 * Currently, this check won't affect UEFI secure boot.
+	 */
+	ret = 0;
+#else
+	/* Allocate the hashing algorithm we're going to need and find out how
+	 * big the hash operational data will be.
+	 */
+	tfm = crypto_alloc_shash(sig->hash_algo, 0, 0);
+	if (IS_ERR(tfm)) {
+		if (PTR_ERR(tfm) == -ENOENT) {
+			cert->unsupported_sig = true;
+			return 0;
+		}
+		return PTR_ERR(tfm);
+	}
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+	sig->digest_size = crypto_shash_digestsize(tfm);
+
+	ret = -ENOMEM;
+	sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
+	if (!sig->digest)
+		goto error;
+
+	desc = kzalloc(desc_size, GFP_KERNEL);
+	if (!desc)
+		goto error;
+
+	desc->tfm = tfm;
+
+	ret = crypto_shash_digest(desc, cert->tbs, cert->tbs_size, sig->digest);
+	if (ret < 0)
+		goto error_2;
+
+	ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
+	if (ret == -EKEYREJECTED) {
+		pr_err("Cert %*phN is blacklisted\n",
+		       sig->digest_size, sig->digest);
+		cert->blacklisted = true;
+		ret = 0;
+	}
+
+error_2:
+	kfree(desc);
+error:
+	crypto_free_shash(tfm);
+#endif /* __UBOOT__ */
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+#ifndef __UBOOT__
+/*
+ * Check for self-signedness in an X.509 cert and if found, check the signature
+ * immediately if we can.
+ */
+int x509_check_for_self_signed(struct x509_certificate *cert)
+{
+	int ret = 0;
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (cert->raw_subject_size != cert->raw_issuer_size ||
+	    memcmp(cert->raw_subject, cert->raw_issuer,
+		   cert->raw_issuer_size) != 0)
+		goto not_self_signed;
+
+	if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) {
+		/* If the AKID is present it may have one or two parts.  If
+		 * both are supplied, both must match.
+		 */
+		bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]);
+		bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]);
+
+		if (!a && !b)
+			goto not_self_signed;
+
+		ret = -EKEYREJECTED;
+		if (((a && !b) || (b && !a)) &&
+		    cert->sig->auth_ids[0] && cert->sig->auth_ids[1])
+			goto out;
+	}
+
+	ret = -EKEYREJECTED;
+	if (strcmp(cert->pub->pkey_algo, cert->sig->pkey_algo) != 0)
+		goto out;
+
+	ret = public_key_verify_signature(cert->pub, cert->sig);
+	if (ret < 0) {
+		if (ret == -ENOPKG) {
+			cert->unsupported_sig = true;
+			ret = 0;
+		}
+		goto out;
+	}
+
+	pr_devel("Cert Self-signature verified");
+	cert->self_signed = true;
+
+out:
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+
+not_self_signed:
+	pr_devel("<==%s() = 0 [not]\n", __func__);
+	return 0;
+}
+
+/*
+ * Attempt to parse a data blob for a key as an X509 certificate.
+ */
+static int x509_key_preparse(struct key_preparsed_payload *prep)
+{
+	struct asymmetric_key_ids *kids;
+	struct x509_certificate *cert;
+	const char *q;
+	size_t srlen, sulen;
+	char *desc = NULL, *p;
+	int ret;
+
+	cert = x509_cert_parse(prep->data, prep->datalen);
+	if (IS_ERR(cert))
+		return PTR_ERR(cert);
+
+	pr_devel("Cert Issuer: %s\n", cert->issuer);
+	pr_devel("Cert Subject: %s\n", cert->subject);
+
+	if (cert->unsupported_key) {
+		ret = -ENOPKG;
+		goto error_free_cert;
+	}
+
+	pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo);
+	pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
+
+	cert->pub->id_type = "X509";
+
+	if (cert->unsupported_sig) {
+		public_key_signature_free(cert->sig);
+		cert->sig = NULL;
+	} else {
+		pr_devel("Cert Signature: %s + %s\n",
+			 cert->sig->pkey_algo, cert->sig->hash_algo);
+	}
+
+	/* Don't permit addition of blacklisted keys */
+	ret = -EKEYREJECTED;
+	if (cert->blacklisted)
+		goto error_free_cert;
+
+	/* Propose a description */
+	sulen = strlen(cert->subject);
+	if (cert->raw_skid) {
+		srlen = cert->raw_skid_size;
+		q = cert->raw_skid;
+	} else {
+		srlen = cert->raw_serial_size;
+		q = cert->raw_serial;
+	}
+
+	ret = -ENOMEM;
+	desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL);
+	if (!desc)
+		goto error_free_cert;
+	p = memcpy(desc, cert->subject, sulen);
+	p += sulen;
+	*p++ = ':';
+	*p++ = ' ';
+	p = bin2hex(p, q, srlen);
+	*p = 0;
+
+	kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
+	if (!kids)
+		goto error_free_desc;
+	kids->id[0] = cert->id;
+	kids->id[1] = cert->skid;
+
+	/* We're pinning the module by being linked against it */
+	__module_get(public_key_subtype.owner);
+	prep->payload.data[asym_subtype] = &public_key_subtype;
+	prep->payload.data[asym_key_ids] = kids;
+	prep->payload.data[asym_crypto] = cert->pub;
+	prep->payload.data[asym_auth] = cert->sig;
+	prep->description = desc;
+	prep->quotalen = 100;
+
+	/* We've finished with the certificate */
+	cert->pub = NULL;
+	cert->id = NULL;
+	cert->skid = NULL;
+	cert->sig = NULL;
+	desc = NULL;
+	ret = 0;
+
+error_free_desc:
+	kfree(desc);
+error_free_cert:
+	x509_free_certificate(cert);
+	return ret;
+}
+
+static struct asymmetric_key_parser x509_key_parser = {
+	.owner	= THIS_MODULE,
+	.name	= "x509",
+	.parse	= x509_key_preparse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init x509_key_init(void)
+{
+	return register_asymmetric_key_parser(&x509_key_parser);
+}
+
+static void __exit x509_key_exit(void)
+{
+	unregister_asymmetric_key_parser(&x509_key_parser);
+}
+
+module_init(x509_key_init);
+module_exit(x509_key_exit);
+#endif /* !__UBOOT__ */
+
+MODULE_DESCRIPTION("X.509 certificate parser");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
-- 
2.21.0


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

* [U-Boot] [PATCH v1 10/11] lib: crypto: add pkcs7 message parser
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (8 preceding siblings ...)
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 09/11] lib: crypto: add x509 parser AKASHI Takahiro
@ 2019-10-11  7:41 ` AKASHI Takahiro
  2019-10-11  7:42 ` [U-Boot] [PATCH v1 11/11] lib: crypto: add rsa public key parser AKASHI Takahiro
  2019-10-11  7:55 ` [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
  11 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:41 UTC (permalink / raw)
  To: u-boot

Imported from linux kernel v5.3.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/crypto/pkcs7.h    |  47 +++
 lib/crypto/Kconfig        |   9 +
 lib/crypto/Makefile       |  11 +
 lib/crypto/pkcs7.asn1     | 135 ++++++++
 lib/crypto/pkcs7_parser.c | 693 ++++++++++++++++++++++++++++++++++++++
 lib/crypto/pkcs7_parser.h |  65 ++++
 scripts/Makefile.build    |   4 +-
 7 files changed, 961 insertions(+), 3 deletions(-)
 create mode 100644 include/crypto/pkcs7.h
 create mode 100644 lib/crypto/pkcs7.asn1
 create mode 100644 lib/crypto/pkcs7_parser.c
 create mode 100644 lib/crypto/pkcs7_parser.h

diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h
new file mode 100644
index 000000000000..8f5c8a7ee3b9
--- /dev/null
+++ b/include/crypto/pkcs7.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* PKCS#7 crypto data parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#ifndef _CRYPTO_PKCS7_H
+#define _CRYPTO_PKCS7_H
+
+#ifndef __UBOOT__
+#include <linux/verification.h>
+#include <crypto/public_key.h>
+#endif
+
+struct key;
+struct pkcs7_message;
+
+/*
+ * pkcs7_parser.c
+ */
+extern struct pkcs7_message *pkcs7_parse_message(const void *data,
+						 size_t datalen);
+extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
+
+extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
+				  const void **_data, size_t *_datalen,
+				  size_t *_headerlen);
+
+#ifndef __UBOOT__
+/*
+ * pkcs7_trust.c
+ */
+extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
+				struct key *trust_keyring);
+
+/*
+ * pkcs7_verify.c
+ */
+extern int pkcs7_verify(struct pkcs7_message *pkcs7,
+			enum key_being_used_for usage);
+
+extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
+				      const void *data, size_t datalen);
+#endif
+
+#endif /* _CRYPTO_PKCS7_H */
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
index a87cf6107fc1..86b676515405 100644
--- a/lib/crypto/Kconfig
+++ b/lib/crypto/Kconfig
@@ -26,4 +26,13 @@ config X509_CERTIFICATE_PARSER
 	  data and provides the ability to instantiate a crypto key from a
 	  public key packet found inside the certificate.
 
+config PKCS7_MESSAGE_PARSER
+	bool "PKCS#7 message parser"
+	depends on X509_CERTIFICATE_PARSER
+	select ASN1
+	select OID_REGISTRY
+	help
+	  This option provides support for parsing PKCS#7 format messages for
+	  signature data and provides the ability to verify the signature.
+
 endif # ASYMMETRIC_KEY_TYPE
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index 2ec2d81a1f3c..a02ad51e698c 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -25,3 +25,14 @@ $(obj)/x509_cert_parser.o: \
 
 $(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h
 $(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h
+
+#
+# PKCS#7 message handling
+#
+obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
+pkcs7_message-y := \
+	pkcs7.asn1.o \
+	pkcs7_parser.o
+
+$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h
+$(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h
diff --git a/lib/crypto/pkcs7.asn1 b/lib/crypto/pkcs7.asn1
new file mode 100644
index 000000000000..1eca740b816a
--- /dev/null
+++ b/lib/crypto/pkcs7.asn1
@@ -0,0 +1,135 @@
+PKCS7ContentInfo ::= SEQUENCE {
+	contentType	ContentType ({ pkcs7_check_content_type }),
+	content		[0] EXPLICIT SignedData OPTIONAL
+}
+
+ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
+
+SignedData ::= SEQUENCE {
+	version			INTEGER ({ pkcs7_note_signeddata_version }),
+	digestAlgorithms	DigestAlgorithmIdentifiers,
+	contentInfo		ContentInfo ({ pkcs7_note_content }),
+	certificates		CHOICE {
+		certSet		[0] IMPLICIT ExtendedCertificatesAndCertificates,
+		certSequence	[2] IMPLICIT Certificates
+	} OPTIONAL ({ pkcs7_note_certificate_list }),
+	crls CHOICE {
+		crlSet		[1] IMPLICIT CertificateRevocationLists,
+		crlSequence	[3] IMPLICIT CRLSequence
+	} OPTIONAL,
+	signerInfos		SignerInfos
+}
+
+ContentInfo ::= SEQUENCE {
+	contentType	ContentType ({ pkcs7_note_OID }),
+	content		[0] EXPLICIT Data OPTIONAL
+}
+
+Data ::= ANY ({ pkcs7_note_data })
+
+DigestAlgorithmIdentifiers ::= CHOICE {
+	daSet			SET OF DigestAlgorithmIdentifier,
+	daSequence		SEQUENCE OF DigestAlgorithmIdentifier
+}
+
+DigestAlgorithmIdentifier ::= SEQUENCE {
+	algorithm   OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+	parameters  ANY OPTIONAL
+}
+
+--
+-- Certificates and certificate lists
+--
+ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate
+
+ExtendedCertificateOrCertificate ::= CHOICE {
+  certificate		Certificate,				-- X.509
+  extendedCertificate	[0] IMPLICIT ExtendedCertificate	-- PKCS#6
+}
+
+ExtendedCertificate ::= Certificate -- cheating
+
+Certificates ::= SEQUENCE OF Certificate
+
+CertificateRevocationLists ::= SET OF CertificateList
+
+CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly
+
+CRLSequence ::= SEQUENCE OF CertificateList
+
+Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509
+
+--
+-- Signer information
+--
+SignerInfos ::= CHOICE {
+	siSet		SET OF SignerInfo,
+	siSequence	SEQUENCE OF SignerInfo
+}
+
+SignerInfo ::= SEQUENCE {
+	version			INTEGER ({ pkcs7_note_signerinfo_version }),
+	sid			SignerIdentifier, -- CMS variant, not PKCS#7
+	digestAlgorithm		DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }),
+	authenticatedAttributes	CHOICE {
+		aaSet		[0] IMPLICIT SetOfAuthenticatedAttribute
+					({ pkcs7_sig_note_set_of_authattrs }),
+		aaSequence	[2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
+			-- Explicit because easier to compute digest on
+			-- sequence of attributes and then reuse encoded
+			-- sequence in aaSequence.
+	} OPTIONAL,
+	digestEncryptionAlgorithm
+				DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }),
+	encryptedDigest		EncryptedDigest,
+	unauthenticatedAttributes CHOICE {
+		uaSet		[1] IMPLICIT SET OF UnauthenticatedAttribute,
+		uaSequence	[3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute
+	} OPTIONAL
+} ({ pkcs7_note_signed_info })
+
+SignerIdentifier ::= CHOICE {
+	-- RFC5652 sec 5.3
+	issuerAndSerialNumber IssuerAndSerialNumber,
+        subjectKeyIdentifier [0] IMPLICIT SubjectKeyIdentifier
+}
+
+IssuerAndSerialNumber ::= SEQUENCE {
+	issuer			Name ({ pkcs7_sig_note_issuer }),
+	serialNumber		CertificateSerialNumber ({ pkcs7_sig_note_serial })
+}
+
+CertificateSerialNumber ::= INTEGER
+
+SubjectKeyIdentifier ::= OCTET STRING ({ pkcs7_sig_note_skid })
+
+SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
+
+AuthenticatedAttribute ::= SEQUENCE {
+	type			OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+	values			SET OF ANY ({ pkcs7_sig_note_authenticated_attr })
+}
+
+UnauthenticatedAttribute ::= SEQUENCE {
+	type			OBJECT IDENTIFIER,
+	values			SET OF ANY
+}
+
+DigestEncryptionAlgorithmIdentifier ::= SEQUENCE {
+	algorithm		OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+	parameters		ANY OPTIONAL
+}
+
+EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature })
+
+---
+--- X.500 Name
+---
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+	attributeType		OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+	attributeValue		ANY
+}
diff --git a/lib/crypto/pkcs7_parser.c b/lib/crypto/pkcs7_parser.c
new file mode 100644
index 000000000000..bf9e7e888f6b
--- /dev/null
+++ b/lib/crypto/pkcs7_parser.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* PKCS#7 parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#define pr_fmt(fmt) "PKCS7: "fmt
+#ifdef __UBOOT__
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#endif
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#endif
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include <crypto/public_key.h>
+#include "pkcs7_parser.h"
+#include "pkcs7.asn1.h"
+
+MODULE_DESCRIPTION("PKCS#7 parser");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+struct pkcs7_parse_context {
+	struct pkcs7_message	*msg;		/* Message being constructed */
+	struct pkcs7_signed_info *sinfo;	/* SignedInfo being constructed */
+	struct pkcs7_signed_info **ppsinfo;
+	struct x509_certificate *certs;		/* Certificate cache */
+	struct x509_certificate **ppcerts;
+	unsigned long	data;			/* Start of data */
+	enum OID	last_oid;		/* Last OID encountered */
+	unsigned	x509_index;
+	unsigned	sinfo_index;
+	const void	*raw_serial;
+	unsigned	raw_serial_size;
+	unsigned	raw_issuer_size;
+	const void	*raw_issuer;
+	const void	*raw_skid;
+	unsigned	raw_skid_size;
+	bool		expect_skid;
+};
+
+/*
+ * Free a signed information block.
+ */
+static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
+{
+	if (sinfo) {
+		public_key_signature_free(sinfo->sig);
+		kfree(sinfo);
+	}
+}
+
+/**
+ * pkcs7_free_message - Free a PKCS#7 message
+ * @pkcs7: The PKCS#7 message to free
+ */
+void pkcs7_free_message(struct pkcs7_message *pkcs7)
+{
+	struct x509_certificate *cert;
+	struct pkcs7_signed_info *sinfo;
+
+	if (pkcs7) {
+		while (pkcs7->certs) {
+			cert = pkcs7->certs;
+			pkcs7->certs = cert->next;
+			x509_free_certificate(cert);
+		}
+		while (pkcs7->crl) {
+			cert = pkcs7->crl;
+			pkcs7->crl = cert->next;
+			x509_free_certificate(cert);
+		}
+		while (pkcs7->signed_infos) {
+			sinfo = pkcs7->signed_infos;
+			pkcs7->signed_infos = sinfo->next;
+			pkcs7_free_signed_info(sinfo);
+		}
+		kfree(pkcs7);
+	}
+}
+EXPORT_SYMBOL_GPL(pkcs7_free_message);
+
+/*
+ * Check authenticatedAttributes are provided or not provided consistently.
+ */
+static int pkcs7_check_authattrs(struct pkcs7_message *msg)
+{
+	struct pkcs7_signed_info *sinfo;
+	bool want = false;
+
+	sinfo = msg->signed_infos;
+	if (!sinfo)
+		goto inconsistent;
+
+	if (sinfo->authattrs) {
+		want = true;
+		msg->have_authattrs = true;
+	}
+
+	for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)
+		if (!!sinfo->authattrs != want)
+			goto inconsistent;
+	return 0;
+
+inconsistent:
+	pr_warn("Inconsistently supplied authAttrs\n");
+	return -EINVAL;
+}
+
+/**
+ * pkcs7_parse_message - Parse a PKCS#7 message
+ * @data: The raw binary ASN.1 encoded message to be parsed
+ * @datalen: The size of the encoded message
+ */
+struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
+{
+	struct pkcs7_parse_context *ctx;
+	struct pkcs7_message *msg = ERR_PTR(-ENOMEM);
+	int ret;
+
+	ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
+	if (!ctx)
+		goto out_no_ctx;
+	ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
+	if (!ctx->msg)
+		goto out_no_msg;
+	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
+	if (!ctx->sinfo)
+		goto out_no_sinfo;
+	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
+				  GFP_KERNEL);
+	if (!ctx->sinfo->sig)
+		goto out_no_sig;
+
+	ctx->data = (unsigned long)data;
+	ctx->ppcerts = &ctx->certs;
+	ctx->ppsinfo = &ctx->msg->signed_infos;
+
+	/* Attempt to decode the signature */
+	ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
+	if (ret < 0) {
+		msg = ERR_PTR(ret);
+		goto out;
+	}
+
+	ret = pkcs7_check_authattrs(ctx->msg);
+	if (ret < 0) {
+		msg = ERR_PTR(ret);
+		goto out;
+	}
+
+	msg = ctx->msg;
+	ctx->msg = NULL;
+
+out:
+	while (ctx->certs) {
+		struct x509_certificate *cert = ctx->certs;
+		ctx->certs = cert->next;
+		x509_free_certificate(cert);
+	}
+out_no_sig:
+	pkcs7_free_signed_info(ctx->sinfo);
+out_no_sinfo:
+	pkcs7_free_message(ctx->msg);
+out_no_msg:
+	kfree(ctx);
+out_no_ctx:
+	return msg;
+}
+EXPORT_SYMBOL_GPL(pkcs7_parse_message);
+
+/**
+ * pkcs7_get_content_data - Get access to the PKCS#7 content
+ * @pkcs7: The preparsed PKCS#7 message to access
+ * @_data: Place to return a pointer to the data
+ * @_data_len: Place to return the data length
+ * @_headerlen: Size of ASN.1 header not included in _data
+ *
+ * Get access to the data content of the PKCS#7 message.  The size of the
+ * header of the ASN.1 object that contains it is also provided and can be used
+ * to adjust *_data and *_data_len to get the entire object.
+ *
+ * Returns -ENODATA if the data object was missing from the message.
+ */
+int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
+			   const void **_data, size_t *_data_len,
+			   size_t *_headerlen)
+{
+	if (!pkcs7->data)
+		return -ENODATA;
+
+	*_data = pkcs7->data;
+	*_data_len = pkcs7->data_len;
+	if (_headerlen)
+		*_headerlen = pkcs7->data_hdrlen;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int pkcs7_note_OID(void *context, size_t hdrlen,
+		   unsigned char tag,
+		   const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	ctx->last_oid = look_up_OID(value, vlen);
+	if (ctx->last_oid == OID__NR) {
+		char buffer[50];
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		printk("PKCS7: Unknown OID: [%lu] %s\n",
+		       (unsigned long)value - ctx->data, buffer);
+	}
+	return 0;
+}
+
+/*
+ * Note the digest algorithm for the signature.
+ */
+int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
+			       unsigned char tag,
+			       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	switch (ctx->last_oid) {
+	case OID_md4:
+		ctx->sinfo->sig->hash_algo = "md4";
+		break;
+	case OID_md5:
+		ctx->sinfo->sig->hash_algo = "md5";
+		break;
+	case OID_sha1:
+		ctx->sinfo->sig->hash_algo = "sha1";
+		break;
+	case OID_sha256:
+		ctx->sinfo->sig->hash_algo = "sha256";
+		break;
+	case OID_sha384:
+		ctx->sinfo->sig->hash_algo = "sha384";
+		break;
+	case OID_sha512:
+		ctx->sinfo->sig->hash_algo = "sha512";
+		break;
+	case OID_sha224:
+		ctx->sinfo->sig->hash_algo = "sha224";
+		break;
+	default:
+		printk("Unsupported digest algo: %u\n", ctx->last_oid);
+		return -ENOPKG;
+	}
+	return 0;
+}
+
+/*
+ * Note the public key algorithm for the signature.
+ */
+int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	switch (ctx->last_oid) {
+	case OID_rsaEncryption:
+		ctx->sinfo->sig->pkey_algo = "rsa";
+		ctx->sinfo->sig->encoding = "pkcs1";
+		break;
+	default:
+		printk("Unsupported pkey algo: %u\n", ctx->last_oid);
+		return -ENOPKG;
+	}
+	return 0;
+}
+
+/*
+ * We only support signed data [RFC2315 sec 9].
+ */
+int pkcs7_check_content_type(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	if (ctx->last_oid != OID_signed_data) {
+		pr_warn("Only support pkcs7_signedData type\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Note the SignedData version
+ */
+int pkcs7_note_signeddata_version(void *context, size_t hdrlen,
+				  unsigned char tag,
+				  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	unsigned version;
+
+	if (vlen != 1)
+		goto unsupported;
+
+	ctx->msg->version = version = *(const u8 *)value;
+	switch (version) {
+	case 1:
+		/* PKCS#7 SignedData [RFC2315 sec 9.1]
+		 * CMS ver 1 SignedData [RFC5652 sec 5.1]
+		 */
+		break;
+	case 3:
+		/* CMS ver 3 SignedData [RFC2315 sec 5.1] */
+		break;
+	default:
+		goto unsupported;
+	}
+
+	return 0;
+
+unsupported:
+	pr_warn("Unsupported SignedData version\n");
+	return -EINVAL;
+}
+
+/*
+ * Note the SignerInfo version
+ */
+int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,
+				  unsigned char tag,
+				  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	unsigned version;
+
+	if (vlen != 1)
+		goto unsupported;
+
+	version = *(const u8 *)value;
+	switch (version) {
+	case 1:
+		/* PKCS#7 SignerInfo [RFC2315 sec 9.2]
+		 * CMS ver 1 SignerInfo [RFC5652 sec 5.3]
+		 */
+		if (ctx->msg->version != 1)
+			goto version_mismatch;
+		ctx->expect_skid = false;
+		break;
+	case 3:
+		/* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */
+		if (ctx->msg->version == 1)
+			goto version_mismatch;
+		ctx->expect_skid = true;
+		break;
+	default:
+		goto unsupported;
+	}
+
+	return 0;
+
+unsupported:
+	pr_warn("Unsupported SignerInfo version\n");
+	return -EINVAL;
+version_mismatch:
+	pr_warn("SignedData-SignerInfo version mismatch\n");
+	return -EBADMSG;
+}
+
+/*
+ * Extract a certificate and store it in the context.
+ */
+int pkcs7_extract_cert(void *context, size_t hdrlen,
+		       unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct x509_certificate *x509;
+
+	if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
+		pr_debug("Cert began with tag %02x@%lu\n",
+			 tag, (unsigned long)ctx - ctx->data);
+		return -EBADMSG;
+	}
+
+	/* We have to correct for the header so that the X.509 parser can start
+	 * from the beginning.  Note that since X.509 stipulates DER, there
+	 * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
+	 * stipulates BER).
+	 */
+	value -= hdrlen;
+	vlen += hdrlen;
+
+	if (((u8*)value)[1] == 0x80)
+		vlen += 2; /* Indefinite length - there should be an EOC */
+
+	x509 = x509_cert_parse(value, vlen);
+	if (IS_ERR(x509))
+		return PTR_ERR(x509);
+
+	x509->index = ++ctx->x509_index;
+	pr_debug("Got cert %u for %s\n", x509->index, x509->subject);
+	pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);
+
+	*ctx->ppcerts = x509;
+	ctx->ppcerts = &x509->next;
+	return 0;
+}
+
+/*
+ * Save the certificate list
+ */
+int pkcs7_note_certificate_list(void *context, size_t hdrlen,
+				unsigned char tag,
+				const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_devel("Got cert list (%02x)\n", tag);
+
+	*ctx->ppcerts = ctx->msg->certs;
+	ctx->msg->certs = ctx->certs;
+	ctx->certs = NULL;
+	ctx->ppcerts = &ctx->certs;
+	return 0;
+}
+
+/*
+ * Note the content type.
+ */
+int pkcs7_note_content(void *context, size_t hdrlen,
+		       unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	if (ctx->last_oid != OID_data &&
+	    ctx->last_oid != OID_msIndirectData) {
+		pr_warn("Unsupported data type %d\n", ctx->last_oid);
+		return -EINVAL;
+	}
+
+	ctx->msg->data_type = ctx->last_oid;
+	return 0;
+}
+
+/*
+ * Extract the data from the message and store that and its content type OID in
+ * the context.
+ */
+int pkcs7_note_data(void *context, size_t hdrlen,
+		    unsigned char tag,
+		    const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_debug("Got data\n");
+
+	ctx->msg->data = value;
+	ctx->msg->data_len = vlen;
+	ctx->msg->data_hdrlen = hdrlen;
+	return 0;
+}
+
+/*
+ * Parse authenticated attributes.
+ */
+int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
+				      unsigned char tag,
+				      const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct pkcs7_signed_info *sinfo = ctx->sinfo;
+	enum OID content_type;
+
+	pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
+
+	switch (ctx->last_oid) {
+	case OID_contentType:
+		if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))
+			goto repeated;
+		content_type = look_up_OID(value, vlen);
+		if (content_type != ctx->msg->data_type) {
+			pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",
+				ctx->msg->data_type, sinfo->index,
+				content_type);
+			return -EBADMSG;
+		}
+		return 0;
+
+	case OID_signingTime:
+		if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))
+			goto repeated;
+		/* Should we check that the signing time is consistent
+		 * with the signer's X.509 cert?
+		 */
+		return x509_decode_time(&sinfo->signing_time,
+					hdrlen, tag, value, vlen);
+
+	case OID_messageDigest:
+		if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))
+			goto repeated;
+		if (tag != ASN1_OTS)
+			return -EBADMSG;
+		sinfo->msgdigest = value;
+		sinfo->msgdigest_len = vlen;
+		return 0;
+
+	case OID_smimeCapabilites:
+		if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))
+			goto repeated;
+#ifdef __UBOOT__ /* OID_data is needed for authenticated UEFI variables */
+		if (ctx->msg->data_type != OID_msIndirectData &&
+		    ctx->msg->data_type != OID_data) {
+#else
+		if (ctx->msg->data_type != OID_msIndirectData) {
+#endif
+			pr_warn("S/MIME Caps only allowed with Authenticode\n");
+			return -EKEYREJECTED;
+		}
+		return 0;
+
+		/* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE
+		 * char URLs and cont[1] 8-bit char URLs.
+		 *
+		 * Microsoft StatementType seems to contain a list of OIDs that
+		 * are also used as extendedKeyUsage types in X.509 certs.
+		 */
+	case OID_msSpOpusInfo:
+		if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))
+			goto repeated;
+		goto authenticode_check;
+	case OID_msStatementType:
+		if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))
+			goto repeated;
+	authenticode_check:
+		if (ctx->msg->data_type != OID_msIndirectData) {
+			pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");
+			return -EKEYREJECTED;
+		}
+		/* I'm not sure how to validate these */
+		return 0;
+	default:
+		return 0;
+	}
+
+repeated:
+	/* We permit max one item per AuthenticatedAttribute and no repeats */
+	pr_warn("Repeated/multivalue AuthAttrs not permitted\n");
+	return -EKEYREJECTED;
+}
+
+/*
+ * Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]
+ */
+int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
+				    unsigned char tag,
+				    const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct pkcs7_signed_info *sinfo = ctx->sinfo;
+
+	if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||
+	    !test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {
+		pr_warn("Missing required AuthAttr\n");
+		return -EBADMSG;
+	}
+
+	if (ctx->msg->data_type != OID_msIndirectData &&
+	    test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {
+		pr_warn("Unexpected Authenticode AuthAttr\n");
+		return -EBADMSG;
+	}
+
+	/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
+	sinfo->authattrs = value - (hdrlen - 1);
+	sinfo->authattrs_len = vlen + (hdrlen - 1);
+	return 0;
+}
+
+/*
+ * Note the issuing certificate serial number
+ */
+int pkcs7_sig_note_serial(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	ctx->raw_serial = value;
+	ctx->raw_serial_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the issuer's name
+ */
+int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	ctx->raw_issuer = value;
+	ctx->raw_issuer_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the issuing cert's subjectKeyIdentifier
+ */
+int pkcs7_sig_note_skid(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
+
+	ctx->raw_skid = value;
+	ctx->raw_skid_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the signature data
+ */
+int pkcs7_sig_note_signature(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
+	if (!ctx->sinfo->sig->s)
+		return -ENOMEM;
+
+	ctx->sinfo->sig->s_size = vlen;
+	return 0;
+}
+
+/*
+ * Note a signature information block
+ */
+int pkcs7_note_signed_info(void *context, size_t hdrlen,
+			   unsigned char tag,
+			   const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct pkcs7_signed_info *sinfo = ctx->sinfo;
+	struct asymmetric_key_id *kid;
+
+	if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {
+		pr_warn("Authenticode requires AuthAttrs\n");
+		return -EBADMSG;
+	}
+
+	/* Generate cert issuer + serial number key ID */
+	if (!ctx->expect_skid) {
+		kid = asymmetric_key_generate_id(ctx->raw_serial,
+						 ctx->raw_serial_size,
+						 ctx->raw_issuer,
+						 ctx->raw_issuer_size);
+	} else {
+		kid = asymmetric_key_generate_id(ctx->raw_skid,
+						 ctx->raw_skid_size,
+						 "", 0);
+	}
+	if (IS_ERR(kid))
+		return PTR_ERR(kid);
+
+	pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
+
+	sinfo->sig->auth_ids[0] = kid;
+	sinfo->index = ++ctx->sinfo_index;
+	*ctx->ppsinfo = sinfo;
+	ctx->ppsinfo = &sinfo->next;
+	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
+	if (!ctx->sinfo)
+		return -ENOMEM;
+	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
+				  GFP_KERNEL);
+	if (!ctx->sinfo->sig)
+		return -ENOMEM;
+	return 0;
+}
diff --git a/lib/crypto/pkcs7_parser.h b/lib/crypto/pkcs7_parser.h
new file mode 100644
index 000000000000..6565fdc2d4ca
--- /dev/null
+++ b/lib/crypto/pkcs7_parser.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* PKCS#7 crypto data parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells at redhat.com)
+ */
+
+#include <linux/oid_registry.h>
+#include <crypto/pkcs7.h>
+#include "x509_parser.h"
+
+#define kenter(FMT, ...) \
+	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+	pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+struct pkcs7_signed_info {
+	struct pkcs7_signed_info *next;
+	struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
+	unsigned	index;
+	bool		unsupported_crypto;	/* T if not usable due to missing crypto */
+	bool		blacklisted;
+
+	/* Message digest - the digest of the Content Data (or NULL) */
+	const void	*msgdigest;
+	unsigned	msgdigest_len;
+
+	/* Authenticated Attribute data (or NULL) */
+	unsigned	authattrs_len;
+	const void	*authattrs;
+	unsigned long	aa_set;
+#define	sinfo_has_content_type		0
+#define	sinfo_has_signing_time		1
+#define	sinfo_has_message_digest	2
+#define sinfo_has_smime_caps		3
+#define	sinfo_has_ms_opus_info		4
+#define	sinfo_has_ms_statement_type	5
+	time64_t	signing_time;
+
+	/* Message signature.
+	 *
+	 * This contains the generated digest of _either_ the Content Data or
+	 * the Authenticated Attributes [RFC2315 9.3].  If the latter, one of
+	 * the attributes contains the digest of the the Content Data within
+	 * it.
+	 *
+	 * THis also contains the issuing cert serial number and issuer's name
+	 * [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3].
+	 */
+	struct public_key_signature *sig;
+};
+
+struct pkcs7_message {
+	struct x509_certificate *certs;	/* Certificate list */
+	struct x509_certificate *crl;	/* Revocation list */
+	struct pkcs7_signed_info *signed_infos;
+	u8		version;	/* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */
+	bool		have_authattrs;	/* T if have authattrs */
+
+	/* Content Data (or NULL) */
+	enum OID	data_type;	/* Type of Data */
+	size_t		data_len;	/* Length of Data */
+	size_t		data_hdrlen;	/* Length of Data ASN.1 header */
+	const void	*data;		/* Content Data (or 0) */
+};
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 9357d310e50b..71458e089cd6 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -419,11 +419,9 @@ targets += $(multi-used-y) $(multi-used-m)
 intermediate_targets = $(foreach sfx, $(2), \
 				$(patsubst %$(strip $(1)),%$(sfx), \
 					$(filter %$(strip $(1)), $(targets))))
-# %.asn1.o <- %.asn1.[ch] <- %.asn1
 # %.lex.o <- %.lex.c <- %.l
 # %.tab.o <- %.tab.[ch] <- %.y
-targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
-	   $(call intermediate_targets, .lex.o, .lex.c) \
+targets += $(call intermediate_targets, .lex.o, .lex.c) \
 	   $(call intermediate_targets, .tab.o, .tab.c .tab.h)
 
 # Descending
-- 
2.21.0

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

* [U-Boot] [PATCH v1 11/11] lib: crypto: add rsa public key parser
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (9 preceding siblings ...)
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 10/11] lib: crypto: add pkcs7 message parser AKASHI Takahiro
@ 2019-10-11  7:42 ` AKASHI Takahiro
  2019-10-12 13:11   ` Heinrich Schuchardt
  2019-10-11  7:55 ` [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:42 UTC (permalink / raw)
  To: u-boot

Imported from linux kernel v5.3.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 include/crypto/internal/rsa.h |  57 ++++++++++
 lib/crypto/Makefile           |  10 +-
 lib/crypto/rsa_helper.c       | 198 ++++++++++++++++++++++++++++++++++
 lib/crypto/rsapubkey.asn1     |   4 +
 4 files changed, 268 insertions(+), 1 deletion(-)
 create mode 100644 include/crypto/internal/rsa.h
 create mode 100644 lib/crypto/rsa_helper.c
 create mode 100644 lib/crypto/rsapubkey.asn1

diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h
new file mode 100644
index 000000000000..e870133f4b77
--- /dev/null
+++ b/include/crypto/internal/rsa.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * RSA internal helpers
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.com>
+ */
+#ifndef _RSA_HELPER_
+#define _RSA_HELPER_
+#include <linux/types.h>
+
+/**
+ * rsa_key - RSA key structure
+ * @n           : RSA modulus raw byte stream
+ * @e           : RSA public exponent raw byte stream
+ * @d           : RSA private exponent raw byte stream
+ * @p           : RSA prime factor p of n raw byte stream
+ * @q           : RSA prime factor q of n raw byte stream
+ * @dp          : RSA exponent d mod (p - 1) raw byte stream
+ * @dq          : RSA exponent d mod (q - 1) raw byte stream
+ * @qinv        : RSA CRT coefficient q^(-1) mod p raw byte stream
+ * @n_sz        : length in bytes of RSA modulus n
+ * @e_sz        : length in bytes of RSA public exponent
+ * @d_sz        : length in bytes of RSA private exponent
+ * @p_sz        : length in bytes of p field
+ * @q_sz        : length in bytes of q field
+ * @dp_sz       : length in bytes of dp field
+ * @dq_sz       : length in bytes of dq field
+ * @qinv_sz     : length in bytes of qinv field
+ */
+struct rsa_key {
+	const u8 *n;
+	const u8 *e;
+	const u8 *d;
+	const u8 *p;
+	const u8 *q;
+	const u8 *dp;
+	const u8 *dq;
+	const u8 *qinv;
+	size_t n_sz;
+	size_t e_sz;
+	size_t d_sz;
+	size_t p_sz;
+	size_t q_sz;
+	size_t dp_sz;
+	size_t dq_sz;
+	size_t qinv_sz;
+};
+
+int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
+		      unsigned int key_len);
+
+int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
+		       unsigned int key_len);
+
+extern struct crypto_template rsa_pkcs1pad_tmpl;
+#endif
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
index a02ad51e698c..ac0ca05298e2 100644
--- a/lib/crypto/Makefile
+++ b/lib/crypto/Makefile
@@ -17,7 +17,9 @@ x509_key_parser-y := \
 	x509.asn1.o \
 	x509_akid.asn1.o \
 	x509_cert_parser.o \
-	x509_public_key.o
+	x509_public_key.o \
+	rsapubkey.asn1.o \
+	rsa_helper.o
 
 $(obj)/x509_cert_parser.o: \
 	$(obj)/x509.asn1.h \
@@ -36,3 +38,9 @@ pkcs7_message-y := \
 
 $(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h
 $(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h
+
+#
+# RSA public key parser
+#
+$(obj)/rsapubkey.asn1.o: $(obj)/rsapubkey.asn1.c $(obj)/rsapubkey.asn1.h
+$(obj)/rsa_helper.o: $(obj)/rsapubkey.asn1.h
diff --git a/lib/crypto/rsa_helper.c b/lib/crypto/rsa_helper.c
new file mode 100644
index 000000000000..aca627a4a619
--- /dev/null
+++ b/lib/crypto/rsa_helper.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RSA key extract helper
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.com>
+ */
+#ifndef __UBOOT__
+#include <linux/kernel.h>
+#include <linux/export.h>
+#endif
+#include <linux/err.h>
+#ifndef __UBOOT__
+#include <linux/fips.h>
+#endif
+#include <crypto/internal/rsa.h>
+#include "rsapubkey.asn1.h"
+#ifndef __UBOOT__
+#include "rsaprivkey.asn1.h"
+#endif
+
+int rsa_get_n(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+#ifndef __UBOOT__
+	const u8 *ptr = value;
+	size_t n_sz = vlen;
+#endif
+
+	/* invalid key provided */
+	if (!value || !vlen)
+		return -EINVAL;
+
+#ifndef __UBOOT__
+	if (fips_enabled) {
+		while (n_sz && !*ptr) {
+			ptr++;
+			n_sz--;
+		}
+
+		/* In FIPS mode only allow key size 2K and higher */
+		if (n_sz < 256) {
+			pr_err("RSA: key size not allowed in FIPS mode\n");
+			return -EINVAL;
+		}
+	}
+#endif
+
+	key->n = value;
+	key->n_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_e(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !key->n_sz || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->e = value;
+	key->e_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_d(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !key->n_sz || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->d = value;
+	key->d_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_p(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->p = value;
+	key->p_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_q(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->q = value;
+	key->q_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_dp(void *context, size_t hdrlen, unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->dp = value;
+	key->dp_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_dq(void *context, size_t hdrlen, unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->dq = value;
+	key->dq_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag,
+		 const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->qinv = value;
+	key->qinv_sz = vlen;
+
+	return 0;
+}
+
+/**
+ * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the
+ *                       provided struct rsa_key, pointers to the raw key as is,
+ *                       so that the caller can copy it or MPI parse it, etc.
+ *
+ * @rsa_key:	struct rsa_key key representation
+ * @key:	key in BER format
+ * @key_len:	length of key
+ *
+ * Return:	0 on success or error code in case of error
+ */
+int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
+		      unsigned int key_len)
+{
+	return asn1_ber_decoder(&rsapubkey_decoder, rsa_key, key, key_len);
+}
+EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
+
+#ifndef __UBOOT__
+/**
+ * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the
+ *                        provided struct rsa_key, pointers to the raw key
+ *                        as is, so that the caller can copy it or MPI parse it,
+ *                        etc.
+ *
+ * @rsa_key:	struct rsa_key key representation
+ * @key:	key in BER format
+ * @key_len:	length of key
+ *
+ * Return:	0 on success or error code in case of error
+ */
+int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
+		       unsigned int key_len)
+{
+	return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len);
+}
+EXPORT_SYMBOL_GPL(rsa_parse_priv_key);
+#endif
diff --git a/lib/crypto/rsapubkey.asn1 b/lib/crypto/rsapubkey.asn1
new file mode 100644
index 000000000000..725498e461d2
--- /dev/null
+++ b/lib/crypto/rsapubkey.asn1
@@ -0,0 +1,4 @@
+RsaPubKey ::= SEQUENCE {
+	n INTEGER ({ rsa_get_n }),
+	e INTEGER ({ rsa_get_e })
+}
-- 
2.21.0

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
                   ` (10 preceding siblings ...)
  2019-10-11  7:42 ` [U-Boot] [PATCH v1 11/11] lib: crypto: add rsa public key parser AKASHI Takahiro
@ 2019-10-11  7:55 ` AKASHI Takahiro
  2019-10-12 13:02   ` Heinrich Schuchardt
  11 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-11  7:55 UTC (permalink / raw)
  To: u-boot

I hope this patch set will be reviewed promptly as I'm aiming to
push my "UEFI secure boot" patch for v2020.01.

On Fri, Oct 11, 2019 at 04:41:49PM +0900, AKASHI Takahiro wrote:
> Asn1 parsers of x509 certificates and pkcs7 messages are required
> to implement image authentication and variable authentication as
> part of UEFI secure boot feature.
> 
> As we discussed before in the thread[1], most people insisted that
> we should re-use corresponding source code from Linux repository
> for this purpose.
> 
> Here is my attempt to import all the necessary files from Linux; Those
> will eventually be part of UEFI secure boot implementation, but I'd like
> to get early feedback from other peoples before submitting the whole
> patchset so that they will be better formatted for merging.
> 
> My approach here is
> * files from the latest Linux
> * modify files as little as possible
> * mark/protect unavoidable changes with "#if(n)def __UBOOT__"
> so that future fixes/differences in Linux repository will easily
> be applied to U-Boot.
> 
> Please note that checkpatch.pl will complain with a bunch of
> warnings/errors but I intentionally left them unchanged for the sake
> of better maintainability I said above.
> 
> Any comments will be appreciated.
> -Takahiro Akashi
> 
> [1] https://lists.denx.de/pipermail/u-boot/2019-April/366423.html
> 
> Changes in v1 (Oct 11, 2019) from RFC
> * change the kernel code base from v5.0 to v5.3

* add preparatory patches (#1, #2 and #3)

-Takahiro Akashi

> * comment off x509_check_for_self_signed() which is not useful
>   for UEFI secure boot (patch#9)
> * improve usages of "#if(n)def __UBOOT__* to minimize differences
>   between U-Boot and linux kernel
> 
> AKASHI Takahiro (11):
>   linux_compat: add kmemdup()
>   include: time.h: define time64_t
>   include: kernel.h: include printk.h
>   cmd: add asn1_compiler
>   Makefile: add build script for asn1 parsers
>   lib: add asn1 decoder
>   lib: add oid registry utility
>   lib: crypto: add public key utility
>   lib: crypto: add x509 parser
>   lib: crypto: add pkcs7 message parser
>   lib: crypto: add rsa public key parser
> 
>  cmd/Kconfig                       |    3 +
>  include/crypto/internal/rsa.h     |   57 +
>  include/crypto/pkcs7.h            |   47 +
>  include/crypto/public_key.h       |   90 ++
>  include/keys/asymmetric-type.h    |   88 ++
>  include/linux/asn1.h              |   65 ++
>  include/linux/asn1_ber_bytecode.h |   89 ++
>  include/linux/asn1_decoder.h      |   20 +
>  include/linux/compat.h            |    4 +-
>  include/linux/kernel.h            |    2 +
>  include/linux/oid_registry.h      |  117 +++
>  include/linux/time.h              |   24 +
>  lib/Kconfig                       |   12 +
>  lib/Makefile                      |   18 +
>  lib/asn1_decoder.c                |  527 ++++++++++
>  lib/build_OID_registry            |  203 ++++
>  lib/crypto/Kconfig                |   38 +
>  lib/crypto/Makefile               |   46 +
>  lib/crypto/asymmetric_type.c      |  668 ++++++++++++
>  lib/crypto/pkcs7.asn1             |  135 +++
>  lib/crypto/pkcs7_parser.c         |  693 +++++++++++++
>  lib/crypto/pkcs7_parser.h         |   65 ++
>  lib/crypto/public_key.c           |  376 +++++++
>  lib/crypto/rsa_helper.c           |  198 ++++
>  lib/crypto/rsapubkey.asn1         |    4 +
>  lib/crypto/x509.asn1              |   60 ++
>  lib/crypto/x509_akid.asn1         |   35 +
>  lib/crypto/x509_cert_parser.c     |  697 +++++++++++++
>  lib/crypto/x509_parser.h          |   57 +
>  lib/crypto/x509_public_key.c      |  292 ++++++
>  lib/linux_compat.c                |   11 +
>  lib/oid_registry.c                |  179 ++++
>  scripts/Makefile                  |    3 +
>  scripts/Makefile.build            |    2 +-
>  scripts/asn1_compiler.c           | 1611 +++++++++++++++++++++++++++++
>  35 files changed, 6533 insertions(+), 3 deletions(-)
>  create mode 100644 include/crypto/internal/rsa.h
>  create mode 100644 include/crypto/pkcs7.h
>  create mode 100644 include/crypto/public_key.h
>  create mode 100644 include/keys/asymmetric-type.h
>  create mode 100644 include/linux/asn1.h
>  create mode 100644 include/linux/asn1_ber_bytecode.h
>  create mode 100644 include/linux/asn1_decoder.h
>  create mode 100644 include/linux/oid_registry.h
>  create mode 100644 lib/asn1_decoder.c
>  create mode 100755 lib/build_OID_registry
>  create mode 100644 lib/crypto/Kconfig
>  create mode 100644 lib/crypto/Makefile
>  create mode 100644 lib/crypto/asymmetric_type.c
>  create mode 100644 lib/crypto/pkcs7.asn1
>  create mode 100644 lib/crypto/pkcs7_parser.c
>  create mode 100644 lib/crypto/pkcs7_parser.h
>  create mode 100644 lib/crypto/public_key.c
>  create mode 100644 lib/crypto/rsa_helper.c
>  create mode 100644 lib/crypto/rsapubkey.asn1
>  create mode 100644 lib/crypto/x509.asn1
>  create mode 100644 lib/crypto/x509_akid.asn1
>  create mode 100644 lib/crypto/x509_cert_parser.c
>  create mode 100644 lib/crypto/x509_parser.h
>  create mode 100644 lib/crypto/x509_public_key.c
>  create mode 100644 lib/oid_registry.c
>  create mode 100644 scripts/asn1_compiler.c
> 
> -- 
> 2.21.0
> 

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

* [U-Boot] [PATCH v1 01/11] linux_compat: add kmemdup()
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 01/11] linux_compat: add kmemdup() AKASHI Takahiro
@ 2019-10-12 11:22   ` Heinrich Schuchardt
  2019-10-17  3:04     ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 11:22 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> Adding kmemdup() will help improve portability from linux kernel
> code (in my case, lib/crypto/x509_cert_parser.c and pkcs7_parser.c).
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/linux/compat.h |  4 ++--
>   lib/linux_compat.c     | 11 +++++++++++
>   2 files changed, 13 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/compat.h b/include/linux/compat.h
> index d0f51baab407..d23ef50454ce 100644
> --- a/include/linux/compat.h
> +++ b/include/linux/compat.h
> @@ -117,6 +117,8 @@ static inline void kmem_cache_destroy(struct kmem_cache *cachep)
>   	free(cachep);
>   }
>
> +void *kmemdup(const void *src, size_t size, int flags);

kememdup() is already implemented in fs/ubifs/ubifs.c and used both in
drivers/mtd/ and in fs/ubifs/.

Why would you want to use a different signature than both the Linux
kernel and current U-Boot?

> +
>   #define DECLARE_WAITQUEUE(...)	do { } while (0)
>   #define add_wait_queue(...)	do { } while (0)
>   #define remove_wait_queue(...)	do { } while (0)
> @@ -346,8 +348,6 @@ struct writeback_control {
>   	unsigned for_sync:1;		/* sync(2) WB_SYNC_ALL writeback */
>   };
>
> -void *kmemdup(const void *src, size_t len, gfp_t gfp);
> -
>   typedef int irqreturn_t;
>
>   struct timer_list {};
> diff --git a/lib/linux_compat.c b/lib/linux_compat.c
> index 6373b4451eb3..dd1e5b3c2087 100644
> --- a/lib/linux_compat.c
> +++ b/lib/linux_compat.c
> @@ -40,3 +40,14 @@ void *kmem_cache_alloc(struct kmem_cache *obj, int flag)
>   {
>   	return malloc_cache_aligned(obj->sz);
>   }
> +
> +void *kmemdup(const void *src, size_t size, int flags)
> +{

You should not duplicate the implementation in the UBI filesystem.
Instead move the existing implementation to a place that can be linked
in your future work.

Best regards

Heinrich

> +	void *p;
> +
> +	p = kmalloc(size, flags);
> +	if (p)
> +		memcpy(p, src, size);
> +
> +	return p;
> +}
>

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

* [U-Boot] [PATCH v1 02/11] include: time.h: define time64_t
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 02/11] include: time.h: define time64_t AKASHI Takahiro
@ 2019-10-12 11:40   ` Heinrich Schuchardt
  2019-10-17  5:39     ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 11:40 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> Adding time64_t definition will help improve portability of linux kernel
> code (in my case, lib/crypto/x509_cert_parser.c).
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/linux/time.h | 24 ++++++++++++++++++++++++
>   1 file changed, 24 insertions(+)
>
> diff --git a/include/linux/time.h b/include/linux/time.h
> index b8d298eb4d68..6186985856d7 100644
> --- a/include/linux/time.h
> +++ b/include/linux/time.h
> @@ -150,4 +150,28 @@ _DEFUN (ctime_r, (tim_p, result),
>       return asctime_r (localtime_r (tim_p, &tm), result);
>   }
>
> +/* from <linux>/kernel/time/time.c */
> +typedef __s64 time64_t;

Wouldn't we want to put these definitions into a file
include/linux/time64.h?

> +
> +inline time64_t mktime64(const unsigned int year0, const unsigned int mon0,
> +			 const unsigned int day, const unsigned int hour,
> +			 const unsigned int min, const unsigned int sec)


Where is the function description?

The Linux kernel does not make this function an inline function. Why
should we inline it in U-Boot?

> +{
> +	unsigned int mon = mon0, year = year0;
> +
> +	/* 1..12 -> 11,12,1..10 */
> +	mon -= 2;
> +	if (0 >= (int)mon) {
> +		mon += 12;	/* Puts Feb last since it has leap day */
> +		year -= 1;
> +	}
> +
> +	return ((((time64_t)
> +		  (year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) +
> +		  year * 365 - 719499
> +	  ) * 24 + hour /* now have hours - midnight tomorrow handled here */
> +	  ) * 60 + min /* now have minutes */
> +	) * 60 + sec; /* finally seconds */

This code is duplicate to rtc_mktime().

Duplication could be avoided by letting rtc_mktime() call mktime64().

Best regards

Heinrich

> +}
> +
>   #endif
>

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

* [U-Boot] [PATCH v1 03/11] include: kernel.h: include printk.h
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 03/11] include: kernel.h: include printk.h AKASHI Takahiro
@ 2019-10-12 11:47   ` Heinrich Schuchardt
  2019-10-17  5:58     ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 11:47 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> Adding "printk.h" will help improve portability from linux kernel
> code (in my case, lib/asn1_decoder.c).
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/linux/kernel.h | 2 ++
>   1 file changed, 2 insertions(+)
>
> diff --git a/include/linux/kernel.h b/include/linux/kernel.h
> index a85c15d8dc28..919d12bdf89c 100644
> --- a/include/linux/kernel.h
> +++ b/include/linux/kernel.h
> @@ -2,7 +2,9 @@
>   #define _LINUX_KERNEL_H
>
>
> +#include <vsprintf.h> /* for printf utilities */

Kernel code has no vsprintf.h. Linux's lib/asn1_decoder.c has not a
single print statement. So why are you inserting vsprintf.h here?

>   #include <linux/types.h>
> +#include <linux/printk.h>

Linux include/linux/kernel.h also includes printk.h. OK.

Best regards

Heinrich
>
>   #define USHRT_MAX	((u16)(~0U))
>   #define SHRT_MAX	((s16)(USHRT_MAX>>1))
>

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

* [U-Boot] [PATCH v1 04/11] cmd: add asn1_compiler
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 04/11] cmd: add asn1_compiler AKASHI Takahiro
@ 2019-10-12 12:22   ` Heinrich Schuchardt
  2019-10-17  6:25     ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 12:22 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> Imported from linux kernel v5.3.
>
> This host command will be used to create a ASN1 parser, for example,
> for pkcs7 or x509.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   cmd/Kconfig                       |    3 +
>   include/linux/asn1.h              |   65 ++
>   include/linux/asn1_ber_bytecode.h |   89 ++
>   include/linux/asn1_decoder.h      |   20 +
>   scripts/Makefile                  |    3 +
>   scripts/asn1_compiler.c           | 1611 +++++++++++++++++++++++++++++

Except for Makefile and Kconfig these are verbatime copies of the Linux
files. OK.

>   6 files changed, 1791 insertions(+)
>   create mode 100644 include/linux/asn1.h
>   create mode 100644 include/linux/asn1_ber_bytecode.h
>   create mode 100644 include/linux/asn1_decoder.h
>   create mode 100644 scripts/asn1_compiler.c
>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 4e61565aab16..605dcfdf4282 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -184,6 +184,9 @@ endmenu
>   config BUILD_BIN2C
>   	bool
>
> +config BUILD_ASN1
> +	bool
> +
>   comment "Commands"
>
>   menu "Info commands"
> diff --git a/include/linux/asn1.h b/include/linux/asn1.h
> new file mode 100644
> index 000000000000..a4d0bdd10711
> --- /dev/null
> +++ b/include/linux/asn1.h
> @@ -0,0 +1,65 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/* ASN.1 BER/DER/CER encoding definitions
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells at redhat.com)
> + */
> +
> +#ifndef _LINUX_ASN1_H
> +#define _LINUX_ASN1_H
> +
> +/* Class */
> +enum asn1_class {
> +	ASN1_UNIV	= 0,	/* Universal */
> +	ASN1_APPL	= 1,	/* Application */
> +	ASN1_CONT	= 2,	/* Context */
> +	ASN1_PRIV	= 3	/* Private */
> +};
> +#define ASN1_CLASS_BITS	0xc0
> +
> +
> +enum asn1_method {
> +	ASN1_PRIM	= 0,	/* Primitive */
> +	ASN1_CONS	= 1	/* Constructed */
> +};
> +#define ASN1_CONS_BIT	0x20
> +
> +/* Tag */
> +enum asn1_tag {
> +	ASN1_EOC	= 0,	/* End Of Contents or N/A */
> +	ASN1_BOOL	= 1,	/* Boolean */
> +	ASN1_INT	= 2,	/* Integer */
> +	ASN1_BTS	= 3,	/* Bit String */
> +	ASN1_OTS	= 4,	/* Octet String */
> +	ASN1_NULL	= 5,	/* Null */
> +	ASN1_OID	= 6,	/* Object Identifier  */
> +	ASN1_ODE	= 7,	/* Object Description */
> +	ASN1_EXT	= 8,	/* External */
> +	ASN1_REAL	= 9,	/* Real float */
> +	ASN1_ENUM	= 10,	/* Enumerated */
> +	ASN1_EPDV	= 11,	/* Embedded PDV */
> +	ASN1_UTF8STR	= 12,	/* UTF8 String */
> +	ASN1_RELOID	= 13,	/* Relative OID */
> +	/* 14 - Reserved */
> +	/* 15 - Reserved */
> +	ASN1_SEQ	= 16,	/* Sequence and Sequence of */
> +	ASN1_SET	= 17,	/* Set and Set of */
> +	ASN1_NUMSTR	= 18,	/* Numerical String */
> +	ASN1_PRNSTR	= 19,	/* Printable String */
> +	ASN1_TEXSTR	= 20,	/* T61 String / Teletext String */
> +	ASN1_VIDSTR	= 21,	/* Videotex String */
> +	ASN1_IA5STR	= 22,	/* IA5 String */
> +	ASN1_UNITIM	= 23,	/* Universal Time */
> +	ASN1_GENTIM	= 24,	/* General Time */
> +	ASN1_GRASTR	= 25,	/* Graphic String */
> +	ASN1_VISSTR	= 26,	/* Visible String */
> +	ASN1_GENSTR	= 27,	/* General String */
> +	ASN1_UNISTR	= 28,	/* Universal String */
> +	ASN1_CHRSTR	= 29,	/* Character String */
> +	ASN1_BMPSTR	= 30,	/* BMP String */
> +	ASN1_LONG_TAG	= 31	/* Long form tag */
> +};
> +
> +#define ASN1_INDEFINITE_LENGTH 0x80
> +
> +#endif /* _LINUX_ASN1_H */
> diff --git a/include/linux/asn1_ber_bytecode.h b/include/linux/asn1_ber_bytecode.h
> new file mode 100644
> index 000000000000..b38361953a48
> --- /dev/null
> +++ b/include/linux/asn1_ber_bytecode.h
> @@ -0,0 +1,89 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/* ASN.1 BER/DER/CER parsing state machine internal definitions
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells at redhat.com)
> + */
> +
> +#ifndef _LINUX_ASN1_BER_BYTECODE_H
> +#define _LINUX_ASN1_BER_BYTECODE_H
> +
> +#ifdef __KERNEL__
> +#include <linux/types.h>
> +#endif
> +#include <linux/asn1.h>
> +
> +typedef int (*asn1_action_t)(void *context,
> +			     size_t hdrlen, /* In case of ANY type */
> +			     unsigned char tag, /* In case of ANY type */
> +			     const void *value, size_t vlen);
> +
> +struct asn1_decoder {
> +	const unsigned char *machine;
> +	size_t machlen;
> +	const asn1_action_t *actions;
> +};
> +
> +enum asn1_opcode {
> +	/* The tag-matching ops come first and the odd-numbered slots
> +	 * are for OR_SKIP ops.
> +	 */
> +#define ASN1_OP_MATCH__SKIP		  0x01
> +#define ASN1_OP_MATCH__ACT		  0x02
> +#define ASN1_OP_MATCH__JUMP		  0x04
> +#define ASN1_OP_MATCH__ANY		  0x08
> +#define ASN1_OP_MATCH__COND		  0x10
> +
> +	ASN1_OP_MATCH			= 0x00,
> +	ASN1_OP_MATCH_OR_SKIP		= 0x01,
> +	ASN1_OP_MATCH_ACT		= 0x02,
> +	ASN1_OP_MATCH_ACT_OR_SKIP	= 0x03,
> +	ASN1_OP_MATCH_JUMP		= 0x04,
> +	ASN1_OP_MATCH_JUMP_OR_SKIP	= 0x05,
> +	ASN1_OP_MATCH_ANY		= 0x08,
> +	ASN1_OP_MATCH_ANY_OR_SKIP	= 0x09,
> +	ASN1_OP_MATCH_ANY_ACT		= 0x0a,
> +	ASN1_OP_MATCH_ANY_ACT_OR_SKIP	= 0x0b,
> +	/* Everything before here matches unconditionally */
> +
> +	ASN1_OP_COND_MATCH_OR_SKIP	= 0x11,
> +	ASN1_OP_COND_MATCH_ACT_OR_SKIP	= 0x13,
> +	ASN1_OP_COND_MATCH_JUMP_OR_SKIP	= 0x15,
> +	ASN1_OP_COND_MATCH_ANY		= 0x18,
> +	ASN1_OP_COND_MATCH_ANY_OR_SKIP	= 0x19,
> +	ASN1_OP_COND_MATCH_ANY_ACT	= 0x1a,
> +	ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP = 0x1b,
> +
> +	/* Everything before here will want a tag from the data */
> +#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP
> +
> +	/* These are here to help fill up space */
> +	ASN1_OP_COND_FAIL		= 0x1c,
> +	ASN1_OP_COMPLETE		= 0x1d,
> +	ASN1_OP_ACT			= 0x1e,
> +	ASN1_OP_MAYBE_ACT		= 0x1f,
> +
> +	/* The following eight have bit 0 -> SET, 1 -> OF, 2 -> ACT */
> +	ASN1_OP_END_SEQ			= 0x20,
> +	ASN1_OP_END_SET			= 0x21,
> +	ASN1_OP_END_SEQ_OF		= 0x22,
> +	ASN1_OP_END_SET_OF		= 0x23,
> +	ASN1_OP_END_SEQ_ACT		= 0x24,
> +	ASN1_OP_END_SET_ACT		= 0x25,
> +	ASN1_OP_END_SEQ_OF_ACT		= 0x26,
> +	ASN1_OP_END_SET_OF_ACT		= 0x27,
> +#define ASN1_OP_END__SET		  0x01
> +#define ASN1_OP_END__OF			  0x02
> +#define ASN1_OP_END__ACT		  0x04
> +
> +	ASN1_OP_RETURN			= 0x28,
> +
> +	ASN1_OP__NR
> +};
> +
> +#define _tag(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | ASN1_##TAG)
> +#define _tagn(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | TAG)
> +#define _jump_target(N) (N)
> +#define _action(N) (N)
> +
> +#endif /* _LINUX_ASN1_BER_BYTECODE_H */
> diff --git a/include/linux/asn1_decoder.h b/include/linux/asn1_decoder.h
> new file mode 100644
> index 000000000000..83f9c6e1e5e9
> --- /dev/null
> +++ b/include/linux/asn1_decoder.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/* ASN.1 decoder
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells at redhat.com)
> + */
> +
> +#ifndef _LINUX_ASN1_DECODER_H
> +#define _LINUX_ASN1_DECODER_H
> +
> +#include <linux/asn1.h>
> +
> +struct asn1_decoder;
> +
> +extern int asn1_ber_decoder(const struct asn1_decoder *decoder,
> +			    void *context,
> +			    const unsigned char *data,
> +			    size_t datalen);
> +
> +#endif /* _LINUX_ASN1_DECODER_H */
> diff --git a/scripts/Makefile b/scripts/Makefile
> index e7b353f77f43..c4e2ae5db2ea 100644
> --- a/scripts/Makefile
> +++ b/scripts/Makefile
> @@ -5,6 +5,9 @@
>   # ---------------------------------------------------------------------------
>
>   hostprogs-$(CONFIG_BUILD_BIN2C)		+= bin2c
> +hostprogs-$(CONFIG_BUILD_ASN1)		+= asn1_compiler
> +
> +HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include

Which problems did you run into that make you use -idirafter here?

Otherwise

Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>


>
>   always		:= $(hostprogs-y)
>
> diff --git a/scripts/asn1_compiler.c b/scripts/asn1_compiler.c
> new file mode 100644
> index 000000000000..adabd4145264
> --- /dev/null
> +++ b/scripts/asn1_compiler.c
> @@ -0,0 +1,1611 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* Simplified ASN.1 notation parser
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells at redhat.com)
> + */
> +
> +#include <stdarg.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <ctype.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <sys/stat.h>
> +#include <linux/asn1_ber_bytecode.h>
> +
> +enum token_type {
> +	DIRECTIVE_ABSENT,
> +	DIRECTIVE_ALL,
> +	DIRECTIVE_ANY,
> +	DIRECTIVE_APPLICATION,
> +	DIRECTIVE_AUTOMATIC,
> +	DIRECTIVE_BEGIN,
> +	DIRECTIVE_BIT,
> +	DIRECTIVE_BMPString,
> +	DIRECTIVE_BOOLEAN,
> +	DIRECTIVE_BY,
> +	DIRECTIVE_CHARACTER,
> +	DIRECTIVE_CHOICE,
> +	DIRECTIVE_CLASS,
> +	DIRECTIVE_COMPONENT,
> +	DIRECTIVE_COMPONENTS,
> +	DIRECTIVE_CONSTRAINED,
> +	DIRECTIVE_CONTAINING,
> +	DIRECTIVE_DEFAULT,
> +	DIRECTIVE_DEFINED,
> +	DIRECTIVE_DEFINITIONS,
> +	DIRECTIVE_EMBEDDED,
> +	DIRECTIVE_ENCODED,
> +	DIRECTIVE_ENCODING_CONTROL,
> +	DIRECTIVE_END,
> +	DIRECTIVE_ENUMERATED,
> +	DIRECTIVE_EXCEPT,
> +	DIRECTIVE_EXPLICIT,
> +	DIRECTIVE_EXPORTS,
> +	DIRECTIVE_EXTENSIBILITY,
> +	DIRECTIVE_EXTERNAL,
> +	DIRECTIVE_FALSE,
> +	DIRECTIVE_FROM,
> +	DIRECTIVE_GeneralString,
> +	DIRECTIVE_GeneralizedTime,
> +	DIRECTIVE_GraphicString,
> +	DIRECTIVE_IA5String,
> +	DIRECTIVE_IDENTIFIER,
> +	DIRECTIVE_IMPLICIT,
> +	DIRECTIVE_IMPLIED,
> +	DIRECTIVE_IMPORTS,
> +	DIRECTIVE_INCLUDES,
> +	DIRECTIVE_INSTANCE,
> +	DIRECTIVE_INSTRUCTIONS,
> +	DIRECTIVE_INTEGER,
> +	DIRECTIVE_INTERSECTION,
> +	DIRECTIVE_ISO646String,
> +	DIRECTIVE_MAX,
> +	DIRECTIVE_MIN,
> +	DIRECTIVE_MINUS_INFINITY,
> +	DIRECTIVE_NULL,
> +	DIRECTIVE_NumericString,
> +	DIRECTIVE_OBJECT,
> +	DIRECTIVE_OCTET,
> +	DIRECTIVE_OF,
> +	DIRECTIVE_OPTIONAL,
> +	DIRECTIVE_ObjectDescriptor,
> +	DIRECTIVE_PATTERN,
> +	DIRECTIVE_PDV,
> +	DIRECTIVE_PLUS_INFINITY,
> +	DIRECTIVE_PRESENT,
> +	DIRECTIVE_PRIVATE,
> +	DIRECTIVE_PrintableString,
> +	DIRECTIVE_REAL,
> +	DIRECTIVE_RELATIVE_OID,
> +	DIRECTIVE_SEQUENCE,
> +	DIRECTIVE_SET,
> +	DIRECTIVE_SIZE,
> +	DIRECTIVE_STRING,
> +	DIRECTIVE_SYNTAX,
> +	DIRECTIVE_T61String,
> +	DIRECTIVE_TAGS,
> +	DIRECTIVE_TRUE,
> +	DIRECTIVE_TeletexString,
> +	DIRECTIVE_UNION,
> +	DIRECTIVE_UNIQUE,
> +	DIRECTIVE_UNIVERSAL,
> +	DIRECTIVE_UTCTime,
> +	DIRECTIVE_UTF8String,
> +	DIRECTIVE_UniversalString,
> +	DIRECTIVE_VideotexString,
> +	DIRECTIVE_VisibleString,
> +	DIRECTIVE_WITH,
> +	NR__DIRECTIVES,
> +	TOKEN_ASSIGNMENT = NR__DIRECTIVES,
> +	TOKEN_OPEN_CURLY,
> +	TOKEN_CLOSE_CURLY,
> +	TOKEN_OPEN_SQUARE,
> +	TOKEN_CLOSE_SQUARE,
> +	TOKEN_OPEN_ACTION,
> +	TOKEN_CLOSE_ACTION,
> +	TOKEN_COMMA,
> +	TOKEN_NUMBER,
> +	TOKEN_TYPE_NAME,
> +	TOKEN_ELEMENT_NAME,
> +	NR__TOKENS
> +};
> +
> +static const unsigned char token_to_tag[NR__TOKENS] = {
> +	/* EOC goes first */
> +	[DIRECTIVE_BOOLEAN]		= ASN1_BOOL,
> +	[DIRECTIVE_INTEGER]		= ASN1_INT,
> +	[DIRECTIVE_BIT]			= ASN1_BTS,
> +	[DIRECTIVE_OCTET]		= ASN1_OTS,
> +	[DIRECTIVE_NULL]		= ASN1_NULL,
> +	[DIRECTIVE_OBJECT]		= ASN1_OID,
> +	[DIRECTIVE_ObjectDescriptor]	= ASN1_ODE,
> +	[DIRECTIVE_EXTERNAL]		= ASN1_EXT,
> +	[DIRECTIVE_REAL]		= ASN1_REAL,
> +	[DIRECTIVE_ENUMERATED]		= ASN1_ENUM,
> +	[DIRECTIVE_EMBEDDED]		= 0,
> +	[DIRECTIVE_UTF8String]		= ASN1_UTF8STR,
> +	[DIRECTIVE_RELATIVE_OID]	= ASN1_RELOID,
> +	/* 14 */
> +	/* 15 */
> +	[DIRECTIVE_SEQUENCE]		= ASN1_SEQ,
> +	[DIRECTIVE_SET]			= ASN1_SET,
> +	[DIRECTIVE_NumericString]	= ASN1_NUMSTR,
> +	[DIRECTIVE_PrintableString]	= ASN1_PRNSTR,
> +	[DIRECTIVE_T61String]		= ASN1_TEXSTR,
> +	[DIRECTIVE_TeletexString]	= ASN1_TEXSTR,
> +	[DIRECTIVE_VideotexString]	= ASN1_VIDSTR,
> +	[DIRECTIVE_IA5String]		= ASN1_IA5STR,
> +	[DIRECTIVE_UTCTime]		= ASN1_UNITIM,
> +	[DIRECTIVE_GeneralizedTime]	= ASN1_GENTIM,
> +	[DIRECTIVE_GraphicString]	= ASN1_GRASTR,
> +	[DIRECTIVE_VisibleString]	= ASN1_VISSTR,
> +	[DIRECTIVE_GeneralString]	= ASN1_GENSTR,
> +	[DIRECTIVE_UniversalString]	= ASN1_UNITIM,
> +	[DIRECTIVE_CHARACTER]		= ASN1_CHRSTR,
> +	[DIRECTIVE_BMPString]		= ASN1_BMPSTR,
> +};
> +
> +static const char asn1_classes[4][5] = {
> +	[ASN1_UNIV]	= "UNIV",
> +	[ASN1_APPL]	= "APPL",
> +	[ASN1_CONT]	= "CONT",
> +	[ASN1_PRIV]	= "PRIV"
> +};
> +
> +static const char asn1_methods[2][5] = {
> +	[ASN1_UNIV]	= "PRIM",
> +	[ASN1_APPL]	= "CONS"
> +};
> +
> +static const char *const asn1_universal_tags[32] = {
> +	"EOC",
> +	"BOOL",
> +	"INT",
> +	"BTS",
> +	"OTS",
> +	"NULL",
> +	"OID",
> +	"ODE",
> +	"EXT",
> +	"REAL",
> +	"ENUM",
> +	"EPDV",
> +	"UTF8STR",
> +	"RELOID",
> +	NULL,		/* 14 */
> +	NULL,		/* 15 */
> +	"SEQ",
> +	"SET",
> +	"NUMSTR",
> +	"PRNSTR",
> +	"TEXSTR",
> +	"VIDSTR",
> +	"IA5STR",
> +	"UNITIM",
> +	"GENTIM",
> +	"GRASTR",
> +	"VISSTR",
> +	"GENSTR",
> +	"UNISTR",
> +	"CHRSTR",
> +	"BMPSTR",
> +	NULL		/* 31 */
> +};
> +
> +static const char *filename;
> +static const char *grammar_name;
> +static const char *outputname;
> +static const char *headername;
> +
> +static const char *const directives[NR__DIRECTIVES] = {
> +#define _(X) [DIRECTIVE_##X] = #X
> +	_(ABSENT),
> +	_(ALL),
> +	_(ANY),
> +	_(APPLICATION),
> +	_(AUTOMATIC),
> +	_(BEGIN),
> +	_(BIT),
> +	_(BMPString),
> +	_(BOOLEAN),
> +	_(BY),
> +	_(CHARACTER),
> +	_(CHOICE),
> +	_(CLASS),
> +	_(COMPONENT),
> +	_(COMPONENTS),
> +	_(CONSTRAINED),
> +	_(CONTAINING),
> +	_(DEFAULT),
> +	_(DEFINED),
> +	_(DEFINITIONS),
> +	_(EMBEDDED),
> +	_(ENCODED),
> +	[DIRECTIVE_ENCODING_CONTROL] = "ENCODING-CONTROL",
> +	_(END),
> +	_(ENUMERATED),
> +	_(EXCEPT),
> +	_(EXPLICIT),
> +	_(EXPORTS),
> +	_(EXTENSIBILITY),
> +	_(EXTERNAL),
> +	_(FALSE),
> +	_(FROM),
> +	_(GeneralString),
> +	_(GeneralizedTime),
> +	_(GraphicString),
> +	_(IA5String),
> +	_(IDENTIFIER),
> +	_(IMPLICIT),
> +	_(IMPLIED),
> +	_(IMPORTS),
> +	_(INCLUDES),
> +	_(INSTANCE),
> +	_(INSTRUCTIONS),
> +	_(INTEGER),
> +	_(INTERSECTION),
> +	_(ISO646String),
> +	_(MAX),
> +	_(MIN),
> +	[DIRECTIVE_MINUS_INFINITY] = "MINUS-INFINITY",
> +	[DIRECTIVE_NULL] = "NULL",
> +	_(NumericString),
> +	_(OBJECT),
> +	_(OCTET),
> +	_(OF),
> +	_(OPTIONAL),
> +	_(ObjectDescriptor),
> +	_(PATTERN),
> +	_(PDV),
> +	[DIRECTIVE_PLUS_INFINITY] = "PLUS-INFINITY",
> +	_(PRESENT),
> +	_(PRIVATE),
> +	_(PrintableString),
> +	_(REAL),
> +	[DIRECTIVE_RELATIVE_OID] = "RELATIVE-OID",
> +	_(SEQUENCE),
> +	_(SET),
> +	_(SIZE),
> +	_(STRING),
> +	_(SYNTAX),
> +	_(T61String),
> +	_(TAGS),
> +	_(TRUE),
> +	_(TeletexString),
> +	_(UNION),
> +	_(UNIQUE),
> +	_(UNIVERSAL),
> +	_(UTCTime),
> +	_(UTF8String),
> +	_(UniversalString),
> +	_(VideotexString),
> +	_(VisibleString),
> +	_(WITH)
> +};
> +
> +struct action {
> +	struct action	*next;
> +	char		*name;
> +	unsigned char	index;
> +};
> +
> +static struct action *action_list;
> +static unsigned nr_actions;
> +
> +struct token {
> +	unsigned short	line;
> +	enum token_type	token_type : 8;
> +	unsigned char	size;
> +	struct action	*action;
> +	char		*content;
> +	struct type	*type;
> +};
> +
> +static struct token *token_list;
> +static unsigned nr_tokens;
> +static bool verbose_opt;
> +static bool debug_opt;
> +
> +#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0)
> +#define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0)
> +
> +static int directive_compare(const void *_key, const void *_pdir)
> +{
> +	const struct token *token = _key;
> +	const char *const *pdir = _pdir, *dir = *pdir;
> +	size_t dlen, clen;
> +	int val;
> +
> +	dlen = strlen(dir);
> +	clen = (dlen < token->size) ? dlen : token->size;
> +
> +	//debug("cmp(%s,%s) = ", token->content, dir);
> +
> +	val = memcmp(token->content, dir, clen);
> +	if (val != 0) {
> +		//debug("%d [cmp]\n", val);
> +		return val;
> +	}
> +
> +	if (dlen == token->size) {
> +		//debug("0\n");
> +		return 0;
> +	}
> +	//debug("%d\n", (int)dlen - (int)token->size);
> +	return dlen - token->size; /* shorter -> negative */
> +}
> +
> +/*
> + * Tokenise an ASN.1 grammar
> + */
> +static void tokenise(char *buffer, char *end)
> +{
> +	struct token *tokens;
> +	char *line, *nl, *start, *p, *q;
> +	unsigned tix, lineno;
> +
> +	/* Assume we're going to have half as many tokens as we have
> +	 * characters
> +	 */
> +	token_list = tokens = calloc((end - buffer) / 2, sizeof(struct token));
> +	if (!tokens) {
> +		perror(NULL);
> +		exit(1);
> +	}
> +	tix = 0;
> +
> +	lineno = 0;
> +	while (buffer < end) {
> +		/* First of all, break out a line */
> +		lineno++;
> +		line = buffer;
> +		nl = memchr(line, '\n', end - buffer);
> +		if (!nl) {
> +			buffer = nl = end;
> +		} else {
> +			buffer = nl + 1;
> +			*nl = '\0';
> +		}
> +
> +		/* Remove "--" comments */
> +		p = line;
> +	next_comment:
> +		while ((p = memchr(p, '-', nl - p))) {
> +			if (p[1] == '-') {
> +				/* Found a comment; see if there's a terminator */
> +				q = p + 2;
> +				while ((q = memchr(q, '-', nl - q))) {
> +					if (q[1] == '-') {
> +						/* There is - excise the comment */
> +						q += 2;
> +						memmove(p, q, nl - q);
> +						goto next_comment;
> +					}
> +					q++;
> +				}
> +				*p = '\0';
> +				nl = p;
> +				break;
> +			} else {
> +				p++;
> +			}
> +		}
> +
> +		p = line;
> +		while (p < nl) {
> +			/* Skip white space */
> +			while (p < nl && isspace(*p))
> +				*(p++) = 0;
> +			if (p >= nl)
> +				break;
> +
> +			tokens[tix].line = lineno;
> +			start = p;
> +
> +			/* Handle string tokens */
> +			if (isalpha(*p)) {
> +				const char **dir;
> +
> +				/* Can be a directive, type name or element
> +				 * name.  Find the end of the name.
> +				 */
> +				q = p + 1;
> +				while (q < nl && (isalnum(*q) || *q == '-' || *q == '_'))
> +					q++;
> +				tokens[tix].size = q - p;
> +				p = q;
> +
> +				tokens[tix].content = malloc(tokens[tix].size + 1);
> +				if (!tokens[tix].content) {
> +					perror(NULL);
> +					exit(1);
> +				}
> +				memcpy(tokens[tix].content, start, tokens[tix].size);
> +				tokens[tix].content[tokens[tix].size] = 0;
> +
> +				/* If it begins with a lowercase letter then
> +				 * it's an element name
> +				 */
> +				if (islower(tokens[tix].content[0])) {
> +					tokens[tix++].token_type = TOKEN_ELEMENT_NAME;
> +					continue;
> +				}
> +
> +				/* Otherwise we need to search the directive
> +				 * table
> +				 */
> +				dir = bsearch(&tokens[tix], directives,
> +					      sizeof(directives) / sizeof(directives[1]),
> +					      sizeof(directives[1]),
> +					      directive_compare);
> +				if (dir) {
> +					tokens[tix++].token_type = dir - directives;
> +					continue;
> +				}
> +
> +				tokens[tix++].token_type = TOKEN_TYPE_NAME;
> +				continue;
> +			}
> +
> +			/* Handle numbers */
> +			if (isdigit(*p)) {
> +				/* Find the end of the number */
> +				q = p + 1;
> +				while (q < nl && (isdigit(*q)))
> +					q++;
> +				tokens[tix].size = q - p;
> +				p = q;
> +				tokens[tix].content = malloc(tokens[tix].size + 1);
> +				if (!tokens[tix].content) {
> +					perror(NULL);
> +					exit(1);
> +				}
> +				memcpy(tokens[tix].content, start, tokens[tix].size);
> +				tokens[tix].content[tokens[tix].size] = 0;
> +				tokens[tix++].token_type = TOKEN_NUMBER;
> +				continue;
> +			}
> +
> +			if (nl - p >= 3) {
> +				if (memcmp(p, "::=", 3) == 0) {
> +					p += 3;
> +					tokens[tix].size = 3;
> +					tokens[tix].content = "::=";
> +					tokens[tix++].token_type = TOKEN_ASSIGNMENT;
> +					continue;
> +				}
> +			}
> +
> +			if (nl - p >= 2) {
> +				if (memcmp(p, "({", 2) == 0) {
> +					p += 2;
> +					tokens[tix].size = 2;
> +					tokens[tix].content = "({";
> +					tokens[tix++].token_type = TOKEN_OPEN_ACTION;
> +					continue;
> +				}
> +				if (memcmp(p, "})", 2) == 0) {
> +					p += 2;
> +					tokens[tix].size = 2;
> +					tokens[tix].content = "})";
> +					tokens[tix++].token_type = TOKEN_CLOSE_ACTION;
> +					continue;
> +				}
> +			}
> +
> +			if (nl - p >= 1) {
> +				tokens[tix].size = 1;
> +				switch (*p) {
> +				case '{':
> +					p += 1;
> +					tokens[tix].content = "{";
> +					tokens[tix++].token_type = TOKEN_OPEN_CURLY;
> +					continue;
> +				case '}':
> +					p += 1;
> +					tokens[tix].content = "}";
> +					tokens[tix++].token_type = TOKEN_CLOSE_CURLY;
> +					continue;
> +				case '[':
> +					p += 1;
> +					tokens[tix].content = "[";
> +					tokens[tix++].token_type = TOKEN_OPEN_SQUARE;
> +					continue;
> +				case ']':
> +					p += 1;
> +					tokens[tix].content = "]";
> +					tokens[tix++].token_type = TOKEN_CLOSE_SQUARE;
> +					continue;
> +				case ',':
> +					p += 1;
> +					tokens[tix].content = ",";
> +					tokens[tix++].token_type = TOKEN_COMMA;
> +					continue;
> +				default:
> +					break;
> +				}
> +			}
> +
> +			fprintf(stderr, "%s:%u: Unknown character in grammar: '%c'\n",
> +				filename, lineno, *p);
> +			exit(1);
> +		}
> +	}
> +
> +	nr_tokens = tix;
> +	verbose("Extracted %u tokens\n", nr_tokens);
> +
> +#if 0
> +	{
> +		int n;
> +		for (n = 0; n < nr_tokens; n++)
> +			debug("Token %3u: '%s'\n", n, token_list[n].content);
> +	}
> +#endif
> +}
> +
> +static void build_type_list(void);
> +static void parse(void);
> +static void dump_elements(void);
> +static void render(FILE *out, FILE *hdr);
> +
> +/*
> + *
> + */
> +int main(int argc, char **argv)
> +{
> +	struct stat st;
> +	ssize_t readlen;
> +	FILE *out, *hdr;
> +	char *buffer, *p;
> +	char *kbuild_verbose;
> +	int fd;
> +
> +	kbuild_verbose = getenv("KBUILD_VERBOSE");
> +	if (kbuild_verbose)
> +		verbose_opt = atoi(kbuild_verbose);
> +
> +	while (argc > 4) {
> +		if (strcmp(argv[1], "-v") == 0)
> +			verbose_opt = true;
> +		else if (strcmp(argv[1], "-d") == 0)
> +			debug_opt = true;
> +		else
> +			break;
> +		memmove(&argv[1], &argv[2], (argc - 2) * sizeof(char *));
> +		argc--;
> +	}
> +
> +	if (argc != 4) {
> +		fprintf(stderr, "Format: %s [-v] [-d] <grammar-file> <c-file> <hdr-file>\n",
> +			argv[0]);
> +		exit(2);
> +	}
> +
> +	filename = argv[1];
> +	outputname = argv[2];
> +	headername = argv[3];
> +
> +	fd = open(filename, O_RDONLY);
> +	if (fd < 0) {
> +		perror(filename);
> +		exit(1);
> +	}
> +
> +	if (fstat(fd, &st) < 0) {
> +		perror(filename);
> +		exit(1);
> +	}
> +
> +	if (!(buffer = malloc(st.st_size + 1))) {
> +		perror(NULL);
> +		exit(1);
> +	}
> +
> +	if ((readlen = read(fd, buffer, st.st_size)) < 0) {
> +		perror(filename);
> +		exit(1);
> +	}
> +
> +	if (close(fd) < 0) {
> +		perror(filename);
> +		exit(1);
> +	}
> +
> +	if (readlen != st.st_size) {
> +		fprintf(stderr, "%s: Short read\n", filename);
> +		exit(1);
> +	}
> +
> +	p = strrchr(argv[1], '/');
> +	p = p ? p + 1 : argv[1];
> +	grammar_name = strdup(p);
> +	if (!p) {
> +		perror(NULL);
> +		exit(1);
> +	}
> +	p = strchr(grammar_name, '.');
> +	if (p)
> +		*p = '\0';
> +
> +	buffer[readlen] = 0;
> +	tokenise(buffer, buffer + readlen);
> +	build_type_list();
> +	parse();
> +	dump_elements();
> +
> +	out = fopen(outputname, "w");
> +	if (!out) {
> +		perror(outputname);
> +		exit(1);
> +	}
> +
> +	hdr = fopen(headername, "w");
> +	if (!hdr) {
> +		perror(headername);
> +		exit(1);
> +	}
> +
> +	render(out, hdr);
> +
> +	if (fclose(out) < 0) {
> +		perror(outputname);
> +		exit(1);
> +	}
> +
> +	if (fclose(hdr) < 0) {
> +		perror(headername);
> +		exit(1);
> +	}
> +
> +	return 0;
> +}
> +
> +enum compound {
> +	NOT_COMPOUND,
> +	SET,
> +	SET_OF,
> +	SEQUENCE,
> +	SEQUENCE_OF,
> +	CHOICE,
> +	ANY,
> +	TYPE_REF,
> +	TAG_OVERRIDE
> +};
> +
> +struct element {
> +	struct type	*type_def;
> +	struct token	*name;
> +	struct token	*type;
> +	struct action	*action;
> +	struct element	*children;
> +	struct element	*next;
> +	struct element	*render_next;
> +	struct element	*list_next;
> +	uint8_t		n_elements;
> +	enum compound	compound : 8;
> +	enum asn1_class	class : 8;
> +	enum asn1_method method : 8;
> +	uint8_t		tag;
> +	unsigned	entry_index;
> +	unsigned	flags;
> +#define ELEMENT_IMPLICIT	0x0001
> +#define ELEMENT_EXPLICIT	0x0002
> +#define ELEMENT_TAG_SPECIFIED	0x0004
> +#define ELEMENT_RENDERED	0x0008
> +#define ELEMENT_SKIPPABLE	0x0010
> +#define ELEMENT_CONDITIONAL	0x0020
> +};
> +
> +struct type {
> +	struct token	*name;
> +	struct token	*def;
> +	struct element	*element;
> +	unsigned	ref_count;
> +	unsigned	flags;
> +#define TYPE_STOP_MARKER	0x0001
> +#define TYPE_BEGIN		0x0002
> +};
> +
> +static struct type *type_list;
> +static struct type **type_index;
> +static unsigned nr_types;
> +
> +static int type_index_compare(const void *_a, const void *_b)
> +{
> +	const struct type *const *a = _a, *const *b = _b;
> +
> +	if ((*a)->name->size != (*b)->name->size)
> +		return (*a)->name->size - (*b)->name->size;
> +	else
> +		return memcmp((*a)->name->content, (*b)->name->content,
> +			      (*a)->name->size);
> +}
> +
> +static int type_finder(const void *_key, const void *_ti)
> +{
> +	const struct token *token = _key;
> +	const struct type *const *ti = _ti;
> +	const struct type *type = *ti;
> +
> +	if (token->size != type->name->size)
> +		return token->size - type->name->size;
> +	else
> +		return memcmp(token->content, type->name->content,
> +			      token->size);
> +}
> +
> +/*
> + * Build up a list of types and a sorted index to that list.
> + */
> +static void build_type_list(void)
> +{
> +	struct type *types;
> +	unsigned nr, t, n;
> +
> +	nr = 0;
> +	for (n = 0; n < nr_tokens - 1; n++)
> +		if (token_list[n + 0].token_type == TOKEN_TYPE_NAME &&
> +		    token_list[n + 1].token_type == TOKEN_ASSIGNMENT)
> +			nr++;
> +
> +	if (nr == 0) {
> +		fprintf(stderr, "%s: No defined types\n", filename);
> +		exit(1);
> +	}
> +
> +	nr_types = nr;
> +	types = type_list = calloc(nr + 1, sizeof(type_list[0]));
> +	if (!type_list) {
> +		perror(NULL);
> +		exit(1);
> +	}
> +	type_index = calloc(nr, sizeof(type_index[0]));
> +	if (!type_index) {
> +		perror(NULL);
> +		exit(1);
> +	}
> +
> +	t = 0;
> +	types[t].flags |= TYPE_BEGIN;
> +	for (n = 0; n < nr_tokens - 1; n++) {
> +		if (token_list[n + 0].token_type == TOKEN_TYPE_NAME &&
> +		    token_list[n + 1].token_type == TOKEN_ASSIGNMENT) {
> +			types[t].name = &token_list[n];
> +			type_index[t] = &types[t];
> +			t++;
> +		}
> +	}
> +	types[t].name = &token_list[n + 1];
> +	types[t].flags |= TYPE_STOP_MARKER;
> +
> +	qsort(type_index, nr, sizeof(type_index[0]), type_index_compare);
> +
> +	verbose("Extracted %u types\n", nr_types);
> +#if 0
> +	for (n = 0; n < nr_types; n++) {
> +		struct type *type = type_index[n];
> +		debug("- %*.*s\n", type->name->content);
> +	}
> +#endif
> +}
> +
> +static struct element *parse_type(struct token **_cursor, struct token *stop,
> +				  struct token *name);
> +
> +/*
> + * Parse the token stream
> + */
> +static void parse(void)
> +{
> +	struct token *cursor;
> +	struct type *type;
> +
> +	/* Parse one type definition statement at a time */
> +	type = type_list;
> +	do {
> +		cursor = type->name;
> +
> +		if (cursor[0].token_type != TOKEN_TYPE_NAME ||
> +		    cursor[1].token_type != TOKEN_ASSIGNMENT)
> +			abort();
> +		cursor += 2;
> +
> +		type->element = parse_type(&cursor, type[1].name, NULL);
> +		type->element->type_def = type;
> +
> +		if (cursor != type[1].name) {
> +			fprintf(stderr, "%s:%d: Parse error at token '%s'\n",
> +				filename, cursor->line, cursor->content);
> +			exit(1);
> +		}
> +
> +	} while (type++, !(type->flags & TYPE_STOP_MARKER));
> +
> +	verbose("Extracted %u actions\n", nr_actions);
> +}
> +
> +static struct element *element_list;
> +
> +static struct element *alloc_elem(struct token *type)
> +{
> +	struct element *e = calloc(1, sizeof(*e));
> +	if (!e) {
> +		perror(NULL);
> +		exit(1);
> +	}
> +	e->list_next = element_list;
> +	element_list = e;
> +	return e;
> +}
> +
> +static struct element *parse_compound(struct token **_cursor, struct token *end,
> +				      int alternates);
> +
> +/*
> + * Parse one type definition statement
> + */
> +static struct element *parse_type(struct token **_cursor, struct token *end,
> +				  struct token *name)
> +{
> +	struct element *top, *element;
> +	struct action *action, **ppaction;
> +	struct token *cursor = *_cursor;
> +	struct type **ref;
> +	char *p;
> +	int labelled = 0, implicit = 0;
> +
> +	top = element = alloc_elem(cursor);
> +	element->class = ASN1_UNIV;
> +	element->method = ASN1_PRIM;
> +	element->tag = token_to_tag[cursor->token_type];
> +	element->name = name;
> +
> +	/* Extract the tag value if one given */
> +	if (cursor->token_type == TOKEN_OPEN_SQUARE) {
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +		switch (cursor->token_type) {
> +		case DIRECTIVE_UNIVERSAL:
> +			element->class = ASN1_UNIV;
> +			cursor++;
> +			break;
> +		case DIRECTIVE_APPLICATION:
> +			element->class = ASN1_APPL;
> +			cursor++;
> +			break;
> +		case TOKEN_NUMBER:
> +			element->class = ASN1_CONT;
> +			break;
> +		case DIRECTIVE_PRIVATE:
> +			element->class = ASN1_PRIV;
> +			cursor++;
> +			break;
> +		default:
> +			fprintf(stderr, "%s:%d: Unrecognised tag class token '%s'\n",
> +				filename, cursor->line, cursor->content);
> +			exit(1);
> +		}
> +
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type != TOKEN_NUMBER) {
> +			fprintf(stderr, "%s:%d: Missing tag number '%s'\n",
> +				filename, cursor->line, cursor->content);
> +			exit(1);
> +		}
> +
> +		element->tag &= ~0x1f;
> +		element->tag |= strtoul(cursor->content, &p, 10);
> +		element->flags |= ELEMENT_TAG_SPECIFIED;
> +		if (p - cursor->content != cursor->size)
> +			abort();
> +		cursor++;
> +
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type != TOKEN_CLOSE_SQUARE) {
> +			fprintf(stderr, "%s:%d: Missing closing square bracket '%s'\n",
> +				filename, cursor->line, cursor->content);
> +			exit(1);
> +		}
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +		labelled = 1;
> +	}
> +
> +	/* Handle implicit and explicit markers */
> +	if (cursor->token_type == DIRECTIVE_IMPLICIT) {
> +		element->flags |= ELEMENT_IMPLICIT;
> +		implicit = 1;
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +	} else if (cursor->token_type == DIRECTIVE_EXPLICIT) {
> +		element->flags |= ELEMENT_EXPLICIT;
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +	}
> +
> +	if (labelled) {
> +		if (!implicit)
> +			element->method |= ASN1_CONS;
> +		element->compound = implicit ? TAG_OVERRIDE : SEQUENCE;
> +		element->children = alloc_elem(cursor);
> +		element = element->children;
> +		element->class = ASN1_UNIV;
> +		element->method = ASN1_PRIM;
> +		element->tag = token_to_tag[cursor->token_type];
> +		element->name = name;
> +	}
> +
> +	/* Extract the type we're expecting here */
> +	element->type = cursor;
> +	switch (cursor->token_type) {
> +	case DIRECTIVE_ANY:
> +		element->compound = ANY;
> +		cursor++;
> +		break;
> +
> +	case DIRECTIVE_NULL:
> +	case DIRECTIVE_BOOLEAN:
> +	case DIRECTIVE_ENUMERATED:
> +	case DIRECTIVE_INTEGER:
> +		element->compound = NOT_COMPOUND;
> +		cursor++;
> +		break;
> +
> +	case DIRECTIVE_EXTERNAL:
> +		element->method = ASN1_CONS;
> +
> +	case DIRECTIVE_BMPString:
> +	case DIRECTIVE_GeneralString:
> +	case DIRECTIVE_GraphicString:
> +	case DIRECTIVE_IA5String:
> +	case DIRECTIVE_ISO646String:
> +	case DIRECTIVE_NumericString:
> +	case DIRECTIVE_PrintableString:
> +	case DIRECTIVE_T61String:
> +	case DIRECTIVE_TeletexString:
> +	case DIRECTIVE_UniversalString:
> +	case DIRECTIVE_UTF8String:
> +	case DIRECTIVE_VideotexString:
> +	case DIRECTIVE_VisibleString:
> +	case DIRECTIVE_ObjectDescriptor:
> +	case DIRECTIVE_GeneralizedTime:
> +	case DIRECTIVE_UTCTime:
> +		element->compound = NOT_COMPOUND;
> +		cursor++;
> +		break;
> +
> +	case DIRECTIVE_BIT:
> +	case DIRECTIVE_OCTET:
> +		element->compound = NOT_COMPOUND;
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type != DIRECTIVE_STRING)
> +			goto parse_error;
> +		cursor++;
> +		break;
> +
> +	case DIRECTIVE_OBJECT:
> +		element->compound = NOT_COMPOUND;
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type != DIRECTIVE_IDENTIFIER)
> +			goto parse_error;
> +		cursor++;
> +		break;
> +
> +	case TOKEN_TYPE_NAME:
> +		element->compound = TYPE_REF;
> +		ref = bsearch(cursor, type_index, nr_types, sizeof(type_index[0]),
> +			      type_finder);
> +		if (!ref) {
> +			fprintf(stderr, "%s:%d: Type '%s' undefined\n",
> +				filename, cursor->line, cursor->content);
> +			exit(1);
> +		}
> +		cursor->type = *ref;
> +		(*ref)->ref_count++;
> +		cursor++;
> +		break;
> +
> +	case DIRECTIVE_CHOICE:
> +		element->compound = CHOICE;
> +		cursor++;
> +		element->children = parse_compound(&cursor, end, 1);
> +		break;
> +
> +	case DIRECTIVE_SEQUENCE:
> +		element->compound = SEQUENCE;
> +		element->method = ASN1_CONS;
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type == DIRECTIVE_OF) {
> +			element->compound = SEQUENCE_OF;
> +			cursor++;
> +			if (cursor >= end)
> +				goto overrun_error;
> +			element->children = parse_type(&cursor, end, NULL);
> +		} else {
> +			element->children = parse_compound(&cursor, end, 0);
> +		}
> +		break;
> +
> +	case DIRECTIVE_SET:
> +		element->compound = SET;
> +		element->method = ASN1_CONS;
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type == DIRECTIVE_OF) {
> +			element->compound = SET_OF;
> +			cursor++;
> +			if (cursor >= end)
> +				goto parse_error;
> +			element->children = parse_type(&cursor, end, NULL);
> +		} else {
> +			element->children = parse_compound(&cursor, end, 1);
> +		}
> +		break;
> +
> +	default:
> +		fprintf(stderr, "%s:%d: Token '%s' does not introduce a type\n",
> +			filename, cursor->line, cursor->content);
> +		exit(1);
> +	}
> +
> +	/* Handle elements that are optional */
> +	if (cursor < end && (cursor->token_type == DIRECTIVE_OPTIONAL ||
> +			     cursor->token_type == DIRECTIVE_DEFAULT)
> +	    ) {
> +		cursor++;
> +		top->flags |= ELEMENT_SKIPPABLE;
> +	}
> +
> +	if (cursor < end && cursor->token_type == TOKEN_OPEN_ACTION) {
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type != TOKEN_ELEMENT_NAME) {
> +			fprintf(stderr, "%s:%d: Token '%s' is not an action function name\n",
> +				filename, cursor->line, cursor->content);
> +			exit(1);
> +		}
> +
> +		action = malloc(sizeof(struct action));
> +		if (!action) {
> +			perror(NULL);
> +			exit(1);
> +		}
> +		action->index = 0;
> +		action->name = cursor->content;
> +
> +		for (ppaction = &action_list;
> +		     *ppaction;
> +		     ppaction = &(*ppaction)->next
> +		     ) {
> +			int cmp = strcmp(action->name, (*ppaction)->name);
> +			if (cmp == 0) {
> +				free(action);
> +				action = *ppaction;
> +				goto found;
> +			}
> +			if (cmp < 0) {
> +				action->next = *ppaction;
> +				*ppaction = action;
> +				nr_actions++;
> +				goto found;
> +			}
> +		}
> +		action->next = NULL;
> +		*ppaction = action;
> +		nr_actions++;
> +	found:
> +
> +		element->action = action;
> +		cursor->action = action;
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type != TOKEN_CLOSE_ACTION) {
> +			fprintf(stderr, "%s:%d: Missing close action, got '%s'\n",
> +				filename, cursor->line, cursor->content);
> +			exit(1);
> +		}
> +		cursor++;
> +	}
> +
> +	*_cursor = cursor;
> +	return top;
> +
> +parse_error:
> +	fprintf(stderr, "%s:%d: Unexpected token '%s'\n",
> +		filename, cursor->line, cursor->content);
> +	exit(1);
> +
> +overrun_error:
> +	fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
> +	exit(1);
> +}
> +
> +/*
> + * Parse a compound type list
> + */
> +static struct element *parse_compound(struct token **_cursor, struct token *end,
> +				      int alternates)
> +{
> +	struct element *children, **child_p = &children, *element;
> +	struct token *cursor = *_cursor, *name;
> +
> +	if (cursor->token_type != TOKEN_OPEN_CURLY) {
> +		fprintf(stderr, "%s:%d: Expected compound to start with brace not '%s'\n",
> +			filename, cursor->line, cursor->content);
> +		exit(1);
> +	}
> +	cursor++;
> +	if (cursor >= end)
> +		goto overrun_error;
> +
> +	if (cursor->token_type == TOKEN_OPEN_CURLY) {
> +		fprintf(stderr, "%s:%d: Empty compound\n",
> +			filename, cursor->line);
> +		exit(1);
> +	}
> +
> +	for (;;) {
> +		name = NULL;
> +		if (cursor->token_type == TOKEN_ELEMENT_NAME) {
> +			name = cursor;
> +			cursor++;
> +			if (cursor >= end)
> +				goto overrun_error;
> +		}
> +
> +		element = parse_type(&cursor, end, name);
> +		if (alternates)
> +			element->flags |= ELEMENT_SKIPPABLE | ELEMENT_CONDITIONAL;
> +
> +		*child_p = element;
> +		child_p = &element->next;
> +
> +		if (cursor >= end)
> +			goto overrun_error;
> +		if (cursor->token_type != TOKEN_COMMA)
> +			break;
> +		cursor++;
> +		if (cursor >= end)
> +			goto overrun_error;
> +	}
> +
> +	children->flags &= ~ELEMENT_CONDITIONAL;
> +
> +	if (cursor->token_type != TOKEN_CLOSE_CURLY) {
> +		fprintf(stderr, "%s:%d: Expected compound closure, got '%s'\n",
> +			filename, cursor->line, cursor->content);
> +		exit(1);
> +	}
> +	cursor++;
> +
> +	*_cursor = cursor;
> +	return children;
> +
> +overrun_error:
> +	fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
> +	exit(1);
> +}
> +
> +static void dump_element(const struct element *e, int level)
> +{
> +	const struct element *c;
> +	const struct type *t = e->type_def;
> +	const char *name = e->name ? e->name->content : ".";
> +	const char *tname = t && t->name ? t->name->content : ".";
> +	char tag[32];
> +
> +	if (e->class == 0 && e->method == 0 && e->tag == 0)
> +		strcpy(tag, "<...>");
> +	else if (e->class == ASN1_UNIV)
> +		sprintf(tag, "%s %s %s",
> +			asn1_classes[e->class],
> +			asn1_methods[e->method],
> +			asn1_universal_tags[e->tag]);
> +	else
> +		sprintf(tag, "%s %s %u",
> +			asn1_classes[e->class],
> +			asn1_methods[e->method],
> +			e->tag);
> +
> +	printf("%c%c%c%c%c %c %*s[*] \e[33m%s\e[m %s %s \e[35m%s\e[m\n",
> +	       e->flags & ELEMENT_IMPLICIT ? 'I' : '-',
> +	       e->flags & ELEMENT_EXPLICIT ? 'E' : '-',
> +	       e->flags & ELEMENT_TAG_SPECIFIED ? 'T' : '-',
> +	       e->flags & ELEMENT_SKIPPABLE ? 'S' : '-',
> +	       e->flags & ELEMENT_CONDITIONAL ? 'C' : '-',
> +	       "-tTqQcaro"[e->compound],
> +	       level, "",
> +	       tag,
> +	       tname,
> +	       name,
> +	       e->action ? e->action->name : "");
> +	if (e->compound == TYPE_REF)
> +		dump_element(e->type->type->element, level + 3);
> +	else
> +		for (c = e->children; c; c = c->next)
> +			dump_element(c, level + 3);
> +}
> +
> +static void dump_elements(void)
> +{
> +	if (debug_opt)
> +		dump_element(type_list[0].element, 0);
> +}
> +
> +static void render_element(FILE *out, struct element *e, struct element *tag);
> +static void render_out_of_line_list(FILE *out);
> +
> +static int nr_entries;
> +static int render_depth = 1;
> +static struct element *render_list, **render_list_p = &render_list;
> +
> +__attribute__((format(printf, 2, 3)))
> +static void render_opcode(FILE *out, const char *fmt, ...)
> +{
> +	va_list va;
> +
> +	if (out) {
> +		fprintf(out, "\t[%4d] =%*s", nr_entries, render_depth, "");
> +		va_start(va, fmt);
> +		vfprintf(out, fmt, va);
> +		va_end(va);
> +	}
> +	nr_entries++;
> +}
> +
> +__attribute__((format(printf, 2, 3)))
> +static void render_more(FILE *out, const char *fmt, ...)
> +{
> +	va_list va;
> +
> +	if (out) {
> +		va_start(va, fmt);
> +		vfprintf(out, fmt, va);
> +		va_end(va);
> +	}
> +}
> +
> +/*
> + * Render the grammar into a state machine definition.
> + */
> +static void render(FILE *out, FILE *hdr)
> +{
> +	struct element *e;
> +	struct action *action;
> +	struct type *root;
> +	int index;
> +
> +	fprintf(hdr, "/*\n");
> +	fprintf(hdr, " * Automatically generated by asn1_compiler.  Do not edit\n");
> +	fprintf(hdr, " *\n");
> +	fprintf(hdr, " * ASN.1 parser for %s\n", grammar_name);
> +	fprintf(hdr, " */\n");
> +	fprintf(hdr, "#include <linux/asn1_decoder.h>\n");
> +	fprintf(hdr, "\n");
> +	fprintf(hdr, "extern const struct asn1_decoder %s_decoder;\n", grammar_name);
> +	if (ferror(hdr)) {
> +		perror(headername);
> +		exit(1);
> +	}
> +
> +	fprintf(out, "/*\n");
> +	fprintf(out, " * Automatically generated by asn1_compiler.  Do not edit\n");
> +	fprintf(out, " *\n");
> +	fprintf(out, " * ASN.1 parser for %s\n", grammar_name);
> +	fprintf(out, " */\n");
> +	fprintf(out, "#include <linux/asn1_ber_bytecode.h>\n");
> +	fprintf(out, "#include \"%s.asn1.h\"\n", grammar_name);
> +	fprintf(out, "\n");
> +	if (ferror(out)) {
> +		perror(outputname);
> +		exit(1);
> +	}
> +
> +	/* Tabulate the action functions we might have to call */
> +	fprintf(hdr, "\n");
> +	index = 0;
> +	for (action = action_list; action; action = action->next) {
> +		action->index = index++;
> +		fprintf(hdr,
> +			"extern int %s(void *, size_t, unsigned char,"
> +			" const void *, size_t);\n",
> +			action->name);
> +	}
> +	fprintf(hdr, "\n");
> +
> +	fprintf(out, "enum %s_actions {\n", grammar_name);
> +	for (action = action_list; action; action = action->next)
> +		fprintf(out, "\tACT_%s = %u,\n",
> +			action->name, action->index);
> +	fprintf(out, "\tNR__%s_actions = %u\n", grammar_name, nr_actions);
> +	fprintf(out, "};\n");
> +
> +	fprintf(out, "\n");
> +	fprintf(out, "static const asn1_action_t %s_action_table[NR__%s_actions] = {\n",
> +		grammar_name, grammar_name);
> +	for (action = action_list; action; action = action->next)
> +		fprintf(out, "\t[%4u] = %s,\n", action->index, action->name);
> +	fprintf(out, "};\n");
> +
> +	if (ferror(out)) {
> +		perror(outputname);
> +		exit(1);
> +	}
> +
> +	/* We do two passes - the first one calculates all the offsets */
> +	verbose("Pass 1\n");
> +	nr_entries = 0;
> +	root = &type_list[0];
> +	render_element(NULL, root->element, NULL);
> +	render_opcode(NULL, "ASN1_OP_COMPLETE,\n");
> +	render_out_of_line_list(NULL);
> +
> +	for (e = element_list; e; e = e->list_next)
> +		e->flags &= ~ELEMENT_RENDERED;
> +
> +	/* And then we actually render */
> +	verbose("Pass 2\n");
> +	fprintf(out, "\n");
> +	fprintf(out, "static const unsigned char %s_machine[] = {\n",
> +		grammar_name);
> +
> +	nr_entries = 0;
> +	root = &type_list[0];
> +	render_element(out, root->element, NULL);
> +	render_opcode(out, "ASN1_OP_COMPLETE,\n");
> +	render_out_of_line_list(out);
> +
> +	fprintf(out, "};\n");
> +
> +	fprintf(out, "\n");
> +	fprintf(out, "const struct asn1_decoder %s_decoder = {\n", grammar_name);
> +	fprintf(out, "\t.machine = %s_machine,\n", grammar_name);
> +	fprintf(out, "\t.machlen = sizeof(%s_machine),\n", grammar_name);
> +	fprintf(out, "\t.actions = %s_action_table,\n", grammar_name);
> +	fprintf(out, "};\n");
> +}
> +
> +/*
> + * Render the out-of-line elements
> + */
> +static void render_out_of_line_list(FILE *out)
> +{
> +	struct element *e, *ce;
> +	const char *act;
> +	int entry;
> +
> +	while ((e = render_list)) {
> +		render_list = e->render_next;
> +		if (!render_list)
> +			render_list_p = &render_list;
> +
> +		render_more(out, "\n");
> +		e->entry_index = entry = nr_entries;
> +		render_depth++;
> +		for (ce = e->children; ce; ce = ce->next)
> +			render_element(out, ce, NULL);
> +		render_depth--;
> +
> +		act = e->action ? "_ACT" : "";
> +		switch (e->compound) {
> +		case SEQUENCE:
> +			render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
> +			break;
> +		case SEQUENCE_OF:
> +			render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act);
> +			render_opcode(out, "_jump_target(%u),\n", entry);
> +			break;
> +		case SET:
> +			render_opcode(out, "ASN1_OP_END_SET%s,\n", act);
> +			break;
> +		case SET_OF:
> +			render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
> +			render_opcode(out, "_jump_target(%u),\n", entry);
> +			break;
> +		default:
> +			break;
> +		}
> +		if (e->action)
> +			render_opcode(out, "_action(ACT_%s),\n",
> +				      e->action->name);
> +		render_opcode(out, "ASN1_OP_RETURN,\n");
> +	}
> +}
> +
> +/*
> + * Render an element.
> + */
> +static void render_element(FILE *out, struct element *e, struct element *tag)
> +{
> +	struct element *ec, *x;
> +	const char *cond, *act;
> +	int entry, skippable = 0, outofline = 0;
> +
> +	if (e->flags & ELEMENT_SKIPPABLE ||
> +	    (tag && tag->flags & ELEMENT_SKIPPABLE))
> +		skippable = 1;
> +
> +	if ((e->type_def && e->type_def->ref_count > 1) ||
> +	    skippable)
> +		outofline = 1;
> +
> +	if (e->type_def && out) {
> +		render_more(out, "\t// %s\n", e->type_def->name->content);
> +	}
> +
> +	/* Render the operation */
> +	cond = (e->flags & ELEMENT_CONDITIONAL ||
> +		(tag && tag->flags & ELEMENT_CONDITIONAL)) ? "COND_" : "";
> +	act = e->action ? "_ACT" : "";
> +	switch (e->compound) {
> +	case ANY:
> +		render_opcode(out, "ASN1_OP_%sMATCH_ANY%s%s,",
> +			      cond, act, skippable ? "_OR_SKIP" : "");
> +		if (e->name)
> +			render_more(out, "\t\t// %s", e->name->content);
> +		render_more(out, "\n");
> +		goto dont_render_tag;
> +
> +	case TAG_OVERRIDE:
> +		render_element(out, e->children, e);
> +		return;
> +
> +	case SEQUENCE:
> +	case SEQUENCE_OF:
> +	case SET:
> +	case SET_OF:
> +		render_opcode(out, "ASN1_OP_%sMATCH%s%s,",
> +			      cond,
> +			      outofline ? "_JUMP" : "",
> +			      skippable ? "_OR_SKIP" : "");
> +		break;
> +
> +	case CHOICE:
> +		goto dont_render_tag;
> +
> +	case TYPE_REF:
> +		if (e->class == ASN1_UNIV && e->method == ASN1_PRIM && e->tag == 0)
> +			goto dont_render_tag;
> +	default:
> +		render_opcode(out, "ASN1_OP_%sMATCH%s%s,",
> +			      cond, act,
> +			      skippable ? "_OR_SKIP" : "");
> +		break;
> +	}
> +
> +	x = tag ?: e;
> +	if (x->name)
> +		render_more(out, "\t\t// %s", x->name->content);
> +	render_more(out, "\n");
> +
> +	/* Render the tag */
> +	if (!tag || !(tag->flags & ELEMENT_TAG_SPECIFIED))
> +		tag = e;
> +
> +	if (tag->class == ASN1_UNIV &&
> +	    tag->tag != 14 &&
> +	    tag->tag != 15 &&
> +	    tag->tag != 31)
> +		render_opcode(out, "_tag(%s, %s, %s),\n",
> +			      asn1_classes[tag->class],
> +			      asn1_methods[tag->method | e->method],
> +			      asn1_universal_tags[tag->tag]);
> +	else
> +		render_opcode(out, "_tagn(%s, %s, %2u),\n",
> +			      asn1_classes[tag->class],
> +			      asn1_methods[tag->method | e->method],
> +			      tag->tag);
> +	tag = NULL;
> +dont_render_tag:
> +
> +	/* Deal with compound types */
> +	switch (e->compound) {
> +	case TYPE_REF:
> +		render_element(out, e->type->type->element, tag);
> +		if (e->action)
> +			render_opcode(out, "ASN1_OP_%sACT,\n",
> +				      skippable ? "MAYBE_" : "");
> +		break;
> +
> +	case SEQUENCE:
> +		if (outofline) {
> +			/* Render out-of-line for multiple use or
> +			 * skipability */
> +			render_opcode(out, "_jump_target(%u),", e->entry_index);
> +			if (e->type_def && e->type_def->name)
> +				render_more(out, "\t\t// --> %s",
> +					    e->type_def->name->content);
> +			render_more(out, "\n");
> +			if (!(e->flags & ELEMENT_RENDERED)) {
> +				e->flags |= ELEMENT_RENDERED;
> +				*render_list_p = e;
> +				render_list_p = &e->render_next;
> +			}
> +			return;
> +		} else {
> +			/* Render inline for single use */
> +			render_depth++;
> +			for (ec = e->children; ec; ec = ec->next)
> +				render_element(out, ec, NULL);
> +			render_depth--;
> +			render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
> +		}
> +		break;
> +
> +	case SEQUENCE_OF:
> +	case SET_OF:
> +		if (outofline) {
> +			/* Render out-of-line for multiple use or
> +			 * skipability */
> +			render_opcode(out, "_jump_target(%u),", e->entry_index);
> +			if (e->type_def && e->type_def->name)
> +				render_more(out, "\t\t// --> %s",
> +					    e->type_def->name->content);
> +			render_more(out, "\n");
> +			if (!(e->flags & ELEMENT_RENDERED)) {
> +				e->flags |= ELEMENT_RENDERED;
> +				*render_list_p = e;
> +				render_list_p = &e->render_next;
> +			}
> +			return;
> +		} else {
> +			/* Render inline for single use */
> +			entry = nr_entries;
> +			render_depth++;
> +			render_element(out, e->children, NULL);
> +			render_depth--;
> +			if (e->compound == SEQUENCE_OF)
> +				render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act);
> +			else
> +				render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
> +			render_opcode(out, "_jump_target(%u),\n", entry);
> +		}
> +		break;
> +
> +	case SET:
> +		/* I can't think of a nice way to do SET support without having
> +		 * a stack of bitmasks to make sure no element is repeated.
> +		 * The bitmask has also to be checked that no non-optional
> +		 * elements are left out whilst not preventing optional
> +		 * elements from being left out.
> +		 */
> +		fprintf(stderr, "The ASN.1 SET type is not currently supported.\n");
> +		exit(1);
> +
> +	case CHOICE:
> +		for (ec = e->children; ec; ec = ec->next)
> +			render_element(out, ec, ec);
> +		if (!skippable)
> +			render_opcode(out, "ASN1_OP_COND_FAIL,\n");
> +		if (e->action)
> +			render_opcode(out, "ASN1_OP_ACT,\n");
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	if (e->action)
> +		render_opcode(out, "_action(ACT_%s),\n", e->action->name);
> +}
>

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

* [U-Boot] [PATCH v1 06/11] lib: add asn1 decoder
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 06/11] lib: add asn1 decoder AKASHI Takahiro
@ 2019-10-12 12:29   ` Heinrich Schuchardt
  2019-10-17  7:02     ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 12:29 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> Imported from linux kernel v5.3.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   lib/Kconfig        |   6 +
>   lib/Makefile       |   1 +
>   lib/asn1_decoder.c | 527 +++++++++++++++++++++++++++++++++++++++++++++

Except for adjustments of include files lib/asn1_decoder.c is a
verbatime copy of the Linux kernel file. OK.

>   3 files changed, 534 insertions(+)
>   create mode 100644 lib/asn1_decoder.c
>
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 3da45a5ec322..26c94f49ecd2 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -521,6 +521,12 @@ config SMBIOS_PRODUCT_NAME
>
>   endmenu
>
> +config ASN1
> +	bool
> +	select BUILD_ASN1

BUILD_ASN1 does not really fit into cmd/Kconfig.

Should we move it here?

Why do we need two separate flags ASN1 and BUILD_ASN1?

Best regards

Heinrich

> +	help
> +	  Enable asn1 decoder library.
> +
>   source lib/efi/Kconfig
>   source lib/efi_loader/Kconfig
>   source lib/optee/Kconfig
> diff --git a/lib/Makefile b/lib/Makefile
> index 2fffd68f943c..eb3a675fb8c2 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_OF_LIVE) += of_live.o
>   obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
>   obj-$(CONFIG_ARCH_AT91) += at91/
>   obj-$(CONFIG_OPTEE) += optee/
> +obj-$(CONFIG_BUILD_ASN1) += asn1_decoder.o
>
>   obj-$(CONFIG_AES) += aes.o
>
> diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c
> new file mode 100644
> index 000000000000..db222625dd0f
> --- /dev/null
> +++ b/lib/asn1_decoder.c
> @@ -0,0 +1,527 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* Decoder for ASN.1 BER/DER/CER encoded bytestream
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells at redhat.com)
> + */
> +
> +#ifdef __UBOOT__
> +#include <linux/compat.h>
> +#else
> +#include <linux/export.h>
> +#endif
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#ifndef __UBOOT__
> +#include <linux/module.h>
> +#endif
> +#include <linux/asn1_decoder.h>
> +#include <linux/asn1_ber_bytecode.h>
> +
> +static const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
> +	/*					OPC TAG JMP ACT */
> +	[ASN1_OP_MATCH]				= 1 + 1,
> +	[ASN1_OP_MATCH_OR_SKIP]			= 1 + 1,
> +	[ASN1_OP_MATCH_ACT]			= 1 + 1     + 1,
> +	[ASN1_OP_MATCH_ACT_OR_SKIP]		= 1 + 1     + 1,
> +	[ASN1_OP_MATCH_JUMP]			= 1 + 1 + 1,
> +	[ASN1_OP_MATCH_JUMP_OR_SKIP]		= 1 + 1 + 1,
> +	[ASN1_OP_MATCH_ANY]			= 1,
> +	[ASN1_OP_MATCH_ANY_OR_SKIP]		= 1,
> +	[ASN1_OP_MATCH_ANY_ACT]			= 1         + 1,
> +	[ASN1_OP_MATCH_ANY_ACT_OR_SKIP]		= 1         + 1,
> +	[ASN1_OP_COND_MATCH_OR_SKIP]		= 1 + 1,
> +	[ASN1_OP_COND_MATCH_ACT_OR_SKIP]	= 1 + 1     + 1,
> +	[ASN1_OP_COND_MATCH_JUMP_OR_SKIP]	= 1 + 1 + 1,
> +	[ASN1_OP_COND_MATCH_ANY]		= 1,
> +	[ASN1_OP_COND_MATCH_ANY_OR_SKIP]	= 1,
> +	[ASN1_OP_COND_MATCH_ANY_ACT]		= 1         + 1,
> +	[ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP]	= 1         + 1,
> +	[ASN1_OP_COND_FAIL]			= 1,
> +	[ASN1_OP_COMPLETE]			= 1,
> +	[ASN1_OP_ACT]				= 1         + 1,
> +	[ASN1_OP_MAYBE_ACT]			= 1         + 1,
> +	[ASN1_OP_RETURN]			= 1,
> +	[ASN1_OP_END_SEQ]			= 1,
> +	[ASN1_OP_END_SEQ_OF]			= 1     + 1,
> +	[ASN1_OP_END_SET]			= 1,
> +	[ASN1_OP_END_SET_OF]			= 1     + 1,
> +	[ASN1_OP_END_SEQ_ACT]			= 1         + 1,
> +	[ASN1_OP_END_SEQ_OF_ACT]		= 1     + 1 + 1,
> +	[ASN1_OP_END_SET_ACT]			= 1         + 1,
> +	[ASN1_OP_END_SET_OF_ACT]		= 1     + 1 + 1,
> +};
> +
> +/*
> + * Find the length of an indefinite length object
> + * @data: The data buffer
> + * @datalen: The end of the innermost containing element in the buffer
> + * @_dp: The data parse cursor (updated before returning)
> + * @_len: Where to return the size of the element.
> + * @_errmsg: Where to return a pointer to an error message on error
> + */
> +static int asn1_find_indefinite_length(const unsigned char *data, size_t datalen,
> +				       size_t *_dp, size_t *_len,
> +				       const char **_errmsg)
> +{
> +	unsigned char tag, tmp;
> +	size_t dp = *_dp, len, n;
> +	int indef_level = 1;
> +
> +next_tag:
> +	if (unlikely(datalen - dp < 2)) {
> +		if (datalen == dp)
> +			goto missing_eoc;
> +		goto data_overrun_error;
> +	}
> +
> +	/* Extract a tag from the data */
> +	tag = data[dp++];
> +	if (tag == ASN1_EOC) {
> +		/* It appears to be an EOC. */
> +		if (data[dp++] != 0)
> +			goto invalid_eoc;
> +		if (--indef_level <= 0) {
> +			*_len = dp - *_dp;
> +			*_dp = dp;
> +			return 0;
> +		}
> +		goto next_tag;
> +	}
> +
> +	if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) {
> +		do {
> +			if (unlikely(datalen - dp < 2))
> +				goto data_overrun_error;
> +			tmp = data[dp++];
> +		} while (tmp & 0x80);
> +	}
> +
> +	/* Extract the length */
> +	len = data[dp++];
> +	if (len <= 0x7f)
> +		goto check_length;
> +
> +	if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
> +		/* Indefinite length */
> +		if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5))
> +			goto indefinite_len_primitive;
> +		indef_level++;
> +		goto next_tag;
> +	}
> +
> +	n = len - 0x80;
> +	if (unlikely(n > sizeof(len) - 1))
> +		goto length_too_long;
> +	if (unlikely(n > datalen - dp))
> +		goto data_overrun_error;
> +	len = 0;
> +	for (; n > 0; n--) {
> +		len <<= 8;
> +		len |= data[dp++];
> +	}
> +check_length:
> +	if (len > datalen - dp)
> +		goto data_overrun_error;
> +	dp += len;
> +	goto next_tag;
> +
> +length_too_long:
> +	*_errmsg = "Unsupported length";
> +	goto error;
> +indefinite_len_primitive:
> +	*_errmsg = "Indefinite len primitive not permitted";
> +	goto error;
> +invalid_eoc:
> +	*_errmsg = "Invalid length EOC";
> +	goto error;
> +data_overrun_error:
> +	*_errmsg = "Data overrun error";
> +	goto error;
> +missing_eoc:
> +	*_errmsg = "Missing EOC in indefinite len cons";
> +error:
> +	*_dp = dp;
> +	return -1;
> +}
> +
> +/**
> + * asn1_ber_decoder - Decoder BER/DER/CER ASN.1 according to pattern
> + * @decoder: The decoder definition (produced by asn1_compiler)
> + * @context: The caller's context (to be passed to the action functions)
> + * @data: The encoded data
> + * @datalen: The size of the encoded data
> + *
> + * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern
> + * produced by asn1_compiler.  Action functions are called on marked tags to
> + * allow the caller to retrieve significant data.
> + *
> + * LIMITATIONS:
> + *
> + * To keep down the amount of stack used by this function, the following limits
> + * have been imposed:
> + *
> + *  (1) This won't handle datalen > 65535 without increasing the size of the
> + *	cons stack elements and length_too_long checking.
> + *
> + *  (2) The stack of constructed types is 10 deep.  If the depth of non-leaf
> + *	constructed types exceeds this, the decode will fail.
> + *
> + *  (3) The SET type (not the SET OF type) isn't really supported as tracking
> + *	what members of the set have been seen is a pain.
> + */
> +int asn1_ber_decoder(const struct asn1_decoder *decoder,
> +		     void *context,
> +		     const unsigned char *data,
> +		     size_t datalen)
> +{
> +	const unsigned char *machine = decoder->machine;
> +	const asn1_action_t *actions = decoder->actions;
> +	size_t machlen = decoder->machlen;
> +	enum asn1_opcode op;
> +	unsigned char tag = 0, csp = 0, jsp = 0, optag = 0, hdr = 0;
> +	const char *errmsg;
> +	size_t pc = 0, dp = 0, tdp = 0, len = 0;
> +	int ret;
> +
> +	unsigned char flags = 0;
> +#define FLAG_INDEFINITE_LENGTH	0x01
> +#define FLAG_MATCHED		0x02
> +#define FLAG_LAST_MATCHED	0x04 /* Last tag matched */
> +#define FLAG_CONS		0x20 /* Corresponds to CONS bit in the opcode tag
> +				      * - ie. whether or not we are going to parse
> +				      *   a compound type.
> +				      */
> +
> +#define NR_CONS_STACK 10
> +	unsigned short cons_dp_stack[NR_CONS_STACK];
> +	unsigned short cons_datalen_stack[NR_CONS_STACK];
> +	unsigned char cons_hdrlen_stack[NR_CONS_STACK];
> +#define NR_JUMP_STACK 10
> +	unsigned char jump_stack[NR_JUMP_STACK];
> +
> +	if (datalen > 65535)
> +		return -EMSGSIZE;
> +
> +next_op:
> +	pr_debug("next_op: pc=\e[32m%zu\e[m/%zu dp=\e[33m%zu\e[m/%zu C=%d J=%d\n",
> +		 pc, machlen, dp, datalen, csp, jsp);
> +	if (unlikely(pc >= machlen))
> +		goto machine_overrun_error;
> +	op = machine[pc];
> +	if (unlikely(pc + asn1_op_lengths[op] > machlen))
> +		goto machine_overrun_error;
> +
> +	/* If this command is meant to match a tag, then do that before
> +	 * evaluating the command.
> +	 */
> +	if (op <= ASN1_OP__MATCHES_TAG) {
> +		unsigned char tmp;
> +
> +		/* Skip conditional matches if possible */
> +		if ((op & ASN1_OP_MATCH__COND && flags & FLAG_MATCHED) ||
> +		    (op & ASN1_OP_MATCH__SKIP && dp == datalen)) {
> +			flags &= ~FLAG_LAST_MATCHED;
> +			pc += asn1_op_lengths[op];
> +			goto next_op;
> +		}
> +
> +		flags = 0;
> +		hdr = 2;
> +
> +		/* Extract a tag from the data */
> +		if (unlikely(datalen - dp < 2))
> +			goto data_overrun_error;
> +		tag = data[dp++];
> +		if (unlikely((tag & 0x1f) == ASN1_LONG_TAG))
> +			goto long_tag_not_supported;
> +
> +		if (op & ASN1_OP_MATCH__ANY) {
> +			pr_debug("- any %02x\n", tag);
> +		} else {
> +			/* Extract the tag from the machine
> +			 * - Either CONS or PRIM are permitted in the data if
> +			 *   CONS is not set in the op stream, otherwise CONS
> +			 *   is mandatory.
> +			 */
> +			optag = machine[pc + 1];
> +			flags |= optag & FLAG_CONS;
> +
> +			/* Determine whether the tag matched */
> +			tmp = optag ^ tag;
> +			tmp &= ~(optag & ASN1_CONS_BIT);
> +			pr_debug("- match? %02x %02x %02x\n", tag, optag, tmp);
> +			if (tmp != 0) {
> +				/* All odd-numbered tags are MATCH_OR_SKIP. */
> +				if (op & ASN1_OP_MATCH__SKIP) {
> +					pc += asn1_op_lengths[op];
> +					dp--;
> +					goto next_op;
> +				}
> +				goto tag_mismatch;
> +			}
> +		}
> +		flags |= FLAG_MATCHED;
> +
> +		len = data[dp++];
> +		if (len > 0x7f) {
> +			if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
> +				/* Indefinite length */
> +				if (unlikely(!(tag & ASN1_CONS_BIT)))
> +					goto indefinite_len_primitive;
> +				flags |= FLAG_INDEFINITE_LENGTH;
> +				if (unlikely(2 > datalen - dp))
> +					goto data_overrun_error;
> +			} else {
> +				int n = len - 0x80;
> +				if (unlikely(n > 2))
> +					goto length_too_long;
> +				if (unlikely(n > datalen - dp))
> +					goto data_overrun_error;
> +				hdr += n;
> +				for (len = 0; n > 0; n--) {
> +					len <<= 8;
> +					len |= data[dp++];
> +				}
> +				if (unlikely(len > datalen - dp))
> +					goto data_overrun_error;
> +			}
> +		} else {
> +			if (unlikely(len > datalen - dp))
> +				goto data_overrun_error;
> +		}
> +
> +		if (flags & FLAG_CONS) {
> +			/* For expected compound forms, we stack the positions
> +			 * of the start and end of the data.
> +			 */
> +			if (unlikely(csp >= NR_CONS_STACK))
> +				goto cons_stack_overflow;
> +			cons_dp_stack[csp] = dp;
> +			cons_hdrlen_stack[csp] = hdr;
> +			if (!(flags & FLAG_INDEFINITE_LENGTH)) {
> +				cons_datalen_stack[csp] = datalen;
> +				datalen = dp + len;
> +			} else {
> +				cons_datalen_stack[csp] = 0;
> +			}
> +			csp++;
> +		}
> +
> +		pr_debug("- TAG: %02x %zu%s\n",
> +			 tag, len, flags & FLAG_CONS ? " CONS" : "");
> +		tdp = dp;
> +	}
> +
> +	/* Decide how to handle the operation */
> +	switch (op) {
> +	case ASN1_OP_MATCH:
> +	case ASN1_OP_MATCH_OR_SKIP:
> +	case ASN1_OP_MATCH_ACT:
> +	case ASN1_OP_MATCH_ACT_OR_SKIP:
> +	case ASN1_OP_MATCH_ANY:
> +	case ASN1_OP_MATCH_ANY_OR_SKIP:
> +	case ASN1_OP_MATCH_ANY_ACT:
> +	case ASN1_OP_MATCH_ANY_ACT_OR_SKIP:
> +	case ASN1_OP_COND_MATCH_OR_SKIP:
> +	case ASN1_OP_COND_MATCH_ACT_OR_SKIP:
> +	case ASN1_OP_COND_MATCH_ANY:
> +	case ASN1_OP_COND_MATCH_ANY_OR_SKIP:
> +	case ASN1_OP_COND_MATCH_ANY_ACT:
> +	case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP:
> +
> +		if (!(flags & FLAG_CONS)) {
> +			if (flags & FLAG_INDEFINITE_LENGTH) {
> +				size_t tmp = dp;
> +
> +				ret = asn1_find_indefinite_length(
> +					data, datalen, &tmp, &len, &errmsg);
> +				if (ret < 0)
> +					goto error;
> +			}
> +			pr_debug("- LEAF: %zu\n", len);
> +		}
> +
> +		if (op & ASN1_OP_MATCH__ACT) {
> +			unsigned char act;
> +
> +			if (op & ASN1_OP_MATCH__ANY)
> +				act = machine[pc + 1];
> +			else
> +				act = machine[pc + 2];
> +			ret = actions[act](context, hdr, tag, data + dp, len);
> +			if (ret < 0)
> +				return ret;
> +		}
> +
> +		if (!(flags & FLAG_CONS))
> +			dp += len;
> +		pc += asn1_op_lengths[op];
> +		goto next_op;
> +
> +	case ASN1_OP_MATCH_JUMP:
> +	case ASN1_OP_MATCH_JUMP_OR_SKIP:
> +	case ASN1_OP_COND_MATCH_JUMP_OR_SKIP:
> +		pr_debug("- MATCH_JUMP\n");
> +		if (unlikely(jsp == NR_JUMP_STACK))
> +			goto jump_stack_overflow;
> +		jump_stack[jsp++] = pc + asn1_op_lengths[op];
> +		pc = machine[pc + 2];
> +		goto next_op;
> +
> +	case ASN1_OP_COND_FAIL:
> +		if (unlikely(!(flags & FLAG_MATCHED)))
> +			goto tag_mismatch;
> +		pc += asn1_op_lengths[op];
> +		goto next_op;
> +
> +	case ASN1_OP_COMPLETE:
> +		if (unlikely(jsp != 0 || csp != 0)) {
> +			pr_err("ASN.1 decoder error: Stacks not empty at completion (%u, %u)\n",
> +			       jsp, csp);
> +			return -EBADMSG;
> +		}
> +		return 0;
> +
> +	case ASN1_OP_END_SET:
> +	case ASN1_OP_END_SET_ACT:
> +		if (unlikely(!(flags & FLAG_MATCHED)))
> +			goto tag_mismatch;
> +		/* fall through */
> +
> +	case ASN1_OP_END_SEQ:
> +	case ASN1_OP_END_SET_OF:
> +	case ASN1_OP_END_SEQ_OF:
> +	case ASN1_OP_END_SEQ_ACT:
> +	case ASN1_OP_END_SET_OF_ACT:
> +	case ASN1_OP_END_SEQ_OF_ACT:
> +		if (unlikely(csp <= 0))
> +			goto cons_stack_underflow;
> +		csp--;
> +		tdp = cons_dp_stack[csp];
> +		hdr = cons_hdrlen_stack[csp];
> +		len = datalen;
> +		datalen = cons_datalen_stack[csp];
> +		pr_debug("- end cons t=%zu dp=%zu l=%zu/%zu\n",
> +			 tdp, dp, len, datalen);
> +		if (datalen == 0) {
> +			/* Indefinite length - check for the EOC. */
> +			datalen = len;
> +			if (unlikely(datalen - dp < 2))
> +				goto data_overrun_error;
> +			if (data[dp++] != 0) {
> +				if (op & ASN1_OP_END__OF) {
> +					dp--;
> +					csp++;
> +					pc = machine[pc + 1];
> +					pr_debug("- continue\n");
> +					goto next_op;
> +				}
> +				goto missing_eoc;
> +			}
> +			if (data[dp++] != 0)
> +				goto invalid_eoc;
> +			len = dp - tdp - 2;
> +		} else {
> +			if (dp < len && (op & ASN1_OP_END__OF)) {
> +				datalen = len;
> +				csp++;
> +				pc = machine[pc + 1];
> +				pr_debug("- continue\n");
> +				goto next_op;
> +			}
> +			if (dp != len)
> +				goto cons_length_error;
> +			len -= tdp;
> +			pr_debug("- cons len l=%zu d=%zu\n", len, dp - tdp);
> +		}
> +
> +		if (op & ASN1_OP_END__ACT) {
> +			unsigned char act;
> +			if (op & ASN1_OP_END__OF)
> +				act = machine[pc + 2];
> +			else
> +				act = machine[pc + 1];
> +			ret = actions[act](context, hdr, 0, data + tdp, len);
> +			if (ret < 0)
> +				return ret;
> +		}
> +		pc += asn1_op_lengths[op];
> +		goto next_op;
> +
> +	case ASN1_OP_MAYBE_ACT:
> +		if (!(flags & FLAG_LAST_MATCHED)) {
> +			pc += asn1_op_lengths[op];
> +			goto next_op;
> +		}
> +		/* fall through */
> +
> +	case ASN1_OP_ACT:
> +		ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len);
> +		if (ret < 0)
> +			return ret;
> +		pc += asn1_op_lengths[op];
> +		goto next_op;
> +
> +	case ASN1_OP_RETURN:
> +		if (unlikely(jsp <= 0))
> +			goto jump_stack_underflow;
> +		pc = jump_stack[--jsp];
> +		flags |= FLAG_MATCHED | FLAG_LAST_MATCHED;
> +		goto next_op;
> +
> +	default:
> +		break;
> +	}
> +
> +	/* Shouldn't reach here */
> +	pr_err("ASN.1 decoder error: Found reserved opcode (%u) pc=%zu\n",
> +	       op, pc);
> +	return -EBADMSG;
> +
> +data_overrun_error:
> +	errmsg = "Data overrun error";
> +	goto error;
> +machine_overrun_error:
> +	errmsg = "Machine overrun error";
> +	goto error;
> +jump_stack_underflow:
> +	errmsg = "Jump stack underflow";
> +	goto error;
> +jump_stack_overflow:
> +	errmsg = "Jump stack overflow";
> +	goto error;
> +cons_stack_underflow:
> +	errmsg = "Cons stack underflow";
> +	goto error;
> +cons_stack_overflow:
> +	errmsg = "Cons stack overflow";
> +	goto error;
> +cons_length_error:
> +	errmsg = "Cons length error";
> +	goto error;
> +missing_eoc:
> +	errmsg = "Missing EOC in indefinite len cons";
> +	goto error;
> +invalid_eoc:
> +	errmsg = "Invalid length EOC";
> +	goto error;
> +length_too_long:
> +	errmsg = "Unsupported length";
> +	goto error;
> +indefinite_len_primitive:
> +	errmsg = "Indefinite len primitive not permitted";
> +	goto error;
> +tag_mismatch:
> +	errmsg = "Unexpected tag";
> +	goto error;
> +long_tag_not_supported:
> +	errmsg = "Long tag not supported";
> +error:
> +	pr_debug("\nASN1: %s [m=%zu d=%zu ot=%02x t=%02x l=%zu]\n",
> +		 errmsg, pc, dp, optag, tag, len);
> +	return -EBADMSG;
> +}
> +EXPORT_SYMBOL_GPL(asn1_ber_decoder);
> +
> +MODULE_LICENSE("GPL");
>

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

* [U-Boot] [PATCH v1 05/11] Makefile: add build script for asn1 parsers
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 05/11] Makefile: add build script for asn1 parsers AKASHI Takahiro
@ 2019-10-12 12:36   ` Heinrich Schuchardt
  2019-10-17  6:40     ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 12:36 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> This rule will be used to build x509 and pkcs7 parsers.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

The ASN1 compiler is generating both *.asn.h and *.asn.c files.

Then I would expect that these files are deleted by `make mrproper'.

Please, adjust the clean: target in /Makefile accordingly.

Best regards

Heinrich

> ---
>   scripts/Makefile.build | 6 ++++--
>   1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> index f7a041296d3d..9357d310e50b 100644
> --- a/scripts/Makefile.build
> +++ b/scripts/Makefile.build
> @@ -331,7 +331,7 @@ quiet_cmd_asn1_compiler = ASN.1   $@
>         cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \
>   				$(subst .h,.c,$@) $(subst .c,.h,$@)
>
> -$(obj)/%-asn1.c $(obj)/%-asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
> +$(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
>   	$(call cmd,asn1_compiler)
>
>   # Build the compiled-in targets
> @@ -419,9 +419,11 @@ targets += $(multi-used-y) $(multi-used-m)
>   intermediate_targets = $(foreach sfx, $(2), \
>   				$(patsubst %$(strip $(1)),%$(sfx), \
>   					$(filter %$(strip $(1)), $(targets))))
> +# %.asn1.o <- %.asn1.[ch] <- %.asn1
>   # %.lex.o <- %.lex.c <- %.l
>   # %.tab.o <- %.tab.[ch] <- %.y
> -targets += $(call intermediate_targets, .lex.o, .lex.c) \
> +targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
> +	   $(call intermediate_targets, .lex.o, .lex.c) \
>   	   $(call intermediate_targets, .tab.o, .tab.c .tab.h)
>
>   # Descending
>

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

* [U-Boot] [PATCH v1 07/11] lib: add oid registry utility
  2019-10-11  7:41 ` [U-Boot] [PATCH v1 07/11] lib: add oid registry utility AKASHI Takahiro
@ 2019-10-12 12:58   ` Heinrich Schuchardt
  0 siblings, 0 replies; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 12:58 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> Imported from linux kernel v5.3.

Please, indicate in the commit message which files are verbatim copies
and which files had to be adjusted. E.g. in lib/oid_registry.c you
adjusted the includes.

>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> ---
>   include/linux/oid_registry.h | 117 ++++++++++++++++++++
>   lib/Kconfig                  |   5 +
>   lib/Makefile                 |  16 +++
>   lib/build_OID_registry       | 203 +++++++++++++++++++++++++++++++++++

Shouldn't this file go into /scripts or /tools?

Best regards

Heinrich

>   lib/oid_registry.c           | 179 ++++++++++++++++++++++++++++++
>   5 files changed, 520 insertions(+)
>   create mode 100644 include/linux/oid_registry.h
>   create mode 100755 lib/build_OID_registry
>   create mode 100644 lib/oid_registry.c
>
> diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
> new file mode 100644
> index 000000000000..657d6bf2c064
> --- /dev/null
> +++ b/include/linux/oid_registry.h
> @@ -0,0 +1,117 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/* ASN.1 Object identifier (OID) registry
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells at redhat.com)
> + */
> +
> +#ifndef _LINUX_OID_REGISTRY_H
> +#define _LINUX_OID_REGISTRY_H
> +
> +#include <linux/types.h>
> +
> +/*
> + * OIDs are turned into these values if possible, or OID__NR if not held here.
> + *
> + * NOTE!  Do not mess with the format of each line as this is read by
> + *	  build_OID_registry.pl to generate the data for look_up_OID().
> + */
> +enum OID {
> +	OID_id_dsa_with_sha1,		/* 1.2.840.10030.4.3 */
> +	OID_id_dsa,			/* 1.2.840.10040.4.1 */
> +	OID_id_ecdsa_with_sha1,		/* 1.2.840.10045.4.1 */
> +	OID_id_ecPublicKey,		/* 1.2.840.10045.2.1 */
> +
> +	/* PKCS#1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)} */
> +	OID_rsaEncryption,		/* 1.2.840.113549.1.1.1 */
> +	OID_md2WithRSAEncryption,	/* 1.2.840.113549.1.1.2 */
> +	OID_md3WithRSAEncryption,	/* 1.2.840.113549.1.1.3 */
> +	OID_md4WithRSAEncryption,	/* 1.2.840.113549.1.1.4 */
> +	OID_sha1WithRSAEncryption,	/* 1.2.840.113549.1.1.5 */
> +	OID_sha256WithRSAEncryption,	/* 1.2.840.113549.1.1.11 */
> +	OID_sha384WithRSAEncryption,	/* 1.2.840.113549.1.1.12 */
> +	OID_sha512WithRSAEncryption,	/* 1.2.840.113549.1.1.13 */
> +	OID_sha224WithRSAEncryption,	/* 1.2.840.113549.1.1.14 */
> +	/* PKCS#7 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-7(7)} */
> +	OID_data,			/* 1.2.840.113549.1.7.1 */
> +	OID_signed_data,		/* 1.2.840.113549.1.7.2 */
> +	/* PKCS#9 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)} */
> +	OID_email_address,		/* 1.2.840.113549.1.9.1 */
> +	OID_contentType,		/* 1.2.840.113549.1.9.3 */
> +	OID_messageDigest,		/* 1.2.840.113549.1.9.4 */
> +	OID_signingTime,		/* 1.2.840.113549.1.9.5 */
> +	OID_smimeCapabilites,		/* 1.2.840.113549.1.9.15 */
> +	OID_smimeAuthenticatedAttrs,	/* 1.2.840.113549.1.9.16.2.11 */
> +
> +	/* {iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2)} */
> +	OID_md2,			/* 1.2.840.113549.2.2 */
> +	OID_md4,			/* 1.2.840.113549.2.4 */
> +	OID_md5,			/* 1.2.840.113549.2.5 */
> +
> +	/* Microsoft Authenticode & Software Publishing */
> +	OID_msIndirectData,		/* 1.3.6.1.4.1.311.2.1.4 */
> +	OID_msStatementType,		/* 1.3.6.1.4.1.311.2.1.11 */
> +	OID_msSpOpusInfo,		/* 1.3.6.1.4.1.311.2.1.12 */
> +	OID_msPeImageDataObjId,		/* 1.3.6.1.4.1.311.2.1.15 */
> +	OID_msIndividualSPKeyPurpose,	/* 1.3.6.1.4.1.311.2.1.21 */
> +	OID_msOutlookExpress,		/* 1.3.6.1.4.1.311.16.4 */
> +
> +	OID_certAuthInfoAccess,		/* 1.3.6.1.5.5.7.1.1 */
> +	OID_sha1,			/* 1.3.14.3.2.26 */
> +	OID_sha256,			/* 2.16.840.1.101.3.4.2.1 */
> +	OID_sha384,			/* 2.16.840.1.101.3.4.2.2 */
> +	OID_sha512,			/* 2.16.840.1.101.3.4.2.3 */
> +	OID_sha224,			/* 2.16.840.1.101.3.4.2.4 */
> +
> +	/* Distinguished Name attribute IDs [RFC 2256] */
> +	OID_commonName,			/* 2.5.4.3 */
> +	OID_surname,			/* 2.5.4.4 */
> +	OID_countryName,		/* 2.5.4.6 */
> +	OID_locality,			/* 2.5.4.7 */
> +	OID_stateOrProvinceName,	/* 2.5.4.8 */
> +	OID_organizationName,		/* 2.5.4.10 */
> +	OID_organizationUnitName,	/* 2.5.4.11 */
> +	OID_title,			/* 2.5.4.12 */
> +	OID_description,		/* 2.5.4.13 */
> +	OID_name,			/* 2.5.4.41 */
> +	OID_givenName,			/* 2.5.4.42 */
> +	OID_initials,			/* 2.5.4.43 */
> +	OID_generationalQualifier,	/* 2.5.4.44 */
> +
> +	/* Certificate extension IDs */
> +	OID_subjectKeyIdentifier,	/* 2.5.29.14 */
> +	OID_keyUsage,			/* 2.5.29.15 */
> +	OID_subjectAltName,		/* 2.5.29.17 */
> +	OID_issuerAltName,		/* 2.5.29.18 */
> +	OID_basicConstraints,		/* 2.5.29.19 */
> +	OID_crlDistributionPoints,	/* 2.5.29.31 */
> +	OID_certPolicies,		/* 2.5.29.32 */
> +	OID_authorityKeyIdentifier,	/* 2.5.29.35 */
> +	OID_extKeyUsage,		/* 2.5.29.37 */
> +
> +	/* EC-RDSA */
> +	OID_gostCPSignA,		/* 1.2.643.2.2.35.1 */
> +	OID_gostCPSignB,		/* 1.2.643.2.2.35.2 */
> +	OID_gostCPSignC,		/* 1.2.643.2.2.35.3 */
> +	OID_gost2012PKey256,		/* 1.2.643.7.1.1.1.1 */
> +	OID_gost2012PKey512,		/* 1.2.643.7.1.1.1.2 */
> +	OID_gost2012Digest256,		/* 1.2.643.7.1.1.2.2 */
> +	OID_gost2012Digest512,		/* 1.2.643.7.1.1.2.3 */
> +	OID_gost2012Signature256,	/* 1.2.643.7.1.1.3.2 */
> +	OID_gost2012Signature512,	/* 1.2.643.7.1.1.3.3 */
> +	OID_gostTC26Sign256A,		/* 1.2.643.7.1.2.1.1.1 */
> +	OID_gostTC26Sign256B,		/* 1.2.643.7.1.2.1.1.2 */
> +	OID_gostTC26Sign256C,		/* 1.2.643.7.1.2.1.1.3 */
> +	OID_gostTC26Sign256D,		/* 1.2.643.7.1.2.1.1.4 */
> +	OID_gostTC26Sign512A,		/* 1.2.643.7.1.2.1.2.1 */
> +	OID_gostTC26Sign512B,		/* 1.2.643.7.1.2.1.2.2 */
> +	OID_gostTC26Sign512C,		/* 1.2.643.7.1.2.1.2.3 */
> +
> +	OID__NR
> +};
> +
> +extern enum OID look_up_OID(const void *data, size_t datasize);
> +extern int sprint_oid(const void *, size_t, char *, size_t);
> +extern int sprint_OID(enum OID, char *, size_t);
> +
> +#endif /* _LINUX_OID_REGISTRY_H */
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 26c94f49ecd2..d2955c8feb0e 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -527,6 +527,11 @@ config ASN1
>   	help
>   	  Enable asn1 decoder library.
>
> +config OID_REGISTRY
> +	bool
> +	help
> +	  Enable fast lookup object identifier registry.
> +
>   source lib/efi/Kconfig
>   source lib/efi_loader/Kconfig
>   source lib/optee/Kconfig
> diff --git a/lib/Makefile b/lib/Makefile
> index eb3a675fb8c2..677ec33a1ce1 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -118,4 +118,20 @@ else
>   obj-y += vsprintf.o strto.o strmhz.o
>   endif
>
> +#
> +# Build a fast OID lookip registry from include/linux/oid_registry.h
> +#
> +obj-$(CONFIG_OID_REGISTRY) += oid_registry.o
> +
> +$(obj)/oid_registry.o: $(obj)/oid_registry_data.c
> +
> +$(obj)/oid_registry_data.c: $(srctree)/include/linux/oid_registry.h \
> +			    $(src)/build_OID_registry
> +	$(call cmd,build_OID_registry)
> +
> +quiet_cmd_build_OID_registry = GEN     $@
> +      cmd_build_OID_registry = perl $(srctree)/$(src)/build_OID_registry $< $@
> +
> +clean-files     += oid_registry_data.c
> +
>   subdir-ccflags-$(CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED) += -O2
> diff --git a/lib/build_OID_registry b/lib/build_OID_registry
> new file mode 100755
> index 000000000000..d7fc32ea8ac2
> --- /dev/null
> +++ b/lib/build_OID_registry
> @@ -0,0 +1,203 @@
> +#!/usr/bin/perl -w
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +#
> +# Build a static ASN.1 Object Identified (OID) registry
> +#
> +# Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> +# Written by David Howells (dhowells at redhat.com)
> +#
> +
> +use strict;
> +
> +my @names = ();
> +my @oids = ();
> +
> +if ($#ARGV != 1) {
> +    print STDERR "Format: ", $0, " <in-h-file> <out-c-file>\n";
> +    exit(2);
> +}
> +
> +#
> +# Open the file to read from
> +#
> +open IN_FILE, "<$ARGV[0]" || die;
> +while (<IN_FILE>) {
> +    chomp;
> +    if (m!\s+OID_([a-zA-z][a-zA-Z0-9_]+),\s+/[*]\s+([012][.0-9]*)\s+[*]/!) {
> +	push @names, $1;
> +	push @oids, $2;
> +    }
> +}
> +close IN_FILE || die;
> +
> +#
> +# Open the files to write into
> +#
> +open C_FILE, ">$ARGV[1]" or die;
> +print C_FILE "/*\n";
> +print C_FILE " * Automatically generated by ", $0, ".  Do not edit\n";
> +print C_FILE " */\n";
> +
> +#
> +# Split the data up into separate lists and also determine the lengths of the
> +# encoded data arrays.
> +#
> +my @indices = ();
> +my @lengths = ();
> +my $total_length = 0;
> +
> +for (my $i = 0; $i <= $#names; $i++) {
> +    my $name = $names[$i];
> +    my $oid = $oids[$i];
> +
> +    my @components = split(/[.]/, $oid);
> +
> +    # Determine the encoded length of this OID
> +    my $size = $#components;
> +    for (my $loop = 2; $loop <= $#components; $loop++) {
> +	my $c = $components[$loop];
> +
> +	# We will base128 encode the number
> +	my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
> +	$tmp = int($tmp / 7);
> +	$size += $tmp;
> +    }
> +    push @lengths, $size;
> +    push @indices, $total_length;
> +    $total_length += $size;
> +}
> +
> +#
> +# Emit the look-up-by-OID index table
> +#
> +print C_FILE "\n";
> +if ($total_length <= 255) {
> +    print C_FILE "static const unsigned char oid_index[OID__NR + 1] = {\n";
> +} else {
> +    print C_FILE "static const unsigned short oid_index[OID__NR + 1] = {\n";
> +}
> +for (my $i = 0; $i <= $#names; $i++) {
> +    print C_FILE "\t[OID_", $names[$i], "] = ", $indices[$i], ",\n"
> +}
> +print C_FILE "\t[OID__NR] = ", $total_length, "\n";
> +print C_FILE "};\n";
> +
> +#
> +# Encode the OIDs
> +#
> +my @encoded_oids = ();
> +
> +for (my $i = 0; $i <= $#names; $i++) {
> +    my @octets = ();
> +
> +    my @components = split(/[.]/, $oids[$i]);
> +
> +    push @octets, $components[0] * 40 + $components[1];
> +
> +    for (my $loop = 2; $loop <= $#components; $loop++) {
> +	my $c = $components[$loop];
> +
> +	# Base128 encode the number
> +	my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
> +	$tmp = int($tmp / 7);
> +
> +	for (; $tmp > 0; $tmp--) {
> +	    push @octets, (($c >> $tmp * 7) & 0x7f) | 0x80;
> +	}
> +	push @octets, $c & 0x7f;
> +    }
> +
> +    push @encoded_oids, \@octets;
> +}
> +
> +#
> +# Create a hash value for each OID
> +#
> +my @hash_values = ();
> +for (my $i = 0; $i <= $#names; $i++) {
> +    my @octets = @{$encoded_oids[$i]};
> +
> +    my $hash = $#octets;
> +    foreach (@octets) {
> +	$hash += $_ * 33;
> +    }
> +
> +    $hash = ($hash >> 24) ^ ($hash >> 16) ^ ($hash >> 8) ^ ($hash);
> +
> +    push @hash_values, $hash & 0xff;
> +}
> +
> +#
> +# Emit the OID data
> +#
> +print C_FILE "\n";
> +print C_FILE "static const unsigned char oid_data[", $total_length, "] = {\n";
> +for (my $i = 0; $i <= $#names; $i++) {
> +    my @octets = @{$encoded_oids[$i]};
> +    print C_FILE "\t";
> +    print C_FILE $_, ", " foreach (@octets);
> +    print C_FILE "\t// ", $names[$i];
> +    print C_FILE "\n";
> +}
> +print C_FILE "};\n";
> +
> +#
> +# Build the search index table (ordered by length then hash then content)
> +#
> +my @index_table = ( 0 .. $#names );
> +
> + at index_table = sort {
> +    my @octets_a = @{$encoded_oids[$a]};
> +    my @octets_b = @{$encoded_oids[$b]};
> +
> +    return $hash_values[$a] <=> $hash_values[$b]
> +	if ($hash_values[$a] != $hash_values[$b]);
> +    return $#octets_a <=> $#octets_b
> +	if ($#octets_a != $#octets_b);
> +    for (my $i = $#octets_a; $i >= 0; $i--) {
> +	return $octets_a[$i] <=> $octets_b[$i]
> +	    if ($octets_a[$i] != $octets_b[$i]);
> +    }
> +    return 0;
> +
> +} @index_table;
> +
> +#
> +# Emit the search index and hash value table
> +#
> +print C_FILE "\n";
> +print C_FILE "static const struct {\n";
> +print C_FILE "\tunsigned char hash;\n";
> +if ($#names <= 255) {
> +    print C_FILE "\tenum OID oid : 8;\n";
> +} else {
> +    print C_FILE "\tenum OID oid : 16;\n";
> +}
> +print C_FILE "} oid_search_table[OID__NR] = {\n";
> +for (my $i = 0; $i <= $#names; $i++) {
> +    my @octets = @{$encoded_oids[$index_table[$i]]};
> +    printf(C_FILE "\t[%3u] = { %3u, OID_%-35s }, // ",
> +	   $i,
> +	   $hash_values[$index_table[$i]],
> +	   $names[$index_table[$i]]);
> +    printf C_FILE "%02x", $_ foreach (@octets);
> +    print C_FILE "\n";
> +}
> +print C_FILE "};\n";
> +
> +#
> +# Emit the OID debugging name table
> +#
> +#print C_FILE "\n";
> +#print C_FILE "const char *const oid_name_table[OID__NR + 1] = {\n";
> +#
> +#for (my $i = 0; $i <= $#names; $i++) {
> +#    print C_FILE "\t\"", $names[$i], "\",\n"
> +#}
> +#print C_FILE "\t\"Unknown-OID\"\n";
> +#print C_FILE "};\n";
> +
> +#
> +# Polish off
> +#
> +close C_FILE or die;
> diff --git a/lib/oid_registry.c b/lib/oid_registry.c
> new file mode 100644
> index 000000000000..209edc73b99f
> --- /dev/null
> +++ b/lib/oid_registry.c
> @@ -0,0 +1,179 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* ASN.1 Object identifier (OID) registry
> + *
> + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> + * Written by David Howells (dhowells at redhat.com)
> + */
> +
> +#ifdef __UBOOT__
> +#include <linux/compat.h>
> +#else
> +#include <linux/module.h>
> +#include <linux/export.h>
> +#endif
> +#include <linux/oid_registry.h>
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/bug.h>
> +#include "oid_registry_data.c"
> +
> +MODULE_DESCRIPTION("OID Registry");
> +MODULE_AUTHOR("Red Hat, Inc.");
> +MODULE_LICENSE("GPL");
> +
> +/**
> + * look_up_OID - Find an OID registration for the specified data
> + * @data: Binary representation of the OID
> + * @datasize: Size of the binary representation
> + */
> +enum OID look_up_OID(const void *data, size_t datasize)
> +{
> +	const unsigned char *octets = data;
> +	enum OID oid;
> +	unsigned char xhash;
> +	unsigned i, j, k, hash;
> +	size_t len;
> +
> +	/* Hash the OID data */
> +	hash = datasize - 1;
> +
> +	for (i = 0; i < datasize; i++)
> +		hash += octets[i] * 33;
> +	hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash;
> +	hash &= 0xff;
> +
> +	/* Binary search the OID registry.  OIDs are stored in ascending order
> +	 * of hash value then ascending order of size and then in ascending
> +	 * order of reverse value.
> +	 */
> +	i = 0;
> +	k = OID__NR;
> +	while (i < k) {
> +		j = (i + k) / 2;
> +
> +		xhash = oid_search_table[j].hash;
> +		if (xhash > hash) {
> +			k = j;
> +			continue;
> +		}
> +		if (xhash < hash) {
> +			i = j + 1;
> +			continue;
> +		}
> +
> +		oid = oid_search_table[j].oid;
> +		len = oid_index[oid + 1] - oid_index[oid];
> +		if (len > datasize) {
> +			k = j;
> +			continue;
> +		}
> +		if (len < datasize) {
> +			i = j + 1;
> +			continue;
> +		}
> +
> +		/* Variation is most likely to be at the tail end of the
> +		 * OID, so do the comparison in reverse.
> +		 */
> +		while (len > 0) {
> +			unsigned char a = oid_data[oid_index[oid] + --len];
> +			unsigned char b = octets[len];
> +			if (a > b) {
> +				k = j;
> +				goto next;
> +			}
> +			if (a < b) {
> +				i = j + 1;
> +				goto next;
> +			}
> +		}
> +		return oid;
> +	next:
> +		;
> +	}
> +
> +	return OID__NR;
> +}
> +EXPORT_SYMBOL_GPL(look_up_OID);
> +
> +/*
> + * sprint_OID - Print an Object Identifier into a buffer
> + * @data: The encoded OID to print
> + * @datasize: The size of the encoded OID
> + * @buffer: The buffer to render into
> + * @bufsize: The size of the buffer
> + *
> + * The OID is rendered into the buffer in "a.b.c.d" format and the number of
> + * bytes is returned.  -EBADMSG is returned if the data could not be intepreted
> + * and -ENOBUFS if the buffer was too small.
> + */
> +int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize)
> +{
> +	const unsigned char *v = data, *end = v + datasize;
> +	unsigned long num;
> +	unsigned char n;
> +	size_t ret;
> +	int count;
> +
> +	if (v >= end)
> +		goto bad;
> +
> +	n = *v++;
> +	ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40);
> +	if (count >= bufsize)
> +		return -ENOBUFS;
> +	buffer += count;
> +	bufsize -= count;
> +
> +	while (v < end) {
> +		num = 0;
> +		n = *v++;
> +		if (!(n & 0x80)) {
> +			num = n;
> +		} else {
> +			num = n & 0x7f;
> +			do {
> +				if (v >= end)
> +					goto bad;
> +				n = *v++;
> +				num <<= 7;
> +				num |= n & 0x7f;
> +			} while (n & 0x80);
> +		}
> +		ret += count = snprintf(buffer, bufsize, ".%lu", num);
> +		if (count >= bufsize)
> +			return -ENOBUFS;
> +		buffer += count;
> +		bufsize -= count;
> +	}
> +
> +	return ret;
> +
> +bad:
> +	snprintf(buffer, bufsize, "(bad)");
> +	return -EBADMSG;
> +}
> +EXPORT_SYMBOL_GPL(sprint_oid);
> +
> +/**
> + * sprint_OID - Print an Object Identifier into a buffer
> + * @oid: The OID to print
> + * @buffer: The buffer to render into
> + * @bufsize: The size of the buffer
> + *
> + * The OID is rendered into the buffer in "a.b.c.d" format and the number of
> + * bytes is returned.
> + */
> +int sprint_OID(enum OID oid, char *buffer, size_t bufsize)
> +{
> +	int ret;
> +
> +	BUG_ON(oid >= OID__NR);
> +
> +	ret = sprint_oid(oid_data + oid_index[oid],
> +			 oid_index[oid + 1] - oid_index[oid],
> +			 buffer, bufsize);
> +	BUG_ON(ret == -EBADMSG);
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(sprint_OID);
>

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-11  7:55 ` [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
@ 2019-10-12 13:02   ` Heinrich Schuchardt
  2019-10-15  3:18     ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 13:02 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
> I hope this patch set will be reviewed promptly as I'm aiming to
> push my "UEFI secure boot" patch for v2020.01.
>

How can I make all of these new files being built to check for build
warnings?

Please, provide unit tests for the patch series.

Please, provide a documentation how these different tools and files work
together.

Best regards

Heinrich

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

* [U-Boot] [PATCH v1 11/11] lib: crypto: add rsa public key parser
  2019-10-11  7:42 ` [U-Boot] [PATCH v1 11/11] lib: crypto: add rsa public key parser AKASHI Takahiro
@ 2019-10-12 13:11   ` Heinrich Schuchardt
  0 siblings, 0 replies; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-12 13:11 UTC (permalink / raw)
  To: u-boot

On 10/11/19 9:42 AM, AKASHI Takahiro wrote:
> Imported from linux kernel v5.3.

It is unclear from your commit message which files are verbatime copies
and which have been adjusted to fit into U-Boot.

Where is the unit test?

Best regards

Heinrich

>
> Signed-off-by: AKASHI Takahiro<takahiro.akashi@linaro.org>
> ---
>   include/crypto/internal/rsa.h |  57 ++++++++++
>   lib/crypto/Makefile           |  10 +-
>   lib/crypto/rsa_helper.c       | 198 ++++++++++++++++++++++++++++++++++
>   lib/crypto/rsapubkey.asn1     |   4 +
>   4 files changed, 268 insertions(+), 1 deletion(-)
>   create mode 100644 include/crypto/internal/rsa.h
>   create mode 100644 lib/crypto/rsa_helper.c
>   create mode 100644 lib/crypto/rsapubkey.asn1

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-12 13:02   ` Heinrich Schuchardt
@ 2019-10-15  3:18     ` AKASHI Takahiro
  2019-10-15  5:33       ` Heinrich Schuchardt
  0 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-15  3:18 UTC (permalink / raw)
  To: u-boot

On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
> On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
> >I hope this patch set will be reviewed promptly as I'm aiming to
> >push my "UEFI secure boot" patch for v2020.01.
> >
> 
> How can I make all of these new files being built to check for build
> warnings?

As always in my case of UEFI secure boot, they have gone through build and
run/tests as part of UEFI secure boot. This is also true for RSA
extension as UEFI secure boot is the only user of those features.

Please note that almost of all the code here come from the latest
linux code without any changes. A few changes that I made are
quite U-Boot/UEFI-secure-boot specific and is *best* tested through
UEFI secure boot test.

That said, you can at least build the code by enabling
  Library routines
    Security support
      Asymmetric Key Support
        CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE
        CONFIG_X509_CERTIFICATE_PARSER
        CONFIG_PKCS7_MESSAGE_PARSER

> Please, provide unit tests for the patch series.

As I said above, it will be exercised and tested under UEFI secure boot
test.

> Please, provide a documentation how these different tools and files work
> together.

What do you mean by different tools?
Asn1 compiler and what?
Do you want to have doc/README.asn1compiler?

Thanks,
-Takahiro Akashi


> Best regards
> 
> Heinrich

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-15  3:18     ` AKASHI Takahiro
@ 2019-10-15  5:33       ` Heinrich Schuchardt
  2019-10-15  8:56         ` AKASHI Takahiro
  2019-10-15  9:25         ` AKASHI Takahiro
  0 siblings, 2 replies; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-15  5:33 UTC (permalink / raw)
  To: u-boot

On 10/15/19 5:18 AM, AKASHI Takahiro wrote:
> On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
>> On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
>>> I hope this patch set will be reviewed promptly as I'm aiming to
>>> push my "UEFI secure boot" patch for v2020.01.
>>>
>>
>> How can I make all of these new files being built to check for build
>> warnings?
>
> As always in my case of UEFI secure boot, they have gone through build and
> run/tests as part of UEFI secure boot. This is also true for RSA
> extension as UEFI secure boot is the only user of those features.

Did you run them through Travis?

>
> Please note that almost of all the code here come from the latest
> linux code without any changes. A few changes that I made are
> quite U-Boot/UEFI-secure-boot specific and is *best* tested through
> UEFI secure boot test.
>
> That said, you can at least build the code by enabling
>    Library routines
>      Security support
>        Asymmetric Key Support
>          CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE
>          CONFIG_X509_CERTIFICATE_PARSER
>          CONFIG_PKCS7_MESSAGE_PARSER
>
>> Please, provide unit tests for the patch series.
>
> As I said above, it will be exercised and tested under UEFI secure boot
> test.

If there is nothing I can test now, I would not know how to evaluate
these patches.

There is good reason that we have unit tests and don't simply say U-Boot
can be tested by booting Linux.

>
>> Please, provide a documentation how these different tools and files work
>> together.
>
> What do you mean by different tools?
> Asn1 compiler and what?
> Do you want to have doc/README.asn1compiler?

This patch series provides some puzzle pieces but doesn't tell me how
they fit together. Maybe a README describing the different elements
provided for UEFI secure boot would be most appropriate.

Best regards

Heinrich

>
> Thanks,
> -Takahiro Akashi
>
>
>> Best regards
>>
>> Heinrich
>

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-15  5:33       ` Heinrich Schuchardt
@ 2019-10-15  8:56         ` AKASHI Takahiro
  2019-10-15 11:10           ` Heinrich Schuchardt
  2019-10-15  9:25         ` AKASHI Takahiro
  1 sibling, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-15  8:56 UTC (permalink / raw)
  To: u-boot

On Tue, Oct 15, 2019 at 07:33:18AM +0200, Heinrich Schuchardt wrote:
> On 10/15/19 5:18 AM, AKASHI Takahiro wrote:
> >On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
> >>On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
> >>>I hope this patch set will be reviewed promptly as I'm aiming to
> >>>push my "UEFI secure boot" patch for v2020.01.
> >>>
> >>
> >>How can I make all of these new files being built to check for build
> >>warnings?
> >
> >As always in my case of UEFI secure boot, they have gone through build and
> >run/tests as part of UEFI secure boot. This is also true for RSA
> >extension as UEFI secure boot is the only user of those features.
> 
> Did you run them through Travis?
> 
> >
> >Please note that almost of all the code here come from the latest
> >linux code without any changes. A few changes that I made are
> >quite U-Boot/UEFI-secure-boot specific and is *best* tested through
> >UEFI secure boot test.
> >
> >That said, you can at least build the code by enabling
> >   Library routines
> >     Security support
> >       Asymmetric Key Support
> >         CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE
> >         CONFIG_X509_CERTIFICATE_PARSER
> >         CONFIG_PKCS7_MESSAGE_PARSER
> >
> >>Please, provide unit tests for the patch series.
> >
> >As I said above, it will be exercised and tested under UEFI secure boot
> >test.
> 
> If there is nothing I can test now, I would not know how to evaluate
> these patches.

Do you ask me to write "unit tests" to test all the aspects of
asn1 compiler and parsers that I have not developed any part of
and that are not changed from the original?
Doesn't make sense.

> There is good reason that we have unit tests and don't simply say U-Boot
> can be tested by booting Linux.

There are lots of examples, one is the original RSA routines, as I said,
which have not direct-linked tests and are only tested by vboot.py.

> >
> >>Please, provide a documentation how these different tools and files work
> >>together.
> >
> >What do you mean by different tools?
> >Asn1 compiler and what?
> >Do you want to have doc/README.asn1compiler?
> 
> This patch series provides some puzzle pieces but doesn't tell me how
> they fit together. Maybe a README describing the different elements
> provided for UEFI secure boot would be most appropriate.

All what you need to know is that the patch set will generate
and provide x509 parser and pkcs7 parser as a result of build process.

I will a few lines of README.asn1compiler to describe that.

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >
> >Thanks,
> >-Takahiro Akashi
> >
> >
> >>Best regards
> >>
> >>Heinrich
> >
> 

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-15  5:33       ` Heinrich Schuchardt
  2019-10-15  8:56         ` AKASHI Takahiro
@ 2019-10-15  9:25         ` AKASHI Takahiro
  2019-10-17 15:23           ` Tom Rini
  1 sibling, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-15  9:25 UTC (permalink / raw)
  To: u-boot

On Tue, Oct 15, 2019 at 07:33:18AM +0200, Heinrich Schuchardt wrote:
> On 10/15/19 5:18 AM, AKASHI Takahiro wrote:
> >On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
> >>On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
> >>>I hope this patch set will be reviewed promptly as I'm aiming to
> >>>push my "UEFI secure boot" patch for v2020.01.
> >>>
> >>
> >>How can I make all of these new files being built to check for build
> >>warnings?
> >
> >As always in my case of UEFI secure boot, they have gone through build and
> >run/tests as part of UEFI secure boot. This is also true for RSA
> >extension as UEFI secure boot is the only user of those features.
> 
> Did you run them through Travis?

As far as this patch set is concerned, no configuration enables
any of kconfig options listed below and running Travis doesn't make sense.

-Takahiro Akashi


> >
> >Please note that almost of all the code here come from the latest
> >linux code without any changes. A few changes that I made are
> >quite U-Boot/UEFI-secure-boot specific and is *best* tested through
> >UEFI secure boot test.
> >
> >That said, you can at least build the code by enabling
> >   Library routines
> >     Security support
> >       Asymmetric Key Support
> >         CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE
> >         CONFIG_X509_CERTIFICATE_PARSER
> >         CONFIG_PKCS7_MESSAGE_PARSER
> >
> >>Please, provide unit tests for the patch series.
> >
> >As I said above, it will be exercised and tested under UEFI secure boot
> >test.
> 
> If there is nothing I can test now, I would not know how to evaluate
> these patches.
> 
> There is good reason that we have unit tests and don't simply say U-Boot
> can be tested by booting Linux.
> 
> >
> >>Please, provide a documentation how these different tools and files work
> >>together.
> >
> >What do you mean by different tools?
> >Asn1 compiler and what?
> >Do you want to have doc/README.asn1compiler?
> 
> This patch series provides some puzzle pieces but doesn't tell me how
> they fit together. Maybe a README describing the different elements
> provided for UEFI secure boot would be most appropriate.
> 
> Best regards
> 
> Heinrich
> 
> >
> >Thanks,
> >-Takahiro Akashi
> >
> >
> >>Best regards
> >>
> >>Heinrich
> >
> 

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-15  8:56         ` AKASHI Takahiro
@ 2019-10-15 11:10           ` Heinrich Schuchardt
  0 siblings, 0 replies; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-15 11:10 UTC (permalink / raw)
  To: u-boot

On 10/15/19 10:56 AM, AKASHI Takahiro wrote:
> On Tue, Oct 15, 2019 at 07:33:18AM +0200, Heinrich Schuchardt wrote:
>> On 10/15/19 5:18 AM, AKASHI Takahiro wrote:
>>> On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
>>>> On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
>>>>> I hope this patch set will be reviewed promptly as I'm aiming to
>>>>> push my "UEFI secure boot" patch for v2020.01.
>>>>>
>>>>
>>>> How can I make all of these new files being built to check for build
>>>> warnings?
>>>
>>> As always in my case of UEFI secure boot, they have gone through build and
>>> run/tests as part of UEFI secure boot. This is also true for RSA
>>> extension as UEFI secure boot is the only user of those features.
>>
>> Did you run them through Travis?
>>
>>>
>>> Please note that almost of all the code here come from the latest
>>> linux code without any changes. A few changes that I made are
>>> quite U-Boot/UEFI-secure-boot specific and is *best* tested through
>>> UEFI secure boot test.
>>>
>>> That said, you can at least build the code by enabling
>>>    Library routines
>>>      Security support
>>>        Asymmetric Key Support
>>>          CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE
>>>          CONFIG_X509_CERTIFICATE_PARSER
>>>          CONFIG_PKCS7_MESSAGE_PARSER
>>>
>>>> Please, provide unit tests for the patch series.
>>>
>>> As I said above, it will be exercised and tested under UEFI secure boot
>>> test.
>>
>> If there is nothing I can test now, I would not know how to evaluate
>> these patches.
>
> Do you ask me to write "unit tests" to test all the aspects of
> asn1 compiler and parsers that I have not developed any part of
> and that are not changed from the original?
> Doesn't make sense.

Wouldn't it be enough to ASN1-compile one file and check the MD5 hash of
the result file?

Regards

Heinrich

>
>> There is good reason that we have unit tests and don't simply say U-Boot
>> can be tested by booting Linux.
>
> There are lots of examples, one is the original RSA routines, as I said,
> which have not direct-linked tests and are only tested by vboot.py.
>
>>>
>>>> Please, provide a documentation how these different tools and files work
>>>> together.
>>>
>>> What do you mean by different tools?
>>> Asn1 compiler and what?
>>> Do you want to have doc/README.asn1compiler?
>>
>> This patch series provides some puzzle pieces but doesn't tell me how
>> they fit together. Maybe a README describing the different elements
>> provided for UEFI secure boot would be most appropriate.
>
> All what you need to know is that the patch set will generate
> and provide x509 parser and pkcs7 parser as a result of build process.
>
> I will a few lines of README.asn1compiler to describe that.
>
> -Takahiro Akashi
>
>> Best regards
>>
>> Heinrich
>>
>>>
>>> Thanks,
>>> -Takahiro Akashi
>>>
>>>
>>>> Best regards
>>>>
>>>> Heinrich
>>>
>>
>

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

* [U-Boot] [PATCH v1 01/11] linux_compat: add kmemdup()
  2019-10-12 11:22   ` Heinrich Schuchardt
@ 2019-10-17  3:04     ` AKASHI Takahiro
  0 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-17  3:04 UTC (permalink / raw)
  To: u-boot

On Sat, Oct 12, 2019 at 01:22:15PM +0200, Heinrich Schuchardt wrote:
> On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> >Adding kmemdup() will help improve portability from linux kernel
> >code (in my case, lib/crypto/x509_cert_parser.c and pkcs7_parser.c).
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  include/linux/compat.h |  4 ++--
> >  lib/linux_compat.c     | 11 +++++++++++
> >  2 files changed, 13 insertions(+), 2 deletions(-)
> >
> >diff --git a/include/linux/compat.h b/include/linux/compat.h
> >index d0f51baab407..d23ef50454ce 100644
> >--- a/include/linux/compat.h
> >+++ b/include/linux/compat.h
> >@@ -117,6 +117,8 @@ static inline void kmem_cache_destroy(struct kmem_cache *cachep)
> >  	free(cachep);
> >  }
> >
> >+void *kmemdup(const void *src, size_t size, int flags);
> 
> kememdup() is already implemented in fs/ubifs/ubifs.c and used both in
> drivers/mtd/ and in fs/ubifs/.

Good catch, I haven't noticed that.
ubifs.c is a very bad place for that function.
# drivers/mtd/mtd*.c doesn't use it as relevant code is guarded by
  "#ifndef __UBOOT__" though.

> Why would you want to use a different signature than both the Linux
> kernel and current U-Boot?
> 
> >+
> >  #define DECLARE_WAITQUEUE(...)	do { } while (0)
> >  #define add_wait_queue(...)	do { } while (0)
> >  #define remove_wait_queue(...)	do { } while (0)
> >@@ -346,8 +348,6 @@ struct writeback_control {
> >  	unsigned for_sync:1;		/* sync(2) WB_SYNC_ALL writeback */
> >  };
> >
> >-void *kmemdup(const void *src, size_t len, gfp_t gfp);
> >-
> >  typedef int irqreturn_t;
> >
> >  struct timer_list {};
> >diff --git a/lib/linux_compat.c b/lib/linux_compat.c
> >index 6373b4451eb3..dd1e5b3c2087 100644
> >--- a/lib/linux_compat.c
> >+++ b/lib/linux_compat.c
> >@@ -40,3 +40,14 @@ void *kmem_cache_alloc(struct kmem_cache *obj, int flag)
> >  {
> >  	return malloc_cache_aligned(obj->sz);
> >  }
> >+
> >+void *kmemdup(const void *src, size_t size, int flags)
> >+{
> 
> You should not duplicate the implementation in the UBI filesystem.
> Instead move the existing implementation to a place that can be linked
> in your future work.

Okay, I'll remove it from ubifs.c.

Thanks,
-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >+	void *p;
> >+
> >+	p = kmalloc(size, flags);
> >+	if (p)
> >+		memcpy(p, src, size);
> >+
> >+	return p;
> >+}
> >
> 

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

* [U-Boot] [PATCH v1 02/11] include: time.h: define time64_t
  2019-10-12 11:40   ` Heinrich Schuchardt
@ 2019-10-17  5:39     ` AKASHI Takahiro
  2019-10-17  5:51       ` Heinrich Schuchardt
  0 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-17  5:39 UTC (permalink / raw)
  To: u-boot

On Sat, Oct 12, 2019 at 01:40:32PM +0200, Heinrich Schuchardt wrote:
> On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> >Adding time64_t definition will help improve portability of linux kernel
> >code (in my case, lib/crypto/x509_cert_parser.c).
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  include/linux/time.h | 24 ++++++++++++++++++++++++
> >  1 file changed, 24 insertions(+)
> >
> >diff --git a/include/linux/time.h b/include/linux/time.h
> >index b8d298eb4d68..6186985856d7 100644
> >--- a/include/linux/time.h
> >+++ b/include/linux/time.h
> >@@ -150,4 +150,28 @@ _DEFUN (ctime_r, (tim_p, result),
> >      return asctime_r (localtime_r (tim_p, &tm), result);
> >  }
> >
> >+/* from <linux>/kernel/time/time.c */
> >+typedef __s64 time64_t;
> 
> Wouldn't we want to put these definitions into a file
> include/linux/time64.h?

Obviously, there is no problem at following your suggestion, but
I hesitate to do so as it adds only one line header file.
Moreover, mktime64(), which is the only reason for this patch,
is also declared in "linux/time.h" even in linux code.
# Please note that "linux/time64.h" is mainly for timespec64 helper
functions, which are never used in U-Boot.

So I'd like to leave as it is. I think that we can re-visit this issue
in the future when other definitions in time64.h are required.

> >+
> >+inline time64_t mktime64(const unsigned int year0, const unsigned int mon0,
> >+			 const unsigned int day, const unsigned int hour,
> >+			 const unsigned int min, const unsigned int sec)
> 
> 
> Where is the function description?
> 
> The Linux kernel does not make this function an inline function. Why
> should we inline it in U-Boot?
> 
> >+{
> >+	unsigned int mon = mon0, year = year0;
> >+
> >+	/* 1..12 -> 11,12,1..10 */
> >+	mon -= 2;
> >+	if (0 >= (int)mon) {
> >+		mon += 12;	/* Puts Feb last since it has leap day */
> >+		year -= 1;
> >+	}
> >+
> >+	return ((((time64_t)
> >+		  (year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) +
> >+		  year * 365 - 719499
> >+	  ) * 24 + hour /* now have hours - midnight tomorrow handled here */
> >+	  ) * 60 + min /* now have minutes */
> >+	) * 60 + sec; /* finally seconds */
> 
> This code is duplicate to rtc_mktime().
> 
> Duplication could be avoided by letting rtc_mktime() call mktime64().

Okay, but as an inline function in this case.
In addition, drivers/rtc/date.c will be moved to lib/ for general use
with CONFIG_LIB_DATE.

Thanks,
-Takahiro Akashi


> Best regards
> 
> Heinrich
> 
> >+}
> >+
> >  #endif
> >
> 

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

* [U-Boot] [PATCH v1 02/11] include: time.h: define time64_t
  2019-10-17  5:39     ` AKASHI Takahiro
@ 2019-10-17  5:51       ` Heinrich Schuchardt
  0 siblings, 0 replies; 39+ messages in thread
From: Heinrich Schuchardt @ 2019-10-17  5:51 UTC (permalink / raw)
  To: u-boot

On 10/17/19 7:39 AM, AKASHI Takahiro wrote:
> On Sat, Oct 12, 2019 at 01:40:32PM +0200, Heinrich Schuchardt wrote:
>> On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
>>> Adding time64_t definition will help improve portability of linux kernel
>>> code (in my case, lib/crypto/x509_cert_parser.c).
>>>
>>> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
>>> ---
>>>   include/linux/time.h | 24 ++++++++++++++++++++++++
>>>   1 file changed, 24 insertions(+)
>>>
>>> diff --git a/include/linux/time.h b/include/linux/time.h
>>> index b8d298eb4d68..6186985856d7 100644
>>> --- a/include/linux/time.h
>>> +++ b/include/linux/time.h
>>> @@ -150,4 +150,28 @@ _DEFUN (ctime_r, (tim_p, result),
>>>       return asctime_r (localtime_r (tim_p, &tm), result);
>>>   }
>>>
>>> +/* from <linux>/kernel/time/time.c */
>>> +typedef __s64 time64_t;
>>
>> Wouldn't we want to put these definitions into a file
>> include/linux/time64.h?
>
> Obviously, there is no problem at following your suggestion, but
> I hesitate to do so as it adds only one line header file.
> Moreover, mktime64(), which is the only reason for this patch,
> is also declared in "linux/time.h" even in linux code.
> # Please note that "linux/time64.h" is mainly for timespec64 helper
> functions, which are never used in U-Boot.
>
> So I'd like to leave as it is. I think that we can re-visit this issue
> in the future when other definitions in time64.h are required.
>
>>> +
>>> +inline time64_t mktime64(const unsigned int year0, const unsigned int mon0,
>>> +			 const unsigned int day, const unsigned int hour,
>>> +			 const unsigned int min, const unsigned int sec)
>>
>>
>> Where is the function description?
>>
>> The Linux kernel does not make this function an inline function. Why
>> should we inline it in U-Boot?
>>
>>> +{
>>> +	unsigned int mon = mon0, year = year0;
>>> +
>>> +	/* 1..12 -> 11,12,1..10 */
>>> +	mon -= 2;
>>> +	if (0 >= (int)mon) {
>>> +		mon += 12;	/* Puts Feb last since it has leap day */
>>> +		year -= 1;
>>> +	}
>>> +
>>> +	return ((((time64_t)
>>> +		  (year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) +
>>> +		  year * 365 - 719499
>>> +	  ) * 24 + hour /* now have hours - midnight tomorrow handled here */
>>> +	  ) * 60 + min /* now have minutes */
>>> +	) * 60 + sec; /* finally seconds */
>>
>> This code is duplicate to rtc_mktime().
>>
>> Duplication could be avoided by letting rtc_mktime() call mktime64().
>
> Okay, but as an inline function in this case.

Inline use in two places adds more bytes to the binary. U-Boot should
stay as small as possible.

Best regards

Heinrich

> In addition, drivers/rtc/date.c will be moved to lib/ for general use
> with CONFIG_LIB_DATE.
>
> Thanks,
> -Takahiro Akashi
>
>
>> Best regards
>>
>> Heinrich
>>
>>> +}
>>> +
>>>   #endif
>>>
>>
>

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

* [U-Boot] [PATCH v1 03/11] include: kernel.h: include printk.h
  2019-10-12 11:47   ` Heinrich Schuchardt
@ 2019-10-17  5:58     ` AKASHI Takahiro
  2019-10-17  6:17       ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-17  5:58 UTC (permalink / raw)
  To: u-boot

On Sat, Oct 12, 2019 at 01:47:06PM +0200, Heinrich Schuchardt wrote:
> On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> >Adding "printk.h" will help improve portability from linux kernel
> >code (in my case, lib/asn1_decoder.c).
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  include/linux/kernel.h | 2 ++
> >  1 file changed, 2 insertions(+)
> >
> >diff --git a/include/linux/kernel.h b/include/linux/kernel.h
> >index a85c15d8dc28..919d12bdf89c 100644
> >--- a/include/linux/kernel.h
> >+++ b/include/linux/kernel.h
> >@@ -2,7 +2,9 @@
> >  #define _LINUX_KERNEL_H
> >
> >
> >+#include <vsprintf.h> /* for printf utilities */
> 
> Kernel code has no vsprintf.h. Linux's lib/asn1_decoder.c has not a
> single print statement. So why are you inserting vsprintf.h here?

I don't remember why I mentioned to asn1_decoder.c here, but
When this statement is removed from kernel.h, some files cannot
be compiled.
The fact is that sprintf() is used in time.h without including any
related headers.
(There are still bunch of *missing headers* issues in U-Boot.)

I will add one more patch against time.h here.

Thanks,
-Takahiro Akashi


> >  #include <linux/types.h>
> >+#include <linux/printk.h>
> 
> Linux include/linux/kernel.h also includes printk.h. OK.
> 
> Best regards
> 
> Heinrich
> >
> >  #define USHRT_MAX	((u16)(~0U))
> >  #define SHRT_MAX	((s16)(USHRT_MAX>>1))
> >
> 

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

* [U-Boot] [PATCH v1 03/11] include: kernel.h: include printk.h
  2019-10-17  5:58     ` AKASHI Takahiro
@ 2019-10-17  6:17       ` AKASHI Takahiro
  0 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-17  6:17 UTC (permalink / raw)
  To: u-boot

On Thu, Oct 17, 2019 at 02:58:45PM +0900, AKASHI Takahiro wrote:
> On Sat, Oct 12, 2019 at 01:47:06PM +0200, Heinrich Schuchardt wrote:
> > On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> > >Adding "printk.h" will help improve portability from linux kernel
> > >code (in my case, lib/asn1_decoder.c).
> > >
> > >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> > >---
> > >  include/linux/kernel.h | 2 ++
> > >  1 file changed, 2 insertions(+)
> > >
> > >diff --git a/include/linux/kernel.h b/include/linux/kernel.h
> > >index a85c15d8dc28..919d12bdf89c 100644
> > >--- a/include/linux/kernel.h
> > >+++ b/include/linux/kernel.h
> > >@@ -2,7 +2,9 @@
> > >  #define _LINUX_KERNEL_H
> > >
> > >
> > >+#include <vsprintf.h> /* for printf utilities */
> > 
> > Kernel code has no vsprintf.h. Linux's lib/asn1_decoder.c has not a
> > single print statement. So why are you inserting vsprintf.h here?
> 
> I don't remember why I mentioned to asn1_decoder.c here, but

For the record, asn1_parser.c uses pr_debug/pr_devel and others
in many places.

-Takahiro Akashi

> When this statement is removed from kernel.h, some files cannot
> be compiled.
> The fact is that sprintf() is used in time.h without including any
> related headers.
> (There are still bunch of *missing headers* issues in U-Boot.)
> 
> I will add one more patch against time.h here.
> 
> Thanks,
> -Takahiro Akashi
> 
> 
> > >  #include <linux/types.h>
> > >+#include <linux/printk.h>
> > 
> > Linux include/linux/kernel.h also includes printk.h. OK.
> > 
> > Best regards
> > 
> > Heinrich
> > >
> > >  #define USHRT_MAX	((u16)(~0U))
> > >  #define SHRT_MAX	((s16)(USHRT_MAX>>1))
> > >
> > 

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

* [U-Boot] [PATCH v1 04/11] cmd: add asn1_compiler
  2019-10-12 12:22   ` Heinrich Schuchardt
@ 2019-10-17  6:25     ` AKASHI Takahiro
  0 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-17  6:25 UTC (permalink / raw)
  To: u-boot

On Sat, Oct 12, 2019 at 02:22:14PM +0200, Heinrich Schuchardt wrote:
> On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> >Imported from linux kernel v5.3.
> >
> >This host command will be used to create a ASN1 parser, for example,
> >for pkcs7 or x509.
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  cmd/Kconfig                       |    3 +
> >  include/linux/asn1.h              |   65 ++
> >  include/linux/asn1_ber_bytecode.h |   89 ++
> >  include/linux/asn1_decoder.h      |   20 +
> >  scripts/Makefile                  |    3 +
> >  scripts/asn1_compiler.c           | 1611 +++++++++++++++++++++++++++++
> 
> Except for Makefile and Kconfig these are verbatime copies of the Linux
> files. OK.
> 
> >  6 files changed, 1791 insertions(+)
> >  create mode 100644 include/linux/asn1.h
> >  create mode 100644 include/linux/asn1_ber_bytecode.h
> >  create mode 100644 include/linux/asn1_decoder.h
> >  create mode 100644 scripts/asn1_compiler.c
> >
> >diff --git a/cmd/Kconfig b/cmd/Kconfig
> >index 4e61565aab16..605dcfdf4282 100644
> >--- a/cmd/Kconfig
> >+++ b/cmd/Kconfig
> >@@ -184,6 +184,9 @@ endmenu
> >  config BUILD_BIN2C
> >  	bool
> >
> >+config BUILD_ASN1
> >+	bool
> >+
> >  comment "Commands"
> >
> >  menu "Info commands"
> >diff --git a/include/linux/asn1.h b/include/linux/asn1.h
> >new file mode 100644
> >index 000000000000..a4d0bdd10711
> >--- /dev/null
> >+++ b/include/linux/asn1.h
> >@@ -0,0 +1,65 @@
> >+/* SPDX-License-Identifier: GPL-2.0-or-later */
> >+/* ASN.1 BER/DER/CER encoding definitions
> >+ *
> >+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> >+ * Written by David Howells (dhowells at redhat.com)
> >+ */
> >+
> >+#ifndef _LINUX_ASN1_H
> >+#define _LINUX_ASN1_H
> >+
> >+/* Class */
> >+enum asn1_class {
> >+	ASN1_UNIV	= 0,	/* Universal */
> >+	ASN1_APPL	= 1,	/* Application */
> >+	ASN1_CONT	= 2,	/* Context */
> >+	ASN1_PRIV	= 3	/* Private */
> >+};
> >+#define ASN1_CLASS_BITS	0xc0
> >+
> >+
> >+enum asn1_method {
> >+	ASN1_PRIM	= 0,	/* Primitive */
> >+	ASN1_CONS	= 1	/* Constructed */
> >+};
> >+#define ASN1_CONS_BIT	0x20
> >+
> >+/* Tag */
> >+enum asn1_tag {
> >+	ASN1_EOC	= 0,	/* End Of Contents or N/A */
> >+	ASN1_BOOL	= 1,	/* Boolean */
> >+	ASN1_INT	= 2,	/* Integer */
> >+	ASN1_BTS	= 3,	/* Bit String */
> >+	ASN1_OTS	= 4,	/* Octet String */
> >+	ASN1_NULL	= 5,	/* Null */
> >+	ASN1_OID	= 6,	/* Object Identifier  */
> >+	ASN1_ODE	= 7,	/* Object Description */
> >+	ASN1_EXT	= 8,	/* External */
> >+	ASN1_REAL	= 9,	/* Real float */
> >+	ASN1_ENUM	= 10,	/* Enumerated */
> >+	ASN1_EPDV	= 11,	/* Embedded PDV */
> >+	ASN1_UTF8STR	= 12,	/* UTF8 String */
> >+	ASN1_RELOID	= 13,	/* Relative OID */
> >+	/* 14 - Reserved */
> >+	/* 15 - Reserved */
> >+	ASN1_SEQ	= 16,	/* Sequence and Sequence of */
> >+	ASN1_SET	= 17,	/* Set and Set of */
> >+	ASN1_NUMSTR	= 18,	/* Numerical String */
> >+	ASN1_PRNSTR	= 19,	/* Printable String */
> >+	ASN1_TEXSTR	= 20,	/* T61 String / Teletext String */
> >+	ASN1_VIDSTR	= 21,	/* Videotex String */
> >+	ASN1_IA5STR	= 22,	/* IA5 String */
> >+	ASN1_UNITIM	= 23,	/* Universal Time */
> >+	ASN1_GENTIM	= 24,	/* General Time */
> >+	ASN1_GRASTR	= 25,	/* Graphic String */
> >+	ASN1_VISSTR	= 26,	/* Visible String */
> >+	ASN1_GENSTR	= 27,	/* General String */
> >+	ASN1_UNISTR	= 28,	/* Universal String */
> >+	ASN1_CHRSTR	= 29,	/* Character String */
> >+	ASN1_BMPSTR	= 30,	/* BMP String */
> >+	ASN1_LONG_TAG	= 31	/* Long form tag */
> >+};
> >+
> >+#define ASN1_INDEFINITE_LENGTH 0x80
> >+
> >+#endif /* _LINUX_ASN1_H */
> >diff --git a/include/linux/asn1_ber_bytecode.h b/include/linux/asn1_ber_bytecode.h
> >new file mode 100644
> >index 000000000000..b38361953a48
> >--- /dev/null
> >+++ b/include/linux/asn1_ber_bytecode.h
> >@@ -0,0 +1,89 @@
> >+/* SPDX-License-Identifier: GPL-2.0-or-later */
> >+/* ASN.1 BER/DER/CER parsing state machine internal definitions
> >+ *
> >+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> >+ * Written by David Howells (dhowells at redhat.com)
> >+ */
> >+
> >+#ifndef _LINUX_ASN1_BER_BYTECODE_H
> >+#define _LINUX_ASN1_BER_BYTECODE_H
> >+
> >+#ifdef __KERNEL__
> >+#include <linux/types.h>
> >+#endif
> >+#include <linux/asn1.h>
> >+
> >+typedef int (*asn1_action_t)(void *context,
> >+			     size_t hdrlen, /* In case of ANY type */
> >+			     unsigned char tag, /* In case of ANY type */
> >+			     const void *value, size_t vlen);
> >+
> >+struct asn1_decoder {
> >+	const unsigned char *machine;
> >+	size_t machlen;
> >+	const asn1_action_t *actions;
> >+};
> >+
> >+enum asn1_opcode {
> >+	/* The tag-matching ops come first and the odd-numbered slots
> >+	 * are for OR_SKIP ops.
> >+	 */
> >+#define ASN1_OP_MATCH__SKIP		  0x01
> >+#define ASN1_OP_MATCH__ACT		  0x02
> >+#define ASN1_OP_MATCH__JUMP		  0x04
> >+#define ASN1_OP_MATCH__ANY		  0x08
> >+#define ASN1_OP_MATCH__COND		  0x10
> >+
> >+	ASN1_OP_MATCH			= 0x00,
> >+	ASN1_OP_MATCH_OR_SKIP		= 0x01,
> >+	ASN1_OP_MATCH_ACT		= 0x02,
> >+	ASN1_OP_MATCH_ACT_OR_SKIP	= 0x03,
> >+	ASN1_OP_MATCH_JUMP		= 0x04,
> >+	ASN1_OP_MATCH_JUMP_OR_SKIP	= 0x05,
> >+	ASN1_OP_MATCH_ANY		= 0x08,
> >+	ASN1_OP_MATCH_ANY_OR_SKIP	= 0x09,
> >+	ASN1_OP_MATCH_ANY_ACT		= 0x0a,
> >+	ASN1_OP_MATCH_ANY_ACT_OR_SKIP	= 0x0b,
> >+	/* Everything before here matches unconditionally */
> >+
> >+	ASN1_OP_COND_MATCH_OR_SKIP	= 0x11,
> >+	ASN1_OP_COND_MATCH_ACT_OR_SKIP	= 0x13,
> >+	ASN1_OP_COND_MATCH_JUMP_OR_SKIP	= 0x15,
> >+	ASN1_OP_COND_MATCH_ANY		= 0x18,
> >+	ASN1_OP_COND_MATCH_ANY_OR_SKIP	= 0x19,
> >+	ASN1_OP_COND_MATCH_ANY_ACT	= 0x1a,
> >+	ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP = 0x1b,
> >+
> >+	/* Everything before here will want a tag from the data */
> >+#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP
> >+
> >+	/* These are here to help fill up space */
> >+	ASN1_OP_COND_FAIL		= 0x1c,
> >+	ASN1_OP_COMPLETE		= 0x1d,
> >+	ASN1_OP_ACT			= 0x1e,
> >+	ASN1_OP_MAYBE_ACT		= 0x1f,
> >+
> >+	/* The following eight have bit 0 -> SET, 1 -> OF, 2 -> ACT */
> >+	ASN1_OP_END_SEQ			= 0x20,
> >+	ASN1_OP_END_SET			= 0x21,
> >+	ASN1_OP_END_SEQ_OF		= 0x22,
> >+	ASN1_OP_END_SET_OF		= 0x23,
> >+	ASN1_OP_END_SEQ_ACT		= 0x24,
> >+	ASN1_OP_END_SET_ACT		= 0x25,
> >+	ASN1_OP_END_SEQ_OF_ACT		= 0x26,
> >+	ASN1_OP_END_SET_OF_ACT		= 0x27,
> >+#define ASN1_OP_END__SET		  0x01
> >+#define ASN1_OP_END__OF			  0x02
> >+#define ASN1_OP_END__ACT		  0x04
> >+
> >+	ASN1_OP_RETURN			= 0x28,
> >+
> >+	ASN1_OP__NR
> >+};
> >+
> >+#define _tag(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | ASN1_##TAG)
> >+#define _tagn(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | TAG)
> >+#define _jump_target(N) (N)
> >+#define _action(N) (N)
> >+
> >+#endif /* _LINUX_ASN1_BER_BYTECODE_H */
> >diff --git a/include/linux/asn1_decoder.h b/include/linux/asn1_decoder.h
> >new file mode 100644
> >index 000000000000..83f9c6e1e5e9
> >--- /dev/null
> >+++ b/include/linux/asn1_decoder.h
> >@@ -0,0 +1,20 @@
> >+/* SPDX-License-Identifier: GPL-2.0-or-later */
> >+/* ASN.1 decoder
> >+ *
> >+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> >+ * Written by David Howells (dhowells at redhat.com)
> >+ */
> >+
> >+#ifndef _LINUX_ASN1_DECODER_H
> >+#define _LINUX_ASN1_DECODER_H
> >+
> >+#include <linux/asn1.h>
> >+
> >+struct asn1_decoder;
> >+
> >+extern int asn1_ber_decoder(const struct asn1_decoder *decoder,
> >+			    void *context,
> >+			    const unsigned char *data,
> >+			    size_t datalen);
> >+
> >+#endif /* _LINUX_ASN1_DECODER_H */
> >diff --git a/scripts/Makefile b/scripts/Makefile
> >index e7b353f77f43..c4e2ae5db2ea 100644
> >--- a/scripts/Makefile
> >+++ b/scripts/Makefile
> >@@ -5,6 +5,9 @@
> >  # ---------------------------------------------------------------------------
> >
> >  hostprogs-$(CONFIG_BUILD_BIN2C)		+= bin2c
> >+hostprogs-$(CONFIG_BUILD_ASN1)		+= asn1_compiler
> >+
> >+HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
> 
> Which problems did you run into that make you use -idirafter here?

Without this directive, a compiler supersedes *host's* include
over U-Boot's include in matching "linux/", then it will miss out
include/linux/asn1_ber_bytecode.h.

-Takahiro Akashi


> Otherwise
> 
> Reviewed-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
> 
> 
> >
> >  always		:= $(hostprogs-y)
> >
> >diff --git a/scripts/asn1_compiler.c b/scripts/asn1_compiler.c
> >new file mode 100644
> >index 000000000000..adabd4145264
> >--- /dev/null
> >+++ b/scripts/asn1_compiler.c
> >@@ -0,0 +1,1611 @@
> >+// SPDX-License-Identifier: GPL-2.0-or-later
> >+/* Simplified ASN.1 notation parser
> >+ *
> >+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> >+ * Written by David Howells (dhowells at redhat.com)
> >+ */
> >+
> >+#include <stdarg.h>
> >+#include <stdio.h>
> >+#include <stdlib.h>
> >+#include <stdint.h>
> >+#include <stdbool.h>
> >+#include <string.h>
> >+#include <ctype.h>
> >+#include <unistd.h>
> >+#include <fcntl.h>
> >+#include <sys/stat.h>
> >+#include <linux/asn1_ber_bytecode.h>
> >+
> >+enum token_type {
> >+	DIRECTIVE_ABSENT,
> >+	DIRECTIVE_ALL,
> >+	DIRECTIVE_ANY,
> >+	DIRECTIVE_APPLICATION,
> >+	DIRECTIVE_AUTOMATIC,
> >+	DIRECTIVE_BEGIN,
> >+	DIRECTIVE_BIT,
> >+	DIRECTIVE_BMPString,
> >+	DIRECTIVE_BOOLEAN,
> >+	DIRECTIVE_BY,
> >+	DIRECTIVE_CHARACTER,
> >+	DIRECTIVE_CHOICE,
> >+	DIRECTIVE_CLASS,
> >+	DIRECTIVE_COMPONENT,
> >+	DIRECTIVE_COMPONENTS,
> >+	DIRECTIVE_CONSTRAINED,
> >+	DIRECTIVE_CONTAINING,
> >+	DIRECTIVE_DEFAULT,
> >+	DIRECTIVE_DEFINED,
> >+	DIRECTIVE_DEFINITIONS,
> >+	DIRECTIVE_EMBEDDED,
> >+	DIRECTIVE_ENCODED,
> >+	DIRECTIVE_ENCODING_CONTROL,
> >+	DIRECTIVE_END,
> >+	DIRECTIVE_ENUMERATED,
> >+	DIRECTIVE_EXCEPT,
> >+	DIRECTIVE_EXPLICIT,
> >+	DIRECTIVE_EXPORTS,
> >+	DIRECTIVE_EXTENSIBILITY,
> >+	DIRECTIVE_EXTERNAL,
> >+	DIRECTIVE_FALSE,
> >+	DIRECTIVE_FROM,
> >+	DIRECTIVE_GeneralString,
> >+	DIRECTIVE_GeneralizedTime,
> >+	DIRECTIVE_GraphicString,
> >+	DIRECTIVE_IA5String,
> >+	DIRECTIVE_IDENTIFIER,
> >+	DIRECTIVE_IMPLICIT,
> >+	DIRECTIVE_IMPLIED,
> >+	DIRECTIVE_IMPORTS,
> >+	DIRECTIVE_INCLUDES,
> >+	DIRECTIVE_INSTANCE,
> >+	DIRECTIVE_INSTRUCTIONS,
> >+	DIRECTIVE_INTEGER,
> >+	DIRECTIVE_INTERSECTION,
> >+	DIRECTIVE_ISO646String,
> >+	DIRECTIVE_MAX,
> >+	DIRECTIVE_MIN,
> >+	DIRECTIVE_MINUS_INFINITY,
> >+	DIRECTIVE_NULL,
> >+	DIRECTIVE_NumericString,
> >+	DIRECTIVE_OBJECT,
> >+	DIRECTIVE_OCTET,
> >+	DIRECTIVE_OF,
> >+	DIRECTIVE_OPTIONAL,
> >+	DIRECTIVE_ObjectDescriptor,
> >+	DIRECTIVE_PATTERN,
> >+	DIRECTIVE_PDV,
> >+	DIRECTIVE_PLUS_INFINITY,
> >+	DIRECTIVE_PRESENT,
> >+	DIRECTIVE_PRIVATE,
> >+	DIRECTIVE_PrintableString,
> >+	DIRECTIVE_REAL,
> >+	DIRECTIVE_RELATIVE_OID,
> >+	DIRECTIVE_SEQUENCE,
> >+	DIRECTIVE_SET,
> >+	DIRECTIVE_SIZE,
> >+	DIRECTIVE_STRING,
> >+	DIRECTIVE_SYNTAX,
> >+	DIRECTIVE_T61String,
> >+	DIRECTIVE_TAGS,
> >+	DIRECTIVE_TRUE,
> >+	DIRECTIVE_TeletexString,
> >+	DIRECTIVE_UNION,
> >+	DIRECTIVE_UNIQUE,
> >+	DIRECTIVE_UNIVERSAL,
> >+	DIRECTIVE_UTCTime,
> >+	DIRECTIVE_UTF8String,
> >+	DIRECTIVE_UniversalString,
> >+	DIRECTIVE_VideotexString,
> >+	DIRECTIVE_VisibleString,
> >+	DIRECTIVE_WITH,
> >+	NR__DIRECTIVES,
> >+	TOKEN_ASSIGNMENT = NR__DIRECTIVES,
> >+	TOKEN_OPEN_CURLY,
> >+	TOKEN_CLOSE_CURLY,
> >+	TOKEN_OPEN_SQUARE,
> >+	TOKEN_CLOSE_SQUARE,
> >+	TOKEN_OPEN_ACTION,
> >+	TOKEN_CLOSE_ACTION,
> >+	TOKEN_COMMA,
> >+	TOKEN_NUMBER,
> >+	TOKEN_TYPE_NAME,
> >+	TOKEN_ELEMENT_NAME,
> >+	NR__TOKENS
> >+};
> >+
> >+static const unsigned char token_to_tag[NR__TOKENS] = {
> >+	/* EOC goes first */
> >+	[DIRECTIVE_BOOLEAN]		= ASN1_BOOL,
> >+	[DIRECTIVE_INTEGER]		= ASN1_INT,
> >+	[DIRECTIVE_BIT]			= ASN1_BTS,
> >+	[DIRECTIVE_OCTET]		= ASN1_OTS,
> >+	[DIRECTIVE_NULL]		= ASN1_NULL,
> >+	[DIRECTIVE_OBJECT]		= ASN1_OID,
> >+	[DIRECTIVE_ObjectDescriptor]	= ASN1_ODE,
> >+	[DIRECTIVE_EXTERNAL]		= ASN1_EXT,
> >+	[DIRECTIVE_REAL]		= ASN1_REAL,
> >+	[DIRECTIVE_ENUMERATED]		= ASN1_ENUM,
> >+	[DIRECTIVE_EMBEDDED]		= 0,
> >+	[DIRECTIVE_UTF8String]		= ASN1_UTF8STR,
> >+	[DIRECTIVE_RELATIVE_OID]	= ASN1_RELOID,
> >+	/* 14 */
> >+	/* 15 */
> >+	[DIRECTIVE_SEQUENCE]		= ASN1_SEQ,
> >+	[DIRECTIVE_SET]			= ASN1_SET,
> >+	[DIRECTIVE_NumericString]	= ASN1_NUMSTR,
> >+	[DIRECTIVE_PrintableString]	= ASN1_PRNSTR,
> >+	[DIRECTIVE_T61String]		= ASN1_TEXSTR,
> >+	[DIRECTIVE_TeletexString]	= ASN1_TEXSTR,
> >+	[DIRECTIVE_VideotexString]	= ASN1_VIDSTR,
> >+	[DIRECTIVE_IA5String]		= ASN1_IA5STR,
> >+	[DIRECTIVE_UTCTime]		= ASN1_UNITIM,
> >+	[DIRECTIVE_GeneralizedTime]	= ASN1_GENTIM,
> >+	[DIRECTIVE_GraphicString]	= ASN1_GRASTR,
> >+	[DIRECTIVE_VisibleString]	= ASN1_VISSTR,
> >+	[DIRECTIVE_GeneralString]	= ASN1_GENSTR,
> >+	[DIRECTIVE_UniversalString]	= ASN1_UNITIM,
> >+	[DIRECTIVE_CHARACTER]		= ASN1_CHRSTR,
> >+	[DIRECTIVE_BMPString]		= ASN1_BMPSTR,
> >+};
> >+
> >+static const char asn1_classes[4][5] = {
> >+	[ASN1_UNIV]	= "UNIV",
> >+	[ASN1_APPL]	= "APPL",
> >+	[ASN1_CONT]	= "CONT",
> >+	[ASN1_PRIV]	= "PRIV"
> >+};
> >+
> >+static const char asn1_methods[2][5] = {
> >+	[ASN1_UNIV]	= "PRIM",
> >+	[ASN1_APPL]	= "CONS"
> >+};
> >+
> >+static const char *const asn1_universal_tags[32] = {
> >+	"EOC",
> >+	"BOOL",
> >+	"INT",
> >+	"BTS",
> >+	"OTS",
> >+	"NULL",
> >+	"OID",
> >+	"ODE",
> >+	"EXT",
> >+	"REAL",
> >+	"ENUM",
> >+	"EPDV",
> >+	"UTF8STR",
> >+	"RELOID",
> >+	NULL,		/* 14 */
> >+	NULL,		/* 15 */
> >+	"SEQ",
> >+	"SET",
> >+	"NUMSTR",
> >+	"PRNSTR",
> >+	"TEXSTR",
> >+	"VIDSTR",
> >+	"IA5STR",
> >+	"UNITIM",
> >+	"GENTIM",
> >+	"GRASTR",
> >+	"VISSTR",
> >+	"GENSTR",
> >+	"UNISTR",
> >+	"CHRSTR",
> >+	"BMPSTR",
> >+	NULL		/* 31 */
> >+};
> >+
> >+static const char *filename;
> >+static const char *grammar_name;
> >+static const char *outputname;
> >+static const char *headername;
> >+
> >+static const char *const directives[NR__DIRECTIVES] = {
> >+#define _(X) [DIRECTIVE_##X] = #X
> >+	_(ABSENT),
> >+	_(ALL),
> >+	_(ANY),
> >+	_(APPLICATION),
> >+	_(AUTOMATIC),
> >+	_(BEGIN),
> >+	_(BIT),
> >+	_(BMPString),
> >+	_(BOOLEAN),
> >+	_(BY),
> >+	_(CHARACTER),
> >+	_(CHOICE),
> >+	_(CLASS),
> >+	_(COMPONENT),
> >+	_(COMPONENTS),
> >+	_(CONSTRAINED),
> >+	_(CONTAINING),
> >+	_(DEFAULT),
> >+	_(DEFINED),
> >+	_(DEFINITIONS),
> >+	_(EMBEDDED),
> >+	_(ENCODED),
> >+	[DIRECTIVE_ENCODING_CONTROL] = "ENCODING-CONTROL",
> >+	_(END),
> >+	_(ENUMERATED),
> >+	_(EXCEPT),
> >+	_(EXPLICIT),
> >+	_(EXPORTS),
> >+	_(EXTENSIBILITY),
> >+	_(EXTERNAL),
> >+	_(FALSE),
> >+	_(FROM),
> >+	_(GeneralString),
> >+	_(GeneralizedTime),
> >+	_(GraphicString),
> >+	_(IA5String),
> >+	_(IDENTIFIER),
> >+	_(IMPLICIT),
> >+	_(IMPLIED),
> >+	_(IMPORTS),
> >+	_(INCLUDES),
> >+	_(INSTANCE),
> >+	_(INSTRUCTIONS),
> >+	_(INTEGER),
> >+	_(INTERSECTION),
> >+	_(ISO646String),
> >+	_(MAX),
> >+	_(MIN),
> >+	[DIRECTIVE_MINUS_INFINITY] = "MINUS-INFINITY",
> >+	[DIRECTIVE_NULL] = "NULL",
> >+	_(NumericString),
> >+	_(OBJECT),
> >+	_(OCTET),
> >+	_(OF),
> >+	_(OPTIONAL),
> >+	_(ObjectDescriptor),
> >+	_(PATTERN),
> >+	_(PDV),
> >+	[DIRECTIVE_PLUS_INFINITY] = "PLUS-INFINITY",
> >+	_(PRESENT),
> >+	_(PRIVATE),
> >+	_(PrintableString),
> >+	_(REAL),
> >+	[DIRECTIVE_RELATIVE_OID] = "RELATIVE-OID",
> >+	_(SEQUENCE),
> >+	_(SET),
> >+	_(SIZE),
> >+	_(STRING),
> >+	_(SYNTAX),
> >+	_(T61String),
> >+	_(TAGS),
> >+	_(TRUE),
> >+	_(TeletexString),
> >+	_(UNION),
> >+	_(UNIQUE),
> >+	_(UNIVERSAL),
> >+	_(UTCTime),
> >+	_(UTF8String),
> >+	_(UniversalString),
> >+	_(VideotexString),
> >+	_(VisibleString),
> >+	_(WITH)
> >+};
> >+
> >+struct action {
> >+	struct action	*next;
> >+	char		*name;
> >+	unsigned char	index;
> >+};
> >+
> >+static struct action *action_list;
> >+static unsigned nr_actions;
> >+
> >+struct token {
> >+	unsigned short	line;
> >+	enum token_type	token_type : 8;
> >+	unsigned char	size;
> >+	struct action	*action;
> >+	char		*content;
> >+	struct type	*type;
> >+};
> >+
> >+static struct token *token_list;
> >+static unsigned nr_tokens;
> >+static bool verbose_opt;
> >+static bool debug_opt;
> >+
> >+#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0)
> >+#define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0)
> >+
> >+static int directive_compare(const void *_key, const void *_pdir)
> >+{
> >+	const struct token *token = _key;
> >+	const char *const *pdir = _pdir, *dir = *pdir;
> >+	size_t dlen, clen;
> >+	int val;
> >+
> >+	dlen = strlen(dir);
> >+	clen = (dlen < token->size) ? dlen : token->size;
> >+
> >+	//debug("cmp(%s,%s) = ", token->content, dir);
> >+
> >+	val = memcmp(token->content, dir, clen);
> >+	if (val != 0) {
> >+		//debug("%d [cmp]\n", val);
> >+		return val;
> >+	}
> >+
> >+	if (dlen == token->size) {
> >+		//debug("0\n");
> >+		return 0;
> >+	}
> >+	//debug("%d\n", (int)dlen - (int)token->size);
> >+	return dlen - token->size; /* shorter -> negative */
> >+}
> >+
> >+/*
> >+ * Tokenise an ASN.1 grammar
> >+ */
> >+static void tokenise(char *buffer, char *end)
> >+{
> >+	struct token *tokens;
> >+	char *line, *nl, *start, *p, *q;
> >+	unsigned tix, lineno;
> >+
> >+	/* Assume we're going to have half as many tokens as we have
> >+	 * characters
> >+	 */
> >+	token_list = tokens = calloc((end - buffer) / 2, sizeof(struct token));
> >+	if (!tokens) {
> >+		perror(NULL);
> >+		exit(1);
> >+	}
> >+	tix = 0;
> >+
> >+	lineno = 0;
> >+	while (buffer < end) {
> >+		/* First of all, break out a line */
> >+		lineno++;
> >+		line = buffer;
> >+		nl = memchr(line, '\n', end - buffer);
> >+		if (!nl) {
> >+			buffer = nl = end;
> >+		} else {
> >+			buffer = nl + 1;
> >+			*nl = '\0';
> >+		}
> >+
> >+		/* Remove "--" comments */
> >+		p = line;
> >+	next_comment:
> >+		while ((p = memchr(p, '-', nl - p))) {
> >+			if (p[1] == '-') {
> >+				/* Found a comment; see if there's a terminator */
> >+				q = p + 2;
> >+				while ((q = memchr(q, '-', nl - q))) {
> >+					if (q[1] == '-') {
> >+						/* There is - excise the comment */
> >+						q += 2;
> >+						memmove(p, q, nl - q);
> >+						goto next_comment;
> >+					}
> >+					q++;
> >+				}
> >+				*p = '\0';
> >+				nl = p;
> >+				break;
> >+			} else {
> >+				p++;
> >+			}
> >+		}
> >+
> >+		p = line;
> >+		while (p < nl) {
> >+			/* Skip white space */
> >+			while (p < nl && isspace(*p))
> >+				*(p++) = 0;
> >+			if (p >= nl)
> >+				break;
> >+
> >+			tokens[tix].line = lineno;
> >+			start = p;
> >+
> >+			/* Handle string tokens */
> >+			if (isalpha(*p)) {
> >+				const char **dir;
> >+
> >+				/* Can be a directive, type name or element
> >+				 * name.  Find the end of the name.
> >+				 */
> >+				q = p + 1;
> >+				while (q < nl && (isalnum(*q) || *q == '-' || *q == '_'))
> >+					q++;
> >+				tokens[tix].size = q - p;
> >+				p = q;
> >+
> >+				tokens[tix].content = malloc(tokens[tix].size + 1);
> >+				if (!tokens[tix].content) {
> >+					perror(NULL);
> >+					exit(1);
> >+				}
> >+				memcpy(tokens[tix].content, start, tokens[tix].size);
> >+				tokens[tix].content[tokens[tix].size] = 0;
> >+
> >+				/* If it begins with a lowercase letter then
> >+				 * it's an element name
> >+				 */
> >+				if (islower(tokens[tix].content[0])) {
> >+					tokens[tix++].token_type = TOKEN_ELEMENT_NAME;
> >+					continue;
> >+				}
> >+
> >+				/* Otherwise we need to search the directive
> >+				 * table
> >+				 */
> >+				dir = bsearch(&tokens[tix], directives,
> >+					      sizeof(directives) / sizeof(directives[1]),
> >+					      sizeof(directives[1]),
> >+					      directive_compare);
> >+				if (dir) {
> >+					tokens[tix++].token_type = dir - directives;
> >+					continue;
> >+				}
> >+
> >+				tokens[tix++].token_type = TOKEN_TYPE_NAME;
> >+				continue;
> >+			}
> >+
> >+			/* Handle numbers */
> >+			if (isdigit(*p)) {
> >+				/* Find the end of the number */
> >+				q = p + 1;
> >+				while (q < nl && (isdigit(*q)))
> >+					q++;
> >+				tokens[tix].size = q - p;
> >+				p = q;
> >+				tokens[tix].content = malloc(tokens[tix].size + 1);
> >+				if (!tokens[tix].content) {
> >+					perror(NULL);
> >+					exit(1);
> >+				}
> >+				memcpy(tokens[tix].content, start, tokens[tix].size);
> >+				tokens[tix].content[tokens[tix].size] = 0;
> >+				tokens[tix++].token_type = TOKEN_NUMBER;
> >+				continue;
> >+			}
> >+
> >+			if (nl - p >= 3) {
> >+				if (memcmp(p, "::=", 3) == 0) {
> >+					p += 3;
> >+					tokens[tix].size = 3;
> >+					tokens[tix].content = "::=";
> >+					tokens[tix++].token_type = TOKEN_ASSIGNMENT;
> >+					continue;
> >+				}
> >+			}
> >+
> >+			if (nl - p >= 2) {
> >+				if (memcmp(p, "({", 2) == 0) {
> >+					p += 2;
> >+					tokens[tix].size = 2;
> >+					tokens[tix].content = "({";
> >+					tokens[tix++].token_type = TOKEN_OPEN_ACTION;
> >+					continue;
> >+				}
> >+				if (memcmp(p, "})", 2) == 0) {
> >+					p += 2;
> >+					tokens[tix].size = 2;
> >+					tokens[tix].content = "})";
> >+					tokens[tix++].token_type = TOKEN_CLOSE_ACTION;
> >+					continue;
> >+				}
> >+			}
> >+
> >+			if (nl - p >= 1) {
> >+				tokens[tix].size = 1;
> >+				switch (*p) {
> >+				case '{':
> >+					p += 1;
> >+					tokens[tix].content = "{";
> >+					tokens[tix++].token_type = TOKEN_OPEN_CURLY;
> >+					continue;
> >+				case '}':
> >+					p += 1;
> >+					tokens[tix].content = "}";
> >+					tokens[tix++].token_type = TOKEN_CLOSE_CURLY;
> >+					continue;
> >+				case '[':
> >+					p += 1;
> >+					tokens[tix].content = "[";
> >+					tokens[tix++].token_type = TOKEN_OPEN_SQUARE;
> >+					continue;
> >+				case ']':
> >+					p += 1;
> >+					tokens[tix].content = "]";
> >+					tokens[tix++].token_type = TOKEN_CLOSE_SQUARE;
> >+					continue;
> >+				case ',':
> >+					p += 1;
> >+					tokens[tix].content = ",";
> >+					tokens[tix++].token_type = TOKEN_COMMA;
> >+					continue;
> >+				default:
> >+					break;
> >+				}
> >+			}
> >+
> >+			fprintf(stderr, "%s:%u: Unknown character in grammar: '%c'\n",
> >+				filename, lineno, *p);
> >+			exit(1);
> >+		}
> >+	}
> >+
> >+	nr_tokens = tix;
> >+	verbose("Extracted %u tokens\n", nr_tokens);
> >+
> >+#if 0
> >+	{
> >+		int n;
> >+		for (n = 0; n < nr_tokens; n++)
> >+			debug("Token %3u: '%s'\n", n, token_list[n].content);
> >+	}
> >+#endif
> >+}
> >+
> >+static void build_type_list(void);
> >+static void parse(void);
> >+static void dump_elements(void);
> >+static void render(FILE *out, FILE *hdr);
> >+
> >+/*
> >+ *
> >+ */
> >+int main(int argc, char **argv)
> >+{
> >+	struct stat st;
> >+	ssize_t readlen;
> >+	FILE *out, *hdr;
> >+	char *buffer, *p;
> >+	char *kbuild_verbose;
> >+	int fd;
> >+
> >+	kbuild_verbose = getenv("KBUILD_VERBOSE");
> >+	if (kbuild_verbose)
> >+		verbose_opt = atoi(kbuild_verbose);
> >+
> >+	while (argc > 4) {
> >+		if (strcmp(argv[1], "-v") == 0)
> >+			verbose_opt = true;
> >+		else if (strcmp(argv[1], "-d") == 0)
> >+			debug_opt = true;
> >+		else
> >+			break;
> >+		memmove(&argv[1], &argv[2], (argc - 2) * sizeof(char *));
> >+		argc--;
> >+	}
> >+
> >+	if (argc != 4) {
> >+		fprintf(stderr, "Format: %s [-v] [-d] <grammar-file> <c-file> <hdr-file>\n",
> >+			argv[0]);
> >+		exit(2);
> >+	}
> >+
> >+	filename = argv[1];
> >+	outputname = argv[2];
> >+	headername = argv[3];
> >+
> >+	fd = open(filename, O_RDONLY);
> >+	if (fd < 0) {
> >+		perror(filename);
> >+		exit(1);
> >+	}
> >+
> >+	if (fstat(fd, &st) < 0) {
> >+		perror(filename);
> >+		exit(1);
> >+	}
> >+
> >+	if (!(buffer = malloc(st.st_size + 1))) {
> >+		perror(NULL);
> >+		exit(1);
> >+	}
> >+
> >+	if ((readlen = read(fd, buffer, st.st_size)) < 0) {
> >+		perror(filename);
> >+		exit(1);
> >+	}
> >+
> >+	if (close(fd) < 0) {
> >+		perror(filename);
> >+		exit(1);
> >+	}
> >+
> >+	if (readlen != st.st_size) {
> >+		fprintf(stderr, "%s: Short read\n", filename);
> >+		exit(1);
> >+	}
> >+
> >+	p = strrchr(argv[1], '/');
> >+	p = p ? p + 1 : argv[1];
> >+	grammar_name = strdup(p);
> >+	if (!p) {
> >+		perror(NULL);
> >+		exit(1);
> >+	}
> >+	p = strchr(grammar_name, '.');
> >+	if (p)
> >+		*p = '\0';
> >+
> >+	buffer[readlen] = 0;
> >+	tokenise(buffer, buffer + readlen);
> >+	build_type_list();
> >+	parse();
> >+	dump_elements();
> >+
> >+	out = fopen(outputname, "w");
> >+	if (!out) {
> >+		perror(outputname);
> >+		exit(1);
> >+	}
> >+
> >+	hdr = fopen(headername, "w");
> >+	if (!hdr) {
> >+		perror(headername);
> >+		exit(1);
> >+	}
> >+
> >+	render(out, hdr);
> >+
> >+	if (fclose(out) < 0) {
> >+		perror(outputname);
> >+		exit(1);
> >+	}
> >+
> >+	if (fclose(hdr) < 0) {
> >+		perror(headername);
> >+		exit(1);
> >+	}
> >+
> >+	return 0;
> >+}
> >+
> >+enum compound {
> >+	NOT_COMPOUND,
> >+	SET,
> >+	SET_OF,
> >+	SEQUENCE,
> >+	SEQUENCE_OF,
> >+	CHOICE,
> >+	ANY,
> >+	TYPE_REF,
> >+	TAG_OVERRIDE
> >+};
> >+
> >+struct element {
> >+	struct type	*type_def;
> >+	struct token	*name;
> >+	struct token	*type;
> >+	struct action	*action;
> >+	struct element	*children;
> >+	struct element	*next;
> >+	struct element	*render_next;
> >+	struct element	*list_next;
> >+	uint8_t		n_elements;
> >+	enum compound	compound : 8;
> >+	enum asn1_class	class : 8;
> >+	enum asn1_method method : 8;
> >+	uint8_t		tag;
> >+	unsigned	entry_index;
> >+	unsigned	flags;
> >+#define ELEMENT_IMPLICIT	0x0001
> >+#define ELEMENT_EXPLICIT	0x0002
> >+#define ELEMENT_TAG_SPECIFIED	0x0004
> >+#define ELEMENT_RENDERED	0x0008
> >+#define ELEMENT_SKIPPABLE	0x0010
> >+#define ELEMENT_CONDITIONAL	0x0020
> >+};
> >+
> >+struct type {
> >+	struct token	*name;
> >+	struct token	*def;
> >+	struct element	*element;
> >+	unsigned	ref_count;
> >+	unsigned	flags;
> >+#define TYPE_STOP_MARKER	0x0001
> >+#define TYPE_BEGIN		0x0002
> >+};
> >+
> >+static struct type *type_list;
> >+static struct type **type_index;
> >+static unsigned nr_types;
> >+
> >+static int type_index_compare(const void *_a, const void *_b)
> >+{
> >+	const struct type *const *a = _a, *const *b = _b;
> >+
> >+	if ((*a)->name->size != (*b)->name->size)
> >+		return (*a)->name->size - (*b)->name->size;
> >+	else
> >+		return memcmp((*a)->name->content, (*b)->name->content,
> >+			      (*a)->name->size);
> >+}
> >+
> >+static int type_finder(const void *_key, const void *_ti)
> >+{
> >+	const struct token *token = _key;
> >+	const struct type *const *ti = _ti;
> >+	const struct type *type = *ti;
> >+
> >+	if (token->size != type->name->size)
> >+		return token->size - type->name->size;
> >+	else
> >+		return memcmp(token->content, type->name->content,
> >+			      token->size);
> >+}
> >+
> >+/*
> >+ * Build up a list of types and a sorted index to that list.
> >+ */
> >+static void build_type_list(void)
> >+{
> >+	struct type *types;
> >+	unsigned nr, t, n;
> >+
> >+	nr = 0;
> >+	for (n = 0; n < nr_tokens - 1; n++)
> >+		if (token_list[n + 0].token_type == TOKEN_TYPE_NAME &&
> >+		    token_list[n + 1].token_type == TOKEN_ASSIGNMENT)
> >+			nr++;
> >+
> >+	if (nr == 0) {
> >+		fprintf(stderr, "%s: No defined types\n", filename);
> >+		exit(1);
> >+	}
> >+
> >+	nr_types = nr;
> >+	types = type_list = calloc(nr + 1, sizeof(type_list[0]));
> >+	if (!type_list) {
> >+		perror(NULL);
> >+		exit(1);
> >+	}
> >+	type_index = calloc(nr, sizeof(type_index[0]));
> >+	if (!type_index) {
> >+		perror(NULL);
> >+		exit(1);
> >+	}
> >+
> >+	t = 0;
> >+	types[t].flags |= TYPE_BEGIN;
> >+	for (n = 0; n < nr_tokens - 1; n++) {
> >+		if (token_list[n + 0].token_type == TOKEN_TYPE_NAME &&
> >+		    token_list[n + 1].token_type == TOKEN_ASSIGNMENT) {
> >+			types[t].name = &token_list[n];
> >+			type_index[t] = &types[t];
> >+			t++;
> >+		}
> >+	}
> >+	types[t].name = &token_list[n + 1];
> >+	types[t].flags |= TYPE_STOP_MARKER;
> >+
> >+	qsort(type_index, nr, sizeof(type_index[0]), type_index_compare);
> >+
> >+	verbose("Extracted %u types\n", nr_types);
> >+#if 0
> >+	for (n = 0; n < nr_types; n++) {
> >+		struct type *type = type_index[n];
> >+		debug("- %*.*s\n", type->name->content);
> >+	}
> >+#endif
> >+}
> >+
> >+static struct element *parse_type(struct token **_cursor, struct token *stop,
> >+				  struct token *name);
> >+
> >+/*
> >+ * Parse the token stream
> >+ */
> >+static void parse(void)
> >+{
> >+	struct token *cursor;
> >+	struct type *type;
> >+
> >+	/* Parse one type definition statement at a time */
> >+	type = type_list;
> >+	do {
> >+		cursor = type->name;
> >+
> >+		if (cursor[0].token_type != TOKEN_TYPE_NAME ||
> >+		    cursor[1].token_type != TOKEN_ASSIGNMENT)
> >+			abort();
> >+		cursor += 2;
> >+
> >+		type->element = parse_type(&cursor, type[1].name, NULL);
> >+		type->element->type_def = type;
> >+
> >+		if (cursor != type[1].name) {
> >+			fprintf(stderr, "%s:%d: Parse error at token '%s'\n",
> >+				filename, cursor->line, cursor->content);
> >+			exit(1);
> >+		}
> >+
> >+	} while (type++, !(type->flags & TYPE_STOP_MARKER));
> >+
> >+	verbose("Extracted %u actions\n", nr_actions);
> >+}
> >+
> >+static struct element *element_list;
> >+
> >+static struct element *alloc_elem(struct token *type)
> >+{
> >+	struct element *e = calloc(1, sizeof(*e));
> >+	if (!e) {
> >+		perror(NULL);
> >+		exit(1);
> >+	}
> >+	e->list_next = element_list;
> >+	element_list = e;
> >+	return e;
> >+}
> >+
> >+static struct element *parse_compound(struct token **_cursor, struct token *end,
> >+				      int alternates);
> >+
> >+/*
> >+ * Parse one type definition statement
> >+ */
> >+static struct element *parse_type(struct token **_cursor, struct token *end,
> >+				  struct token *name)
> >+{
> >+	struct element *top, *element;
> >+	struct action *action, **ppaction;
> >+	struct token *cursor = *_cursor;
> >+	struct type **ref;
> >+	char *p;
> >+	int labelled = 0, implicit = 0;
> >+
> >+	top = element = alloc_elem(cursor);
> >+	element->class = ASN1_UNIV;
> >+	element->method = ASN1_PRIM;
> >+	element->tag = token_to_tag[cursor->token_type];
> >+	element->name = name;
> >+
> >+	/* Extract the tag value if one given */
> >+	if (cursor->token_type == TOKEN_OPEN_SQUARE) {
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		switch (cursor->token_type) {
> >+		case DIRECTIVE_UNIVERSAL:
> >+			element->class = ASN1_UNIV;
> >+			cursor++;
> >+			break;
> >+		case DIRECTIVE_APPLICATION:
> >+			element->class = ASN1_APPL;
> >+			cursor++;
> >+			break;
> >+		case TOKEN_NUMBER:
> >+			element->class = ASN1_CONT;
> >+			break;
> >+		case DIRECTIVE_PRIVATE:
> >+			element->class = ASN1_PRIV;
> >+			cursor++;
> >+			break;
> >+		default:
> >+			fprintf(stderr, "%s:%d: Unrecognised tag class token '%s'\n",
> >+				filename, cursor->line, cursor->content);
> >+			exit(1);
> >+		}
> >+
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type != TOKEN_NUMBER) {
> >+			fprintf(stderr, "%s:%d: Missing tag number '%s'\n",
> >+				filename, cursor->line, cursor->content);
> >+			exit(1);
> >+		}
> >+
> >+		element->tag &= ~0x1f;
> >+		element->tag |= strtoul(cursor->content, &p, 10);
> >+		element->flags |= ELEMENT_TAG_SPECIFIED;
> >+		if (p - cursor->content != cursor->size)
> >+			abort();
> >+		cursor++;
> >+
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type != TOKEN_CLOSE_SQUARE) {
> >+			fprintf(stderr, "%s:%d: Missing closing square bracket '%s'\n",
> >+				filename, cursor->line, cursor->content);
> >+			exit(1);
> >+		}
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		labelled = 1;
> >+	}
> >+
> >+	/* Handle implicit and explicit markers */
> >+	if (cursor->token_type == DIRECTIVE_IMPLICIT) {
> >+		element->flags |= ELEMENT_IMPLICIT;
> >+		implicit = 1;
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+	} else if (cursor->token_type == DIRECTIVE_EXPLICIT) {
> >+		element->flags |= ELEMENT_EXPLICIT;
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+	}
> >+
> >+	if (labelled) {
> >+		if (!implicit)
> >+			element->method |= ASN1_CONS;
> >+		element->compound = implicit ? TAG_OVERRIDE : SEQUENCE;
> >+		element->children = alloc_elem(cursor);
> >+		element = element->children;
> >+		element->class = ASN1_UNIV;
> >+		element->method = ASN1_PRIM;
> >+		element->tag = token_to_tag[cursor->token_type];
> >+		element->name = name;
> >+	}
> >+
> >+	/* Extract the type we're expecting here */
> >+	element->type = cursor;
> >+	switch (cursor->token_type) {
> >+	case DIRECTIVE_ANY:
> >+		element->compound = ANY;
> >+		cursor++;
> >+		break;
> >+
> >+	case DIRECTIVE_NULL:
> >+	case DIRECTIVE_BOOLEAN:
> >+	case DIRECTIVE_ENUMERATED:
> >+	case DIRECTIVE_INTEGER:
> >+		element->compound = NOT_COMPOUND;
> >+		cursor++;
> >+		break;
> >+
> >+	case DIRECTIVE_EXTERNAL:
> >+		element->method = ASN1_CONS;
> >+
> >+	case DIRECTIVE_BMPString:
> >+	case DIRECTIVE_GeneralString:
> >+	case DIRECTIVE_GraphicString:
> >+	case DIRECTIVE_IA5String:
> >+	case DIRECTIVE_ISO646String:
> >+	case DIRECTIVE_NumericString:
> >+	case DIRECTIVE_PrintableString:
> >+	case DIRECTIVE_T61String:
> >+	case DIRECTIVE_TeletexString:
> >+	case DIRECTIVE_UniversalString:
> >+	case DIRECTIVE_UTF8String:
> >+	case DIRECTIVE_VideotexString:
> >+	case DIRECTIVE_VisibleString:
> >+	case DIRECTIVE_ObjectDescriptor:
> >+	case DIRECTIVE_GeneralizedTime:
> >+	case DIRECTIVE_UTCTime:
> >+		element->compound = NOT_COMPOUND;
> >+		cursor++;
> >+		break;
> >+
> >+	case DIRECTIVE_BIT:
> >+	case DIRECTIVE_OCTET:
> >+		element->compound = NOT_COMPOUND;
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type != DIRECTIVE_STRING)
> >+			goto parse_error;
> >+		cursor++;
> >+		break;
> >+
> >+	case DIRECTIVE_OBJECT:
> >+		element->compound = NOT_COMPOUND;
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type != DIRECTIVE_IDENTIFIER)
> >+			goto parse_error;
> >+		cursor++;
> >+		break;
> >+
> >+	case TOKEN_TYPE_NAME:
> >+		element->compound = TYPE_REF;
> >+		ref = bsearch(cursor, type_index, nr_types, sizeof(type_index[0]),
> >+			      type_finder);
> >+		if (!ref) {
> >+			fprintf(stderr, "%s:%d: Type '%s' undefined\n",
> >+				filename, cursor->line, cursor->content);
> >+			exit(1);
> >+		}
> >+		cursor->type = *ref;
> >+		(*ref)->ref_count++;
> >+		cursor++;
> >+		break;
> >+
> >+	case DIRECTIVE_CHOICE:
> >+		element->compound = CHOICE;
> >+		cursor++;
> >+		element->children = parse_compound(&cursor, end, 1);
> >+		break;
> >+
> >+	case DIRECTIVE_SEQUENCE:
> >+		element->compound = SEQUENCE;
> >+		element->method = ASN1_CONS;
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type == DIRECTIVE_OF) {
> >+			element->compound = SEQUENCE_OF;
> >+			cursor++;
> >+			if (cursor >= end)
> >+				goto overrun_error;
> >+			element->children = parse_type(&cursor, end, NULL);
> >+		} else {
> >+			element->children = parse_compound(&cursor, end, 0);
> >+		}
> >+		break;
> >+
> >+	case DIRECTIVE_SET:
> >+		element->compound = SET;
> >+		element->method = ASN1_CONS;
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type == DIRECTIVE_OF) {
> >+			element->compound = SET_OF;
> >+			cursor++;
> >+			if (cursor >= end)
> >+				goto parse_error;
> >+			element->children = parse_type(&cursor, end, NULL);
> >+		} else {
> >+			element->children = parse_compound(&cursor, end, 1);
> >+		}
> >+		break;
> >+
> >+	default:
> >+		fprintf(stderr, "%s:%d: Token '%s' does not introduce a type\n",
> >+			filename, cursor->line, cursor->content);
> >+		exit(1);
> >+	}
> >+
> >+	/* Handle elements that are optional */
> >+	if (cursor < end && (cursor->token_type == DIRECTIVE_OPTIONAL ||
> >+			     cursor->token_type == DIRECTIVE_DEFAULT)
> >+	    ) {
> >+		cursor++;
> >+		top->flags |= ELEMENT_SKIPPABLE;
> >+	}
> >+
> >+	if (cursor < end && cursor->token_type == TOKEN_OPEN_ACTION) {
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type != TOKEN_ELEMENT_NAME) {
> >+			fprintf(stderr, "%s:%d: Token '%s' is not an action function name\n",
> >+				filename, cursor->line, cursor->content);
> >+			exit(1);
> >+		}
> >+
> >+		action = malloc(sizeof(struct action));
> >+		if (!action) {
> >+			perror(NULL);
> >+			exit(1);
> >+		}
> >+		action->index = 0;
> >+		action->name = cursor->content;
> >+
> >+		for (ppaction = &action_list;
> >+		     *ppaction;
> >+		     ppaction = &(*ppaction)->next
> >+		     ) {
> >+			int cmp = strcmp(action->name, (*ppaction)->name);
> >+			if (cmp == 0) {
> >+				free(action);
> >+				action = *ppaction;
> >+				goto found;
> >+			}
> >+			if (cmp < 0) {
> >+				action->next = *ppaction;
> >+				*ppaction = action;
> >+				nr_actions++;
> >+				goto found;
> >+			}
> >+		}
> >+		action->next = NULL;
> >+		*ppaction = action;
> >+		nr_actions++;
> >+	found:
> >+
> >+		element->action = action;
> >+		cursor->action = action;
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type != TOKEN_CLOSE_ACTION) {
> >+			fprintf(stderr, "%s:%d: Missing close action, got '%s'\n",
> >+				filename, cursor->line, cursor->content);
> >+			exit(1);
> >+		}
> >+		cursor++;
> >+	}
> >+
> >+	*_cursor = cursor;
> >+	return top;
> >+
> >+parse_error:
> >+	fprintf(stderr, "%s:%d: Unexpected token '%s'\n",
> >+		filename, cursor->line, cursor->content);
> >+	exit(1);
> >+
> >+overrun_error:
> >+	fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
> >+	exit(1);
> >+}
> >+
> >+/*
> >+ * Parse a compound type list
> >+ */
> >+static struct element *parse_compound(struct token **_cursor, struct token *end,
> >+				      int alternates)
> >+{
> >+	struct element *children, **child_p = &children, *element;
> >+	struct token *cursor = *_cursor, *name;
> >+
> >+	if (cursor->token_type != TOKEN_OPEN_CURLY) {
> >+		fprintf(stderr, "%s:%d: Expected compound to start with brace not '%s'\n",
> >+			filename, cursor->line, cursor->content);
> >+		exit(1);
> >+	}
> >+	cursor++;
> >+	if (cursor >= end)
> >+		goto overrun_error;
> >+
> >+	if (cursor->token_type == TOKEN_OPEN_CURLY) {
> >+		fprintf(stderr, "%s:%d: Empty compound\n",
> >+			filename, cursor->line);
> >+		exit(1);
> >+	}
> >+
> >+	for (;;) {
> >+		name = NULL;
> >+		if (cursor->token_type == TOKEN_ELEMENT_NAME) {
> >+			name = cursor;
> >+			cursor++;
> >+			if (cursor >= end)
> >+				goto overrun_error;
> >+		}
> >+
> >+		element = parse_type(&cursor, end, name);
> >+		if (alternates)
> >+			element->flags |= ELEMENT_SKIPPABLE | ELEMENT_CONDITIONAL;
> >+
> >+		*child_p = element;
> >+		child_p = &element->next;
> >+
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+		if (cursor->token_type != TOKEN_COMMA)
> >+			break;
> >+		cursor++;
> >+		if (cursor >= end)
> >+			goto overrun_error;
> >+	}
> >+
> >+	children->flags &= ~ELEMENT_CONDITIONAL;
> >+
> >+	if (cursor->token_type != TOKEN_CLOSE_CURLY) {
> >+		fprintf(stderr, "%s:%d: Expected compound closure, got '%s'\n",
> >+			filename, cursor->line, cursor->content);
> >+		exit(1);
> >+	}
> >+	cursor++;
> >+
> >+	*_cursor = cursor;
> >+	return children;
> >+
> >+overrun_error:
> >+	fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
> >+	exit(1);
> >+}
> >+
> >+static void dump_element(const struct element *e, int level)
> >+{
> >+	const struct element *c;
> >+	const struct type *t = e->type_def;
> >+	const char *name = e->name ? e->name->content : ".";
> >+	const char *tname = t && t->name ? t->name->content : ".";
> >+	char tag[32];
> >+
> >+	if (e->class == 0 && e->method == 0 && e->tag == 0)
> >+		strcpy(tag, "<...>");
> >+	else if (e->class == ASN1_UNIV)
> >+		sprintf(tag, "%s %s %s",
> >+			asn1_classes[e->class],
> >+			asn1_methods[e->method],
> >+			asn1_universal_tags[e->tag]);
> >+	else
> >+		sprintf(tag, "%s %s %u",
> >+			asn1_classes[e->class],
> >+			asn1_methods[e->method],
> >+			e->tag);
> >+
> >+	printf("%c%c%c%c%c %c %*s[*] \e[33m%s\e[m %s %s \e[35m%s\e[m\n",
> >+	       e->flags & ELEMENT_IMPLICIT ? 'I' : '-',
> >+	       e->flags & ELEMENT_EXPLICIT ? 'E' : '-',
> >+	       e->flags & ELEMENT_TAG_SPECIFIED ? 'T' : '-',
> >+	       e->flags & ELEMENT_SKIPPABLE ? 'S' : '-',
> >+	       e->flags & ELEMENT_CONDITIONAL ? 'C' : '-',
> >+	       "-tTqQcaro"[e->compound],
> >+	       level, "",
> >+	       tag,
> >+	       tname,
> >+	       name,
> >+	       e->action ? e->action->name : "");
> >+	if (e->compound == TYPE_REF)
> >+		dump_element(e->type->type->element, level + 3);
> >+	else
> >+		for (c = e->children; c; c = c->next)
> >+			dump_element(c, level + 3);
> >+}
> >+
> >+static void dump_elements(void)
> >+{
> >+	if (debug_opt)
> >+		dump_element(type_list[0].element, 0);
> >+}
> >+
> >+static void render_element(FILE *out, struct element *e, struct element *tag);
> >+static void render_out_of_line_list(FILE *out);
> >+
> >+static int nr_entries;
> >+static int render_depth = 1;
> >+static struct element *render_list, **render_list_p = &render_list;
> >+
> >+__attribute__((format(printf, 2, 3)))
> >+static void render_opcode(FILE *out, const char *fmt, ...)
> >+{
> >+	va_list va;
> >+
> >+	if (out) {
> >+		fprintf(out, "\t[%4d] =%*s", nr_entries, render_depth, "");
> >+		va_start(va, fmt);
> >+		vfprintf(out, fmt, va);
> >+		va_end(va);
> >+	}
> >+	nr_entries++;
> >+}
> >+
> >+__attribute__((format(printf, 2, 3)))
> >+static void render_more(FILE *out, const char *fmt, ...)
> >+{
> >+	va_list va;
> >+
> >+	if (out) {
> >+		va_start(va, fmt);
> >+		vfprintf(out, fmt, va);
> >+		va_end(va);
> >+	}
> >+}
> >+
> >+/*
> >+ * Render the grammar into a state machine definition.
> >+ */
> >+static void render(FILE *out, FILE *hdr)
> >+{
> >+	struct element *e;
> >+	struct action *action;
> >+	struct type *root;
> >+	int index;
> >+
> >+	fprintf(hdr, "/*\n");
> >+	fprintf(hdr, " * Automatically generated by asn1_compiler.  Do not edit\n");
> >+	fprintf(hdr, " *\n");
> >+	fprintf(hdr, " * ASN.1 parser for %s\n", grammar_name);
> >+	fprintf(hdr, " */\n");
> >+	fprintf(hdr, "#include <linux/asn1_decoder.h>\n");
> >+	fprintf(hdr, "\n");
> >+	fprintf(hdr, "extern const struct asn1_decoder %s_decoder;\n", grammar_name);
> >+	if (ferror(hdr)) {
> >+		perror(headername);
> >+		exit(1);
> >+	}
> >+
> >+	fprintf(out, "/*\n");
> >+	fprintf(out, " * Automatically generated by asn1_compiler.  Do not edit\n");
> >+	fprintf(out, " *\n");
> >+	fprintf(out, " * ASN.1 parser for %s\n", grammar_name);
> >+	fprintf(out, " */\n");
> >+	fprintf(out, "#include <linux/asn1_ber_bytecode.h>\n");
> >+	fprintf(out, "#include \"%s.asn1.h\"\n", grammar_name);
> >+	fprintf(out, "\n");
> >+	if (ferror(out)) {
> >+		perror(outputname);
> >+		exit(1);
> >+	}
> >+
> >+	/* Tabulate the action functions we might have to call */
> >+	fprintf(hdr, "\n");
> >+	index = 0;
> >+	for (action = action_list; action; action = action->next) {
> >+		action->index = index++;
> >+		fprintf(hdr,
> >+			"extern int %s(void *, size_t, unsigned char,"
> >+			" const void *, size_t);\n",
> >+			action->name);
> >+	}
> >+	fprintf(hdr, "\n");
> >+
> >+	fprintf(out, "enum %s_actions {\n", grammar_name);
> >+	for (action = action_list; action; action = action->next)
> >+		fprintf(out, "\tACT_%s = %u,\n",
> >+			action->name, action->index);
> >+	fprintf(out, "\tNR__%s_actions = %u\n", grammar_name, nr_actions);
> >+	fprintf(out, "};\n");
> >+
> >+	fprintf(out, "\n");
> >+	fprintf(out, "static const asn1_action_t %s_action_table[NR__%s_actions] = {\n",
> >+		grammar_name, grammar_name);
> >+	for (action = action_list; action; action = action->next)
> >+		fprintf(out, "\t[%4u] = %s,\n", action->index, action->name);
> >+	fprintf(out, "};\n");
> >+
> >+	if (ferror(out)) {
> >+		perror(outputname);
> >+		exit(1);
> >+	}
> >+
> >+	/* We do two passes - the first one calculates all the offsets */
> >+	verbose("Pass 1\n");
> >+	nr_entries = 0;
> >+	root = &type_list[0];
> >+	render_element(NULL, root->element, NULL);
> >+	render_opcode(NULL, "ASN1_OP_COMPLETE,\n");
> >+	render_out_of_line_list(NULL);
> >+
> >+	for (e = element_list; e; e = e->list_next)
> >+		e->flags &= ~ELEMENT_RENDERED;
> >+
> >+	/* And then we actually render */
> >+	verbose("Pass 2\n");
> >+	fprintf(out, "\n");
> >+	fprintf(out, "static const unsigned char %s_machine[] = {\n",
> >+		grammar_name);
> >+
> >+	nr_entries = 0;
> >+	root = &type_list[0];
> >+	render_element(out, root->element, NULL);
> >+	render_opcode(out, "ASN1_OP_COMPLETE,\n");
> >+	render_out_of_line_list(out);
> >+
> >+	fprintf(out, "};\n");
> >+
> >+	fprintf(out, "\n");
> >+	fprintf(out, "const struct asn1_decoder %s_decoder = {\n", grammar_name);
> >+	fprintf(out, "\t.machine = %s_machine,\n", grammar_name);
> >+	fprintf(out, "\t.machlen = sizeof(%s_machine),\n", grammar_name);
> >+	fprintf(out, "\t.actions = %s_action_table,\n", grammar_name);
> >+	fprintf(out, "};\n");
> >+}
> >+
> >+/*
> >+ * Render the out-of-line elements
> >+ */
> >+static void render_out_of_line_list(FILE *out)
> >+{
> >+	struct element *e, *ce;
> >+	const char *act;
> >+	int entry;
> >+
> >+	while ((e = render_list)) {
> >+		render_list = e->render_next;
> >+		if (!render_list)
> >+			render_list_p = &render_list;
> >+
> >+		render_more(out, "\n");
> >+		e->entry_index = entry = nr_entries;
> >+		render_depth++;
> >+		for (ce = e->children; ce; ce = ce->next)
> >+			render_element(out, ce, NULL);
> >+		render_depth--;
> >+
> >+		act = e->action ? "_ACT" : "";
> >+		switch (e->compound) {
> >+		case SEQUENCE:
> >+			render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
> >+			break;
> >+		case SEQUENCE_OF:
> >+			render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act);
> >+			render_opcode(out, "_jump_target(%u),\n", entry);
> >+			break;
> >+		case SET:
> >+			render_opcode(out, "ASN1_OP_END_SET%s,\n", act);
> >+			break;
> >+		case SET_OF:
> >+			render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
> >+			render_opcode(out, "_jump_target(%u),\n", entry);
> >+			break;
> >+		default:
> >+			break;
> >+		}
> >+		if (e->action)
> >+			render_opcode(out, "_action(ACT_%s),\n",
> >+				      e->action->name);
> >+		render_opcode(out, "ASN1_OP_RETURN,\n");
> >+	}
> >+}
> >+
> >+/*
> >+ * Render an element.
> >+ */
> >+static void render_element(FILE *out, struct element *e, struct element *tag)
> >+{
> >+	struct element *ec, *x;
> >+	const char *cond, *act;
> >+	int entry, skippable = 0, outofline = 0;
> >+
> >+	if (e->flags & ELEMENT_SKIPPABLE ||
> >+	    (tag && tag->flags & ELEMENT_SKIPPABLE))
> >+		skippable = 1;
> >+
> >+	if ((e->type_def && e->type_def->ref_count > 1) ||
> >+	    skippable)
> >+		outofline = 1;
> >+
> >+	if (e->type_def && out) {
> >+		render_more(out, "\t// %s\n", e->type_def->name->content);
> >+	}
> >+
> >+	/* Render the operation */
> >+	cond = (e->flags & ELEMENT_CONDITIONAL ||
> >+		(tag && tag->flags & ELEMENT_CONDITIONAL)) ? "COND_" : "";
> >+	act = e->action ? "_ACT" : "";
> >+	switch (e->compound) {
> >+	case ANY:
> >+		render_opcode(out, "ASN1_OP_%sMATCH_ANY%s%s,",
> >+			      cond, act, skippable ? "_OR_SKIP" : "");
> >+		if (e->name)
> >+			render_more(out, "\t\t// %s", e->name->content);
> >+		render_more(out, "\n");
> >+		goto dont_render_tag;
> >+
> >+	case TAG_OVERRIDE:
> >+		render_element(out, e->children, e);
> >+		return;
> >+
> >+	case SEQUENCE:
> >+	case SEQUENCE_OF:
> >+	case SET:
> >+	case SET_OF:
> >+		render_opcode(out, "ASN1_OP_%sMATCH%s%s,",
> >+			      cond,
> >+			      outofline ? "_JUMP" : "",
> >+			      skippable ? "_OR_SKIP" : "");
> >+		break;
> >+
> >+	case CHOICE:
> >+		goto dont_render_tag;
> >+
> >+	case TYPE_REF:
> >+		if (e->class == ASN1_UNIV && e->method == ASN1_PRIM && e->tag == 0)
> >+			goto dont_render_tag;
> >+	default:
> >+		render_opcode(out, "ASN1_OP_%sMATCH%s%s,",
> >+			      cond, act,
> >+			      skippable ? "_OR_SKIP" : "");
> >+		break;
> >+	}
> >+
> >+	x = tag ?: e;
> >+	if (x->name)
> >+		render_more(out, "\t\t// %s", x->name->content);
> >+	render_more(out, "\n");
> >+
> >+	/* Render the tag */
> >+	if (!tag || !(tag->flags & ELEMENT_TAG_SPECIFIED))
> >+		tag = e;
> >+
> >+	if (tag->class == ASN1_UNIV &&
> >+	    tag->tag != 14 &&
> >+	    tag->tag != 15 &&
> >+	    tag->tag != 31)
> >+		render_opcode(out, "_tag(%s, %s, %s),\n",
> >+			      asn1_classes[tag->class],
> >+			      asn1_methods[tag->method | e->method],
> >+			      asn1_universal_tags[tag->tag]);
> >+	else
> >+		render_opcode(out, "_tagn(%s, %s, %2u),\n",
> >+			      asn1_classes[tag->class],
> >+			      asn1_methods[tag->method | e->method],
> >+			      tag->tag);
> >+	tag = NULL;
> >+dont_render_tag:
> >+
> >+	/* Deal with compound types */
> >+	switch (e->compound) {
> >+	case TYPE_REF:
> >+		render_element(out, e->type->type->element, tag);
> >+		if (e->action)
> >+			render_opcode(out, "ASN1_OP_%sACT,\n",
> >+				      skippable ? "MAYBE_" : "");
> >+		break;
> >+
> >+	case SEQUENCE:
> >+		if (outofline) {
> >+			/* Render out-of-line for multiple use or
> >+			 * skipability */
> >+			render_opcode(out, "_jump_target(%u),", e->entry_index);
> >+			if (e->type_def && e->type_def->name)
> >+				render_more(out, "\t\t// --> %s",
> >+					    e->type_def->name->content);
> >+			render_more(out, "\n");
> >+			if (!(e->flags & ELEMENT_RENDERED)) {
> >+				e->flags |= ELEMENT_RENDERED;
> >+				*render_list_p = e;
> >+				render_list_p = &e->render_next;
> >+			}
> >+			return;
> >+		} else {
> >+			/* Render inline for single use */
> >+			render_depth++;
> >+			for (ec = e->children; ec; ec = ec->next)
> >+				render_element(out, ec, NULL);
> >+			render_depth--;
> >+			render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act);
> >+		}
> >+		break;
> >+
> >+	case SEQUENCE_OF:
> >+	case SET_OF:
> >+		if (outofline) {
> >+			/* Render out-of-line for multiple use or
> >+			 * skipability */
> >+			render_opcode(out, "_jump_target(%u),", e->entry_index);
> >+			if (e->type_def && e->type_def->name)
> >+				render_more(out, "\t\t// --> %s",
> >+					    e->type_def->name->content);
> >+			render_more(out, "\n");
> >+			if (!(e->flags & ELEMENT_RENDERED)) {
> >+				e->flags |= ELEMENT_RENDERED;
> >+				*render_list_p = e;
> >+				render_list_p = &e->render_next;
> >+			}
> >+			return;
> >+		} else {
> >+			/* Render inline for single use */
> >+			entry = nr_entries;
> >+			render_depth++;
> >+			render_element(out, e->children, NULL);
> >+			render_depth--;
> >+			if (e->compound == SEQUENCE_OF)
> >+				render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act);
> >+			else
> >+				render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act);
> >+			render_opcode(out, "_jump_target(%u),\n", entry);
> >+		}
> >+		break;
> >+
> >+	case SET:
> >+		/* I can't think of a nice way to do SET support without having
> >+		 * a stack of bitmasks to make sure no element is repeated.
> >+		 * The bitmask has also to be checked that no non-optional
> >+		 * elements are left out whilst not preventing optional
> >+		 * elements from being left out.
> >+		 */
> >+		fprintf(stderr, "The ASN.1 SET type is not currently supported.\n");
> >+		exit(1);
> >+
> >+	case CHOICE:
> >+		for (ec = e->children; ec; ec = ec->next)
> >+			render_element(out, ec, ec);
> >+		if (!skippable)
> >+			render_opcode(out, "ASN1_OP_COND_FAIL,\n");
> >+		if (e->action)
> >+			render_opcode(out, "ASN1_OP_ACT,\n");
> >+		break;
> >+
> >+	default:
> >+		break;
> >+	}
> >+
> >+	if (e->action)
> >+		render_opcode(out, "_action(ACT_%s),\n", e->action->name);
> >+}
> >
> 

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

* [U-Boot] [PATCH v1 05/11] Makefile: add build script for asn1 parsers
  2019-10-12 12:36   ` Heinrich Schuchardt
@ 2019-10-17  6:40     ` AKASHI Takahiro
  0 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-17  6:40 UTC (permalink / raw)
  To: u-boot

On Sat, Oct 12, 2019 at 02:36:48PM +0200, Heinrich Schuchardt wrote:
> On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> >This rule will be used to build x509 and pkcs7 parsers.
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> 
> The ASN1 compiler is generating both *.asn.h and *.asn.c files.
> 
> Then I would expect that these files are deleted by `make mrproper'.
> 
> Please, adjust the clean: target in /Makefile accordingly.

Good catch, thank you.

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >---
> >  scripts/Makefile.build | 6 ++++--
> >  1 file changed, 4 insertions(+), 2 deletions(-)
> >
> >diff --git a/scripts/Makefile.build b/scripts/Makefile.build
> >index f7a041296d3d..9357d310e50b 100644
> >--- a/scripts/Makefile.build
> >+++ b/scripts/Makefile.build
> >@@ -331,7 +331,7 @@ quiet_cmd_asn1_compiler = ASN.1   $@
> >        cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \
> >  				$(subst .h,.c,$@) $(subst .c,.h,$@)
> >
> >-$(obj)/%-asn1.c $(obj)/%-asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
> >+$(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
> >  	$(call cmd,asn1_compiler)
> >
> >  # Build the compiled-in targets
> >@@ -419,9 +419,11 @@ targets += $(multi-used-y) $(multi-used-m)
> >  intermediate_targets = $(foreach sfx, $(2), \
> >  				$(patsubst %$(strip $(1)),%$(sfx), \
> >  					$(filter %$(strip $(1)), $(targets))))
> >+# %.asn1.o <- %.asn1.[ch] <- %.asn1
> >  # %.lex.o <- %.lex.c <- %.l
> >  # %.tab.o <- %.tab.[ch] <- %.y
> >-targets += $(call intermediate_targets, .lex.o, .lex.c) \
> >+targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
> >+	   $(call intermediate_targets, .lex.o, .lex.c) \
> >  	   $(call intermediate_targets, .tab.o, .tab.c .tab.h)
> >
> >  # Descending
> >
> 

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

* [U-Boot] [PATCH v1 06/11] lib: add asn1 decoder
  2019-10-12 12:29   ` Heinrich Schuchardt
@ 2019-10-17  7:02     ` AKASHI Takahiro
  0 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-17  7:02 UTC (permalink / raw)
  To: u-boot

On Sat, Oct 12, 2019 at 02:29:49PM +0200, Heinrich Schuchardt wrote:
> On 10/11/19 9:41 AM, AKASHI Takahiro wrote:
> >Imported from linux kernel v5.3.
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> >  lib/Kconfig        |   6 +
> >  lib/Makefile       |   1 +
> >  lib/asn1_decoder.c | 527 +++++++++++++++++++++++++++++++++++++++++++++
> 
> Except for adjustments of include files lib/asn1_decoder.c is a
> verbatime copy of the Linux kernel file. OK.
> 
> >  3 files changed, 534 insertions(+)
> >  create mode 100644 lib/asn1_decoder.c
> >
> >diff --git a/lib/Kconfig b/lib/Kconfig
> >index 3da45a5ec322..26c94f49ecd2 100644
> >--- a/lib/Kconfig
> >+++ b/lib/Kconfig
> >@@ -521,6 +521,12 @@ config SMBIOS_PRODUCT_NAME
> >
> >  endmenu
> >
> >+config ASN1
> >+	bool
> >+	select BUILD_ASN1
> 
> BUILD_ASN1 does not really fit into cmd/Kconfig.

I just follows the way of BUILD_BIN2C, but

> Should we move it here?

yeah, bin2c is only used under cmd and so moving BUILD_ASN1
to lib/ is fine to me.

> Why do we need two separate flags ASN1 and BUILD_ASN1?

My intension here is that CONFIG_ASN1 is for asn1_decoder.o and
CONFIG_BUILD_ASN1 is for asn1 compiler.
(lib/Makefile is wrong in this sense.)
I believe that having different config's here makes sense
as those two targets are closely related but different objects.

So I will change their names to clarify this.

-Takahiro Akashi

> Best regards
> 
> Heinrich
> 
> >+	help
> >+	  Enable asn1 decoder library.
> >+
> >  source lib/efi/Kconfig
> >  source lib/efi_loader/Kconfig
> >  source lib/optee/Kconfig
> >diff --git a/lib/Makefile b/lib/Makefile
> >index 2fffd68f943c..eb3a675fb8c2 100644
> >--- a/lib/Makefile
> >+++ b/lib/Makefile
> >@@ -17,6 +17,7 @@ obj-$(CONFIG_OF_LIVE) += of_live.o
> >  obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
> >  obj-$(CONFIG_ARCH_AT91) += at91/
> >  obj-$(CONFIG_OPTEE) += optee/
> >+obj-$(CONFIG_BUILD_ASN1) += asn1_decoder.o
> >
> >  obj-$(CONFIG_AES) += aes.o
> >
> >diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c
> >new file mode 100644
> >index 000000000000..db222625dd0f
> >--- /dev/null
> >+++ b/lib/asn1_decoder.c
> >@@ -0,0 +1,527 @@
> >+// SPDX-License-Identifier: GPL-2.0-or-later
> >+/* Decoder for ASN.1 BER/DER/CER encoded bytestream
> >+ *
> >+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
> >+ * Written by David Howells (dhowells at redhat.com)
> >+ */
> >+
> >+#ifdef __UBOOT__
> >+#include <linux/compat.h>
> >+#else
> >+#include <linux/export.h>
> >+#endif
> >+#include <linux/kernel.h>
> >+#include <linux/errno.h>
> >+#ifndef __UBOOT__
> >+#include <linux/module.h>
> >+#endif
> >+#include <linux/asn1_decoder.h>
> >+#include <linux/asn1_ber_bytecode.h>
> >+
> >+static const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
> >+	/*					OPC TAG JMP ACT */
> >+	[ASN1_OP_MATCH]				= 1 + 1,
> >+	[ASN1_OP_MATCH_OR_SKIP]			= 1 + 1,
> >+	[ASN1_OP_MATCH_ACT]			= 1 + 1     + 1,
> >+	[ASN1_OP_MATCH_ACT_OR_SKIP]		= 1 + 1     + 1,
> >+	[ASN1_OP_MATCH_JUMP]			= 1 + 1 + 1,
> >+	[ASN1_OP_MATCH_JUMP_OR_SKIP]		= 1 + 1 + 1,
> >+	[ASN1_OP_MATCH_ANY]			= 1,
> >+	[ASN1_OP_MATCH_ANY_OR_SKIP]		= 1,
> >+	[ASN1_OP_MATCH_ANY_ACT]			= 1         + 1,
> >+	[ASN1_OP_MATCH_ANY_ACT_OR_SKIP]		= 1         + 1,
> >+	[ASN1_OP_COND_MATCH_OR_SKIP]		= 1 + 1,
> >+	[ASN1_OP_COND_MATCH_ACT_OR_SKIP]	= 1 + 1     + 1,
> >+	[ASN1_OP_COND_MATCH_JUMP_OR_SKIP]	= 1 + 1 + 1,
> >+	[ASN1_OP_COND_MATCH_ANY]		= 1,
> >+	[ASN1_OP_COND_MATCH_ANY_OR_SKIP]	= 1,
> >+	[ASN1_OP_COND_MATCH_ANY_ACT]		= 1         + 1,
> >+	[ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP]	= 1         + 1,
> >+	[ASN1_OP_COND_FAIL]			= 1,
> >+	[ASN1_OP_COMPLETE]			= 1,
> >+	[ASN1_OP_ACT]				= 1         + 1,
> >+	[ASN1_OP_MAYBE_ACT]			= 1         + 1,
> >+	[ASN1_OP_RETURN]			= 1,
> >+	[ASN1_OP_END_SEQ]			= 1,
> >+	[ASN1_OP_END_SEQ_OF]			= 1     + 1,
> >+	[ASN1_OP_END_SET]			= 1,
> >+	[ASN1_OP_END_SET_OF]			= 1     + 1,
> >+	[ASN1_OP_END_SEQ_ACT]			= 1         + 1,
> >+	[ASN1_OP_END_SEQ_OF_ACT]		= 1     + 1 + 1,
> >+	[ASN1_OP_END_SET_ACT]			= 1         + 1,
> >+	[ASN1_OP_END_SET_OF_ACT]		= 1     + 1 + 1,
> >+};
> >+
> >+/*
> >+ * Find the length of an indefinite length object
> >+ * @data: The data buffer
> >+ * @datalen: The end of the innermost containing element in the buffer
> >+ * @_dp: The data parse cursor (updated before returning)
> >+ * @_len: Where to return the size of the element.
> >+ * @_errmsg: Where to return a pointer to an error message on error
> >+ */
> >+static int asn1_find_indefinite_length(const unsigned char *data, size_t datalen,
> >+				       size_t *_dp, size_t *_len,
> >+				       const char **_errmsg)
> >+{
> >+	unsigned char tag, tmp;
> >+	size_t dp = *_dp, len, n;
> >+	int indef_level = 1;
> >+
> >+next_tag:
> >+	if (unlikely(datalen - dp < 2)) {
> >+		if (datalen == dp)
> >+			goto missing_eoc;
> >+		goto data_overrun_error;
> >+	}
> >+
> >+	/* Extract a tag from the data */
> >+	tag = data[dp++];
> >+	if (tag == ASN1_EOC) {
> >+		/* It appears to be an EOC. */
> >+		if (data[dp++] != 0)
> >+			goto invalid_eoc;
> >+		if (--indef_level <= 0) {
> >+			*_len = dp - *_dp;
> >+			*_dp = dp;
> >+			return 0;
> >+		}
> >+		goto next_tag;
> >+	}
> >+
> >+	if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) {
> >+		do {
> >+			if (unlikely(datalen - dp < 2))
> >+				goto data_overrun_error;
> >+			tmp = data[dp++];
> >+		} while (tmp & 0x80);
> >+	}
> >+
> >+	/* Extract the length */
> >+	len = data[dp++];
> >+	if (len <= 0x7f)
> >+		goto check_length;
> >+
> >+	if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
> >+		/* Indefinite length */
> >+		if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5))
> >+			goto indefinite_len_primitive;
> >+		indef_level++;
> >+		goto next_tag;
> >+	}
> >+
> >+	n = len - 0x80;
> >+	if (unlikely(n > sizeof(len) - 1))
> >+		goto length_too_long;
> >+	if (unlikely(n > datalen - dp))
> >+		goto data_overrun_error;
> >+	len = 0;
> >+	for (; n > 0; n--) {
> >+		len <<= 8;
> >+		len |= data[dp++];
> >+	}
> >+check_length:
> >+	if (len > datalen - dp)
> >+		goto data_overrun_error;
> >+	dp += len;
> >+	goto next_tag;
> >+
> >+length_too_long:
> >+	*_errmsg = "Unsupported length";
> >+	goto error;
> >+indefinite_len_primitive:
> >+	*_errmsg = "Indefinite len primitive not permitted";
> >+	goto error;
> >+invalid_eoc:
> >+	*_errmsg = "Invalid length EOC";
> >+	goto error;
> >+data_overrun_error:
> >+	*_errmsg = "Data overrun error";
> >+	goto error;
> >+missing_eoc:
> >+	*_errmsg = "Missing EOC in indefinite len cons";
> >+error:
> >+	*_dp = dp;
> >+	return -1;
> >+}
> >+
> >+/**
> >+ * asn1_ber_decoder - Decoder BER/DER/CER ASN.1 according to pattern
> >+ * @decoder: The decoder definition (produced by asn1_compiler)
> >+ * @context: The caller's context (to be passed to the action functions)
> >+ * @data: The encoded data
> >+ * @datalen: The size of the encoded data
> >+ *
> >+ * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern
> >+ * produced by asn1_compiler.  Action functions are called on marked tags to
> >+ * allow the caller to retrieve significant data.
> >+ *
> >+ * LIMITATIONS:
> >+ *
> >+ * To keep down the amount of stack used by this function, the following limits
> >+ * have been imposed:
> >+ *
> >+ *  (1) This won't handle datalen > 65535 without increasing the size of the
> >+ *	cons stack elements and length_too_long checking.
> >+ *
> >+ *  (2) The stack of constructed types is 10 deep.  If the depth of non-leaf
> >+ *	constructed types exceeds this, the decode will fail.
> >+ *
> >+ *  (3) The SET type (not the SET OF type) isn't really supported as tracking
> >+ *	what members of the set have been seen is a pain.
> >+ */
> >+int asn1_ber_decoder(const struct asn1_decoder *decoder,
> >+		     void *context,
> >+		     const unsigned char *data,
> >+		     size_t datalen)
> >+{
> >+	const unsigned char *machine = decoder->machine;
> >+	const asn1_action_t *actions = decoder->actions;
> >+	size_t machlen = decoder->machlen;
> >+	enum asn1_opcode op;
> >+	unsigned char tag = 0, csp = 0, jsp = 0, optag = 0, hdr = 0;
> >+	const char *errmsg;
> >+	size_t pc = 0, dp = 0, tdp = 0, len = 0;
> >+	int ret;
> >+
> >+	unsigned char flags = 0;
> >+#define FLAG_INDEFINITE_LENGTH	0x01
> >+#define FLAG_MATCHED		0x02
> >+#define FLAG_LAST_MATCHED	0x04 /* Last tag matched */
> >+#define FLAG_CONS		0x20 /* Corresponds to CONS bit in the opcode tag
> >+				      * - ie. whether or not we are going to parse
> >+				      *   a compound type.
> >+				      */
> >+
> >+#define NR_CONS_STACK 10
> >+	unsigned short cons_dp_stack[NR_CONS_STACK];
> >+	unsigned short cons_datalen_stack[NR_CONS_STACK];
> >+	unsigned char cons_hdrlen_stack[NR_CONS_STACK];
> >+#define NR_JUMP_STACK 10
> >+	unsigned char jump_stack[NR_JUMP_STACK];
> >+
> >+	if (datalen > 65535)
> >+		return -EMSGSIZE;
> >+
> >+next_op:
> >+	pr_debug("next_op: pc=\e[32m%zu\e[m/%zu dp=\e[33m%zu\e[m/%zu C=%d J=%d\n",
> >+		 pc, machlen, dp, datalen, csp, jsp);
> >+	if (unlikely(pc >= machlen))
> >+		goto machine_overrun_error;
> >+	op = machine[pc];
> >+	if (unlikely(pc + asn1_op_lengths[op] > machlen))
> >+		goto machine_overrun_error;
> >+
> >+	/* If this command is meant to match a tag, then do that before
> >+	 * evaluating the command.
> >+	 */
> >+	if (op <= ASN1_OP__MATCHES_TAG) {
> >+		unsigned char tmp;
> >+
> >+		/* Skip conditional matches if possible */
> >+		if ((op & ASN1_OP_MATCH__COND && flags & FLAG_MATCHED) ||
> >+		    (op & ASN1_OP_MATCH__SKIP && dp == datalen)) {
> >+			flags &= ~FLAG_LAST_MATCHED;
> >+			pc += asn1_op_lengths[op];
> >+			goto next_op;
> >+		}
> >+
> >+		flags = 0;
> >+		hdr = 2;
> >+
> >+		/* Extract a tag from the data */
> >+		if (unlikely(datalen - dp < 2))
> >+			goto data_overrun_error;
> >+		tag = data[dp++];
> >+		if (unlikely((tag & 0x1f) == ASN1_LONG_TAG))
> >+			goto long_tag_not_supported;
> >+
> >+		if (op & ASN1_OP_MATCH__ANY) {
> >+			pr_debug("- any %02x\n", tag);
> >+		} else {
> >+			/* Extract the tag from the machine
> >+			 * - Either CONS or PRIM are permitted in the data if
> >+			 *   CONS is not set in the op stream, otherwise CONS
> >+			 *   is mandatory.
> >+			 */
> >+			optag = machine[pc + 1];
> >+			flags |= optag & FLAG_CONS;
> >+
> >+			/* Determine whether the tag matched */
> >+			tmp = optag ^ tag;
> >+			tmp &= ~(optag & ASN1_CONS_BIT);
> >+			pr_debug("- match? %02x %02x %02x\n", tag, optag, tmp);
> >+			if (tmp != 0) {
> >+				/* All odd-numbered tags are MATCH_OR_SKIP. */
> >+				if (op & ASN1_OP_MATCH__SKIP) {
> >+					pc += asn1_op_lengths[op];
> >+					dp--;
> >+					goto next_op;
> >+				}
> >+				goto tag_mismatch;
> >+			}
> >+		}
> >+		flags |= FLAG_MATCHED;
> >+
> >+		len = data[dp++];
> >+		if (len > 0x7f) {
> >+			if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
> >+				/* Indefinite length */
> >+				if (unlikely(!(tag & ASN1_CONS_BIT)))
> >+					goto indefinite_len_primitive;
> >+				flags |= FLAG_INDEFINITE_LENGTH;
> >+				if (unlikely(2 > datalen - dp))
> >+					goto data_overrun_error;
> >+			} else {
> >+				int n = len - 0x80;
> >+				if (unlikely(n > 2))
> >+					goto length_too_long;
> >+				if (unlikely(n > datalen - dp))
> >+					goto data_overrun_error;
> >+				hdr += n;
> >+				for (len = 0; n > 0; n--) {
> >+					len <<= 8;
> >+					len |= data[dp++];
> >+				}
> >+				if (unlikely(len > datalen - dp))
> >+					goto data_overrun_error;
> >+			}
> >+		} else {
> >+			if (unlikely(len > datalen - dp))
> >+				goto data_overrun_error;
> >+		}
> >+
> >+		if (flags & FLAG_CONS) {
> >+			/* For expected compound forms, we stack the positions
> >+			 * of the start and end of the data.
> >+			 */
> >+			if (unlikely(csp >= NR_CONS_STACK))
> >+				goto cons_stack_overflow;
> >+			cons_dp_stack[csp] = dp;
> >+			cons_hdrlen_stack[csp] = hdr;
> >+			if (!(flags & FLAG_INDEFINITE_LENGTH)) {
> >+				cons_datalen_stack[csp] = datalen;
> >+				datalen = dp + len;
> >+			} else {
> >+				cons_datalen_stack[csp] = 0;
> >+			}
> >+			csp++;
> >+		}
> >+
> >+		pr_debug("- TAG: %02x %zu%s\n",
> >+			 tag, len, flags & FLAG_CONS ? " CONS" : "");
> >+		tdp = dp;
> >+	}
> >+
> >+	/* Decide how to handle the operation */
> >+	switch (op) {
> >+	case ASN1_OP_MATCH:
> >+	case ASN1_OP_MATCH_OR_SKIP:
> >+	case ASN1_OP_MATCH_ACT:
> >+	case ASN1_OP_MATCH_ACT_OR_SKIP:
> >+	case ASN1_OP_MATCH_ANY:
> >+	case ASN1_OP_MATCH_ANY_OR_SKIP:
> >+	case ASN1_OP_MATCH_ANY_ACT:
> >+	case ASN1_OP_MATCH_ANY_ACT_OR_SKIP:
> >+	case ASN1_OP_COND_MATCH_OR_SKIP:
> >+	case ASN1_OP_COND_MATCH_ACT_OR_SKIP:
> >+	case ASN1_OP_COND_MATCH_ANY:
> >+	case ASN1_OP_COND_MATCH_ANY_OR_SKIP:
> >+	case ASN1_OP_COND_MATCH_ANY_ACT:
> >+	case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP:
> >+
> >+		if (!(flags & FLAG_CONS)) {
> >+			if (flags & FLAG_INDEFINITE_LENGTH) {
> >+				size_t tmp = dp;
> >+
> >+				ret = asn1_find_indefinite_length(
> >+					data, datalen, &tmp, &len, &errmsg);
> >+				if (ret < 0)
> >+					goto error;
> >+			}
> >+			pr_debug("- LEAF: %zu\n", len);
> >+		}
> >+
> >+		if (op & ASN1_OP_MATCH__ACT) {
> >+			unsigned char act;
> >+
> >+			if (op & ASN1_OP_MATCH__ANY)
> >+				act = machine[pc + 1];
> >+			else
> >+				act = machine[pc + 2];
> >+			ret = actions[act](context, hdr, tag, data + dp, len);
> >+			if (ret < 0)
> >+				return ret;
> >+		}
> >+
> >+		if (!(flags & FLAG_CONS))
> >+			dp += len;
> >+		pc += asn1_op_lengths[op];
> >+		goto next_op;
> >+
> >+	case ASN1_OP_MATCH_JUMP:
> >+	case ASN1_OP_MATCH_JUMP_OR_SKIP:
> >+	case ASN1_OP_COND_MATCH_JUMP_OR_SKIP:
> >+		pr_debug("- MATCH_JUMP\n");
> >+		if (unlikely(jsp == NR_JUMP_STACK))
> >+			goto jump_stack_overflow;
> >+		jump_stack[jsp++] = pc + asn1_op_lengths[op];
> >+		pc = machine[pc + 2];
> >+		goto next_op;
> >+
> >+	case ASN1_OP_COND_FAIL:
> >+		if (unlikely(!(flags & FLAG_MATCHED)))
> >+			goto tag_mismatch;
> >+		pc += asn1_op_lengths[op];
> >+		goto next_op;
> >+
> >+	case ASN1_OP_COMPLETE:
> >+		if (unlikely(jsp != 0 || csp != 0)) {
> >+			pr_err("ASN.1 decoder error: Stacks not empty at completion (%u, %u)\n",
> >+			       jsp, csp);
> >+			return -EBADMSG;
> >+		}
> >+		return 0;
> >+
> >+	case ASN1_OP_END_SET:
> >+	case ASN1_OP_END_SET_ACT:
> >+		if (unlikely(!(flags & FLAG_MATCHED)))
> >+			goto tag_mismatch;
> >+		/* fall through */
> >+
> >+	case ASN1_OP_END_SEQ:
> >+	case ASN1_OP_END_SET_OF:
> >+	case ASN1_OP_END_SEQ_OF:
> >+	case ASN1_OP_END_SEQ_ACT:
> >+	case ASN1_OP_END_SET_OF_ACT:
> >+	case ASN1_OP_END_SEQ_OF_ACT:
> >+		if (unlikely(csp <= 0))
> >+			goto cons_stack_underflow;
> >+		csp--;
> >+		tdp = cons_dp_stack[csp];
> >+		hdr = cons_hdrlen_stack[csp];
> >+		len = datalen;
> >+		datalen = cons_datalen_stack[csp];
> >+		pr_debug("- end cons t=%zu dp=%zu l=%zu/%zu\n",
> >+			 tdp, dp, len, datalen);
> >+		if (datalen == 0) {
> >+			/* Indefinite length - check for the EOC. */
> >+			datalen = len;
> >+			if (unlikely(datalen - dp < 2))
> >+				goto data_overrun_error;
> >+			if (data[dp++] != 0) {
> >+				if (op & ASN1_OP_END__OF) {
> >+					dp--;
> >+					csp++;
> >+					pc = machine[pc + 1];
> >+					pr_debug("- continue\n");
> >+					goto next_op;
> >+				}
> >+				goto missing_eoc;
> >+			}
> >+			if (data[dp++] != 0)
> >+				goto invalid_eoc;
> >+			len = dp - tdp - 2;
> >+		} else {
> >+			if (dp < len && (op & ASN1_OP_END__OF)) {
> >+				datalen = len;
> >+				csp++;
> >+				pc = machine[pc + 1];
> >+				pr_debug("- continue\n");
> >+				goto next_op;
> >+			}
> >+			if (dp != len)
> >+				goto cons_length_error;
> >+			len -= tdp;
> >+			pr_debug("- cons len l=%zu d=%zu\n", len, dp - tdp);
> >+		}
> >+
> >+		if (op & ASN1_OP_END__ACT) {
> >+			unsigned char act;
> >+			if (op & ASN1_OP_END__OF)
> >+				act = machine[pc + 2];
> >+			else
> >+				act = machine[pc + 1];
> >+			ret = actions[act](context, hdr, 0, data + tdp, len);
> >+			if (ret < 0)
> >+				return ret;
> >+		}
> >+		pc += asn1_op_lengths[op];
> >+		goto next_op;
> >+
> >+	case ASN1_OP_MAYBE_ACT:
> >+		if (!(flags & FLAG_LAST_MATCHED)) {
> >+			pc += asn1_op_lengths[op];
> >+			goto next_op;
> >+		}
> >+		/* fall through */
> >+
> >+	case ASN1_OP_ACT:
> >+		ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len);
> >+		if (ret < 0)
> >+			return ret;
> >+		pc += asn1_op_lengths[op];
> >+		goto next_op;
> >+
> >+	case ASN1_OP_RETURN:
> >+		if (unlikely(jsp <= 0))
> >+			goto jump_stack_underflow;
> >+		pc = jump_stack[--jsp];
> >+		flags |= FLAG_MATCHED | FLAG_LAST_MATCHED;
> >+		goto next_op;
> >+
> >+	default:
> >+		break;
> >+	}
> >+
> >+	/* Shouldn't reach here */
> >+	pr_err("ASN.1 decoder error: Found reserved opcode (%u) pc=%zu\n",
> >+	       op, pc);
> >+	return -EBADMSG;
> >+
> >+data_overrun_error:
> >+	errmsg = "Data overrun error";
> >+	goto error;
> >+machine_overrun_error:
> >+	errmsg = "Machine overrun error";
> >+	goto error;
> >+jump_stack_underflow:
> >+	errmsg = "Jump stack underflow";
> >+	goto error;
> >+jump_stack_overflow:
> >+	errmsg = "Jump stack overflow";
> >+	goto error;
> >+cons_stack_underflow:
> >+	errmsg = "Cons stack underflow";
> >+	goto error;
> >+cons_stack_overflow:
> >+	errmsg = "Cons stack overflow";
> >+	goto error;
> >+cons_length_error:
> >+	errmsg = "Cons length error";
> >+	goto error;
> >+missing_eoc:
> >+	errmsg = "Missing EOC in indefinite len cons";
> >+	goto error;
> >+invalid_eoc:
> >+	errmsg = "Invalid length EOC";
> >+	goto error;
> >+length_too_long:
> >+	errmsg = "Unsupported length";
> >+	goto error;
> >+indefinite_len_primitive:
> >+	errmsg = "Indefinite len primitive not permitted";
> >+	goto error;
> >+tag_mismatch:
> >+	errmsg = "Unexpected tag";
> >+	goto error;
> >+long_tag_not_supported:
> >+	errmsg = "Long tag not supported";
> >+error:
> >+	pr_debug("\nASN1: %s [m=%zu d=%zu ot=%02x t=%02x l=%zu]\n",
> >+		 errmsg, pc, dp, optag, tag, len);
> >+	return -EBADMSG;
> >+}
> >+EXPORT_SYMBOL_GPL(asn1_ber_decoder);
> >+
> >+MODULE_LICENSE("GPL");
> >
> 

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-15  9:25         ` AKASHI Takahiro
@ 2019-10-17 15:23           ` Tom Rini
  2019-10-18  8:36             ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Tom Rini @ 2019-10-17 15:23 UTC (permalink / raw)
  To: u-boot

On Tue, Oct 15, 2019 at 06:25:19PM +0900, AKASHI Takahiro wrote:
> On Tue, Oct 15, 2019 at 07:33:18AM +0200, Heinrich Schuchardt wrote:
> > On 10/15/19 5:18 AM, AKASHI Takahiro wrote:
> > >On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
> > >>On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
> > >>>I hope this patch set will be reviewed promptly as I'm aiming to
> > >>>push my "UEFI secure boot" patch for v2020.01.
> > >>>
> > >>
> > >>How can I make all of these new files being built to check for build
> > >>warnings?
> > >
> > >As always in my case of UEFI secure boot, they have gone through build and
> > >run/tests as part of UEFI secure boot. This is also true for RSA
> > >extension as UEFI secure boot is the only user of those features.
> > 
> > Did you run them through Travis?
> 
> As far as this patch set is concerned, no configuration enables
> any of kconfig options listed below and running Travis doesn't make sense.

That's a problem that needs to be fixed.  I am not OK with the idea of
adding a new feature that will not be put through our CI and so when
things break it (which will happen, inadvertently) it won't be caught
until much later.  Figuring out how to extend our CI to test this is a
must.  Thanks!

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20191017/6a6458df/attachment.sig>

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-17 15:23           ` Tom Rini
@ 2019-10-18  8:36             ` AKASHI Takahiro
  2019-10-18 12:35               ` Tom Rini
  0 siblings, 1 reply; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-18  8:36 UTC (permalink / raw)
  To: u-boot

Hi Tom,

On Thu, Oct 17, 2019 at 11:23:21AM -0400, Tom Rini wrote:
> On Tue, Oct 15, 2019 at 06:25:19PM +0900, AKASHI Takahiro wrote:
> > On Tue, Oct 15, 2019 at 07:33:18AM +0200, Heinrich Schuchardt wrote:
> > > On 10/15/19 5:18 AM, AKASHI Takahiro wrote:
> > > >On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
> > > >>On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
> > > >>>I hope this patch set will be reviewed promptly as I'm aiming to
> > > >>>push my "UEFI secure boot" patch for v2020.01.
> > > >>>
> > > >>
> > > >>How can I make all of these new files being built to check for build
> > > >>warnings?
> > > >
> > > >As always in my case of UEFI secure boot, they have gone through build and
> > > >run/tests as part of UEFI secure boot. This is also true for RSA
> > > >extension as UEFI secure boot is the only user of those features.
> > > 
> > > Did you run them through Travis?
> > 
> > As far as this patch set is concerned, no configuration enables
> > any of kconfig options listed below and running Travis doesn't make sense.
> 
> That's a problem that needs to be fixed.  I am not OK with the idea of
> adding a new feature that will not be put through our CI and so when
> things break it (which will happen, inadvertently) it won't be caught
> until much later.  Figuring out how to extend our CI to test this is a
> must.  Thanks!

I added a simple *unit* test under "test/lib."
As I said before, however, no existing configuration enables either
   CONFIG_X509_CERTIFICATE_PARSER, nor
   CONFIG_PKCS7_MESSAGE_PARSER

and the related code won't be built or exercised in any way.
So I made a small trick to Kconfig:

=== 8< ===
config UT_LIB
        bool "Unit tests for library functions"
        depends on UNIT_TEST
        default y
        help
          Enables the 'ut lib' command which tests library functions like
          memcat(), memcyp(), memmove().

if UT_LIB

config UT_LIB_ASN1
        bool "Unit test for asn1 compiler and decoder function"
        default y
        imply ASYMMETRIC_KEY_TYPE
        imply ASYMMETRIC_PUBLIC_KEY_SUBTYPE
        imply X509_CERTIFICATE_PARSER
        imply PKCS7_MESSAGE_PARSER
        imply RSA_PUBLIC_KEY_PARSER
        help
          Enables a test which exercises asn1 compiler and decoder function
          via various parsers.

endif
=== >8 ===

So as long as UT_LIB is enabled and run by one of Travis CI test cases,
a new test for ASN1 will also be exercised.
(I don't know which one will invoke "ut" command.)

Do you agree to this approach?

Thanks,
-Takahiro Akashi

> -- 
> Tom

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-18  8:36             ` AKASHI Takahiro
@ 2019-10-18 12:35               ` Tom Rini
  2019-10-23  6:43                 ` AKASHI Takahiro
  0 siblings, 1 reply; 39+ messages in thread
From: Tom Rini @ 2019-10-18 12:35 UTC (permalink / raw)
  To: u-boot

On Fri, Oct 18, 2019 at 05:36:28PM +0900, AKASHI Takahiro wrote:
> Hi Tom,
> 
> On Thu, Oct 17, 2019 at 11:23:21AM -0400, Tom Rini wrote:
> > On Tue, Oct 15, 2019 at 06:25:19PM +0900, AKASHI Takahiro wrote:
> > > On Tue, Oct 15, 2019 at 07:33:18AM +0200, Heinrich Schuchardt wrote:
> > > > On 10/15/19 5:18 AM, AKASHI Takahiro wrote:
> > > > >On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
> > > > >>On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
> > > > >>>I hope this patch set will be reviewed promptly as I'm aiming to
> > > > >>>push my "UEFI secure boot" patch for v2020.01.
> > > > >>>
> > > > >>
> > > > >>How can I make all of these new files being built to check for build
> > > > >>warnings?
> > > > >
> > > > >As always in my case of UEFI secure boot, they have gone through build and
> > > > >run/tests as part of UEFI secure boot. This is also true for RSA
> > > > >extension as UEFI secure boot is the only user of those features.
> > > > 
> > > > Did you run them through Travis?
> > > 
> > > As far as this patch set is concerned, no configuration enables
> > > any of kconfig options listed below and running Travis doesn't make sense.
> > 
> > That's a problem that needs to be fixed.  I am not OK with the idea of
> > adding a new feature that will not be put through our CI and so when
> > things break it (which will happen, inadvertently) it won't be caught
> > until much later.  Figuring out how to extend our CI to test this is a
> > must.  Thanks!
> 
> I added a simple *unit* test under "test/lib."
> As I said before, however, no existing configuration enables either
>    CONFIG_X509_CERTIFICATE_PARSER, nor
>    CONFIG_PKCS7_MESSAGE_PARSER
> 
> and the related code won't be built or exercised in any way.
> So I made a small trick to Kconfig:
> 
> === 8< ===
> config UT_LIB
>         bool "Unit tests for library functions"
>         depends on UNIT_TEST
>         default y
>         help
>           Enables the 'ut lib' command which tests library functions like
>           memcat(), memcyp(), memmove().
> 
> if UT_LIB
> 
> config UT_LIB_ASN1
>         bool "Unit test for asn1 compiler and decoder function"
>         default y
>         imply ASYMMETRIC_KEY_TYPE
>         imply ASYMMETRIC_PUBLIC_KEY_SUBTYPE
>         imply X509_CERTIFICATE_PARSER
>         imply PKCS7_MESSAGE_PARSER
>         imply RSA_PUBLIC_KEY_PARSER
>         help
>           Enables a test which exercises asn1 compiler and decoder function
>           via various parsers.
> 
> endif
> === >8 ===
> 
> So as long as UT_LIB is enabled and run by one of Travis CI test cases,
> a new test for ASN1 will also be exercised.
> (I don't know which one will invoke "ut" command.)
> 
> Do you agree to this approach?

I think you're going to need to get a bit more familiar with some
aspects of testing and building.  Today, CONFIG_UNIT_TEST is enabled for
sandbox.  And we want as much as possible enabled on sandbox as that's
also where coverity scan is performed.  So that will get us part of the
way forward here longer term.  I think however you're going to also need
to enable some tests on the QEMU platforms so that we can have all of
this new secure boot code put through CI there.  Thanks!

-- 
Tom
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20191018/69c91ac0/attachment.sig>

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

* [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux
  2019-10-18 12:35               ` Tom Rini
@ 2019-10-23  6:43                 ` AKASHI Takahiro
  0 siblings, 0 replies; 39+ messages in thread
From: AKASHI Takahiro @ 2019-10-23  6:43 UTC (permalink / raw)
  To: u-boot

Tom,

On Fri, Oct 18, 2019 at 08:35:23AM -0400, Tom Rini wrote:
> On Fri, Oct 18, 2019 at 05:36:28PM +0900, AKASHI Takahiro wrote:
> > Hi Tom,
> > 
> > On Thu, Oct 17, 2019 at 11:23:21AM -0400, Tom Rini wrote:
> > > On Tue, Oct 15, 2019 at 06:25:19PM +0900, AKASHI Takahiro wrote:
> > > > On Tue, Oct 15, 2019 at 07:33:18AM +0200, Heinrich Schuchardt wrote:
> > > > > On 10/15/19 5:18 AM, AKASHI Takahiro wrote:
> > > > > >On Sat, Oct 12, 2019 at 03:02:09PM +0200, Heinrich Schuchardt wrote:
> > > > > >>On 10/11/19 9:55 AM, AKASHI Takahiro wrote:
> > > > > >>>I hope this patch set will be reviewed promptly as I'm aiming to
> > > > > >>>push my "UEFI secure boot" patch for v2020.01.
> > > > > >>>
> > > > > >>
> > > > > >>How can I make all of these new files being built to check for build
> > > > > >>warnings?
> > > > > >
> > > > > >As always in my case of UEFI secure boot, they have gone through build and
> > > > > >run/tests as part of UEFI secure boot. This is also true for RSA
> > > > > >extension as UEFI secure boot is the only user of those features.
> > > > > 
> > > > > Did you run them through Travis?
> > > > 
> > > > As far as this patch set is concerned, no configuration enables
> > > > any of kconfig options listed below and running Travis doesn't make sense.
> > > 
> > > That's a problem that needs to be fixed.  I am not OK with the idea of
> > > adding a new feature that will not be put through our CI and so when
> > > things break it (which will happen, inadvertently) it won't be caught
> > > until much later.  Figuring out how to extend our CI to test this is a
> > > must.  Thanks!
> > 
> > I added a simple *unit* test under "test/lib."
> > As I said before, however, no existing configuration enables either
> >    CONFIG_X509_CERTIFICATE_PARSER, nor
> >    CONFIG_PKCS7_MESSAGE_PARSER
> > 
> > and the related code won't be built or exercised in any way.
> > So I made a small trick to Kconfig:
> > 
> > === 8< ===
> > config UT_LIB
> >         bool "Unit tests for library functions"
> >         depends on UNIT_TEST
> >         default y
> >         help
> >           Enables the 'ut lib' command which tests library functions like
> >           memcat(), memcyp(), memmove().
> > 
> > if UT_LIB
> > 
> > config UT_LIB_ASN1
> >         bool "Unit test for asn1 compiler and decoder function"
> >         default y
> >         imply ASYMMETRIC_KEY_TYPE
> >         imply ASYMMETRIC_PUBLIC_KEY_SUBTYPE
> >         imply X509_CERTIFICATE_PARSER
> >         imply PKCS7_MESSAGE_PARSER
> >         imply RSA_PUBLIC_KEY_PARSER
> >         help
> >           Enables a test which exercises asn1 compiler and decoder function
> >           via various parsers.
> > 
> > endif
> > === >8 ===
> > 
> > So as long as UT_LIB is enabled and run by one of Travis CI test cases,
> > a new test for ASN1 will also be exercised.
> > (I don't know which one will invoke "ut" command.)
> > 
> > Do you agree to this approach?

I'd like to confirm exactly what you suggested here:

> I think you're going to need to get a bit more familiar with some
> aspects of testing and building.  Today, CONFIG_UNIT_TEST is enabled for
> sandbox.  And we want as much as possible enabled on sandbox as that's
> also where coverity scan is performed.

Is this ("as much as possible") true? I wonder how it should be achieved.

As far as my RSA test approach above is concerned,
1) If UT_TEST is enabled, UT_LIB and then UT_LIB_ASN1 are also
   enabled *by default* and expected to be run automatically through
   Travis's sandbox build with test/py even if, say, X509_CERTIRFFICATE_PARSER
   is *not* enabled in any of sandbox_*_defconfig.
   Is this approach is OK for you?

Or,
2) Should we add ASYMMETRIC_*/X509_CERTIFICATE_PARSER/PKCS7_MESSAGE_PARSER
   to one (or all) of sandbox_*_defconfig (even though there is no explicit
   user of these features before my secure boot patch will be merged)?
Or,
3) Should we create a new sandbox_*_defconfig for any further tests?
Or,
4) Would we better set ASYMMETRIC_*/X509_CERTIFICATE_PARSER/PKCS7_MESSAGE_PARSER
   enabled by default on Sandbox? I mean,
=== 8< ===
menuconfig ASYMMETRIC_KEY_TYPE
	bool "Asymmetric (public-key cryptographic) key Support"
	default y if SANDBOX	<== added

if ASYMMETRIC_KEY_TYPE
...
config X509_CERTIFICATE_PARSER
        bool "RSA public key parser"
        depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
	default y if SANDBOX	<== added
...
=== >8 ===

> So that will get us part of the
> way forward here longer term.  I think however you're going to also need
> to enable some tests on the QEMU platforms so that we can have all of
> this new secure boot code put through CI there.  Thanks!

Are you talking about this patch set(asn1 parsers) or is this your
general comment on my overall "UEFI secure boot" patch?

Please note that my secure boot patch is architecture agnostic and  will
perfectly work on Sandbox and all the related py tests will also be done
on Sandbox.

Thanks,
-Takahiro Akashi


> -- 
> Tom

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

end of thread, other threads:[~2019-10-23  6:43 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-11  7:41 [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
2019-10-11  7:41 ` [U-Boot] [PATCH v1 01/11] linux_compat: add kmemdup() AKASHI Takahiro
2019-10-12 11:22   ` Heinrich Schuchardt
2019-10-17  3:04     ` AKASHI Takahiro
2019-10-11  7:41 ` [U-Boot] [PATCH v1 02/11] include: time.h: define time64_t AKASHI Takahiro
2019-10-12 11:40   ` Heinrich Schuchardt
2019-10-17  5:39     ` AKASHI Takahiro
2019-10-17  5:51       ` Heinrich Schuchardt
2019-10-11  7:41 ` [U-Boot] [PATCH v1 03/11] include: kernel.h: include printk.h AKASHI Takahiro
2019-10-12 11:47   ` Heinrich Schuchardt
2019-10-17  5:58     ` AKASHI Takahiro
2019-10-17  6:17       ` AKASHI Takahiro
2019-10-11  7:41 ` [U-Boot] [PATCH v1 04/11] cmd: add asn1_compiler AKASHI Takahiro
2019-10-12 12:22   ` Heinrich Schuchardt
2019-10-17  6:25     ` AKASHI Takahiro
2019-10-11  7:41 ` [U-Boot] [PATCH v1 05/11] Makefile: add build script for asn1 parsers AKASHI Takahiro
2019-10-12 12:36   ` Heinrich Schuchardt
2019-10-17  6:40     ` AKASHI Takahiro
2019-10-11  7:41 ` [U-Boot] [PATCH v1 06/11] lib: add asn1 decoder AKASHI Takahiro
2019-10-12 12:29   ` Heinrich Schuchardt
2019-10-17  7:02     ` AKASHI Takahiro
2019-10-11  7:41 ` [U-Boot] [PATCH v1 07/11] lib: add oid registry utility AKASHI Takahiro
2019-10-12 12:58   ` Heinrich Schuchardt
2019-10-11  7:41 ` [U-Boot] [PATCH v1 08/11] lib: crypto: add public key utility AKASHI Takahiro
2019-10-11  7:41 ` [U-Boot] [PATCH v1 09/11] lib: crypto: add x509 parser AKASHI Takahiro
2019-10-11  7:41 ` [U-Boot] [PATCH v1 10/11] lib: crypto: add pkcs7 message parser AKASHI Takahiro
2019-10-11  7:42 ` [U-Boot] [PATCH v1 11/11] lib: crypto: add rsa public key parser AKASHI Takahiro
2019-10-12 13:11   ` Heinrich Schuchardt
2019-10-11  7:55 ` [U-Boot] [PATCH v1 00/11] import x509/pkcs7 parsers from linux AKASHI Takahiro
2019-10-12 13:02   ` Heinrich Schuchardt
2019-10-15  3:18     ` AKASHI Takahiro
2019-10-15  5:33       ` Heinrich Schuchardt
2019-10-15  8:56         ` AKASHI Takahiro
2019-10-15 11:10           ` Heinrich Schuchardt
2019-10-15  9:25         ` AKASHI Takahiro
2019-10-17 15:23           ` Tom Rini
2019-10-18  8:36             ` AKASHI Takahiro
2019-10-18 12:35               ` Tom Rini
2019-10-23  6:43                 ` AKASHI Takahiro

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.