Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4520c6a4 authored by David Howells's avatar David Howells Committed by Rusty Russell
Browse files

X.509: Add simple ASN.1 grammar compiler



Add a simple ASN.1 grammar compiler.  This produces a bytecode output that can
be fed to a decoder to inform the decoder how to interpret the ASN.1 stream it
is trying to parse.

Action functions can be specified in the grammar by interpolating:

	({ foo })

after a type, for example:

	SubjectPublicKeyInfo ::= SEQUENCE {
		algorithm		AlgorithmIdentifier,
		subjectPublicKey	BIT STRING ({ do_key_data })
		}

The decoder is expected to call these after matching this type and parsing the
contents if it is a constructed type.

The grammar compiler does not currently support the SET type (though it does
support SET OF) as I can't see a good way of tracking which members have been
encountered yet without using up extra stack space.

Currently, the grammar compiler will fail if more than 256 bytes of bytecode
would be produced or more than 256 actions have been specified as it uses
8-bit jump values and action indices to keep space usage down.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 4f73175d
Loading
Loading
Loading
Loading

include/linux/asn1.h

0 → 100644
+67 −0
Original line number Diff line number Diff line
/* ASN.1 BER/DER/CER encoding definitions
 *
 * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */

#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 */
};

#endif /* _LINUX_ASN1_H */
+87 −0
Original line number Diff line number Diff line
/* ASN.1 BER/DER/CER parsing state machine internal definitions
 *
 * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public Licence
 * as published by the Free Software Foundation; either version
 * 2 of the Licence, or (at your option) any later version.
 */

#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_ACT		= 0x0a,
	/* 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_ACT	= 0x1a,

	/* Everything before here will want a tag from the data */
#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT

	/* These are here to help fill up space */
	ASN1_OP_COND_FAIL		= 0x1b,
	ASN1_OP_COMPLETE		= 0x1c,
	ASN1_OP_ACT			= 0x1d,
	ASN1_OP_RETURN			= 0x1e,

	/* 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__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 */
+8 −0
Original line number Diff line number Diff line
@@ -1612,4 +1612,12 @@ config PADATA
	depends on SMP
	bool

config ASN1
	tristate
	help
	  Build a simple ASN.1 grammar compiler that produces a bytecode output
	  that can be interpreted by the ASN.1 stream decoder and used to
	  inform it as to what tags are to be expected in a stream and what
	  functions to call on what tags.

source "kernel/Kconfig.locks"
+1 −0
Original line number Diff line number Diff line
@@ -10,3 +10,4 @@ ihex2fw
recordmcount
docproc
sortextable
asn1_compiler
+2 −0
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@ hostprogs-$(CONFIG_VT) += conmakehash
hostprogs-$(CONFIG_IKCONFIG)     += bin2c
hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount
hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable
hostprogs-$(CONFIG_ASN1)	 += asn1_compiler

HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include
HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include

always		:= $(hostprogs-y) $(hostprogs-m)

Loading